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