Blame gettext-runtime/gnulib-lib/progreloc.c

Packit 5b56b6
/* Provide relocatable programs.
Packit 5b56b6
   Copyright (C) 2003-2015 Free Software Foundation, Inc.
Packit 5b56b6
   Written by Bruno Haible <bruno@clisp.org>, 2003.
Packit 5b56b6
Packit 5b56b6
   This program is free software: you can redistribute it and/or modify
Packit 5b56b6
   it under the terms of the GNU General Public License as published by
Packit 5b56b6
   the Free Software Foundation; either version 3 of the License, or
Packit 5b56b6
   (at your option) any later version.
Packit 5b56b6
Packit 5b56b6
   This program is distributed in the hope that it will be useful,
Packit 5b56b6
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 5b56b6
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 5b56b6
   GNU General Public License for more details.
Packit 5b56b6
Packit 5b56b6
   You should have received a copy of the GNU General Public License
Packit 5b56b6
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 5b56b6
Packit 5b56b6
Packit 5b56b6
#define _GL_USE_STDLIB_ALLOC 1
Packit 5b56b6
#include <config.h>
Packit 5b56b6
Packit 5b56b6
/* Specification.  */
Packit 5b56b6
#include "progname.h"
Packit 5b56b6
Packit 5b56b6
#include <stdbool.h>
Packit 5b56b6
#include <stdio.h>
Packit 5b56b6
#include <stdlib.h>
Packit 5b56b6
#include <string.h>
Packit 5b56b6
#include <fcntl.h>
Packit 5b56b6
#include <unistd.h>
Packit 5b56b6
#include <sys/stat.h>
Packit 5b56b6
Packit 5b56b6
/* Get declaration of _NSGetExecutablePath on Mac OS X 10.2 or newer.  */
Packit 5b56b6
#if HAVE_MACH_O_DYLD_H
Packit 5b56b6
# include <mach-o/dyld.h>
Packit 5b56b6
#endif
Packit 5b56b6
Packit 5b56b6
#if (defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__
Packit 5b56b6
# define WINDOWS_NATIVE
Packit 5b56b6
#endif
Packit 5b56b6
Packit 5b56b6
#ifdef WINDOWS_NATIVE
Packit 5b56b6
# define WIN32_LEAN_AND_MEAN
Packit 5b56b6
# include <windows.h>
Packit 5b56b6
#endif
Packit 5b56b6
Packit 5b56b6
#ifdef __EMX__
Packit 5b56b6
# define INCL_DOS
Packit 5b56b6
# include <os2.h>
Packit 5b56b6
#endif
Packit 5b56b6
Packit 5b56b6
#include "relocatable.h"
Packit 5b56b6
Packit 5b56b6
#ifdef NO_XMALLOC
Packit 5b56b6
# include "areadlink.h"
Packit 5b56b6
# define xreadlink areadlink
Packit 5b56b6
#else
Packit 5b56b6
# include "xreadlink.h"
Packit 5b56b6
#endif
Packit 5b56b6
Packit 5b56b6
#ifdef NO_XMALLOC
Packit 5b56b6
# define xmalloc malloc
Packit 5b56b6
# define xstrdup strdup
Packit 5b56b6
#else
Packit 5b56b6
# include "xalloc.h"
Packit 5b56b6
#endif
Packit 5b56b6
Packit 5b56b6
#ifndef O_EXEC
Packit 5b56b6
# define O_EXEC O_RDONLY /* This is often close enough in older systems.  */
Packit 5b56b6
#endif
Packit 5b56b6
Packit 5b56b6
/* Declare canonicalize_file_name.
Packit 5b56b6
   The <stdlib.h> included above may be the system's one, not the gnulib
Packit 5b56b6
   one.  */
Packit 5b56b6
extern char * canonicalize_file_name (const char *name);
Packit 5b56b6
Packit 5b56b6
/* Pathname support.
Packit 5b56b6
   ISSLASH(C)           tests whether C is a directory separator character.
Packit 5b56b6
   IS_PATH_WITH_DIR(P)  tests whether P contains a directory specification.
Packit 5b56b6
 */
Packit 5b56b6
#if ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __EMX__ || defined __DJGPP__
Packit 5b56b6
  /* Native Windows, OS/2, DOS */
Packit 5b56b6
# define ISSLASH(C) ((C) == '/' || (C) == '\\')
Packit 5b56b6
# define HAS_DEVICE(P) \
Packit 5b56b6
    ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
