/* skeleton based on version from Fedora Core 3 */

#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif

#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 verbose_exec(failcode, path...)			\
  do							\
    {							\
      char *const arr[] = { path, NULL };		\
      vexec (failcode, arr);				\
    } while (0)

__attribute__((noinline)) void vexec (int failcode, char *const path[]);
__attribute__((noinline)) void says (const char *str);
__attribute__((noinline)) void sayn (long num);
__attribute__((noinline)) void message (char *const path[]);

int
main (void)
{
  /* First, get rid of platform-optimized libraries. We remove any we have
     ever built, since otherwise we might end up using some old leftover
     libraries when new ones aren't installed in their place anymore. */
#ifdef REMOVE_TLS_DIRS
  const char *library[] = {"libc.so.6", "libc.so.6.1", "libm.so.6",
			   "libm.so.6.1", "librt.so.1", "librtkaio.so.1",
			   "libpthread.so.0", "libthread_db.so.1"};
  const char *remove_dir[] = {
#ifdef __i386__
	"/lib/i686/",
#endif
#ifdef __powerpc64__
#ifdef REMOVE_PPC_OPTIMIZE_POWER4
	"/lib64/power4/",
	"/lib64/ppc970/",
#endif
#ifdef REMOVE_PPC_OPTIMIZE_POWER5
	"/lib64/power5/",
	"/lib64/power5+/",
#endif
#ifdef REMOVE_PPC_OPTIMIZE_POWER6
	"/lib64/power6/",
	"/lib64/power6x/",
#endif
#ifdef REMOVE_PPC_OPTIMIZE_POWER7
	"/lib64/power7/",
#endif
#ifdef REMOVE_PPC_OPTIMIZE_CELL
	"/lib64/ppc-cell-be/",
#endif
#endif /* __powerpc64__ */
#ifdef __powerpc__
#ifdef REMOVE_PPC_OPTIMIZE_POWER4
	"/lib/power4/",
	"/lib/ppc970/",
#endif
#ifdef REMOVE_PPC_OPTIMIZE_POWER5
	"/lib/power5/",
	"/lib/power5+/",
#endif
#ifdef REMOVE_PPC_OPTIMIZE_POWER6
	"/lib/power6/",
	"/lib/power6x/",
#endif
#ifdef REMOVE_PPC_OPTIMIZE_POWER7
	"/lib/power7/",
#endif
#ifdef REMOVE_PPC_OPTIMIZE_CELL
	"/lib/ppc-cell-be/",
#endif
#endif /* __powerpc__ */
	LIBDIR"/tls/" };
  int i, j;

  for (i = 0; i < sizeof (remove_dir) / sizeof (remove_dir[0]); ++i)
    for (j = 0; j < sizeof (library) / sizeof (library[0]); j++)
      {
	char buf[strlen (remove_dir[i]) + strlen (library[j]) + 1];
	char readlink_buf[(strlen (remove_dir[i]) + strlen (library[j])) * 2 + 30];
	ssize_t len;
	char *cp;

	cp = stpcpy (buf, remove_dir[i]);
	strcpy (cp, library[j]);
	/* This file could be a symlink to library-%{version}.so, so check
	   this and don't remove only the link, but also the library itself. */
	cp = stpcpy (readlink_buf, remove_dir[i]);
	if ((len = readlink (buf, cp, (sizeof (readlink_buf)
				       - (cp - readlink_buf) - 1))) > 0)
	  {
	    cp[len] = '\0';
	    if (cp[0] != '/') cp = readlink_buf;
	    unlink (cp);
	  }
	unlink (buf);
      }
#endif

  /* 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) == 0)
    verbose_exec (110, "/sbin/ldconfig", "/sbin/ldconfig", "-X");

  if (utimes (GCONV_MODULES_DIR "/gconv-modules.cache", NULL) == 0)
    {
#ifndef ICONVCONFIG
#define ICONVCONFIG "/usr/sbin/iconvconfig"
#endif
      verbose_exec (113, ICONVCONFIG, "/usr/sbin/iconvconfig",
		    "-o", GCONV_MODULES_DIR"/gconv-modules.cache",
		    "--nostdlib", GCONV_MODULES_DIR);
    }

  _exit(0);
}

void
vexec (int failcode, char *const path[])
{
  pid_t pid;
  int status, save_errno;

  pid = vfork ();
  if (pid == 0)
    {
      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));
    }
}

void
says (const char *str)
{
  write (1, str, strlen (str));
}

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);
}

void
message (char *const path[])
{
  says ("/usr/sbin/glibc_post_upgrade: While trying to execute ");
  says (path[0]);
}