root/maint/gnulib/lib/progreloc.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. safe_read
  2. full_read
  3. maybe_executable
  4. find_executable
  5. prepare_relocate
  6. set_program_name_and_installdir
  7. get_full_program_name

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

/* [previous][next][first][last][top][bottom][index][help] */