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