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