737f7e8
A helper program is needed to clean up the system configuration
737f7e8
early during RPM package installation, so that other scriptlets
737f7e8
can run successfully.
737f7e8
737f7e8
diff --git a/elf/Makefile b/elf/Makefile
737f7e8
index 2a432d8beebcd207..368dcae477fff2ae 100644
737f7e8
--- a/elf/Makefile
737f7e8
+++ b/elf/Makefile
737f7e8
@@ -117,6 +117,14 @@ others-extras   = $(ldconfig-modules)
737f7e8
 endif
737f7e8
 endif
737f7e8
 
737f7e8
+# This needs to be statically linked because it is executed at a time
737f7e8
+# when there might be incompatible shared objects on disk, and the
737f7e8
+# purpose of this program is to remove them (among other things).
737f7e8
+others-static += glibc_post_upgrade
737f7e8
+others += glibc_post_upgrade
737f7e8
+glibc_post_upgrade-modules := static-stubs
737f7e8
+CFLAGS-glibc_post_upgrade.c += -DGCONV_MODULES_DIR='"$(gconvdir)"'
737f7e8
+
737f7e8
 # To find xmalloc.c and xstrdup.c
737f7e8
 vpath %.c ../locale/programs
737f7e8
 
737f7e8
@@ -559,6 +567,8 @@ $(objpfx)sln: $(sln-modules:%=$(objpfx)%.o)
737f7e8
 
737f7e8
 $(objpfx)ldconfig: $(ldconfig-modules:%=$(objpfx)%.o)
737f7e8
 
737f7e8
+$(objpfx)glibc_post_upgrade: $(glibc_post_upgrade-modules:%=$(objpfx)%.o)
737f7e8
+
737f7e8
 SYSCONF-FLAGS := -D'SYSCONFDIR="$(sysconfdir)"'
737f7e8
 CFLAGS-ldconfig.c += $(SYSCONF-FLAGS) -D'LIBDIR="$(libdir)"' \
737f7e8
 		    -D'SLIBDIR="$(slibdir)"'
