glibc/glibc-post_upgrade.patch

361 lines
11 KiB
Diff
Raw Normal View History

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..3c9839ae523d2cc7
--- /dev/null
+++ b/elf/glibc_post_upgrade.c
@@ -0,0 +1,322 @@
+#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>
+#include <sys/stat.h>
+#include <elf.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[]);
+__attribute__((noinline)) static int check_elf (const char *name);
+
+int
+main (void)
+{
+ struct stat statbuf;
+ 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);
+ }
+
+ /* Check if telinit is available and either SysVInit fifo,
+ or upstart telinit. */
+ if (access ("/sbin/telinit", X_OK)
+ || ((!!access ("/dev/initctl", F_OK))
+ ^ !access ("/sbin/initctl", X_OK)))
+ _exit (0);
+
+ /* Check if we are not inside of some chroot, because we'd just
+ timeout and leave /etc/initrunlvl.
+
+ On more modern systems this test is not sufficient to detect
+ if we're in a chroot. */
+ if (readlink ("/proc/1/exe", initpath, 256) <= 0 ||
+ readlink ("/proc/1/root", initpath, 256) <= 0)
+ _exit (0);
+
+ /* Here's another well known way to detect chroot, at least on an
+ ext and xfs filesystems and assuming nothing mounted on the chroot's
+ root. */
+ if (stat ("/", &statbuf) != 0
+ || (statbuf.st_ino != 2
+ && statbuf.st_ino != 128))
+ _exit (0);
+
+ if (check_elf ("/proc/1/exe"))
+ verbose_exec (116,
+ (char *) "/sbin/telinit",
+ (char *) "/sbin/telinit",
+ (char *) "u");
+
+ /* Check if we can safely condrestart sshd. */
+ if (access ("/sbin/service", X_OK) == 0
+ && access ("/usr/sbin/sshd", X_OK) == 0
+ && access ("/etc/rc.d/init.d/sshd", X_OK) == 0
+ && access ("/bin/bash", X_OK) == 0)
+ {
+ if (check_elf ("/usr/sbin/sshd"))
+ verbose_exec (-121,
+ (char *) "/sbin/service",
+ (char *) "/sbin/service",
+ (char *) "sshd",
+ (char *) "condrestart");
+ }
+
+ _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]);
+}
+
+static int
+check_elf (const char *name)
+{
+ /* Play safe, if we can't open or read, assume it might be
+ ELF for the current arch. */
+ int ret = 1;
+ int fd = open (name, O_RDONLY);
+ if (fd >= 0)
+ {
+ Elf32_Ehdr ehdr;
+ if (read (fd, &ehdr, offsetof (Elf32_Ehdr, e_version))
+ == offsetof (Elf32_Ehdr, e_version))
+ {
+ ret = 0;
+ if (ehdr.e_ident[EI_CLASS]
+ == (sizeof (long) == 8 ? ELFCLASS64 : ELFCLASS32))
+ {
+#if defined __i386__
+ ret = ehdr.e_machine == EM_386;
+#elif defined __x86_64__
+ ret = ehdr.e_machine == EM_X86_64;
+#elif defined __powerpc64__
+ ret = ehdr.e_machine == EM_PPC64;
+#elif defined __powerpc__
+ ret = ehdr.e_machine == EM_PPC;
+#elif defined __s390__ || defined __s390x__
+ ret = ehdr.e_machine == EM_S390;
+#elif defined __x86_64__
+ ret = ehdr.e_machine == EM_X86_64;
+#elif defined __sparc__
+ if (sizeof (long) == 8)
+ ret = ehdr.e_machine == EM_SPARCV9;
+ else
+ ret = (ehdr.e_machine == EM_SPARC
+ || ehdr.e_machine == EM_SPARC32PLUS);
+#else
+ ret = 1;
+#endif
+ }
+ }
+ close (fd);
+ }
+ return ret;
+}