1 /* Provide relocatable programs.
2 Copyright (C) 2003 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 it
6 under the terms of the GNU Library General Public License as published
7 by the Free Software Foundation; either version 2, or (at your option)
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 GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
38 #if defined _WIN32 || defined __WIN32__
39 # undef WIN32 /* avoid warning on mingw32 */
44 # define WIN32_LEAN_AND_MEAN
48 #include "xreadlink.h"
49 #include "canonicalize.h"
50 #include "relocatable.h"
53 # define xmalloc malloc
59 ISSLASH(C) tests whether C is a directory separator character.
60 IS_PATH_WITH_DIR(P) tests whether P contains a directory specification.
62 #if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
63 /* Win32, OS/2, DOS */
64 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
65 # define HAS_DEVICE(P) \
66 ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
68 # define IS_PATH_WITH_DIR(P) \
69 (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
70 # define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
73 # define ISSLASH(C) ((C) == '/')
74 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
75 # define FILESYSTEM_PREFIX_LEN(P) 0
78 #undef set_program_name
81 #if ENABLE_RELOCATABLE
84 /* File descriptor of the executable.
85 (Only used to verify that we find the correct executable.) */
86 static int executable_fd = -1;
89 /* Tests whether a given pathname may belong to the executable. */
91 maybe_executable (const char *filename)
94 if (access (filename, X_OK) < 0)
98 if (executable_fd >= 0)
100 /* If we already have an executable_fd, check that filename points to
103 struct stat statfile;
105 if (fstat (executable_fd, &statexe) >= 0)
107 if (stat (filename, &statfile) < 0)
109 if (!(statfile.st_dev
110 && statfile.st_dev == statexe.st_dev
111 && statfile.st_ino == statexe.st_ino))
121 /* Determine the full pathname of the current executable, freshly allocated.
122 Return NULL if unknown.
123 Guaranteed to work on Linux and Woe32. Likely to work on the other
124 Unixes (maybe except BeOS), under most conditions. */
126 find_executable (const char *argv0)
130 int length = GetModuleFileName (NULL, buf, sizeof (buf));
133 if (!IS_PATH_WITH_DIR (buf))
134 /* Shouldn't happen. */
136 return xstrdup (buf);
139 /* The executable is accessible as /proc/<pid>/exe. In newer Linux
140 versions, also as /proc/self/exe. Linux >= 2.1 provides a symlink
141 to the true pathname; older Linux versions give only device and ino,
142 enclosed in brackets, which we cannot use here. */
146 link = xreadlink ("/proc/self/exe");
147 if (link != NULL && link[0] != '[')
149 if (executable_fd < 0)
150 executable_fd = open ("/proc/self/exe", O_RDONLY, 0);
154 sprintf (buf, "/proc/%d/exe", getpid ());
155 link = xreadlink (buf);
156 if (link != NULL && link[0] != '[')
158 if (executable_fd < 0)
159 executable_fd = open (buf, O_RDONLY, 0);
163 /* Guess the executable's full path. We assume the executable has been
164 called via execlp() or execvp() with properly set up argv[0]. The
165 login(1) convention to add a '-' prefix to argv[0] is not supported. */
167 bool has_slash = false;
170 for (p = argv0; *p; p++)
179 /* exec searches paths without slashes in the directory list given
181 const char *path = getenv ("PATH");
188 for (p = path; *p; p = p_next)
198 p_next = (*q == '\0' ? q : q + 1);
200 /* We have a path item at p, of length p_len.
201 Now concatenate the path item and argv0. */
202 concat_name = (char *) xmalloc (p_len + strlen (argv0) + 2);
204 if (concat_name == NULL)
208 /* An empty PATH element designates the current directory. */
209 strcpy (concat_name, argv0);
212 memcpy (concat_name, p, p_len);
213 concat_name[p_len] = '/';
214 strcpy (concat_name + p_len + 1, argv0);
216 if (maybe_executable (concat_name))
217 return canonicalize_file_name (concat_name);
221 /* Not found in the PATH, assume the current directory. */
223 /* exec treats paths containing slashes as relative to the current
225 if (maybe_executable (argv0))
226 return canonicalize_file_name (argv0);
228 /* No way to find the executable. */
233 /* Full pathname of executable, or NULL. */
234 static char *executable_fullname;
237 prepare_relocate (const char *orig_installprefix, const char *orig_installdir,
240 const char *curr_prefix;
242 /* Determine the full pathname of the current executable. */
243 executable_fullname = find_executable (argv0);
245 /* Determine the current installation prefix from it. */
246 curr_prefix = compute_curr_prefix (orig_installprefix, orig_installdir,
247 executable_fullname);
248 if (curr_prefix != NULL)
249 /* Now pass this prefix to all copies of the relocate.c source file. */
250 set_relocation_prefix (orig_installprefix, curr_prefix);
253 /* Set program_name, based on argv[0], and original installation prefix and
254 directory, for relocatability. */
256 set_program_name_and_installdir (const char *argv0,
257 const char *orig_installprefix,
258 const char *orig_installdir)
260 const char *argv0_stripped = argv0;
262 /* Relocatable programs are renamed to .bin by install-reloc. Remove
265 size_t argv0_len = strlen (argv0);
266 if (argv0_len > 4 && memcmp (argv0 + argv0_len - 4, ".bin", 4) == 0)
268 char *shorter = (char *) xmalloc (argv0_len - 4 + 1);
273 memcpy (shorter, argv0, argv0_len - 4);
274 shorter[argv0_len - 4] = '\0';
275 argv0_stripped = shorter;
280 set_program_name (argv0_stripped);
282 prepare_relocate (orig_installprefix, orig_installdir, argv0);
285 /* Return the full pathname of the current executable, based on the earlier
286 call to set_program_name_and_installdir. Return NULL if unknown. */
288 get_full_program_name (void)
290 return executable_fullname;