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