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