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