Short description: RPM Post-upgrade cleanup program. Author(s): Fedora glibc team 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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]); +}