737f7e8
diff --git a/elf/glibc_post_upgrade.c b/elf/glibc_post_upgrade.c
737f7e8
new file mode 100644
a071c68
index 0000000000000000..19b59f70e2308032
737f7e8
--- /dev/null
737f7e8
+++ b/elf/glibc_post_upgrade.c
a071c68
@@ -0,0 +1,229 @@
737f7e8
+#include <sys/types.h>
737f7e8
+#include <sys/wait.h>
737f7e8
+#include <stdio.h>
737f7e8
+#include <errno.h>
737f7e8
+#include <unistd.h>
737f7e8
+#include <sys/time.h>
737f7e8
+#include <dirent.h>
737f7e8
+#include <stddef.h>
737f7e8
+#include <fcntl.h>
737f7e8
+#include <string.h>
737f7e8
+
737f7e8
+#define LD_SO_CONF "/etc/ld.so.conf"
737f7e8
+#define ICONVCONFIG "/usr/sbin/iconvconfig"
737f7e8
+
737f7e8
+#define verbose_exec(failcode, path...) \
737f7e8
+  do                                                    \
737f7e8
+    {                                                   \
737f7e8
+      char *const arr[] = { path, NULL };               \
737f7e8
+      vexec (failcode, arr);                            \
737f7e8
+    } while (0)
737f7e8
+
737f7e8
+__attribute__((noinline)) static void vexec (int failcode, char *const path[]);
737f7e8
+__attribute__((noinline)) static void says (const char *str);
737f7e8
+__attribute__((noinline)) static void sayn (long num);
737f7e8
+__attribute__((noinline)) static void message (char *const path[]);
737f7e8
+
737f7e8
+int
737f7e8
+main (void)
737f7e8
+{
737f7e8
+  char initpath[256];
737f7e8
+
737f7e8
+  char buffer[4096];
737f7e8
+  struct pref {
737f7e8
+    const char *p;
737f7e8
+    int len;
737f7e8
+  } prefix[] = { { "libc-", 5 }, { "libm-", 5 },
737f7e8
+                 { "librt-", 6 }, { "libpthread-", 11 },
737f7e8
+                 { "librtkaio-", 10 }, { "libthread_db-", 13 } };
737f7e8
+  int i, j, fd;
737f7e8
+  off_t base;
737f7e8
+  ssize_t ret;
737f7e8
+
737f7e8
+  /* In order to support in-place upgrades, we must immediately remove
737f7e8
+     obsolete platform directories after installing a new glibc
737f7e8
+     version.  RPM only deletes files removed by updates near the end
737f7e8
+     of the transaction.  If we did not remove the obsolete platform
737f7e8
+     directories here, they would be preferred by the dynamic linker
737f7e8
+     during the execution of subsequent RPM scriptlets, likely
737f7e8
+     resulting in process startup failures.  */
737f7e8
+  const char *remove_dirs[] =
737f7e8
+    {
737f7e8
+#if defined (__i386__)
737f7e8
+      "/lib/i686",
737f7e8
+      "/lib/i686/nosegneg",
737f7e8
+#elif defined (__powerpc64__) && _CALL_ELF != 2
737f7e8
+      "/lib64/power6",
737f7e8
+#endif
737f7e8
+    };
737f7e8
+  for (j = 0; j < sizeof (remove_dirs) / sizeof (remove_dirs[0]); ++j)
737f7e8
+    {
737f7e8
+      size_t rmlen = strlen (remove_dirs[j]);
737f7e8
+      fd = open (remove_dirs[j], O_RDONLY);
737f7e8
+      if (fd >= 0
737f7e8
+          && (ret = getdirentries (fd, buffer, sizeof (buffer), &base))
737f7e8
+             >= (ssize_t) offsetof (struct dirent, d_name))
737f7e8
+        {
737f7e8
+          for (base = 0; base + offsetof (struct dirent, d_name) < ret; )
737f7e8
+            {
737f7e8
+              struct dirent *d = (struct dirent *) (buffer + base);
737f7e8
+
737f7e8
+              for (i = 0; i < sizeof (prefix) / sizeof (prefix[0]); i++)
737f7e8
+                if (! strncmp (d->d_name, prefix[i].p, prefix[i].len))
737f7e8
+                  {
737f7e8
+                    char *p = d->d_name + prefix[i].len;
737f7e8
+
737f7e8
+                    while (*p == '.' || (*p >= '0' && *p <= '9')) p++;
737f7e8
+                    if (p[0] == 's' && p[1] == 'o' && p[2] == '\0'
737f7e8
+                        && p + 3 - d->d_name
737f7e8
+                           < sizeof (initpath) - rmlen - 1)
737f7e8
+                      {
737f7e8
+                        memcpy (initpath, remove_dirs[j], rmlen);
737f7e8
+                        initpath[rmlen] = '/';
737f7e8
+                        strcpy (initpath + rmlen + 1, d->d_name);
737f7e8
+                        unlink (initpath);
737f7e8
+                        break;
737f7e8
+                      }
737f7e8
+                  }
737f7e8
+              base += d->d_reclen;
737f7e8
+            }
737f7e8
+          close (fd);
737f7e8
+        }
737f7e8
+    }
737f7e8
+
737f7e8
+  int ldsocfd = open (LD_SO_CONF, O_RDONLY);
737f7e8
+  struct stat ldsocst;
737f7e8
+  if (ldsocfd >= 0 && fstat (ldsocfd, &ldsocst) >= 0)
737f7e8
+    {
737f7e8
+      char p[ldsocst.st_size + 1];
737f7e8
+      if (read (ldsocfd, p, ldsocst.st_size) == ldsocst.st_size)
737f7e8
+        {
737f7e8
+          p[ldsocst.st_size] = '\0';
737f7e8
+          if (strstr (p, "include ld.so.conf.d/*.conf") == NULL)
737f7e8
+            {
737f7e8
+              close (ldsocfd);
737f7e8
+              ldsocfd = open (LD_SO_CONF, O_WRONLY | O_TRUNC);
737f7e8
+              if (ldsocfd >= 0)
737f7e8
+                {
737f7e8
+                  size_t slen = strlen ("include ld.so.conf.d/*.conf\n");
737f7e8
+                  if (write (ldsocfd, "include ld.so.conf.d/*.conf\n", slen)
737f7e8
+                      != slen
737f7e8
+                      || write (ldsocfd, p, ldsocst.st_size) != ldsocst.st_size)
737f7e8
+                    _exit (109);
737f7e8
+                }
737f7e8
+            }
737f7e8
+        }
737f7e8
+      if (ldsocfd >= 0)
737f7e8
+        close (ldsocfd);
737f7e8
+    }
737f7e8
+
737f7e8
+  /* If installing bi-arch glibc, rpm sometimes doesn't unpack all files
737f7e8
+     before running one of the lib's %post scriptlet.  /sbin/ldconfig will
737f7e8
+     then be run by the other arch's %post.  */
737f7e8
+  if (! access ("/sbin/ldconfig", X_OK))
737f7e8
+    verbose_exec (110,
737f7e8
+                  (char *) "/sbin/ldconfig",
737f7e8
+                  (char *) "/sbin/ldconfig");
737f7e8
+
737f7e8
+  if (! utimes (GCONV_MODULES_DIR "/gconv-modules.cache", NULL))
737f7e8
+    {
737f7e8
+      const char *iconv_cache = GCONV_MODULES_DIR "/gconv-modules.cache";
737f7e8
+      const char *iconv_dir = GCONV_MODULES_DIR;
737f7e8
+      verbose_exec (113,
737f7e8
+                    (char *) ICONVCONFIG,
737f7e8
+                    (char *) "/usr/sbin/iconvconfig",
737f7e8
+                    (char *) "-o",
737f7e8
+                    (char *) iconv_cache,
737f7e8
+                    (char *) "--nostdlib",
737f7e8
+                    (char *) iconv_dir);
737f7e8
+    }
737f7e8
+
737f7e8
+  _exit(0);
737f7e8
+}
737f7e8
+
737f7e8
+void
737f7e8
+vexec (int failcode, char *const path[])
737f7e8
+{
737f7e8
+  pid_t pid;
737f7e8
+  int status, save_errno;
737f7e8
+  int devnull = 0;
737f7e8
+
737f7e8
+  if (failcode < 0)
737f7e8
+    {
737f7e8
+      devnull = 1;
737f7e8
+      failcode = -failcode;
737f7e8
+    }
737f7e8
+  pid = vfork ();
737f7e8
+  if (pid == 0)
737f7e8
+    {
737f7e8
+      int fd;
737f7e8
+      if (devnull && (fd = open ("/dev/null", O_WRONLY)) >= 0)
737f7e8
+        {
737f7e8
+          dup2 (fd, 1);
737f7e8
+          dup2 (fd, 2);
737f7e8
+          close (fd);
737f7e8
+        }
737f7e8
+      execv (path[0], path + 1);
737f7e8
+      save_errno = errno;
737f7e8
+      message (path);
737f7e8
+      says (" exec failed with errno ");
737f7e8
+      sayn (save_errno);
737f7e8
+      says ("\n");
737f7e8
+      _exit (failcode);
737f7e8
+    }
737f7e8
+  else if (pid < 0)
737f7e8
+    {
737f7e8
+      save_errno = errno;
737f7e8
+      message (path);
737f7e8
+      says (" fork failed with errno ");
737f7e8
+      sayn (save_errno);
737f7e8
+      says ("\n");
737f7e8
+      _exit (failcode + 1);
737f7e8
+    }
737f7e8
+  if (waitpid (0, &status, 0) != pid || !WIFEXITED (status))
737f7e8
+    {
737f7e8
+      message (path);
737f7e8
+      says (" child terminated abnormally\n");
737f7e8
+      _exit (failcode + 2);
737f7e8
+    }
737f7e8
+  if (WEXITSTATUS (status))
737f7e8
+    {
737f7e8
+      message (path);
737f7e8
+      says (" child exited with exit code ");
737f7e8
+      sayn (WEXITSTATUS (status));
737f7e8
+      says ("\n");
737f7e8
+      _exit (WEXITSTATUS (status));
737f7e8
+    }
737f7e8
+}
737f7e8
+
737f7e8
+static void
737f7e8
+says (const char *str)
737f7e8
+{
737f7e8
+  write (1, str, strlen (str));
737f7e8
+}
737f7e8
+
737f7e8
+static void
737f7e8
+sayn (long num)
737f7e8
+{
737f7e8
+  char string[sizeof (long) * 3 + 1];
737f7e8
+  char *p = string + sizeof (string) - 1;
737f7e8
+
737f7e8
+  *p = '\0';
737f7e8
+  if (num == 0)
737f7e8
+    *--p = '0';
737f7e8
+  else
737f7e8
+    while (num)
737f7e8
+      {
737f7e8
+        *--p = '0' + num % 10;
737f7e8
+        num = num / 10;
737f7e8
+      }
737f7e8
+
737f7e8
+  says (p);
737f7e8
+}
737f7e8
+
737f7e8
+static void
737f7e8
+message (char *const path[])
737f7e8
+{
737f7e8
+  says ("/usr/sbin/glibc_post_upgrade: While trying to execute ");
737f7e8
+  says (path[0]);
737f7e8
+}