1 /*
2  * Copyright © 2003 Felix Kuehling
3  * Copyright © 2018 Advanced Micro Devices, Inc.
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sub license, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
16  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17  * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, AUTHORS
18  * AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21  * USE OR OTHER DEALINGS IN THE SOFTWARE.
22  *
23  * The above copyright notice and this permission notice (including the
24  * next paragraph) shall be included in all copies or substantial portions
25  * of the Software.
26  */
27 
28 #include "util/os_misc.h"
29 #include "u_process.h"
30 #include "detect_os.h"
31 #include "macros.h"
32 #include <string.h>
33 #include <errno.h>
34 #include <stdlib.h>
35 
36 #if DETECT_OS_WINDOWS
37 #include <windows.h>
38 #else
39 #include <unistd.h>
40 #endif
41 
42 #if DETECT_OS_APPLE
43 #include <mach-o/dyld.h>
44 #endif
45 
46 #if DETECT_OS_BSD
47 #include <sys/types.h>
48 #include <sys/sysctl.h>
49 #endif
50 
51 #if DETECT_OS_LINUX
52 #include <fcntl.h>
53 #endif
54 
55 #include "util/u_call_once.h"
56 
57 #undef GET_PROGRAM_NAME_NOT_AVAILABLE
58 
59 #if defined(__linux__) && defined(HAVE_PROGRAM_INVOCATION_NAME)
60 static char *
__getProgramName()61 __getProgramName()
62 {
63    char * arg = strrchr(program_invocation_name, '/');
64    if (arg) {
65       char *program_name = NULL;
66       /* If the / character was found this is likely a linux path or
67        * an invocation path for a 64-bit wine program.
68        *
69        * However, some programs pass command line arguments into argv[0].
70        * Strip these arguments out by using the realpath only if it was
71        * a prefix of the invocation name.
72        */
73       char *path = realpath("/proc/self/exe", NULL);
74 
75       if (path && strncmp(path, program_invocation_name, strlen(path)) == 0) {
76          /* This shouldn't be null because path is a a prefix,
77           * but check it anyway since path is static. */
78          char * name = strrchr(path, '/');
79          if (name)
80             program_name = strdup(name + 1);
81       }
82       if (path) {
83          free(path);
84       }
85       if (!program_name) {
86          program_name = strdup(arg+1);
87       }
88       return program_name;
89    }
90 
91    /* If there was no '/' at all we likely have a windows like path from
92     * a wine application.
93     */
94    arg = strrchr(program_invocation_name, '\\');
95    if (arg)
96       return strdup(arg+1);
97 
98    return strdup(program_invocation_name);
99 }
100 #elif defined(HAVE_PROGRAM_INVOCATION_NAME)
101 static char *
__getProgramName()102 __getProgramName()
103 {
104    return strdup(program_invocation_short_name);
105 }
106 #elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__) || defined(ANDROID) || defined(__NetBSD__)
107 #if defined(__NetBSD__)
108 #    include <sys/param.h>
109 #endif
110 #if defined(__NetBSD_Version__) && (__NetBSD_Version__ < 106000100)
111 #define GET_PROGRAM_NAME_NOT_AVAILABLE
112 #else /* !(defined(__NetBSD_Version__) && (__NetBSD_Version__ < 106000100)) */
113 static char *
__getProgramName()114 __getProgramName()
115 {
116    const char *program_name = getprogname();
117    if (program_name) {
118       return strdup(program_name);
119    }
120    return NULL;
121 }
122 #endif /* defined(__NetBSD_Version__) && (__NetBSD_Version__ < 106000100) */
123 #elif defined(__sun)
124 /* Solaris has getexecname() which returns the full path - return just
125    the basename to match BSD getprogname() */
126 #    include <libgen.h>
127 
128 static char *
__getProgramName()129 __getProgramName()
130 {
131    char *progname = NULL;
132    const char *e = getexecname();
133    if (e != NULL) {
134       /* Have to make a copy since getexecname can return a readonly
135          string, but basename expects to be able to modify its arg. */
136       char *n = strdup(e);
137       if (n != NULL) {
138          progname = strdup(basename(n));
139          free(n);
140       }
141    }
142    return progname;
143 }
144 #elif DETECT_OS_WINDOWS
145 static char *
__getProgramName()146 __getProgramName()
147 {
148    char *progname;
149    static char buf[MAX_PATH];
150    GetModuleFileNameA(NULL, buf, sizeof(buf));
151    progname = strrchr(buf, '\\');
152    if (progname)
153       progname++;
154    else
155       progname = buf;
156    return strdup(progname);
157 }
158 #elif DETECT_OS_HAIKU
159 #  include <kernel/OS.h>
160 #  include <kernel/image.h>
161 static char *
__getProgramName()162 __getProgramName()
163 {
164    image_info info;
165    get_image_info(B_CURRENT_TEAM, &info);
166    return strdup(info.name);
167 }
168 #else
169 #define GET_PROGRAM_NAME_NOT_AVAILABLE
170 #endif
171 
172 #if defined(GET_PROGRAM_NAME_NOT_AVAILABLE)
173 #if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__UCLIBC__) || defined(ANDROID)
174 /* This is a hack. It's said to work on OpenBSD, NetBSD and GNU.
175  * Rogelio M.Serrano Jr. reported it's also working with UCLIBC. It's
176  * used as a last resort, if there is no documented facility available. */
177 static char *
__getProgramName()178 __getProgramName()
179 {
180     extern const char *__progname;
181     char * arg = strrchr(__progname, '/');
182     if (arg)
183         return strdup(arg+1);
184     else
185         return strdup(__progname);
186 }
187 #else
188 #pragma message ( "Warning: Per application configuration won't work with your OS version." )
189 static char *
__getProgramName()190 __getProgramName()
191 {
192    return strdup("");
193 }
194 #endif
195 #endif /* GET_PROGRAM_NAME_NOT_AVAILABLE */
196 
197 static char *program_name;
198 
199 static void
free_program_name(void)200 free_program_name(void)
201 {
202    free(program_name);
203    program_name = NULL;
204 }
205 
206 static void
util_get_process_name_callback(void)207 util_get_process_name_callback(void)
208 {
209    const char *override_name = os_get_option("MESA_PROCESS_NAME");
210    program_name = override_name ? strdup(override_name) : __getProgramName();
211 
212    if (program_name)
213       atexit(free_program_name);
214 }
215 
216 const char *
util_get_process_name(void)217 util_get_process_name(void)
218 {
219    static util_once_flag once_state = UTIL_ONCE_FLAG_INIT;
220    util_call_once(&once_state, util_get_process_name_callback);
221    return program_name;
222 }
223 
224 size_t
util_get_process_exec_path(char * process_path,size_t len)225 util_get_process_exec_path(char* process_path, size_t len)
226 {
227 #if DETECT_OS_WINDOWS
228    return GetModuleFileNameA(NULL, process_path, len);
229 #elif DETECT_OS_APPLE
230    uint32_t bufSize = len;
231    int result = _NSGetExecutablePath(process_path, &bufSize);
232 
233    return (result == 0) ? strlen(process_path) : 0;
234 #elif DETECT_OS_FREEBSD
235    int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
236 
237    (void) sysctl(mib, 4, process_path, &len, NULL, 0);
238    process_path[len - 1] = '\0';
239 
240    return len;
241 #elif DETECT_OS_UNIX
242    ssize_t r;
243 
244    if ((r = readlink("/proc/self/exe", process_path, len)) > 0)
245       goto success;
246    if ((r = readlink("/proc/curproc/exe", process_path, len)) > 0)
247       goto success;
248    if ((r = readlink("/proc/curproc/file", process_path, len)) > 0)
249       goto success;
250 
251     return 0;
252 success:
253    if (r == len)
254       return 0;
255 
256     process_path[r] = '\0';
257     return r;
258 
259 #endif
260    return 0;
261 }
262 
263 bool
util_get_command_line(char * cmdline,size_t size)264 util_get_command_line(char *cmdline, size_t size)
265 {
266 #if DETECT_OS_WINDOWS
267    const char *args = GetCommandLineA();
268    if (args) {
269       strncpy(cmdline, args, size);
270       // make sure we terminate the string
271       cmdline[size - 1] = 0;
272       return true;
273    }
274 #elif DETECT_OS_LINUX
275    int f = open("/proc/self/cmdline", O_RDONLY);
276    if (f != -1) {
277       const int n = read(f, cmdline, size - 1);
278       int i;
279       assert(n < size);
280       // The arguments are separated by '\0' chars.  Convert them to spaces.
281       for (i = 0; i < n; i++) {
282          if (cmdline[i] == 0) {
283             cmdline[i] = ' ';
284          }
285       }
286       // terminate the string
287       cmdline[n] = 0;
288       close(f);
289       return true;
290    }
291 #elif DETECT_OS_BSD
292    int mib[] = {
293       CTL_KERN,
294 #if DETECT_OS_NETBSD || DETECT_OS_OPENBSD
295       KERN_PROC_ARGS,
296       getpid(),
297       KERN_PROC_ARGV,
298 #else
299       KERN_PROC,
300       KERN_PROC_ARGS,
301       getpid(),
302 #endif
303    };
304 
305    /* Like /proc/pid/cmdline each argument is separated by NUL byte */
306    if (sysctl(mib, ARRAY_SIZE(mib), cmdline, &size, NULL, 0) == -1) {
307       return false;
308    }
309 
310    /* Replace NUL with space except terminating NUL */
311    for (size_t i = 0; i < (size - 1); i++) {
312       if (cmdline[i] == '\0')
313          cmdline[i] = ' ';
314    }
315 
316    return true;
317 #endif
318 
319    /* XXX to-do: implement this function for other operating systems */
320 
321    cmdline[0] = 0;
322    return false;
323 }
324