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