]> git.sur5r.net Git - i3/i3/blob - libi3/get_exe_path.c
Merge branch 'master' into next
[i3/i3] / libi3 / get_exe_path.c
1 #include <unistd.h>
2 #include <string.h>
3 #include <stdio.h>
4 #include <limits.h>
5 #include <stdlib.h>
6 #include <errno.h>
7
8 #include "libi3.h"
9
10 /*
11  * This function returns the absolute path to the executable it is running in.
12  *
13  * The implementation follows http://stackoverflow.com/a/933996/712014
14  *
15  * Returned value must be freed by the caller.
16  */
17 char *get_exe_path(const char *argv0) {
18         size_t destpath_size = 1024;
19         size_t tmp_size = 1024;
20         char *destpath = smalloc(destpath_size);
21         char *tmp = smalloc(tmp_size);
22
23
24 #if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
25         /* Linux and Debian/kFreeBSD provide /proc/self/exe */
26 #if defined(__linux__) || defined(__FreeBSD_kernel__)
27         const char *exepath = "/proc/self/exe";
28 #elif defined(__FreeBSD__)
29         const char *exepath = "/proc/curproc/file";
30 #endif
31         ssize_t linksize;
32
33         while ((linksize = readlink(exepath, destpath, destpath_size)) == (ssize_t)destpath_size) {
34                 destpath_size = destpath_size * 2;
35                 destpath = srealloc(destpath, destpath_size);
36         }
37         if (linksize != -1) {
38                 /* readlink() does not NULL-terminate strings, so we have to. */
39                 destpath[linksize] = '\0';
40                 free(tmp);
41                 return destpath;
42         }
43 #endif
44
45         /* argv[0] is most likely a full path if it starts with a slash. */
46         if (argv0[0] == '/') {
47                 free(tmp);
48                 free(destpath);
49                 return sstrdup(argv0);
50         }
51
52         /* if argv[0] contains a /, prepend the working directory */
53         if (strchr(argv0, '/') != NULL) {
54                 char *retgcwd;
55                 while ((retgcwd = getcwd(tmp, tmp_size)) == NULL && errno == ERANGE) {
56                         tmp_size = tmp_size * 2;
57                         tmp = srealloc(tmp, tmp_size);
58                 }
59                 if (retgcwd != NULL) {
60                         free(destpath);
61                         sasprintf(&destpath, "%s/%s", tmp, argv0);
62                         free(tmp);
63                         return destpath;
64                 }
65         }
66
67         /* Fall back to searching $PATH (or _CS_PATH in absence of $PATH). */
68         char *path = getenv("PATH");
69         if (path == NULL) {
70                 /* _CS_PATH is typically something like "/bin:/usr/bin" */
71                 while (confstr(_CS_PATH, tmp, tmp_size) > tmp_size) {
72                         tmp_size = tmp_size * 2;
73                         tmp = srealloc(tmp, tmp_size);
74                 }
75                 sasprintf(&path, ":%s", tmp);
76         } else {
77                 path = strdup(path);
78         }
79         const char *component;
80         char *str = path;
81         while (1) {
82                 if ((component = strtok(str, ":")) == NULL)
83                         break;
84                 str = NULL;
85                 free(destpath);
86                 sasprintf(&destpath, "%s/%s", component, argv0);
87                 /* Of course this is not 100% equivalent to actually exec()ing the
88                  * binary, but meh. */
89                 if (access(destpath, X_OK) == 0) {
90                         free(path);
91                         free(tmp);
92                         return destpath;
93                 }
94         }
95         free(destpath);
96         free(path);
97         free(tmp);
98
99         /* Last resort: maybe it’s in /usr/bin? */
100         return sstrdup("/usr/bin/i3-nagbar");
101 }