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