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