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