os_installation.c 22 KB

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