1 /* Provide relocatable programs.
2 Copyright (C) 2003-2009 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2003.
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.
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.
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/>. */
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>
37 #if defined _WIN32 || defined __WIN32__
41 #if defined WIN32_NATIVE || defined __CYGWIN__
42 # define WIN32_LEAN_AND_MEAN
46 #include "canonicalize.h"
47 #include "relocatable.h"
50 # include "areadlink.h"
51 # define xreadlink areadlink
53 # include "xreadlink.h"
57 # define xmalloc malloc
58 # define xstrdup strdup
64 ISSLASH(C) tests whether C is a directory separator character.
65 IS_PATH_WITH_DIR(P) tests whether P contains a directory specification.
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')) \
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)
78 # define ISSLASH(C) ((C) == '/')
79 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
80 # define FILE_SYSTEM_PREFIX_LEN(P) 0
83 /* The results of open() in this file are not used with fchdir,
84 therefore save some unnecessary work in fchdir.c. */
88 #undef set_program_name
91 #if ENABLE_RELOCATABLE
94 /* File descriptor of the executable.
95 (Only used to verify that we find the correct executable.) */
96 static int executable_fd = -1;
99 /* Tests whether a given pathname may belong to the executable. */
101 maybe_executable (const char *filename)
103 /* Woe32 lacks the access() function, but Cygwin doesn't. */
104 #if !(defined WIN32_NATIVE && !defined __CYGWIN__)
105 if (access (filename, X_OK) < 0)
109 if (executable_fd >= 0)
111 /* If we already have an executable_fd, check that filename points to
114 struct stat statfile;
116 if (fstat (executable_fd, &statexe) >= 0)
118 if (stat (filename, &statfile) < 0)
120 if (!(statfile.st_dev
121 && statfile.st_dev == statexe.st_dev
122 && statfile.st_ino == statexe.st_ino))
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. */
137 find_executable (const char *argv0)
139 #if defined WIN32_NATIVE || defined __CYGWIN__
140 char location[MAX_PATH];
141 int length = GetModuleFileName (NULL, location, sizeof (location));
144 if (!IS_PATH_WITH_DIR (location))
145 /* Shouldn't happen. */
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. */
165 /* Call canonicalize_file_name, because Cygwin supports symbolic links. */
166 return canonicalize_file_name (location_as_posix_path);
168 return xstrdup (location);
171 #else /* Unix && !Cygwin */
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. */
180 link = xreadlink ("/proc/self/exe");
181 if (link != NULL && link[0] != '[')
183 if (executable_fd < 0)
184 executable_fd = open ("/proc/self/exe", O_RDONLY, 0);
188 sprintf (buf, "/proc/%d/exe", getpid ());
189 link = xreadlink (buf);
190 if (link != NULL && link[0] != '[')
192 if (executable_fd < 0)
193 executable_fd = open (buf, O_RDONLY, 0);
197 #if HAVE_MACH_O_DYLD_H && HAVE__NSGETEXECUTABLEPATH
198 /* On MacOS X 10.2 or newer, the function
199 int _NSGetExecutablePath (char *buf, uint32_t *bufsize);
200 can be used to retrieve the executable's full path. */
202 unsigned int length = sizeof (location);
203 if (_NSGetExecutablePath (location, &length) == 0
204 && location[0] == '/')
205 return canonicalize_file_name (location);
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. */
211 bool has_slash = false;
214 for (p = argv0; *p; p++)
223 /* exec searches paths without slashes in the directory list given
225 const char *path = getenv ("PATH");
232 for (p = path; *p; p = p_next)
242 p_next = (*q == '\0' ? q : q + 1);
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);
248 if (concat_name == NULL)
252 /* An empty PATH element designates the current directory. */
253 strcpy (concat_name, argv0);
256 memcpy (concat_name, p, p_len);
257 concat_name[p_len] = '/';
258 strcpy (concat_name + p_len + 1, argv0);
260 if (maybe_executable (concat_name))
261 return canonicalize_file_name (concat_name);
265 /* Not found in the PATH, assume the current directory. */
267 /* exec treats paths containing slashes as relative to the current
269 if (maybe_executable (argv0))
270 return canonicalize_file_name (argv0);
272 /* No way to find the executable. */
277 /* Full pathname of executable, or NULL. */
278 static char *executable_fullname;
281 prepare_relocate (const char *orig_installprefix, const char *orig_installdir,
286 /* Determine the full pathname of the current executable. */
287 executable_fullname = find_executable (argv0);
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)
294 /* Now pass this prefix to all copies of the relocate.c source file. */
295 set_relocation_prefix (orig_installprefix, curr_prefix);
301 /* Set program_name, based on argv[0], and original installation prefix and
302 directory, for relocatability. */
304 set_program_name_and_installdir (const char *argv0,
305 const char *orig_installprefix,
306 const char *orig_installdir)
308 const char *argv0_stripped = argv0;
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. */
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)
319 if (sizeof (EXEEXT) > sizeof (""))
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++)
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))
337 /* Remove ".bin" before EXEEXT or its equivalent. */
339 char *shorter = (char *) xmalloc (argv0_len - 4 + 1);
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,
349 shorter[argv0_len - 4] = '\0';
350 argv0_stripped = shorter;
357 set_program_name (argv0_stripped);
359 prepare_relocate (orig_installprefix, orig_installdir, argv0);
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. */
365 get_full_program_name (void)
367 return executable_fullname;