(root)/
gettext-0.22.4/
gettext-tools/
gnulib-lib/
progreloc.c
       1  /* Provide relocatable programs.
       2     Copyright (C) 2003-2023 Free Software Foundation, Inc.
       3     Written by Bruno Haible <bruno@clisp.org>, 2003.
       4  
       5     This program is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU General Public License as published by
       7     the Free Software Foundation, either version 3 of the License, or
       8     (at your option) any later version.
       9  
      10     This program is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU General Public License for more details.
      14  
      15     You should have received a copy of the GNU General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  
      19  #define _GL_USE_STDLIB_ALLOC 1
      20  #include <config.h>
      21  
      22  /* Specification.  */
      23  #include "progname.h"
      24  
      25  #include <errno.h>
      26  #include <stdio.h>
      27  #include <stdlib.h>
      28  #include <string.h>
      29  #include <fcntl.h>
      30  #include <unistd.h>
      31  #include <sys/stat.h>
      32  
      33  /* Get declaration of _NSGetExecutablePath on Mac OS X 10.2 or newer.  */
      34  #if HAVE_MACH_O_DYLD_H
      35  # include <mach-o/dyld.h>
      36  #endif
      37  
      38  #if defined _WIN32 && !defined __CYGWIN__
      39  # define WINDOWS_NATIVE
      40  #endif
      41  
      42  #ifdef WINDOWS_NATIVE
      43  # define WIN32_LEAN_AND_MEAN
      44  # include <windows.h>
      45  #endif
      46  
      47  #ifdef __EMX__
      48  # define INCL_DOS
      49  # include <os2.h>
      50  #endif
      51  
      52  #include "relocatable.h"
      53  
      54  #ifdef NO_XMALLOC
      55  # include "areadlink.h"
      56  # define xreadlink areadlink
      57  #else
      58  # include "xreadlink.h"
      59  #endif
      60  
      61  #ifdef NO_XMALLOC
      62  # define xmalloc malloc
      63  # define xstrdup strdup
      64  #else
      65  # include "xalloc.h"
      66  #endif
      67  
      68  #ifndef O_EXEC
      69  # define O_EXEC O_RDONLY /* This is often close enough in older systems.  */
      70  #endif
      71  
      72  #if defined IN_RELOCWRAPPER && (!defined O_CLOEXEC || GNULIB_defined_O_CLOEXEC)
      73  # undef O_CLOEXEC
      74  # define O_CLOEXEC 0
      75  #endif
      76  
      77  /* Declare canonicalize_file_name.
      78     The <stdlib.h> included above may be the system's one, not the gnulib
      79     one.  */
      80  extern char * canonicalize_file_name (const char *name);
      81  
      82  #if defined WINDOWS_NATIVE
      83  /* Don't assume that UNICODE is not defined.  */
      84  # undef GetModuleFileName
      85  # define GetModuleFileName GetModuleFileNameA
      86  #endif
      87  
      88  /* Pathname support.
      89     ISSLASH(C)                tests whether C is a directory separator character.
      90     IS_FILE_NAME_WITH_DIR(P)  tests whether P contains a directory specification.
      91   */
      92  #if (defined _WIN32 && !defined __CYGWIN__) || defined __EMX__ || defined __DJGPP__
      93    /* Native Windows, OS/2, DOS */
      94  # define ISSLASH(C) ((C) == '/' || (C) == '\\')
      95  # define HAS_DEVICE(P) \
      96      ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
      97       && (P)[1] == ':')
      98  # define IS_FILE_NAME_WITH_DIR(P) \
      99      (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
     100  # define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
     101  #else
     102    /* Unix */
     103  # define ISSLASH(C) ((C) == '/')
     104  # define IS_FILE_NAME_WITH_DIR(P) (strchr (P, '/') != NULL)
     105  # define FILE_SYSTEM_PREFIX_LEN(P) 0
     106  #endif
     107  
     108  /* Use the system functions, not the gnulib overrides in this file.  */
     109  #undef sprintf
     110  
     111  #undef set_program_name
     112  
     113  
     114  #if ENABLE_RELOCATABLE
     115  
     116  #ifdef __sun
     117  
     118  /* Helper function, from gnulib module 'safe-read'.  */
     119  static size_t
     120  safe_read (int fd, void *buf, size_t count)
     121  {
     122    for (;;)
     123      {
     124        ssize_t result = read (fd, buf, count);
     125  
     126        if (0 <= result || errno != EINTR)
     127          return result;
     128      }
     129  }
     130  
     131  /* Helper function, from gnulib module 'full-read'.  */
     132  static size_t
     133  full_read (int fd, void *buf, size_t count)
     134  {
     135    size_t total = 0;
     136    char *ptr = (char *) buf;
     137  
     138    while (count > 0)
     139      {
     140        size_t n = safe_read (fd, ptr, count);
     141        if (n == (size_t) -1)
     142          break;
     143        if (n == 0)
     144          {
     145            errno = 0;
     146            break;
     147          }
     148        total += n;
     149        ptr += n;
     150        count -= n;
     151      }
     152  
     153    return total;
     154  }
     155  
     156  #endif
     157  
     158  #if defined __linux__ || defined __CYGWIN__
     159  /* File descriptor of the executable.
     160     (Only used to verify that we find the correct executable.)  */
     161  static int executable_fd = -1;
     162  #endif
     163  
     164  /* Define this function only when it's needed.  */
     165  #if !(defined WINDOWS_NATIVE || defined __EMX__)
     166  
     167  /* Tests whether a given filename may belong to the executable.  */
     168  static bool
     169  maybe_executable (const char *filename)
     170  {
     171    /* The native Windows API lacks the access() function.  */
     172  # if !defined WINDOWS_NATIVE
     173    if (access (filename, X_OK) < 0)
     174      return false;
     175  # endif
     176  
     177  # if defined __linux__ || defined __CYGWIN__
     178    if (executable_fd >= 0)
     179      {
     180        /* If we already have an executable_fd, check that filename points to
     181           the same inode.  */
     182        struct stat statexe;
     183        struct stat statfile;
     184  
     185        if (fstat (executable_fd, &statexe) >= 0)
     186          return (stat (filename, &statfile) >= 0
     187                  && statfile.st_dev
     188                  && statfile.st_dev == statexe.st_dev
     189                  && statfile.st_ino == statexe.st_ino);
     190      }
     191  # endif
     192  
     193    /* Check that the filename does not point to a directory.  */
     194    {
     195      struct stat statfile;
     196  
     197      return (stat (filename, &statfile) >= 0
     198              && ! S_ISDIR (statfile.st_mode));
     199    }
     200  }
     201  
     202  #endif
     203  
     204  /* Determine the full pathname of the current executable, freshly allocated.
     205     Return NULL if unknown.
     206     Guaranteed to work on Linux and native Windows.  Likely to work on the
     207     other Unixes (maybe except BeOS), under most conditions.  */
     208  static char *
     209  find_executable (const char *argv0)
     210  {
     211  #if defined WINDOWS_NATIVE
     212    /* Native Windows only.
     213       On Cygwin, it is better to use the Cygwin provided /proc interface, than
     214       to use native Windows API and cygwin_conv_to_posix_path, because it
     215       supports longer file names
     216       (see <https://cygwin.com/ml/cygwin/2011-01/msg00410.html>).  */
     217    char location[MAX_PATH];
     218    int length = GetModuleFileName (NULL, location, sizeof (location));
     219    if (length < 0)
     220      return NULL;
     221    if (!IS_FILE_NAME_WITH_DIR (location))
     222      /* Shouldn't happen.  */
     223      return NULL;
     224    return xstrdup (location);
     225  #elif defined __EMX__
     226    PPIB ppib;
     227    char location[CCHMAXPATH];
     228  
     229    /* See http://cyberkinetica.homeunix.net/os2tk45/cp1/619_L2H_DosGetInfoBlocksSynt.html
     230       for specification of DosGetInfoBlocks().  */
     231    if (DosGetInfoBlocks (NULL, &ppib))
     232      return NULL;
     233  
     234    /* See http://cyberkinetica.homeunix.net/os2tk45/cp1/1247_L2H_DosQueryModuleNameSy.html
     235       for specification of DosQueryModuleName().  */
     236    if (DosQueryModuleName (ppib->pib_hmte, sizeof (location), location))
     237      return NULL;
     238  
     239    _fnslashify (location);
     240  
     241    return xstrdup (location);
     242  #else /* Unix */
     243  # if defined __linux__
     244    /* The executable is accessible as /proc/<pid>/exe.  In newer Linux
     245       versions, also as /proc/self/exe.  Linux >= 2.1 provides a symlink
     246       to the true pathname; older Linux versions give only device and ino,
     247       enclosed in brackets, which we cannot use here.  */
     248    {
     249      char *link;
     250  
     251      link = xreadlink ("/proc/self/exe");
     252      if (link != NULL && link[0] != '[')
     253        return link;
     254      if (executable_fd < 0)
     255        executable_fd = open ("/proc/self/exe", O_EXEC | O_CLOEXEC, 0);
     256  
     257      {
     258        char buf[6+10+5];
     259        sprintf (buf, "/proc/%d/exe", getpid ());
     260        link = xreadlink (buf);
     261        if (link != NULL && link[0] != '[')
     262          return link;
     263        if (executable_fd < 0)
     264          executable_fd = open (buf, O_EXEC | O_CLOEXEC, 0);
     265      }
     266    }
     267  # endif
     268  # if defined __ANDROID__ || defined __FreeBSD_kernel__
     269    /* On Android and GNU/kFreeBSD, the executable is accessible as
     270       /proc/<pid>/exe and /proc/self/exe.  */
     271    {
     272      char *link;
     273  
     274      link = xreadlink ("/proc/self/exe");
     275      if (link != NULL)
     276        return link;
     277    }
     278  # endif
     279  # if defined __FreeBSD__ || defined __DragonFly__
     280    /* In FreeBSD >= 5.0, the executable is accessible as /proc/<pid>/file and
     281       /proc/curproc/file.  */
     282    {
     283      char *link;
     284  
     285      link = xreadlink ("/proc/curproc/file");
     286      if (link != NULL)
     287        {
     288          if (strcmp (link, "unknown") != 0)
     289            return link;
     290          free (link);
     291        }
     292    }
     293  # endif
     294  # if defined __NetBSD__
     295    /* In NetBSD >= 4.0, the executable is accessible as /proc/<pid>/exe and
     296       /proc/curproc/exe.  */
     297    {
     298      char *link;
     299  
     300      link = xreadlink ("/proc/curproc/exe");
     301      if (link != NULL)
     302        return link;
     303    }
     304  # endif
     305  # if defined __sun
     306    /* On Solaris >= 11.4, /proc/<pid>/execname and /proc/self/execname contains
     307       the name of the executable, either as an absolute file name or relative to
     308       the current directory.  */
     309    {
     310      char namebuf[4096];
     311      int fd = open ("/proc/self/execname", O_RDONLY | O_CLOEXEC, 0);
     312      if (fd >= 0)
     313        {
     314          size_t len = full_read (fd, namebuf, sizeof (namebuf));
     315          close (fd);
     316          if (len > 0 && len < sizeof (namebuf))
     317            {
     318              namebuf[len] = '\0';
     319              return canonicalize_file_name (namebuf);
     320            }
     321        }
     322    }
     323  # endif
     324  # if defined __CYGWIN__
     325    /* The executable is accessible as /proc/<pid>/exe, at least in
     326       Cygwin >= 1.5.  */
     327    {
     328      char *link;
     329  
     330      link = xreadlink ("/proc/self/exe");
     331      if (link != NULL)
     332        return link;
     333      if (executable_fd < 0)
     334        executable_fd = open ("/proc/self/exe", O_EXEC | O_CLOEXEC, 0);
     335    }
     336  # endif
     337  # if HAVE_MACH_O_DYLD_H && HAVE__NSGETEXECUTABLEPATH
     338    /* On Mac OS X 10.2 or newer, the function
     339         int _NSGetExecutablePath (char *buf, uint32_t *bufsize);
     340       can be used to retrieve the executable's full path.  */
     341    char location[4096];
     342    unsigned int length = sizeof (location);
     343    if (_NSGetExecutablePath (location, &length) == 0
     344        && location[0] == '/')
     345      return canonicalize_file_name (location);
     346  # endif
     347    /* Guess the executable's full path.  We assume the executable has been
     348       called via execlp() or execvp() with properly set up argv[0].  The
     349       login(1) convention to add a '-' prefix to argv[0] is not supported.  */
     350    {
     351      bool has_slash = false;
     352      {
     353        const char *p;
     354        for (p = argv0; *p; p++)
     355          if (*p == '/')
     356            {
     357              has_slash = true;
     358              break;
     359            }
     360      }
     361      if (!has_slash)
     362        {
     363          /* exec searches paths without slashes in the directory list given
     364             by $PATH.  */
     365          const char *path = getenv ("PATH");
     366  
     367          if (path != NULL)
     368            {
     369              const char *p;
     370              const char *p_next;
     371  
     372              for (p = path; *p; p = p_next)
     373                {
     374                  const char *q;
     375                  size_t p_len;
     376                  char *concat_name;
     377  
     378                  for (q = p; *q; q++)
     379                    if (*q == ':')
     380                      break;
     381                  p_len = q - p;
     382                  p_next = (*q == '\0' ? q : q + 1);
     383  
     384                  /* We have a path item at p, of length p_len.
     385                     Now concatenate the path item and argv0.  */
     386                  concat_name = (char *) xmalloc (p_len + strlen (argv0) + 2);
     387  # ifdef NO_XMALLOC
     388                  if (concat_name == NULL)
     389                    return NULL;
     390  # endif
     391                  if (p_len == 0)
     392                    /* An empty PATH element designates the current directory.  */
     393                    strcpy (concat_name, argv0);
     394                  else
     395                    {
     396                      memcpy (concat_name, p, p_len);
     397                      concat_name[p_len] = '/';
     398                      strcpy (concat_name + p_len + 1, argv0);
     399                    }
     400                  if (maybe_executable (concat_name))
     401                    return canonicalize_file_name (concat_name);
     402                  free (concat_name);
     403                }
     404            }
     405          /* Not found in the PATH, assume the current directory.  */
     406        }
     407      /* exec treats paths containing slashes as relative to the current
     408         directory.  */
     409      if (maybe_executable (argv0))
     410        return canonicalize_file_name (argv0);
     411    }
     412    /* No way to find the executable.  */
     413    return NULL;
     414  #endif
     415  }
     416  
     417  /* Full pathname of executable, or NULL.  */
     418  static char *executable_fullname;
     419  
     420  static void
     421  prepare_relocate (const char *orig_installprefix, const char *orig_installdir,
     422                    const char *argv0)
     423  {
     424    char *curr_prefix;
     425  
     426    /* Determine the full pathname of the current executable.  */
     427    executable_fullname = find_executable (argv0);
     428  
     429    /* Determine the current installation prefix from it.  */
     430    curr_prefix = compute_curr_prefix (orig_installprefix, orig_installdir,
     431                                       executable_fullname);
     432    if (curr_prefix != NULL)
     433      {
     434        /* Now pass this prefix to all copies of the relocate.c source file.  */
     435        set_relocation_prefix (orig_installprefix, curr_prefix);
     436  
     437        free (curr_prefix);
     438      }
     439  }
     440  
     441  /* Set program_name, based on argv[0], and original installation prefix and
     442     directory, for relocatability.  */
     443  void
     444  set_program_name_and_installdir (const char *argv0,
     445                                   const char *orig_installprefix,
     446                                   const char *orig_installdir)
     447  {
     448    const char *argv0_stripped = argv0;
     449  
     450    /* Relocatable programs are renamed to .bin by install-reloc.  Or, more
     451       generally, their suffix is changed from $exeext to .bin$exeext.
     452       Remove the ".bin" here.  */
     453    {
     454      size_t argv0_len = strlen (argv0);
     455      const size_t exeext_len = sizeof (EXEEXT) - sizeof ("");
     456      if (argv0_len > 4 + exeext_len)
     457        if (memcmp (argv0 + argv0_len - exeext_len - 4, ".bin", 4) == 0)
     458          {
     459            if (sizeof (EXEEXT) > sizeof (""))
     460              {
     461                /* Compare using an inlined copy of c_strncasecmp(), because
     462                   the filenames may have undergone a case conversion since
     463                   they were packaged.  In other words, EXEEXT may be ".exe"
     464                   on one system and ".EXE" on another.  */
     465                static const char exeext[] = EXEEXT;
     466                const char *s1 = argv0 + argv0_len - exeext_len;
     467                const char *s2 = exeext;
     468                for (; *s1 != '\0'; s1++, s2++)
     469                  {
     470                    unsigned char c1 = *s1;
     471                    unsigned char c2 = *s2;
     472                    if ((c1 >= 'A' && c1 <= 'Z' ? c1 - 'A' + 'a' : c1)
     473                        != (c2 >= 'A' && c2 <= 'Z' ? c2 - 'A' + 'a' : c2))
     474                      goto done_stripping;
     475                  }
     476              }
     477            /* Remove ".bin" before EXEEXT or its equivalent.  */
     478            {
     479              char *shorter = (char *) xmalloc (argv0_len - 4 + 1);
     480  #ifdef NO_XMALLOC
     481              if (shorter != NULL)
     482  #endif
     483                {
     484                  memcpy (shorter, argv0, argv0_len - exeext_len - 4);
     485                  if (sizeof (EXEEXT) > sizeof (""))
     486                    memcpy (shorter + argv0_len - exeext_len - 4,
     487                            argv0 + argv0_len - exeext_len - 4,
     488                            exeext_len);
     489                  shorter[argv0_len - 4] = '\0';
     490                  argv0_stripped = shorter;
     491                }
     492            }
     493           done_stripping: ;
     494        }
     495    }
     496  
     497    set_program_name (argv0_stripped);
     498  
     499    prepare_relocate (orig_installprefix, orig_installdir, argv0);
     500  }
     501  
     502  /* Return the full pathname of the current executable, based on the earlier
     503     call to set_program_name_and_installdir.  Return NULL if unknown.  */
     504  char *
     505  get_full_program_name (void)
     506  {
     507    return executable_fullname;
     508  }
     509  
     510  #endif