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