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