Home | History | Annotate | Line # | Download | only in gnulib-lib
progreloc.c revision 1.1.1.1
      1 /* Provide relocatable programs.
      2    Copyright (C) 2003-2006 Free Software Foundation, Inc.
      3    Written by Bruno Haible <bruno (at) 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 2, or (at your option)
      8    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, write to the Free Software Foundation,
     17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
     18 
     19 
     20 #include <config.h>
     21 
     22 /* Specification.  */
     23 #include "progname.h"
     24 
     25 #include <stdbool.h>
     26 #include <stdio.h>
     27 #include <stdlib.h>
     28 #include <string.h>
     29 #include <fcntl.h>
     30 #if HAVE_UNISTD_H
     31 # include <unistd.h>
     32 #endif
     33 #include <sys/stat.h>
     34 
     35 /* Get declaration of _NSGetExecutablePath on MacOS X 10.2 or newer.  */
     36 #if HAVE_MACH_O_DYLD_H
     37 # include <mach-o/dyld.h>
     38 #endif
     39 
     40 #if defined _WIN32 || defined __WIN32__
     41 # define WIN32_NATIVE
     42 #endif
     43 
     44 #if defined WIN32_NATIVE || defined __CYGWIN__
     45 # define WIN32_LEAN_AND_MEAN
     46 # include <windows.h>
     47 #endif
     48 
     49 #include "xreadlink.h"
     50 #include "canonicalize.h"
     51 #include "relocatable.h"
     52 
     53 #ifdef NO_XMALLOC
     54 # define xmalloc malloc
     55 # define xstrdup strdup
     56 #else
     57 # include "xalloc.h"
     58 #endif
     59 
     60 /* Pathname support.
     61    ISSLASH(C)           tests whether C is a directory separator character.
     62    IS_PATH_WITH_DIR(P)  tests whether P contains a directory specification.
     63  */
     64 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
     65   /* Win32, Cygwin, OS/2, DOS */
     66 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
     67 # define HAS_DEVICE(P) \
     68     ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
     69      && (P)[1] == ':')
     70 # define IS_PATH_WITH_DIR(P) \
     71     (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
     72 # define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
     73 #else
     74   /* Unix */
     75 # define ISSLASH(C) ((C) == '/')
     76 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
     77 # define FILE_SYSTEM_PREFIX_LEN(P) 0
     78 #endif
     79 
     80 #undef set_program_name
     81 
     82 
     83 #if ENABLE_RELOCATABLE
     84 
     85 #ifdef __linux__
     86 /* File descriptor of the executable.
     87    (Only used to verify that we find the correct executable.)  */
     88 static int executable_fd = -1;
     89 #endif
     90 
     91 /* Tests whether a given pathname may belong to the executable.  */
     92 static bool
     93 maybe_executable (const char *filename)
     94 {
     95   /* Woe32 lacks the access() function, but Cygwin doesn't.  */
     96 #if !(defined WIN32_NATIVE && !defined __CYGWIN__)
     97   if (access (filename, X_OK) < 0)
     98     return false;
     99 
    100 #ifdef __linux__
    101   if (executable_fd >= 0)
    102     {
    103       /* If we already have an executable_fd, check that filename points to
    104 	 the same inode.  */
    105       struct stat statexe;
    106       struct stat statfile;
    107 
    108       if (fstat (executable_fd, &statexe) >= 0)
    109 	{
    110 	  if (stat (filename, &statfile) < 0)
    111 	    return false;
    112 	  if (!(statfile.st_dev
    113 		&& statfile.st_dev == statexe.st_dev
    114 		&& statfile.st_ino == statexe.st_ino))
    115 	    return false;
    116 	}
    117     }
    118 #endif
    119 #endif
    120 
    121   return true;
    122 }
    123 
    124 /* Determine the full pathname of the current executable, freshly allocated.
    125    Return NULL if unknown.
    126    Guaranteed to work on Linux and Woe32.  Likely to work on the other
    127    Unixes (maybe except BeOS), under most conditions.  */
    128 static char *
    129 find_executable (const char *argv0)
    130 {
    131 #if defined WIN32_NATIVE || defined __CYGWIN__
    132   char location[MAX_PATH];
    133   int length = GetModuleFileName (NULL, location, sizeof (location));
    134   if (length < 0)
    135     return NULL;
    136   if (!IS_PATH_WITH_DIR (location))
    137     /* Shouldn't happen.  */
    138     return NULL;
    139   {
    140 #if defined __CYGWIN__
    141     /* cygwin-1.5.13 (2005-03-01) or newer would also allow a Linux-like
    142        implementation: readlink of "/proc/self/exe".  But using the
    143        result of the Win32 system call is simpler and is consistent with the
    144        code in relocatable.c.  */
    145     /* On Cygwin, we need to convert paths coming from Win32 system calls
    146        to the Unix-like slashified notation.  */
    147     static char location_as_posix_path[2 * MAX_PATH];
    148     /* There's no error return defined for cygwin_conv_to_posix_path.
    149        See cygwin-api/func-cygwin-conv-to-posix-path.html.
    150        Does it overflow the buffer of expected size MAX_PATH or does it
    151        truncate the path?  I don't know.  Let's catch both.  */
    152     cygwin_conv_to_posix_path (location, location_as_posix_path);
    153     location_as_posix_path[MAX_PATH - 1] = '\0';
    154     if (strlen (location_as_posix_path) >= MAX_PATH - 1)
    155       /* A sign of buffer overflow or path truncation.  */
    156       return NULL;
    157     /* Call canonicalize_file_name, because Cygwin supports symbolic links.  */
    158     return canonicalize_file_name (location_as_posix_path);
    159 #else
    160     return xstrdup (location);
    161 #endif
    162   }
    163 #else /* Unix && !Cygwin */
    164 #ifdef __linux__
    165   /* The executable is accessible as /proc/<pid>/exe.  In newer Linux
    166      versions, also as /proc/self/exe.  Linux >= 2.1 provides a symlink
    167      to the true pathname; older Linux versions give only device and ino,
    168      enclosed in brackets, which we cannot use here.  */
    169   {
    170     char *link;
    171 
    172     link = xreadlink ("/proc/self/exe");
    173     if (link != NULL && link[0] != '[')
    174       return link;
    175     if (executable_fd < 0)
    176       executable_fd = open ("/proc/self/exe", O_RDONLY, 0);
    177 
    178     {
    179       char buf[6+10+5];
    180       sprintf (buf, "/proc/%d/exe", getpid ());
    181       link = xreadlink (buf);
    182       if (link != NULL && link[0] != '[')
    183 	return link;
    184       if (executable_fd < 0)
    185 	executable_fd = open (buf, O_RDONLY, 0);
    186     }
    187   }
    188 #endif
    189 #if HAVE_MACH_O_DYLD_H && HAVE__NSGETEXECUTABLEPATH
    190   /* On MacOS X 10.2 or newer, the function
    191        int _NSGetExecutablePath (char *buf, unsigned long *bufsize);
    192      can be used to retrieve the executable's full path.  */
    193   char location[4096];
    194   unsigned long length = sizeof (location);
    195   if (_NSGetExecutablePath (location, &length) == 0
    196       && location[0] == '/')
    197     return canonicalize_file_name (location);
    198 #endif
    199   /* Guess the executable's full path.  We assume the executable has been
    200      called via execlp() or execvp() with properly set up argv[0].  The
    201      login(1) convention to add a '-' prefix to argv[0] is not supported.  */
    202   {
    203     bool has_slash = false;
    204     {
    205       const char *p;
    206       for (p = argv0; *p; p++)
    207 	if (*p == '/')
    208 	  {
    209 	    has_slash = true;
    210 	    break;
    211 	  }
    212     }
    213     if (!has_slash)
    214       {
    215 	/* exec searches paths without slashes in the directory list given
    216 	   by $PATH.  */
    217 	const char *path = getenv ("PATH");
    218 
    219 	if (path != NULL)
    220 	  {
    221 	    const char *p;
    222 	    const char *p_next;
    223 
    224 	    for (p = path; *p; p = p_next)
    225 	      {
    226 		const char *q;
    227 		size_t p_len;
    228 		char *concat_name;
    229 
    230 		for (q = p; *q; q++)
    231 		  if (*q == ':')
    232 		    break;
    233 		p_len = q - p;
    234 		p_next = (*q == '\0' ? q : q + 1);
    235 
    236 		/* We have a path item at p, of length p_len.
    237 		   Now concatenate the path item and argv0.  */
    238 		concat_name = (char *) xmalloc (p_len + strlen (argv0) + 2);
    239 #ifdef NO_XMALLOC
    240 		if (concat_name == NULL)
    241 		  return NULL;
    242 #endif
    243 		if (p_len == 0)
    244 		  /* An empty PATH element designates the current directory.  */
    245 		  strcpy (concat_name, argv0);
    246 		else
    247 		  {
    248 		    memcpy (concat_name, p, p_len);
    249 		    concat_name[p_len] = '/';
    250 		    strcpy (concat_name + p_len + 1, argv0);
    251 		  }
    252 		if (maybe_executable (concat_name))
    253 		  return canonicalize_file_name (concat_name);
    254 		free (concat_name);
    255 	      }
    256 	  }
    257 	/* Not found in the PATH, assume the current directory.  */
    258       }
    259     /* exec treats paths containing slashes as relative to the current
    260        directory.  */
    261     if (maybe_executable (argv0))
    262       return canonicalize_file_name (argv0);
    263   }
    264   /* No way to find the executable.  */
    265   return NULL;
    266 #endif
    267 }
    268 
    269 /* Full pathname of executable, or NULL.  */
    270 static char *executable_fullname;
    271 
    272 static void
    273 prepare_relocate (const char *orig_installprefix, const char *orig_installdir,
    274 		  const char *argv0)
    275 {
    276   const char *curr_prefix;
    277 
    278   /* Determine the full pathname of the current executable.  */
    279   executable_fullname = find_executable (argv0);
    280 
    281   /* Determine the current installation prefix from it.  */
    282   curr_prefix = compute_curr_prefix (orig_installprefix, orig_installdir,
    283 				     executable_fullname);
    284   if (curr_prefix != NULL)
    285     /* Now pass this prefix to all copies of the relocate.c source file.  */
    286     set_relocation_prefix (orig_installprefix, curr_prefix);
    287 }
    288 
    289 /* Set program_name, based on argv[0], and original installation prefix and
    290    directory, for relocatability.  */
    291 void
    292 set_program_name_and_installdir (const char *argv0,
    293 				 const char *orig_installprefix,
    294 				 const char *orig_installdir)
    295 {
    296   const char *argv0_stripped = argv0;
    297 
    298   /* Relocatable programs are renamed to .bin by install-reloc.  Or, more
    299      generally, their suffix is changed from $exeext to .bin$exeext.
    300      Remove the ".bin" here.  */
    301   {
    302     size_t argv0_len = strlen (argv0);
    303     const size_t exeext_len = sizeof (EXEEXT) - sizeof ("");
    304     if (argv0_len > 4 + exeext_len)
    305       if (memcmp (argv0 + argv0_len - exeext_len - 4, ".bin", 4) == 0)
    306 	{
    307 	  if (sizeof (EXEEXT) > sizeof (""))
    308 	    {
    309 	      /* Compare using an inlined copy of c_strncasecmp(), because
    310 		 the filenames may have undergone a case conversion since
    311 		 they were packaged.  In other words, EXEEXT may be ".exe"
    312 		 on one system and ".EXE" on another.  */
    313 	      static const char exeext[] = EXEEXT;
    314 	      const char *s1 = argv0 + argv0_len - exeext_len;
    315 	      const char *s2 = exeext;
    316 	      for (; *s1 != '\0'; s1++, s2++)
    317 		{
    318 		  unsigned char c1 = *s1;
    319 		  unsigned char c2 = *s2;
    320 		  if ((c1 >= 'A' && c1 <= 'Z' ? c1 - 'A' + 'a' : c1)
    321 		      != (c2 >= 'A' && c2 <= 'Z' ? c2 - 'A' + 'a' : c2))
    322 		    goto done_stripping;
    323 		}
    324 	    }
    325 	  /* Remove ".bin" before EXEEXT or its equivalent.  */
    326 	  {
    327 	    char *shorter = (char *) xmalloc (argv0_len - 4 + 1);
    328 #ifdef NO_XMALLOC
    329 	    if (shorter != NULL)
    330 #endif
    331 	      {
    332 		memcpy (shorter, argv0, argv0_len - exeext_len - 4);
    333 		if (sizeof (EXEEXT) > sizeof (""))
    334 		  memcpy (shorter + argv0_len - exeext_len - 4,
    335 			  argv0 + argv0_len - exeext_len - 4,
    336 			  exeext_len);
    337 		shorter[argv0_len - 4] = '\0';
    338 		argv0_stripped = shorter;
    339 	      }
    340 	  }
    341 	 done_stripping: ;
    342       }
    343   }
    344 
    345   set_program_name (argv0_stripped);
    346 
    347   prepare_relocate (orig_installprefix, orig_installdir, argv0);
    348 }
    349 
    350 /* Return the full pathname of the current executable, based on the earlier
    351    call to set_program_name_and_installdir.  Return NULL if unknown.  */
    352 char *
    353 get_full_program_name (void)
    354 {
    355   return executable_fullname;
    356 }
    357 
    358 #endif
    359