1 /* Provide relocatable programs.
2 Copyright (C) 2003-2004 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 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
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, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
37 #if defined _WIN32 || defined __WIN32__
38 # undef WIN32 /* avoid warning on mingw32 */
43 # define WIN32_LEAN_AND_MEAN
47 #include "xreadlink.h"
48 #include "canonicalize.h"
49 #include "relocatable.h"
52 # define xmalloc malloc
58 ISSLASH(C) tests whether C is a directory separator character.
59 IS_PATH_WITH_DIR(P) tests whether P contains a directory specification.
61 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
62 /* Win32, Cygwin, OS/2, DOS */
63 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
64 # define HAS_DEVICE(P) \
65 ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
67 # define IS_PATH_WITH_DIR(P) \
68 (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
69 # define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
72 # define ISSLASH(C) ((C) == '/')
73 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
74 # define FILE_SYSTEM_PREFIX_LEN(P) 0
77 #undef set_program_name
80 #if ENABLE_RELOCATABLE
83 /* File descriptor of the executable.
84 (Only used to verify that we find the correct executable.) */
85 static int executable_fd = -1;
88 /* Tests whether a given pathname may belong to the executable. */
90 maybe_executable (const char *filename)
93 if (access (filename, X_OK) < 0)
97 if (executable_fd >= 0)
99 /* If we already have an executable_fd, check that filename points to
102 struct stat statfile;
104 if (fstat (executable_fd, &statexe) >= 0)
106 if (stat (filename, &statfile) < 0)
108 if (!(statfile.st_dev
109 && statfile.st_dev == statexe.st_dev
110 && statfile.st_ino == statexe.st_ino))
120 /* Determine the full pathname of the current executable, freshly allocated.
121 Return NULL if unknown.
122 Guaranteed to work on Linux and Woe32. Likely to work on the other
123 Unixes (maybe except BeOS), under most conditions. */
125 find_executable (const char *argv0)
129 int length = GetModuleFileName (NULL, buf, sizeof (buf));
132 if (!IS_PATH_WITH_DIR (buf))
133 /* Shouldn't happen. */
135 return xstrdup (buf);
138 /* The executable is accessible as /proc/<pid>/exe. In newer Linux
139 versions, also as /proc/self/exe. Linux >= 2.1 provides a symlink
140 to the true pathname; older Linux versions give only device and ino,
141 enclosed in brackets, which we cannot use here. */
145 link = xreadlink ("/proc/self/exe");
146 if (link != NULL && link[0] != '[')
148 if (executable_fd < 0)
149 executable_fd = open ("/proc/self/exe", O_RDONLY, 0);
153 sprintf (buf, "/proc/%d/exe", getpid ());
154 link = xreadlink (buf);
155 if (link != NULL && link[0] != '[')
157 if (executable_fd < 0)
158 executable_fd = open (buf, O_RDONLY, 0);
162 /* Guess the executable's full path. We assume the executable has been
163 called via execlp() or execvp() with properly set up argv[0]. The
164 login(1) convention to add a '-' prefix to argv[0] is not supported. */
166 bool has_slash = false;
169 for (p = argv0; *p; p++)
178 /* exec searches paths without slashes in the directory list given
180 const char *path = getenv ("PATH");
187 for (p = path; *p; p = p_next)
197 p_next = (*q == '\0' ? q : q + 1);
199 /* We have a path item at p, of length p_len.
200 Now concatenate the path item and argv0. */
201 concat_name = (char *) xmalloc (p_len + strlen (argv0) + 2);
203 if (concat_name == NULL)
207 /* An empty PATH element designates the current directory. */
208 strcpy (concat_name, argv0);
211 memcpy (concat_name, p, p_len);
212 concat_name[p_len] = '/';
213 strcpy (concat_name + p_len + 1, argv0);
215 if (maybe_executable (concat_name))
216 return canonicalize_file_name (concat_name);
220 /* Not found in the PATH, assume the current directory. */
222 /* exec treats paths containing slashes as relative to the current
224 if (maybe_executable (argv0))
225 return canonicalize_file_name (argv0);
227 /* No way to find the executable. */
232 /* Full pathname of executable, or NULL. */
233 static char *executable_fullname;
236 prepare_relocate (const char *orig_installprefix, const char *orig_installdir,
239 const char *curr_prefix;
241 /* Determine the full pathname of the current executable. */
242 executable_fullname = find_executable (argv0);
244 /* Determine the current installation prefix from it. */
245 curr_prefix = compute_curr_prefix (orig_installprefix, orig_installdir,
246 executable_fullname);
247 if (curr_prefix != NULL)
248 /* Now pass this prefix to all copies of the relocate.c source file. */
249 set_relocation_prefix (orig_installprefix, curr_prefix);
252 /* Set program_name, based on argv[0], and original installation prefix and
253 directory, for relocatability. */
255 set_program_name_and_installdir (const char *argv0,
256 const char *orig_installprefix,
257 const char *orig_installdir)
259 const char *argv0_stripped = argv0;
261 /* Relocatable programs are renamed to .bin by install-reloc. Remove
264 size_t argv0_len = strlen (argv0);
265 if (argv0_len > 4 && memcmp (argv0 + argv0_len - 4, ".bin", 4) == 0)
267 char *shorter = (char *) xmalloc (argv0_len - 4 + 1);
272 memcpy (shorter, argv0, argv0_len - 4);
273 shorter[argv0_len - 4] = '\0';
274 argv0_stripped = shorter;
279 set_program_name (argv0_stripped);
281 prepare_relocate (orig_installprefix, orig_installdir, argv0);
284 /* Return the full pathname of the current executable, based on the earlier
285 call to set_program_name_and_installdir. Return NULL if unknown. */
287 get_full_program_name (void)
289 return executable_fullname;