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