openat: don’t close (-1)
[gnulib.git] / lib / progreloc.c
blob9f49ed16ff49bf7ff008caf787d03f63f092d41b
1 /* Provide relocatable programs.
2 Copyright (C) 2003-2024 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 3 of the License, or
8 (at your option) any later version.
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, see <https://www.gnu.org/licenses/>. */
19 #define _GL_USE_STDLIB_ALLOC 1
20 #include <config.h>
22 /* Specification. */
23 #include "progname.h"
25 #include <errno.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <sys/stat.h>
33 /* Get declaration of _NSGetExecutablePath on Mac OS X 10.2 or newer. */
34 #if HAVE_MACH_O_DYLD_H
35 # include <mach-o/dyld.h>
36 #endif
38 #if defined _WIN32 && !defined __CYGWIN__
39 # define WINDOWS_NATIVE
40 #endif
42 #ifdef WINDOWS_NATIVE
43 # define WIN32_LEAN_AND_MEAN
44 # include <windows.h>
45 #endif
47 #ifdef __EMX__
48 # define INCL_DOS
49 # include <os2.h>
50 #endif
52 #include "relocatable.h"
54 #ifdef NO_XMALLOC
55 # include "areadlink.h"
56 # define xreadlink areadlink
57 #else
58 # include "xreadlink.h"
59 #endif
61 #ifdef NO_XMALLOC
62 # define xmalloc malloc
63 # define xstrdup strdup
64 #else
65 # include "xalloc.h"
66 #endif
68 #ifndef O_EXEC
69 # define O_EXEC O_RDONLY /* This is often close enough in older systems. */
70 #endif
72 #if defined IN_RELOCWRAPPER && (!defined O_CLOEXEC || GNULIB_defined_O_CLOEXEC)
73 # undef O_CLOEXEC
74 # define O_CLOEXEC 0
75 #endif
77 /* Declare canonicalize_file_name.
78 The <stdlib.h> included above may be the system's one, not the gnulib
79 one. */
80 extern char * canonicalize_file_name (const char *name);
82 #if defined WINDOWS_NATIVE
83 /* Don't assume that UNICODE is not defined. */
84 # undef GetModuleFileName
85 # define GetModuleFileName GetModuleFileNameA
86 #endif
88 /* Pathname support.
89 ISSLASH(C) tests whether C is a directory separator character.
90 IS_FILE_NAME_WITH_DIR(P) tests whether P contains a directory specification.
92 #if (defined _WIN32 && !defined __CYGWIN__) || defined __EMX__ || defined __DJGPP__
93 /* Native Windows, OS/2, DOS */
94 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
95 # define HAS_DEVICE(P) \
96 ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
97 && (P)[1] == ':')
98 # define IS_FILE_NAME_WITH_DIR(P) \
99 (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
100 # define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
101 #else
102 /* Unix */
103 # define ISSLASH(C) ((C) == '/')
104 # define IS_FILE_NAME_WITH_DIR(P) (strchr (P, '/') != NULL)
105 # define FILE_SYSTEM_PREFIX_LEN(P) 0
106 #endif
108 /* Use the system functions, not the gnulib overrides in this file. */
109 #undef sprintf
111 #undef set_program_name
114 #if ENABLE_RELOCATABLE
116 #ifdef __sun
118 /* Helper function, from gnulib module 'safe-read'. */
119 static size_t
120 safe_read (int fd, void *buf, size_t count)
122 for (;;)
124 ssize_t result = read (fd, buf, count);
126 if (0 <= result || errno != EINTR)
127 return result;
131 /* Helper function, from gnulib module 'full-read'. */
132 static size_t
133 full_read (int fd, void *buf, size_t count)
135 size_t total = 0;
136 char *ptr = (char *) buf;
138 while (count > 0)
140 size_t n = safe_read (fd, ptr, count);
141 if (n == (size_t) -1)
142 break;
143 if (n == 0)
145 errno = 0;
146 break;
148 total += n;
149 ptr += n;
150 count -= n;
153 return total;
156 #endif
158 #if defined __linux__ || defined __CYGWIN__
159 /* File descriptor of the executable.
160 (Only used to verify that we find the correct executable.) */
161 static int executable_fd = -1;
162 #endif
164 /* Define this function only when it's needed. */
165 #if !(defined WINDOWS_NATIVE || defined __EMX__)
167 /* Tests whether a given filename may belong to the executable. */
168 static bool
169 maybe_executable (const char *filename)
171 /* The native Windows API lacks the access() function. */
172 # if !defined WINDOWS_NATIVE
173 if (access (filename, X_OK) < 0)
174 return false;
175 # endif
177 # if defined __linux__ || defined __CYGWIN__
178 if (executable_fd >= 0)
180 /* If we already have an executable_fd, check that filename points to
181 the same inode. */
182 struct stat statexe;
183 struct stat statfile;
185 if (fstat (executable_fd, &statexe) >= 0)
186 return (stat (filename, &statfile) >= 0
187 && statfile.st_dev
188 && statfile.st_dev == statexe.st_dev
189 && statfile.st_ino == statexe.st_ino);
191 # endif
193 /* Check that the filename does not point to a directory. */
195 struct stat statfile;
197 return (stat (filename, &statfile) >= 0
198 && ! S_ISDIR (statfile.st_mode));
202 #endif
204 /* Determine the full pathname of the current executable, freshly allocated.
205 Return NULL if unknown.
206 Guaranteed to work on Linux and native Windows. Likely to work on the
207 other Unixes (maybe except BeOS), under most conditions. */
208 static char *
209 find_executable (const char *argv0)
211 #if defined WINDOWS_NATIVE
212 /* Native Windows only.
213 On Cygwin, it is better to use the Cygwin provided /proc interface, than
214 to use native Windows API and cygwin_conv_to_posix_path, because it
215 supports longer file names
216 (see <https://cygwin.com/ml/cygwin/2011-01/msg00410.html>). */
217 char location[MAX_PATH];
218 int length = GetModuleFileName (NULL, location, sizeof (location));
219 if (length < 0)
220 return NULL;
221 if (!IS_FILE_NAME_WITH_DIR (location))
222 /* Shouldn't happen. */
223 return NULL;
224 return xstrdup (location);
225 #elif defined __EMX__
226 PPIB ppib;
227 char location[CCHMAXPATH];
229 /* See http://cyberkinetica.homeunix.net/os2tk45/cp1/619_L2H_DosGetInfoBlocksSynt.html
230 for specification of DosGetInfoBlocks(). */
231 if (DosGetInfoBlocks (NULL, &ppib))
232 return NULL;
234 /* See http://cyberkinetica.homeunix.net/os2tk45/cp1/1247_L2H_DosQueryModuleNameSy.html
235 for specification of DosQueryModuleName(). */
236 if (DosQueryModuleName (ppib->pib_hmte, sizeof (location), location))
237 return NULL;
239 _fnslashify (location);
241 return xstrdup (location);
242 #else /* Unix */
243 # if defined __linux__
244 /* The executable is accessible as /proc/<pid>/exe. In newer Linux
245 versions, also as /proc/self/exe. Linux >= 2.1 provides a symlink
246 to the true pathname; older Linux versions give only device and ino,
247 enclosed in brackets, which we cannot use here. */
249 char *link;
251 link = xreadlink ("/proc/self/exe");
252 if (link != NULL && link[0] != '[')
253 return link;
254 if (executable_fd < 0)
255 executable_fd = open ("/proc/self/exe", O_EXEC | O_CLOEXEC, 0);
258 char buf[6+10+5];
259 sprintf (buf, "/proc/%d/exe", getpid ());
260 link = xreadlink (buf);
261 if (link != NULL && link[0] != '[')
262 return link;
263 if (executable_fd < 0)
264 executable_fd = open (buf, O_EXEC | O_CLOEXEC, 0);
267 # endif
268 # if defined __ANDROID__ || defined __FreeBSD_kernel__
269 /* On Android and GNU/kFreeBSD, the executable is accessible as
270 /proc/<pid>/exe and /proc/self/exe. */
272 char *link;
274 link = xreadlink ("/proc/self/exe");
275 if (link != NULL)
276 return link;
278 # endif
279 # if defined __FreeBSD__ || defined __DragonFly__
280 /* In FreeBSD >= 5.0, the executable is accessible as /proc/<pid>/file and
281 /proc/curproc/file. */
283 char *link;
285 link = xreadlink ("/proc/curproc/file");
286 if (link != NULL)
288 if (strcmp (link, "unknown") != 0)
289 return link;
290 free (link);
293 # endif
294 # if defined __NetBSD__
295 /* In NetBSD >= 4.0, the executable is accessible as /proc/<pid>/exe and
296 /proc/curproc/exe. */
298 char *link;
300 link = xreadlink ("/proc/curproc/exe");
301 if (link != NULL)
302 return link;
304 # endif
305 # if defined __sun
306 /* On Solaris >= 11.4, /proc/<pid>/execname and /proc/self/execname contains
307 the name of the executable, either as an absolute file name or relative to
308 the current directory. */
310 char namebuf[4096];
311 int fd = open ("/proc/self/execname", O_RDONLY | O_CLOEXEC, 0);
312 if (fd >= 0)
314 size_t len = full_read (fd, namebuf, sizeof (namebuf));
315 close (fd);
316 if (len > 0 && len < sizeof (namebuf))
318 namebuf[len] = '\0';
319 return canonicalize_file_name (namebuf);
323 # endif
324 # if defined __CYGWIN__
325 /* The executable is accessible as /proc/<pid>/exe, at least in
326 Cygwin >= 1.5. */
328 char *link;
330 link = xreadlink ("/proc/self/exe");
331 if (link != NULL)
332 return link;
333 if (executable_fd < 0)
334 executable_fd = open ("/proc/self/exe", O_EXEC | O_CLOEXEC, 0);
336 # endif
337 # if HAVE_MACH_O_DYLD_H && HAVE__NSGETEXECUTABLEPATH
338 /* On Mac OS X 10.2 or newer, the function
339 int _NSGetExecutablePath (char *buf, uint32_t *bufsize);
340 can be used to retrieve the executable's full path. */
341 char location[4096];
342 unsigned int length = sizeof (location);
343 if (_NSGetExecutablePath (location, &length) == 0
344 && location[0] == '/')
345 return canonicalize_file_name (location);
346 # endif
347 /* Guess the executable's full path. We assume the executable has been
348 called via execlp() or execvp() with properly set up argv[0]. The
349 login(1) convention to add a '-' prefix to argv[0] is not supported. */
351 bool has_slash = false;
353 const char *p;
354 for (p = argv0; *p; p++)
355 if (*p == '/')
357 has_slash = true;
358 break;
361 if (!has_slash)
363 /* exec searches paths without slashes in the directory list given
364 by $PATH. */
365 const char *path = getenv ("PATH");
367 if (path != NULL)
369 const char *p;
370 const char *p_next;
372 for (p = path; *p; p = p_next)
374 const char *q;
375 size_t p_len;
376 char *concat_name;
378 for (q = p; *q; q++)
379 if (*q == ':')
380 break;
381 p_len = q - p;
382 p_next = (*q == '\0' ? q : q + 1);
384 /* We have a path item at p, of length p_len.
385 Now concatenate the path item and argv0. */
386 concat_name = (char *) xmalloc (p_len + strlen (argv0) + 2);
387 # ifdef NO_XMALLOC
388 if (concat_name == NULL)
389 return NULL;
390 # endif
391 if (p_len == 0)
392 /* An empty PATH element designates the current directory. */
393 strcpy (concat_name, argv0);
394 else
396 memcpy (concat_name, p, p_len);
397 concat_name[p_len] = '/';
398 strcpy (concat_name + p_len + 1, argv0);
400 if (maybe_executable (concat_name))
401 return canonicalize_file_name (concat_name);
402 free (concat_name);
405 /* Not found in the PATH, assume the current directory. */
407 /* exec treats paths containing slashes as relative to the current
408 directory. */
409 if (maybe_executable (argv0))
410 return canonicalize_file_name (argv0);
412 /* No way to find the executable. */
413 return NULL;
414 #endif
417 /* Full pathname of executable, or NULL. */
418 static char *executable_fullname;
420 static void
421 prepare_relocate (const char *orig_installprefix, const char *orig_installdir,
422 const char *argv0)
424 char *curr_prefix;
426 /* Determine the full pathname of the current executable. */
427 executable_fullname = find_executable (argv0);
429 /* Determine the current installation prefix from it. */
430 curr_prefix = compute_curr_prefix (orig_installprefix, orig_installdir,
431 executable_fullname);
432 if (curr_prefix != NULL)
434 /* Now pass this prefix to all copies of the relocate.c source file. */
435 set_relocation_prefix (orig_installprefix, curr_prefix);
437 free (curr_prefix);
441 /* Set program_name, based on argv[0], and original installation prefix and
442 directory, for relocatability. */
443 void
444 set_program_name_and_installdir (const char *argv0,
445 const char *orig_installprefix,
446 const char *orig_installdir)
448 const char *argv0_stripped = argv0;
450 /* Relocatable programs are renamed to .bin by install-reloc. Or, more
451 generally, their suffix is changed from $exeext to .bin$exeext.
452 Remove the ".bin" here. */
454 size_t argv0_len = strlen (argv0);
455 const size_t exeext_len = sizeof (EXEEXT) - sizeof ("");
456 if (argv0_len > 4 + exeext_len)
457 if (memcmp (argv0 + argv0_len - exeext_len - 4, ".bin", 4) == 0)
459 if (sizeof (EXEEXT) > sizeof (""))
461 /* Compare using an inlined copy of c_strncasecmp(), because
462 the filenames may have undergone a case conversion since
463 they were packaged. In other words, EXEEXT may be ".exe"
464 on one system and ".EXE" on another. */
465 static const char exeext[] = EXEEXT;
466 const char *s1 = argv0 + argv0_len - exeext_len;
467 const char *s2 = exeext;
468 for (; *s1 != '\0'; s1++, s2++)
470 unsigned char c1 = *s1;
471 unsigned char c2 = *s2;
472 if ((c1 >= 'A' && c1 <= 'Z' ? c1 - 'A' + 'a' : c1)
473 != (c2 >= 'A' && c2 <= 'Z' ? c2 - 'A' + 'a' : c2))
474 goto done_stripping;
477 /* Remove ".bin" before EXEEXT or its equivalent. */
479 char *shorter = (char *) xmalloc (argv0_len - 4 + 1);
480 #ifdef NO_XMALLOC
481 if (shorter != NULL)
482 #endif
484 memcpy (shorter, argv0, argv0_len - exeext_len - 4);
485 if (sizeof (EXEEXT) > sizeof (""))
486 memcpy (shorter + argv0_len - exeext_len - 4,
487 argv0 + argv0_len - exeext_len - 4,
488 exeext_len);
489 shorter[argv0_len - 4] = '\0';
490 argv0_stripped = shorter;
493 done_stripping: ;
497 set_program_name (argv0_stripped);
499 prepare_relocate (orig_installprefix, orig_installdir, argv0);
502 /* Return the full pathname of the current executable, based on the earlier
503 call to set_program_name_and_installdir. Return NULL if unknown. */
504 char *
505 get_full_program_name (void)
507 return executable_fullname;
510 #endif