diff options
Diffstat (limited to 'src/lib/util/os_installation.c')
-rw-r--r-- | src/lib/util/os_installation.c | 829 |
1 files changed, 829 insertions, 0 deletions
diff --git a/src/lib/util/os_installation.c b/src/lib/util/os_installation.c new file mode 100644 index 000000000..ff04a8a7f --- /dev/null +++ b/src/lib/util/os_installation.c | |||
@@ -0,0 +1,829 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2006-2018, 2022 GNUnet e.V. | ||
4 | |||
5 | GNUnet is free software: you can redistribute it and/or modify it | ||
6 | under the terms of the GNU Affero General Public License as published | ||
7 | by the Free Software Foundation, either version 3 of the License, | ||
8 | or (at your option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Affero General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Affero General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | SPDX-License-Identifier: AGPL3.0-or-later | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file src/util/os_installation.c | ||
23 | * @brief get paths used by the program | ||
24 | * @author Milan | ||
25 | * @author Christian Fuchs | ||
26 | * @author Christian Grothoff | ||
27 | * @author Matthias Wachs | ||
28 | * @author Heikki Lindholm | ||
29 | * @author LRN | ||
30 | */ | ||
31 | #include "platform.h" | ||
32 | #include <sys/stat.h> | ||
33 | #include <stdlib.h> | ||
34 | #include <string.h> | ||
35 | #include <unistd.h> | ||
36 | #include <unistr.h> /* for u16_to_u8 */ | ||
37 | |||
38 | |||
39 | #include "gnunet_util_lib.h" | ||
40 | #if DARWIN | ||
41 | #include <mach-o/ldsyms.h> | ||
42 | #include <mach-o/dyld.h> | ||
43 | #endif | ||
44 | |||
45 | |||
46 | #define LOG(kind, ...) \ | ||
47 | GNUNET_log_from (kind, "util-os-installation", __VA_ARGS__) | ||
48 | |||
49 | #define LOG_STRERROR_FILE(kind, syscall, filename) \ | ||
50 | GNUNET_log_from_strerror_file (kind, \ | ||
51 | "util-os-installation", \ | ||
52 | syscall, \ | ||
53 | filename) | ||
54 | |||
55 | |||
56 | /** | ||
57 | * Default project data used for installation path detection | ||
58 | * for GNUnet (core). | ||
59 | */ | ||
60 | static const struct GNUNET_OS_ProjectData default_pd = { | ||
61 | .libname = "libgnunetutil", | ||
62 | .project_dirname = "gnunet", | ||
63 | .binary_name = "gnunet-arm", | ||
64 | .version = PACKAGE_VERSION " " VCS_VERSION, | ||
65 | .env_varname = "GNUNET_PREFIX", | ||
66 | .base_config_varname = "GNUNET_BASE_CONFIG", | ||
67 | .bug_email = "gnunet-developers@gnu.org", | ||
68 | .homepage = "http://www.gnu.org/s/gnunet/", | ||
69 | .config_file = "gnunet.conf", | ||
70 | .user_config_file = "~/.config/gnunet.conf", | ||
71 | .is_gnu = 1, | ||
72 | .gettext_domain = "gnunet", | ||
73 | .gettext_path = NULL, | ||
74 | .agpl_url = GNUNET_AGPL_URL, | ||
75 | }; | ||
76 | |||
77 | /** | ||
78 | * Which project data do we currently use for installation | ||
79 | * path detection? Never NULL. | ||
80 | */ | ||
81 | static const struct GNUNET_OS_ProjectData *current_pd = &default_pd; | ||
82 | |||
83 | /** | ||
84 | * PD for which gettext has been initialized last. | ||
85 | * Note that the gettext initialization done within | ||
86 | * GNUNET_PROGRAM_run2 is for the specific application. | ||
87 | */ | ||
88 | static const struct GNUNET_OS_ProjectData *gettextinit; | ||
89 | |||
90 | |||
91 | /** | ||
92 | * Return default project data used by 'libgnunetutil' for GNUnet. | ||
93 | */ | ||
94 | const struct GNUNET_OS_ProjectData * | ||
95 | GNUNET_OS_project_data_default (void) | ||
96 | { | ||
97 | return &default_pd; | ||
98 | } | ||
99 | |||
100 | |||
101 | /** | ||
102 | * @return current project data. | ||
103 | */ | ||
104 | const struct GNUNET_OS_ProjectData * | ||
105 | GNUNET_OS_project_data_get () | ||
106 | { | ||
107 | if (current_pd != gettextinit) | ||
108 | { | ||
109 | char *path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LOCALEDIR); | ||
110 | |||
111 | if (NULL != path) | ||
112 | bindtextdomain (PACKAGE, | ||
113 | path); | ||
114 | GNUNET_free (path); | ||
115 | gettextinit = current_pd; | ||
116 | } | ||
117 | return current_pd; | ||
118 | } | ||
119 | |||
120 | |||
121 | void | ||
122 | GNUNET_OS_init (const struct GNUNET_OS_ProjectData *pd) | ||
123 | { | ||
124 | GNUNET_assert (NULL != pd); | ||
125 | current_pd = pd; | ||
126 | if (pd != gettextinit) | ||
127 | { | ||
128 | char *path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LOCALEDIR); | ||
129 | |||
130 | if (NULL != path) | ||
131 | bindtextdomain (PACKAGE, | ||
132 | path); | ||
133 | GNUNET_free (path); | ||
134 | gettextinit = pd; | ||
135 | } | ||
136 | } | ||
137 | |||
138 | |||
139 | #ifdef __linux__ | ||
140 | /** | ||
141 | * Try to determine path by reading /proc/PID/exe | ||
142 | * | ||
143 | * @return NULL on error | ||
144 | */ | ||
145 | static char * | ||
146 | get_path_from_proc_maps (void) | ||
147 | { | ||
148 | char fn[64]; | ||
149 | char line[1024]; | ||
150 | char dir[1024]; | ||
151 | FILE *f; | ||
152 | char *lgu; | ||
153 | |||
154 | if (NULL == current_pd->libname) | ||
155 | return NULL; | ||
156 | GNUNET_snprintf (fn, | ||
157 | sizeof(fn), | ||
158 | "/proc/%u/maps", | ||
159 | getpid ()); | ||
160 | if (NULL == (f = fopen (fn, "r"))) | ||
161 | return NULL; | ||
162 | while (NULL != fgets (line, sizeof(line), f)) | ||
163 | { | ||
164 | if ((1 == sscanf (line, | ||
165 | "%*p-%*p %*c%*c%*c%*c %*x %*x:%*x %*u%*[ ]%1023s", | ||
166 | dir)) && | ||
167 | (NULL != (lgu = strstr (dir, | ||
168 | current_pd->libname)))) | ||
169 | { | ||
170 | lgu[0] = '\0'; | ||
171 | fclose (f); | ||
172 | return GNUNET_strdup (dir); | ||
173 | } | ||
174 | } | ||
175 | fclose (f); | ||
176 | return NULL; | ||
177 | } | ||
178 | |||
179 | |||
180 | /** | ||
181 | * Try to determine path by reading /proc/PID/exe | ||
182 | * | ||
183 | * @return NULL on error | ||
184 | */ | ||
185 | static char * | ||
186 | get_path_from_proc_exe (void) | ||
187 | { | ||
188 | char fn[64]; | ||
189 | char lnk[1024]; | ||
190 | ssize_t size; | ||
191 | char *lep; | ||
192 | |||
193 | GNUNET_snprintf (fn, sizeof(fn), "/proc/%u/exe", getpid ()); | ||
194 | size = readlink (fn, lnk, sizeof(lnk) - 1); | ||
195 | if (size <= 0) | ||
196 | { | ||
197 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "readlink", fn); | ||
198 | return NULL; | ||
199 | } | ||
200 | GNUNET_assert (((size_t) size) < sizeof(lnk)); | ||
201 | lnk[size] = '\0'; | ||
202 | while ((lnk[size] != '/') && (size > 0)) | ||
203 | size--; | ||
204 | GNUNET_asprintf (&lep, "/%s/libexec/", current_pd->project_dirname); | ||
205 | /* test for being in lib/gnunet/libexec/ or lib/MULTIARCH/gnunet/libexec */ | ||
206 | if ((((size_t) size) > strlen (lep)) && | ||
207 | (0 == strcmp (lep, &lnk[size - strlen (lep)]))) | ||
208 | size -= strlen (lep) - 1; | ||
209 | GNUNET_free (lep); | ||
210 | if ((size < 4) || (lnk[size - 4] != '/')) | ||
211 | { | ||
212 | /* not installed in "/bin/" -- binary path probably useless */ | ||
213 | return NULL; | ||
214 | } | ||
215 | lnk[size] = '\0'; | ||
216 | return GNUNET_strdup (lnk); | ||
217 | } | ||
218 | |||
219 | |||
220 | #endif | ||
221 | |||
222 | |||
223 | #if DARWIN | ||
224 | /** | ||
225 | * Signature of the '_NSGetExecutablePath" function. | ||
226 | * | ||
227 | * @param buf where to write the path | ||
228 | * @param number of bytes available in @a buf | ||
229 | * @return 0 on success, otherwise desired number of bytes is stored in 'bufsize' | ||
230 | */ | ||
231 | typedef int (*MyNSGetExecutablePathProto) (char *buf, size_t *bufsize); | ||
232 | |||
233 | |||
234 | /** | ||
235 | * Try to obtain the path of our executable using '_NSGetExecutablePath'. | ||
236 | * | ||
237 | * @return NULL on error | ||
238 | */ | ||
239 | static char * | ||
240 | get_path_from_NSGetExecutablePath (void) | ||
241 | { | ||
242 | static char zero = '\0'; | ||
243 | char *path; | ||
244 | size_t len; | ||
245 | MyNSGetExecutablePathProto func; | ||
246 | |||
247 | path = NULL; | ||
248 | if (NULL == | ||
249 | (func = (MyNSGetExecutablePathProto) dlsym (RTLD_DEFAULT, | ||
250 | "_NSGetExecutablePath"))) | ||
251 | return NULL; | ||
252 | path = &zero; | ||
253 | len = 0; | ||
254 | /* get the path len, including the trailing \0 */ | ||
255 | (void) func (path, &len); | ||
256 | if (0 == len) | ||
257 | return NULL; | ||
258 | path = GNUNET_malloc (len); | ||
259 | if (0 != func (path, &len)) | ||
260 | { | ||
261 | GNUNET_free (path); | ||
262 | return NULL; | ||
263 | } | ||
264 | len = strlen (path); | ||
265 | while ((path[len] != '/') && (len > 0)) | ||
266 | len--; | ||
267 | path[len] = '\0'; | ||
268 | return path; | ||
269 | } | ||
270 | |||
271 | |||
272 | /** | ||
273 | * Try to obtain the path of our executable using '_dyld_image' API. | ||
274 | * | ||
275 | * @return NULL on error | ||
276 | */ | ||
277 | static char * | ||
278 | get_path_from_dyld_image (void) | ||
279 | { | ||
280 | const char *path; | ||
281 | char *p; | ||
282 | char *s; | ||
283 | unsigned int i; | ||
284 | int c; | ||
285 | |||
286 | c = _dyld_image_count (); | ||
287 | for (i = 0; i < c; i++) | ||
288 | { | ||
289 | if (((const void *) _dyld_get_image_header (i)) != | ||
290 | ((const void *) &_mh_dylib_header)) | ||
291 | continue; | ||
292 | path = _dyld_get_image_name (i); | ||
293 | if ((NULL == path) || (0 == strlen (path))) | ||
294 | continue; | ||
295 | p = GNUNET_strdup (path); | ||
296 | s = p + strlen (p); | ||
297 | while ((s > p) && ('/' != *s)) | ||
298 | s--; | ||
299 | s++; | ||
300 | *s = '\0'; | ||
301 | return p; | ||
302 | } | ||
303 | return NULL; | ||
304 | } | ||
305 | |||
306 | |||
307 | #endif | ||
308 | |||
309 | |||
310 | /** | ||
311 | * Return the actual path to a file found in the current | ||
312 | * PATH environment variable. | ||
313 | * | ||
314 | * @param binary the name of the file to find | ||
315 | * @return path to binary, NULL if not found | ||
316 | */ | ||
317 | static char * | ||
318 | get_path_from_PATH (const char *binary) | ||
319 | { | ||
320 | char *path; | ||
321 | char *pos; | ||
322 | char *end; | ||
323 | char *buf; | ||
324 | const char *p; | ||
325 | |||
326 | if (NULL == (p = getenv ("PATH"))) | ||
327 | return NULL; | ||
328 | |||
329 | path = GNUNET_strdup (p); /* because we write on it */ | ||
330 | |||
331 | buf = GNUNET_malloc (strlen (path) + strlen (binary) + 1 + 1); | ||
332 | pos = path; | ||
333 | while (NULL != (end = strchr (pos, PATH_SEPARATOR))) | ||
334 | { | ||
335 | *end = '\0'; | ||
336 | sprintf (buf, "%s/%s", pos, binary); | ||
337 | if (GNUNET_DISK_file_test (buf) == GNUNET_YES) | ||
338 | { | ||
339 | pos = GNUNET_strdup (pos); | ||
340 | GNUNET_free (buf); | ||
341 | GNUNET_free (path); | ||
342 | return pos; | ||
343 | } | ||
344 | pos = end + 1; | ||
345 | } | ||
346 | sprintf (buf, "%s/%s", pos, binary); | ||
347 | if (GNUNET_YES == GNUNET_DISK_file_test (buf)) | ||
348 | { | ||
349 | pos = GNUNET_strdup (pos); | ||
350 | GNUNET_free (buf); | ||
351 | GNUNET_free (path); | ||
352 | return pos; | ||
353 | } | ||
354 | GNUNET_free (buf); | ||
355 | GNUNET_free (path); | ||
356 | return NULL; | ||
357 | } | ||
358 | |||
359 | |||
360 | /** | ||
361 | * Try to obtain the installation path using the "GNUNET_PREFIX" environment | ||
362 | * variable. | ||
363 | * | ||
364 | * @return NULL on error (environment variable not set) | ||
365 | */ | ||
366 | static char * | ||
367 | get_path_from_GNUNET_PREFIX (void) | ||
368 | { | ||
369 | const char *p; | ||
370 | |||
371 | if ((NULL != current_pd->env_varname) && | ||
372 | (NULL != (p = getenv (current_pd->env_varname)))) | ||
373 | return GNUNET_strdup (p); | ||
374 | if ((NULL != current_pd->env_varname_alt) && | ||
375 | (NULL != (p = getenv (current_pd->env_varname_alt)))) | ||
376 | return GNUNET_strdup (p); | ||
377 | return NULL; | ||
378 | } | ||
379 | |||
380 | |||
381 | /** | ||
382 | * @brief get the path to GNUnet bin/ or lib/, preferring the lib/ path | ||
383 | * @author Milan | ||
384 | * | ||
385 | * @return a pointer to the executable path, or NULL on error | ||
386 | */ | ||
387 | static char * | ||
388 | os_get_gnunet_path (void) | ||
389 | { | ||
390 | char *ret; | ||
391 | |||
392 | if (NULL != (ret = get_path_from_GNUNET_PREFIX ())) | ||
393 | return ret; | ||
394 | #ifdef __linux__ | ||
395 | if (NULL != (ret = get_path_from_proc_maps ())) | ||
396 | return ret; | ||
397 | /* try path *first*, before /proc/exe, as /proc/exe can be wrong */ | ||
398 | if ((NULL != current_pd->binary_name) && | ||
399 | (NULL != (ret = get_path_from_PATH (current_pd->binary_name)))) | ||
400 | return ret; | ||
401 | if (NULL != (ret = get_path_from_proc_exe ())) | ||
402 | return ret; | ||
403 | #endif | ||
404 | #if DARWIN | ||
405 | if (NULL != (ret = get_path_from_dyld_image ())) | ||
406 | return ret; | ||
407 | if (NULL != (ret = get_path_from_NSGetExecutablePath ())) | ||
408 | return ret; | ||
409 | #endif | ||
410 | if ((NULL != current_pd->binary_name) && | ||
411 | (NULL != (ret = get_path_from_PATH (current_pd->binary_name)))) | ||
412 | return ret; | ||
413 | /* other attempts here */ | ||
414 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
415 | _ ( | ||
416 | "Could not determine installation path for %s. Set `%s' environment variable.\n"), | ||
417 | current_pd->project_dirname, | ||
418 | current_pd->env_varname); | ||
419 | return NULL; | ||
420 | } | ||
421 | |||
422 | |||
423 | /** | ||
424 | * @brief get the path to current app's bin/ | ||
425 | * @return a pointer to the executable path, or NULL on error | ||
426 | */ | ||
427 | static char * | ||
428 | os_get_exec_path () | ||
429 | { | ||
430 | char *ret = NULL; | ||
431 | |||
432 | #ifdef __linux__ | ||
433 | if (NULL != (ret = get_path_from_proc_exe ())) | ||
434 | return ret; | ||
435 | #endif | ||
436 | #if DARWIN | ||
437 | if (NULL != (ret = get_path_from_NSGetExecutablePath ())) | ||
438 | return ret; | ||
439 | #endif | ||
440 | /* other attempts here */ | ||
441 | return ret; | ||
442 | } | ||
443 | |||
444 | |||
445 | /** | ||
446 | * @brief get the path to a specific GNUnet installation directory or, | ||
447 | * with #GNUNET_OS_IPK_SELF_PREFIX, the current running apps installation directory | ||
448 | * @return a pointer to the dir path (to be freed by the caller) | ||
449 | */ | ||
450 | char * | ||
451 | GNUNET_OS_installation_get_path (enum GNUNET_OS_InstallationPathKind dirkind) | ||
452 | { | ||
453 | size_t n; | ||
454 | char *dirname; | ||
455 | char *execpath = NULL; | ||
456 | char *tmp; | ||
457 | char *multiarch; | ||
458 | char *libdir; | ||
459 | int isbasedir; | ||
460 | |||
461 | /* if wanted, try to get the current app's bin/ */ | ||
462 | if (dirkind == GNUNET_OS_IPK_SELF_PREFIX) | ||
463 | execpath = os_get_exec_path (); | ||
464 | |||
465 | /* try to get GNUnet's bin/ or lib/, or if previous was unsuccessful some | ||
466 | * guess for the current app */ | ||
467 | if (NULL == execpath) | ||
468 | execpath = os_get_gnunet_path (); | ||
469 | if (NULL == execpath) | ||
470 | return NULL; | ||
471 | |||
472 | n = strlen (execpath); | ||
473 | if (0 == n) | ||
474 | { | ||
475 | /* should never happen, but better safe than sorry */ | ||
476 | GNUNET_free (execpath); | ||
477 | return NULL; | ||
478 | } | ||
479 | /* remove filename itself */ | ||
480 | while ((n > 1) && (DIR_SEPARATOR == execpath[n - 1])) | ||
481 | execpath[--n] = '\0'; | ||
482 | |||
483 | isbasedir = 1; | ||
484 | if ((n > 6) && ((0 == strcasecmp (&execpath[n - 6], "/lib32")) || | ||
485 | (0 == strcasecmp (&execpath[n - 6], "/lib64")))) | ||
486 | { | ||
487 | if ((GNUNET_OS_IPK_LIBDIR != dirkind) && | ||
488 | (GNUNET_OS_IPK_LIBEXECDIR != dirkind)) | ||
489 | { | ||
490 | /* strip '/lib32' or '/lib64' */ | ||
491 | execpath[n - 6] = '\0'; | ||
492 | n -= 6; | ||
493 | } | ||
494 | else | ||
495 | isbasedir = 0; | ||
496 | } | ||
497 | else if ((n > 4) && ((0 == strcasecmp (&execpath[n - 4], "/bin")) || | ||
498 | (0 == strcasecmp (&execpath[n - 4], "/lib")))) | ||
499 | { | ||
500 | /* strip '/bin' or '/lib' */ | ||
501 | execpath[n - 4] = '\0'; | ||
502 | n -= 4; | ||
503 | } | ||
504 | multiarch = NULL; | ||
505 | if (NULL != (libdir = strstr (execpath, "/lib/"))) | ||
506 | { | ||
507 | /* test for multi-arch path of the form "PREFIX/lib/MULTIARCH/"; | ||
508 | here we need to re-add 'multiarch' to lib and libexec paths later! */ | ||
509 | multiarch = &libdir[5]; | ||
510 | if (NULL == strchr (multiarch, '/')) | ||
511 | libdir[0] = | ||
512 | '\0'; /* Debian multiarch format, cut of from 'execpath' but preserve in multicarch */ | ||
513 | else | ||
514 | multiarch = | ||
515 | NULL; /* maybe not, multiarch still has a '/', which is not OK */ | ||
516 | } | ||
517 | /* in case this was a directory named foo-bin, remove "foo-" */ | ||
518 | while ((n > 1) && (execpath[n - 1] == DIR_SEPARATOR)) | ||
519 | execpath[--n] = '\0'; | ||
520 | switch (dirkind) | ||
521 | { | ||
522 | case GNUNET_OS_IPK_PREFIX: | ||
523 | case GNUNET_OS_IPK_SELF_PREFIX: | ||
524 | dirname = GNUNET_strdup (DIR_SEPARATOR_STR); | ||
525 | break; | ||
526 | |||
527 | case GNUNET_OS_IPK_BINDIR: | ||
528 | dirname = GNUNET_strdup (DIR_SEPARATOR_STR "bin" DIR_SEPARATOR_STR); | ||
529 | break; | ||
530 | |||
531 | case GNUNET_OS_IPK_LIBDIR: | ||
532 | if (isbasedir) | ||
533 | { | ||
534 | GNUNET_asprintf (&tmp, | ||
535 | "%s%s%s%s%s%s%s", | ||
536 | execpath, | ||
537 | DIR_SEPARATOR_STR "lib", | ||
538 | (NULL != multiarch) ? DIR_SEPARATOR_STR : "", | ||
539 | (NULL != multiarch) ? multiarch : "", | ||
540 | DIR_SEPARATOR_STR, | ||
541 | current_pd->project_dirname, | ||
542 | DIR_SEPARATOR_STR); | ||
543 | if (GNUNET_YES == GNUNET_DISK_directory_test (tmp, GNUNET_YES)) | ||
544 | { | ||
545 | GNUNET_free (execpath); | ||
546 | return tmp; | ||
547 | } | ||
548 | GNUNET_free (tmp); | ||
549 | tmp = NULL; | ||
550 | dirname = NULL; | ||
551 | if (4 == sizeof(void *)) | ||
552 | { | ||
553 | GNUNET_asprintf (&dirname, | ||
554 | DIR_SEPARATOR_STR "lib32" DIR_SEPARATOR_STR | ||
555 | "%s" DIR_SEPARATOR_STR, | ||
556 | current_pd->project_dirname); | ||
557 | GNUNET_asprintf (&tmp, "%s%s", execpath, dirname); | ||
558 | } | ||
559 | if (8 == sizeof(void *)) | ||
560 | { | ||
561 | GNUNET_asprintf (&dirname, | ||
562 | DIR_SEPARATOR_STR "lib64" DIR_SEPARATOR_STR | ||
563 | "%s" DIR_SEPARATOR_STR, | ||
564 | current_pd->project_dirname); | ||
565 | GNUNET_asprintf (&tmp, "%s%s", execpath, dirname); | ||
566 | } | ||
567 | |||
568 | if ((NULL != tmp) && | ||
569 | (GNUNET_YES == GNUNET_DISK_directory_test (tmp, GNUNET_YES))) | ||
570 | { | ||
571 | GNUNET_free (execpath); | ||
572 | GNUNET_free (dirname); | ||
573 | return tmp; | ||
574 | } | ||
575 | GNUNET_free (tmp); | ||
576 | GNUNET_free (dirname); | ||
577 | } | ||
578 | GNUNET_asprintf (&dirname, | ||
579 | DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR, | ||
580 | current_pd->project_dirname); | ||
581 | break; | ||
582 | |||
583 | case GNUNET_OS_IPK_DATADIR: | ||
584 | GNUNET_asprintf (&dirname, | ||
585 | DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR | ||
586 | "%s" DIR_SEPARATOR_STR, | ||
587 | current_pd->project_dirname); | ||
588 | break; | ||
589 | |||
590 | case GNUNET_OS_IPK_LOCALEDIR: | ||
591 | dirname = GNUNET_strdup (DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR | ||
592 | "locale" DIR_SEPARATOR_STR); | ||
593 | break; | ||
594 | |||
595 | case GNUNET_OS_IPK_ICONDIR: | ||
596 | dirname = GNUNET_strdup (DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR | ||
597 | "icons" DIR_SEPARATOR_STR); | ||
598 | break; | ||
599 | |||
600 | case GNUNET_OS_IPK_DOCDIR: | ||
601 | GNUNET_asprintf (&dirname, | ||
602 | DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR | ||
603 | "doc" DIR_SEPARATOR_STR | ||
604 | "%s" DIR_SEPARATOR_STR, | ||
605 | current_pd->project_dirname); | ||
606 | break; | ||
607 | |||
608 | case GNUNET_OS_IPK_LIBEXECDIR: | ||
609 | if (isbasedir) | ||
610 | { | ||
611 | GNUNET_asprintf (&dirname, | ||
612 | DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR | ||
613 | "libexec" DIR_SEPARATOR_STR, | ||
614 | current_pd->project_dirname); | ||
615 | GNUNET_asprintf (&tmp, | ||
616 | "%s%s%s%s", | ||
617 | execpath, | ||
618 | DIR_SEPARATOR_STR "lib" DIR_SEPARATOR_STR, | ||
619 | (NULL != multiarch) ? multiarch : "", | ||
620 | dirname); | ||
621 | GNUNET_free (dirname); | ||
622 | if (GNUNET_YES == GNUNET_DISK_directory_test (tmp, GNUNET_YES)) | ||
623 | { | ||
624 | GNUNET_free (execpath); | ||
625 | return tmp; | ||
626 | } | ||
627 | GNUNET_free (tmp); | ||
628 | tmp = NULL; | ||
629 | dirname = NULL; | ||
630 | if (4 == sizeof(void *)) | ||
631 | { | ||
632 | GNUNET_asprintf (&dirname, | ||
633 | DIR_SEPARATOR_STR "lib32" DIR_SEPARATOR_STR | ||
634 | "%s" DIR_SEPARATOR_STR | ||
635 | "libexec" DIR_SEPARATOR_STR, | ||
636 | current_pd->project_dirname); | ||
637 | GNUNET_asprintf (&tmp, "%s%s", execpath, dirname); | ||
638 | } | ||
639 | if (8 == sizeof(void *)) | ||
640 | { | ||
641 | GNUNET_asprintf (&dirname, | ||
642 | DIR_SEPARATOR_STR "lib64" DIR_SEPARATOR_STR | ||
643 | "%s" DIR_SEPARATOR_STR | ||
644 | "libexec" DIR_SEPARATOR_STR, | ||
645 | current_pd->project_dirname); | ||
646 | GNUNET_asprintf (&tmp, "%s%s", execpath, dirname); | ||
647 | } | ||
648 | if ((NULL != tmp) && | ||
649 | (GNUNET_YES == GNUNET_DISK_directory_test (tmp, GNUNET_YES))) | ||
650 | { | ||
651 | GNUNET_free (execpath); | ||
652 | GNUNET_free (dirname); | ||
653 | return tmp; | ||
654 | } | ||
655 | GNUNET_free (tmp); | ||
656 | GNUNET_free (dirname); | ||
657 | } | ||
658 | GNUNET_asprintf (&dirname, | ||
659 | DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR | ||
660 | "libexec" DIR_SEPARATOR_STR, | ||
661 | current_pd->project_dirname); | ||
662 | break; | ||
663 | |||
664 | default: | ||
665 | GNUNET_free (execpath); | ||
666 | return NULL; | ||
667 | } | ||
668 | GNUNET_asprintf (&tmp, "%s%s", execpath, dirname); | ||
669 | GNUNET_free (dirname); | ||
670 | GNUNET_free (execpath); | ||
671 | return tmp; | ||
672 | } | ||
673 | |||
674 | |||
675 | /** | ||
676 | * Given the name of a gnunet-helper, gnunet-service or gnunet-daemon | ||
677 | * binary, try to prefix it with the libexec/-directory to get the | ||
678 | * full path. | ||
679 | * | ||
680 | * @param progname name of the binary | ||
681 | * @return full path to the binary, if possible, otherwise copy of 'progname' | ||
682 | */ | ||
683 | char * | ||
684 | GNUNET_OS_get_libexec_binary_path (const char *progname) | ||
685 | { | ||
686 | static char *cache; | ||
687 | char *libexecdir; | ||
688 | char *binary; | ||
689 | |||
690 | if ((DIR_SEPARATOR == progname[0]) || | ||
691 | (GNUNET_YES == | ||
692 | GNUNET_STRINGS_path_is_absolute (progname, GNUNET_NO, NULL, NULL))) | ||
693 | return GNUNET_strdup (progname); | ||
694 | if (NULL != cache) | ||
695 | libexecdir = cache; | ||
696 | else | ||
697 | libexecdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBEXECDIR); | ||
698 | if (NULL == libexecdir) | ||
699 | return GNUNET_strdup (progname); | ||
700 | GNUNET_asprintf (&binary, "%s%s", libexecdir, progname); | ||
701 | cache = libexecdir; | ||
702 | return binary; | ||
703 | } | ||
704 | |||
705 | |||
706 | /** | ||
707 | * Given the name of a helper, service or daemon binary construct the full | ||
708 | * path to the binary using the SUID_BINARY_PATH in the PATHS section of the | ||
709 | * configuration. If that option is not present, fall back to | ||
710 | * GNUNET_OS_get_libexec_binary_path. If @a progname is an absolute path, a | ||
711 | * copy of this path is returned. | ||
712 | * | ||
713 | * @param cfg configuration to inspect | ||
714 | * @param progname name of the binary | ||
715 | * @return full path to the binary, if possible, a copy of @a progname | ||
716 | * otherwise | ||
717 | */ | ||
718 | char * | ||
719 | GNUNET_OS_get_suid_binary_path (const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
720 | const char *progname) | ||
721 | { | ||
722 | static char *cache; | ||
723 | char *binary = NULL; | ||
724 | char *path = NULL; | ||
725 | size_t path_len; | ||
726 | |||
727 | if (GNUNET_YES == | ||
728 | GNUNET_STRINGS_path_is_absolute (progname, GNUNET_NO, NULL, NULL)) | ||
729 | { | ||
730 | return GNUNET_strdup (progname); | ||
731 | } | ||
732 | if (NULL != cache) | ||
733 | path = cache; | ||
734 | else | ||
735 | GNUNET_CONFIGURATION_get_value_string (cfg, | ||
736 | "PATHS", | ||
737 | "SUID_BINARY_PATH", | ||
738 | &path); | ||
739 | if ((NULL == path) || (0 == strlen (path))) | ||
740 | { | ||
741 | if (NULL != path) | ||
742 | GNUNET_free (path); | ||
743 | cache = NULL; | ||
744 | return GNUNET_OS_get_libexec_binary_path (progname); | ||
745 | } | ||
746 | path_len = strlen (path); | ||
747 | GNUNET_asprintf (&binary, | ||
748 | "%s%s%s", | ||
749 | path, | ||
750 | (path[path_len - 1] == DIR_SEPARATOR) ? "" | ||
751 | : DIR_SEPARATOR_STR, | ||
752 | progname); | ||
753 | cache = path; | ||
754 | return binary; | ||
755 | } | ||
756 | |||
757 | |||
758 | enum GNUNET_GenericReturnValue | ||
759 | GNUNET_OS_check_helper_binary (const char *binary, | ||
760 | bool check_suid, | ||
761 | const char *params) | ||
762 | { | ||
763 | struct stat statbuf; | ||
764 | char *p; | ||
765 | char *pf; | ||
766 | |||
767 | if ((GNUNET_YES == | ||
768 | GNUNET_STRINGS_path_is_absolute (binary, GNUNET_NO, NULL, NULL)) || | ||
769 | (0 == strncmp (binary, "./", 2))) | ||
770 | { | ||
771 | p = GNUNET_strdup (binary); | ||
772 | } | ||
773 | else | ||
774 | { | ||
775 | p = get_path_from_PATH (binary); | ||
776 | if (NULL != p) | ||
777 | { | ||
778 | GNUNET_asprintf (&pf, "%s/%s", p, binary); | ||
779 | GNUNET_free (p); | ||
780 | p = pf; | ||
781 | } | ||
782 | } | ||
783 | |||
784 | if (NULL == p) | ||
785 | { | ||
786 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
787 | _ ("Could not find binary `%s' in PATH!\n"), | ||
788 | binary); | ||
789 | return GNUNET_SYSERR; | ||
790 | } | ||
791 | if (0 != access (p, X_OK)) | ||
792 | { | ||
793 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", p); | ||
794 | GNUNET_free (p); | ||
795 | return GNUNET_SYSERR; | ||
796 | } | ||
797 | |||
798 | if (0 == getuid ()) | ||
799 | { | ||
800 | /* as we run as root, we don't insist on SUID */ | ||
801 | GNUNET_free (p); | ||
802 | return GNUNET_YES; | ||
803 | } | ||
804 | |||
805 | if (0 != stat (p, &statbuf)) | ||
806 | { | ||
807 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", p); | ||
808 | GNUNET_free (p); | ||
809 | return GNUNET_SYSERR; | ||
810 | } | ||
811 | if (check_suid) | ||
812 | { | ||
813 | (void) params; | ||
814 | if ((0 != (statbuf.st_mode & S_ISUID)) && (0 == statbuf.st_uid)) | ||
815 | { | ||
816 | GNUNET_free (p); | ||
817 | return GNUNET_YES; | ||
818 | } | ||
819 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
820 | _ ("Binary `%s' exists, but is not SUID\n"), | ||
821 | p); | ||
822 | /* binary exists, but not SUID */ | ||
823 | } | ||
824 | GNUNET_free (p); | ||
825 | return GNUNET_NO; | ||
826 | } | ||
827 | |||
828 | |||
829 | /* end of os_installation.c */ | ||