1 /*
2 * Finding the full path of the executable.
3 * Bruno Haible 20.12.1994, 2017
4 * Sam Steingold 2004-2006, 2008, 2017
5 */
6
7 /* This assumes that the executable is not removed or renamed while
8 running. */
9
10 /* file name of the executable */
11 static char* executable_name = NULL;
12 #if defined(WIN32_NATIVE) || defined(UNIX_CYGWIN)
13 /* note that up to and including win2000, detaching from a process kills it
14 <https://sourceware.org/ml/cygwin/2003-06/msg00932.html>
15 <https://sourceware.org/ml/cygwin/2003-06/msg00933.html>
16 <https://sourceware.org/ml/cygwin/2003-06/msg00937.html>
17 <http://article.gmane.org/gmane.os.cygwin/32245>
18 <http://article.gmane.org/gmane.os.cygwin/32246>
19 <http://article.gmane.org/gmane.os.cygwin/32250> */
20 #define default_executable_name "lisp.exe"
21 #else
22 #define default_executable_name "lisp.run"
23 #endif
24
25 /* file descriptor of the executable
26 (Only used to verify that we find the correct executable.) */
27 static int executable_fd = -1;
28
29 #if defined(UNIX)
30 /* maybe_executable(pathname)
31 checks whether a given pathname may belong to the executable. */
maybe_executable(const char * filename)32 static int maybe_executable (const char * filename) {
33 struct stat statexe;
34 struct stat statfile;
35 if (access(filename,R_OK|X_OK) < 0)
36 return 0/*false*/;
37 if (executable_fd < 0)
38 return 1/*true*/;
39 /* If we already have an executable_fd, check that filename points to
40 the same inode. */
41 if (fstat(executable_fd,&statexe) < 0)
42 return 1/*true*/;
43 if (stat(filename,&statfile) < 0)
44 return 0/*false*/;
45 if (statfile.st_dev
46 && statfile.st_dev == statexe.st_dev
47 && statfile.st_ino == statexe.st_ino)
48 return 1/*true*/;
49 return 0/*false*/;
50 }
51 #endif
52
53 /* return the executable name */
54 char *get_executable_name (void);
get_executable_name(void)55 char *get_executable_name (void) { return executable_name; }
56
57 /* find_executable(program_name)
58 is to be called immediately after the program starts,
59 with program_name = argv[0],
60 before any chdir() operation and before any setenv("PATH",...).
61 It determines the full program path and opens a file descriptor to
62 the executable, for later use.
63 Return value is 0 if successful, -1 and errno set if not. */
find_executable(const char * program_name)64 int find_executable (const char * program_name) {
65 /* Do not need to execute this more than once. */
66 if (executable_name != NULL) return 0;
67 #if defined(WIN32_NATIVE)
68 { /* an illustration that win32 API can be sometimes useful */
69 char execname[MAX_PATH];
70 if (!GetModuleFileName(NULL,execname,MAX_PATH))
71 goto notfound;
72 executable_name = (char*)malloc(strlen(execname)+1);
73 strcpy(executable_name,execname);
74 return 0; }
75 #elif defined(UNIX)
76 #if defined(UNIX_LINUX) || defined(UNIX_CYGWIN)
77 { /* The executable is accessible as /proc/<pid>/exe. We try this first
78 because it is safer: no race condition w.r.t. the file system. It may
79 fail, however, if the user has not compiled /proc support into his
80 kernel. */
81 int fd = open("/proc/self/exe",O_RDONLY,my_open_mask);
82 if (fd >= 0)
83 executable_fd = fd;
84 }
85 #endif
86 { /* Now we guess the executable's full path. We assume the executable
87 has been called via execlp() or execvp() with properly set up argv[0].
88 The login(1) convention to add a '-' prefix to argv[0] is not supported. */
89 const char * p;
90 for (p = program_name; *p; p++)
91 if (*p == '/')
92 goto has_slash;
93 }
94 { /* exec searches paths without slashes in the directory list given
95 by $PATH. */
96 const char * path = getenv("PATH");
97 if (!(path==NULL)) {
98 const char * p;
99 const char * p_next;
100 for (p = path; *p; p = p_next) {
101 const char * q;
102 unsigned long p_len;
103 for (q = p; *q; q++) { if (*q == ':') break; }
104 p_len = q-p; p_next = (*q=='\0' ? q : q+1);
105 { /* We have a path item at p, of length p_len.
106 Now concatenate the path item and program_name. */
107 char * concat_name =
108 (char*) malloc(p_len + strlen(program_name) + 2);
109 if (concat_name == NULL) { errno = ENOMEM; goto notfound; }
110 if (p_len == 0) {
111 /* empty PATH element designates the current directory */
112 strcpy(concat_name,program_name);
113 } else {
114 memcpy(concat_name, p, p_len);
115 concat_name[p_len] = '/';
116 strcpy(concat_name+p_len+1, program_name);
117 }
118 if (maybe_executable(concat_name)) {
119 /* Assume we have found the executable */
120 program_name = concat_name; goto resolve;
121 }
122 free(concat_name);
123 }
124 }
125 }
126 /* Not found in the PATH, assume the current directory. */
127 }
128 has_slash:
129 /* exec treats paths containing slashes as relative to the current
130 directory */
131 if (maybe_executable(program_name)) {
132 resolve:
133 /* resolve program_name: */
134 # if !defined(MAXPATHLEN)
135 # define MAXPATHLEN 1024 /* see unix.d */
136 # endif
137 executable_name = (char*) malloc(MAXPATHLEN);
138 if (executable_name == NULL) { errno = ENOMEM; goto notfound; }
139 if (realpath(program_name,executable_name) == NULL) {
140 free(executable_name); goto notfound;
141 }
142 #if defined(UNIX_CYGWIN)
143 { /* cygwin does not append ".exe" on its own */
144 int len = strlen(executable_name);
145 if (!(len > 4 && (executable_name[len-4] == '.') &&
146 (executable_name[len-1] == 'e' || executable_name[len-1] == 'E') &&
147 (executable_name[len-2] == 'x' || executable_name[len-2] == 'X') &&
148 (executable_name[len-3] == 'e' || executable_name[len-3] == 'E')))
149 strcat(executable_name,".exe");
150 }
151 #endif
152 return 0;
153 }
154 errno = ENOENT;
155 #else
156 #error "not implemented: find_executable()"
157 #endif
158 notfound:
159 executable_name = (char*)default_executable_name; return -1;
160 }
161