Packit 5b56b6
     && (P)[1] == ':')
Packit 5b56b6
# define IS_PATH_WITH_DIR(P) \
Packit 5b56b6
    (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
Packit 5b56b6
# define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
Packit 5b56b6
#else
Packit 5b56b6
  /* Unix */
Packit 5b56b6
# define ISSLASH(C) ((C) == '/')
Packit 5b56b6
# define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
Packit 5b56b6
# define FILE_SYSTEM_PREFIX_LEN(P) 0
Packit 5b56b6
#endif
Packit 5b56b6
Packit 5b56b6
/* Use the system functions, not the gnulib overrides in this file.  */
Packit 5b56b6
#undef sprintf
Packit 5b56b6
Packit 5b56b6
#undef set_program_name
Packit 5b56b6
Packit 5b56b6
Packit 5b56b6
#if ENABLE_RELOCATABLE
Packit 5b56b6
Packit 5b56b6
#if defined __linux__ || defined __CYGWIN__
Packit 5b56b6
/* File descriptor of the executable.
Packit 5b56b6
   (Only used to verify that we find the correct executable.)  */
Packit 5b56b6
static int executable_fd = -1;
Packit 5b56b6
#endif
Packit 5b56b6
Packit 5b56b6
/* Tests whether a given pathname may belong to the executable.  */
Packit 5b56b6
static bool
Packit 5b56b6
maybe_executable (const char *filename)
Packit 5b56b6
{
Packit 5b56b6
  /* The native Windows API lacks the access() function.  */
Packit 5b56b6
#if !defined WINDOWS_NATIVE
Packit 5b56b6
  if (access (filename, X_OK) < 0)
Packit 5b56b6
    return false;
Packit 5b56b6
#endif
Packit 5b56b6
Packit 5b56b6
#if defined __linux__ || defined __CYGWIN__
Packit 5b56b6
  if (executable_fd >= 0)
Packit 5b56b6
    {
Packit 5b56b6
      /* If we already have an executable_fd, check that filename points to
Packit 5b56b6
         the same inode.  */
Packit 5b56b6
      struct stat statexe;
Packit 5b56b6
      struct stat statfile;
Packit 5b56b6
Packit 5b56b6
      if (fstat (executable_fd, &statexe) >= 0)
Packit 5b56b6
        {
Packit 5b56b6
          if (stat (filename, &statfile) < 0)
Packit 5b56b6
            return false;
Packit 5b56b6
          if (!(statfile.st_dev
Packit 5b56b6
                && statfile.st_dev == statexe.st_dev
Packit 5b56b6
                && statfile.st_ino == statexe.st_ino))
Packit 5b56b6
            return false;
Packit 5b56b6
        }
Packit 5b56b6
    }
Packit 5b56b6
#endif
Packit 5b56b6
Packit 5b56b6
  return true;
Packit 5b56b6
}
Packit 5b56b6
Packit 5b56b6
/* Determine the full pathname of the current executable, freshly allocated.
Packit 5b56b6
   Return NULL if unknown.
Packit 5b56b6
   Guaranteed to work on Linux and native Windows.  Likely to work on the
Packit 5b56b6
   other Unixes (maybe except BeOS), under most conditions.  */
Packit 5b56b6
static char *
Packit 5b56b6
find_executable (const char *argv0)
Packit 5b56b6
{
Packit 5b56b6
#if defined WINDOWS_NATIVE
Packit 5b56b6
  /* Native Windows only.
Packit 5b56b6
     On Cygwin, it is better to use the Cygwin provided /proc interface, than
Packit 5b56b6
     to use native Windows API and cygwin_conv_to_posix_path, because it
Packit 5b56b6
     supports longer file names
Packit 5b56b6
     (see <http://cygwin.com/ml/cygwin/2011-01/msg00410.html>).  */
Packit 5b56b6
  char location[MAX_PATH];
Packit 5b56b6
  int length = GetModuleFileName (NULL, location, sizeof (location));
Packit 5b56b6
  if (length < 0)
Packit 5b56b6
    return NULL;
Packit 5b56b6
  if (!IS_PATH_WITH_DIR (location))
Packit 5b56b6
    /* Shouldn't happen.  */
Packit 5b56b6
    return NULL;
Packit 5b56b6
  return xstrdup (location);
Packit 5b56b6
#elif defined __EMX__
Packit 5b56b6
  PPIB ppib;
Packit 5b56b6
  char location[CCHMAXPATH];
Packit 5b56b6
Packit 5b56b6
  /* See http://cyberkinetica.homeunix.net/os2tk45/cp1/619_L2H_DosGetInfoBlocksSynt.html
Packit 5b56b6
     for specification of DosGetInfoBlocks().  */
Packit 5b56b6
  if (DosGetInfoBlocks (NULL, &ppib))
Packit 5b56b6
    return NULL;
Packit 5b56b6
Packit 5b56b6
  /* See http://cyberkinetica.homeunix.net/os2tk45/cp1/1247_L2H_DosQueryModuleNameSy.html
Packit 5b56b6
     for specification of DosQueryModuleName().  */
Packit 5b56b6
  if (DosQueryModuleName (ppib->pib_hmte, sizeof (location), location))
Packit 5b56b6
    return NULL;
Packit 5b56b6
Packit 5b56b6
  _fnslashify (location);
Packit 5b56b6
Packit 5b56b6
  return xstrdup (location);
Packit 5b56b6
#else /* Unix */
Packit 5b56b6
# ifdef __linux__
Packit 5b56b6
  /* The executable is accessible as /proc/<pid>/exe.  In newer Linux
Packit 5b56b6
     versions, also as /proc/self/exe.  Linux >= 2.1 provides a symlink
Packit 5b56b6
     to the true pathname; older Linux versions give only device and ino,
Packit 5b56b6
     enclosed in brackets, which we cannot use here.  */
Packit 5b56b6
  {
Packit 5b56b6
    char *link;
Packit 5b56b6
Packit 5b56b6
    link = xreadlink ("/proc/self/exe");
Packit 5b56b6
    if (link != NULL && link[0] != '[')
Packit 5b56b6
      return link;
Packit 5b56b6
    if (executable_fd < 0)
Packit 5b56b6
      executable_fd = open ("/proc/self/exe", O_EXEC, 0);
Packit 5b56b6
Packit 5b56b6
    {
Packit 5b56b6
      char buf[6+10+5];
Packit 5b56b6
      sprintf (buf, "/proc/%d/exe", getpid ());
Packit 5b56b6
      link = xreadlink (buf);
Packit 5b56b6
      if (link != NULL && link[0] != '[')
Packit 5b56b6
        return link;
Packit 5b56b6
      if (executable_fd < 0)
Packit 5b56b6
        executable_fd = open (buf, O_EXEC, 0);
Packit 5b56b6
    }
Packit 5b56b6
  }
Packit 5b56b6
# endif
Packit 5b56b6
# ifdef __CYGWIN__
Packit 5b56b6
  /* The executable is accessible as /proc/<pid>/exe, at least in
Packit 5b56b6
     Cygwin >= 1.5.  */
Packit 5b56b6
  {
Packit 5b56b6
    char *link;
Packit 5b56b6
Packit 5b56b6
    link = xreadlink ("/proc/self/exe");
Packit 5b56b6
    if (link != NULL)
Packit 5b56b6
      return link;
Packit 5b56b6
    if (executable_fd < 0)
Packit 5b56b6
      executable_fd = open ("/proc/self/exe", O_EXEC, 0);
Packit 5b56b6
  }
Packit 5b56b6
# endif
Packit 5b56b6
# if HAVE_MACH_O_DYLD_H && HAVE__NSGETEXECUTABLEPATH
Packit 5b56b6
  /* On Mac OS X 10.2 or newer, the function
Packit 5b56b6
       int _NSGetExecutablePath (char *buf, uint32_t *bufsize);
Packit 5b56b6
     can be used to retrieve the executable's full path.  */
Packit 5b56b6
  char location[4096];
Packit 5b56b6
  unsigned int length = sizeof (location);
Packit 5b56b6
  if (_NSGetExecutablePath (location, &length) == 0
Packit 5b56b6
      && location[0] == '/')
Packit 5b56b6
    return canonicalize_file_name (location);
Packit 5b56b6
# endif
Packit 5b56b6
  /* Guess the executable's full path.  We assume the executable has been
Packit 5b56b6
     called via execlp() or execvp() with properly set up argv[0].  The
Packit 5b56b6
     login(1) convention to add a '-' prefix to argv[0] is not supported.  */
Packit 5b56b6
  {
Packit 5b56b6
    bool has_slash = false;
Packit 5b56b6
    {
Packit 5b56b6
      const char *p;
Packit 5b56b6
      for (p = argv0; *p; p++)
Packit 5b56b6
        if (*p == '/')
Packit 5b56b6
          {
Packit 5b56b6
            has_slash = true;
Packit 5b56b6
            break;
Packit 5b56b6
          }
Packit 5b56b6
    }
Packit 5b56b6
    if (!has_slash)
Packit 5b56b6
      {
Packit 5b56b6
        /* exec searches paths without slashes in the directory list given
Packit 5b56b6
           by $PATH.  */
Packit 5b56b6
        const char *path = getenv ("PATH");
Packit 5b56b6
Packit 5b56b6
        if (path != NULL)
Packit 5b56b6
          {
Packit 5b56b6
            const char *p;
Packit 5b56b6
            const char *p_next;
Packit 5b56b6
Packit 5b56b6
            for (p = path; *p; p = p_next)
Packit 5b56b6
              {
Packit 5b56b6
                const char *q;
Packit 5b56b6
                size_t p_len;
Packit 5b56b6
                char *concat_name;
Packit 5b56b6
Packit 5b56b6
                for (q = p; *q; q++)
Packit 5b56b6
                  if (*q == ':')
Packit 5b56b6
                    break;
Packit 5b56b6
                p_len = q - p;
Packit 5b56b6
                p_next = (*q == '\0' ? q : q + 1);
Packit 5b56b6
Packit 5b56b6
                /* We have a path item at p, of length p_len.
Packit 5b56b6
                   Now concatenate the path item and argv0.  */
Packit 5b56b6
                concat_name = (char *) xmalloc (p_len + strlen (argv0) + 2);
Packit 5b56b6
# ifdef NO_XMALLOC
Packit 5b56b6
                if (concat_name == NULL)
Packit 5b56b6
                  return NULL;
Packit 5b56b6
# endif
Packit 5b56b6
                if (p_len == 0)
Packit 5b56b6
                  /* An empty PATH element designates the current directory.  */
Packit 5b56b6
                  strcpy (concat_name, argv0);
Packit 5b56b6
                else
Packit 5b56b6
                  {
Packit 5b56b6
                    memcpy (concat_name, p, p_len);
Packit 5b56b6
                    concat_name[p_len] = '/';
Packit 5b56b6
                    strcpy (concat_name + p_len + 1, argv0);
Packit 5b56b6
                  }
Packit 5b56b6
                if (maybe_executable (concat_name))
Packit 5b56b6
                  return canonicalize_file_name (concat_name);
Packit 5b56b6
                free (concat_name);
Packit 5b56b6
              }
Packit 5b56b6
          }
Packit 5b56b6
        /* Not found in the PATH, assume the current directory.  */
Packit 5b56b6
      }
Packit 5b56b6
    /* exec treats paths containing slashes as relative to the current
Packit 5b56b6
       directory.  */
Packit 5b56b6
    if (maybe_executable (argv0))
Packit 5b56b6
      return canonicalize_file_name (argv0);
Packit 5b56b6
  }
Packit 5b56b6
  /* No way to find the executable.  */
Packit 5b56b6
  return NULL;
Packit 5b56b6
#endif
Packit 5b56b6
}
Packit 5b56b6
Packit 5b56b6
/* Full pathname of executable, or NULL.  */
Packit 5b56b6
static char *executable_fullname;
Packit 5b56b6
Packit 5b56b6
static void
Packit 5b56b6
prepare_relocate (const char *orig_installprefix, const char *orig_installdir,
Packit 5b56b6
                  const char *argv0)
Packit 5b56b6
{
Packit 5b56b6
  char *curr_prefix;
Packit 5b56b6
Packit 5b56b6
  /* Determine the full pathname of the current executable.  */
Packit 5b56b6
  executable_fullname = find_executable (argv0);
Packit 5b56b6
Packit 5b56b6
  /* Determine the current installation prefix from it.  */
Packit 5b56b6
  curr_prefix = compute_curr_prefix (orig_installprefix, orig_installdir,
Packit 5b56b6
                                     executable_fullname);
Packit 5b56b6
  if (curr_prefix != NULL)
Packit 5b56b6
    {
Packit 5b56b6
      /* Now pass this prefix to all copies of the relocate.c source file.  */
Packit 5b56b6
      set_relocation_prefix (orig_installprefix, curr_prefix);
Packit 5b56b6
Packit 5b56b6
      free (curr_prefix);
Packit 5b56b6
    }
Packit 5b56b6
}
Packit 5b56b6
Packit 5b56b6
/* Set program_name, based on argv[0], and original installation prefix and
Packit 5b56b6
   directory, for relocatability.  */
Packit 5b56b6
void
Packit 5b56b6
set_program_name_and_installdir (const char *argv0,
Packit 5b56b6
                                 const char *orig_installprefix,
Packit 5b56b6
                                 const char *orig_installdir)
Packit 5b56b6
{
Packit 5b56b6
  const char *argv0_stripped = argv0;
Packit 5b56b6
Packit 5b56b6
  /* Relocatable programs are renamed to .bin by install-reloc.  Or, more
Packit 5b56b6
     generally, their suffix is changed from $exeext to .bin$exeext.
Packit 5b56b6
     Remove the ".bin" here.  */
Packit 5b56b6
  {
Packit 5b56b6
    size_t argv0_len = strlen (argv0);
Packit 5b56b6
    const size_t exeext_len = sizeof (EXEEXT) - sizeof ("");
Packit 5b56b6
    if (argv0_len > 4 + exeext_len)
Packit 5b56b6
      if (memcmp (argv0 + argv0_len - exeext_len - 4, ".bin", 4) == 0)
Packit 5b56b6
        {
Packit 5b56b6
          if (sizeof (EXEEXT) > sizeof (""))
Packit 5b56b6
            {
Packit 5b56b6
              /* Compare using an inlined copy of c_strncasecmp(), because
Packit 5b56b6
                 the filenames may have undergone a case conversion since
Packit 5b56b6
                 they were packaged.  In other words, EXEEXT may be ".exe"
Packit 5b56b6
                 on one system and ".EXE" on another.  */
Packit 5b56b6
              static const char exeext[] = EXEEXT;
Packit 5b56b6
              const char *s1 = argv0 + argv0_len - exeext_len;
Packit 5b56b6
              const char *s2 = exeext;
Packit 5b56b6
              for (; *s1 != '\0'; s1++, s2++)
Packit 5b56b6
                {
Packit 5b56b6
                  unsigned char c1 = *s1;
Packit 5b56b6
                  unsigned char c2 = *s2;
Packit 5b56b6
                  if ((c1 >= 'A' && c1 <= 'Z' ? c1 - 'A' + 'a' : c1)
Packit 5b56b6
                      != (c2 >= 'A' && c2 <= 'Z' ? c2 - 'A' + 'a' : c2))
Packit 5b56b6
                    goto done_stripping;
Packit 5b56b6
                }
Packit 5b56b6
            }
Packit 5b56b6
          /* Remove ".bin" before EXEEXT or its equivalent.  */
Packit 5b56b6
          {
Packit 5b56b6
            char *shorter = (char *) xmalloc (argv0_len - 4 + 1);
Packit 5b56b6
#ifdef NO_XMALLOC
Packit 5b56b6
            if (shorter != NULL)
Packit 5b56b6
#endif
Packit 5b56b6
              {
Packit 5b56b6
                memcpy (shorter, argv0, argv0_len - exeext_len - 4);
Packit 5b56b6
                if (sizeof (EXEEXT) > sizeof (""))
Packit 5b56b6
                  memcpy (shorter + argv0_len - exeext_len - 4,
Packit 5b56b6
                          argv0 + argv0_len - exeext_len - 4,
Packit 5b56b6
                          exeext_len);
Packit 5b56b6
                shorter[argv0_len - 4] = '\0';
Packit 5b56b6
                argv0_stripped = shorter;
Packit 5b56b6
              }
Packit 5b56b6
          }
Packit 5b56b6
         done_stripping: ;
Packit 5b56b6
      }
Packit 5b56b6
  }
Packit 5b56b6
Packit 5b56b6
  set_program_name (argv0_stripped);
Packit 5b56b6
Packit 5b56b6
  prepare_relocate (orig_installprefix, orig_installdir, argv0);
Packit 5b56b6
}
Packit 5b56b6
Packit 5b56b6
/* Return the full pathname of the current executable, based on the earlier
Packit 5b56b6
   call to set_program_name_and_installdir.  Return NULL if unknown.  */
Packit 5b56b6
char *
Packit 5b56b6
get_full_program_name (void)
Packit 5b56b6
{
Packit 5b56b6
  return executable_fullname;
Packit 5b56b6
}
Packit 5b56b6
Packit 5b56b6
#endif