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