Blob Blame History Raw
Short description: RPM Post-upgrade cleanup program.
Author(s): Fedora glibc team <glibc@lists.fedoraproject.org>
Origin: PATCH
Upstream status: not-needed

A helper program is needed to clean up the system configuration
early during RPM package installation, so that other scriptlets
can run successfully.

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