os_installation.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853
  1. /*
  2. This file is part of GNUnet.
  3. (C) 2006-2014 Christian Grothoff (and other contributing authors)
  4. GNUnet is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published
  6. by the Free Software Foundation; either version 3, or (at your
  7. 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. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNUnet; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  15. Boston, MA 02111-1307, USA.
  16. */
  17. /**
  18. * @file src/util/os_installation.c
  19. * @brief get paths used by the program
  20. * @author Milan
  21. */
  22. #include <sys/stat.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <unistd.h>
  26. #include <unistr.h> /* for u16_to_u8 */
  27. #include "platform.h"
  28. #include "gnunet_util_lib.h"
  29. #if DARWIN
  30. #include <mach-o/ldsyms.h>
  31. #include <mach-o/dyld.h>
  32. #elif WINDOWS
  33. #include <windows.h>
  34. #endif
  35. #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
  36. #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
  37. #if LINUX
  38. /**
  39. * Try to determine path by reading /proc/PID/exe
  40. *
  41. * @return NULL on error
  42. */
  43. static char *
  44. get_path_from_proc_maps ()
  45. {
  46. char fn[64];
  47. char line[1024];
  48. char dir[1024];
  49. FILE *f;
  50. char *lgu;
  51. GNUNET_snprintf (fn, sizeof (fn), "/proc/%u/maps", getpid ());
  52. if (NULL == (f = FOPEN (fn, "r")))
  53. return NULL;
  54. while (NULL != fgets (line, sizeof (line), f))
  55. {
  56. if ((1 ==
  57. SSCANF (line, "%*x-%*x %*c%*c%*c%*c %*x %*2x:%*2x %*u%*[ ]%1023s", dir)) &&
  58. (NULL != (lgu = strstr (dir, "libgnunetutil"))))
  59. {
  60. lgu[0] = '\0';
  61. FCLOSE (f);
  62. return GNUNET_strdup (dir);
  63. }
  64. }
  65. FCLOSE (f);
  66. return NULL;
  67. }
  68. /**
  69. * Try to determine path by reading /proc/PID/exe
  70. *
  71. * @return NULL on error
  72. */
  73. static char *
  74. get_path_from_proc_exe ()
  75. {
  76. char fn[64];
  77. char lnk[1024];
  78. ssize_t size;
  79. GNUNET_snprintf (fn, sizeof (fn), "/proc/%u/exe", getpid ());
  80. size = readlink (fn, lnk, sizeof (lnk) - 1);
  81. if (size <= 0)
  82. {
  83. LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "readlink", fn);
  84. return NULL;
  85. }
  86. GNUNET_assert (size < sizeof (lnk));
  87. lnk[size] = '\0';
  88. while ((lnk[size] != '/') && (size > 0))
  89. size--;
  90. /* test for being in lib/gnunet/libexec/ or lib/MULTIARCH/gnunet/libexec */
  91. if ( (size > strlen ("/gnunet/libexec/")) &&
  92. (0 == strcmp ("/gnunet/libexec/",
  93. &lnk[size - strlen ("/gnunet/libexec/")])) )
  94. size -= strlen ("gnunet/libexec/");
  95. if ((size < 4) || (lnk[size - 4] != '/'))
  96. {
  97. /* not installed in "/bin/" -- binary path probably useless */
  98. return NULL;
  99. }
  100. lnk[size] = '\0';
  101. return GNUNET_strdup (lnk);
  102. }
  103. #endif
  104. #if WINDOWS
  105. static HINSTANCE dll_instance;
  106. /**
  107. * GNUNET_util_cl_init() in common_logging.c is preferred.
  108. * This function is only for thread-local storage (not used in GNUnet)
  109. * and hInstance saving.
  110. */
  111. BOOL WINAPI
  112. DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
  113. {
  114. switch (fdwReason)
  115. {
  116. case DLL_PROCESS_ATTACH:
  117. dll_instance = hinstDLL;
  118. break;
  119. case DLL_THREAD_ATTACH:
  120. break;
  121. case DLL_THREAD_DETACH:
  122. break;
  123. case DLL_PROCESS_DETACH:
  124. break;
  125. }
  126. return TRUE;
  127. }
  128. /**
  129. * Try to determine path with win32-specific function
  130. *
  131. * @return NULL on error
  132. */
  133. static char *
  134. get_path_from_module_filename ()
  135. {
  136. size_t pathlen = 512;
  137. DWORD real_pathlen;
  138. wchar_t *idx;
  139. wchar_t *modulepath = NULL;
  140. char *upath;
  141. uint8_t *u8_string;
  142. size_t u8_string_length;
  143. /* This braindead function won't tell us how much space it needs, so
  144. * we start at 1024 and double the space up if it doesn't fit, until
  145. * it fits, or we exceed the threshold.
  146. */
  147. do
  148. {
  149. pathlen = pathlen * 2;
  150. modulepath = GNUNET_realloc (modulepath, pathlen * sizeof (wchar_t));
  151. SetLastError (0);
  152. real_pathlen = GetModuleFileNameW (dll_instance, modulepath, pathlen * sizeof (wchar_t));
  153. } while (real_pathlen >= pathlen && pathlen < 16*1024);
  154. if (real_pathlen >= pathlen)
  155. GNUNET_abort ();
  156. /* To be safe */
  157. modulepath[real_pathlen] = '\0';
  158. idx = modulepath + real_pathlen;
  159. while ((idx > modulepath) && (*idx != L'\\') && (*idx != L'/'))
  160. idx--;
  161. *idx = L'\0';
  162. /* Now modulepath holds full path to the directory where libgnunetutil is.
  163. * This directory should look like <GNUNET_PREFIX>/bin or <GNUNET_PREFIX>.
  164. */
  165. if (wcschr (modulepath, L'/') || wcschr (modulepath, L'\\'))
  166. {
  167. /* At least one directory component (i.e. we're not in a root directory) */
  168. wchar_t *dirname = idx;
  169. while ((dirname > modulepath) && (*dirname != L'\\') && (*dirname != L'/'))
  170. dirname--;
  171. *dirname = L'\0';
  172. if (dirname > modulepath)
  173. {
  174. dirname++;
  175. /* Now modulepath holds full path to the parent directory of the directory
  176. * where libgnunetutil is.
  177. * dirname holds the name of the directory where libgnunetutil is.
  178. */
  179. if (wcsicmp (dirname, L"bin") == 0)
  180. {
  181. /* pass */
  182. }
  183. else
  184. {
  185. /* Roll back our changes to modulepath */
  186. dirname--;
  187. *dirname = L'/';
  188. }
  189. }
  190. }
  191. /* modulepath is GNUNET_PREFIX */
  192. u8_string = u16_to_u8 (modulepath, wcslen (modulepath), NULL, &u8_string_length);
  193. if (NULL == u8_string)
  194. GNUNET_abort ();
  195. upath = GNUNET_malloc (u8_string_length + 1);
  196. memcpy (upath, u8_string, u8_string_length);
  197. upath[u8_string_length] = '\0';
  198. free (u8_string);
  199. GNUNET_free (modulepath);
  200. return upath;
  201. }
  202. #endif
  203. #if DARWIN
  204. /**
  205. * Signature of the '_NSGetExecutablePath" function.
  206. *
  207. * @param buf where to write the path
  208. * @param number of bytes available in 'buf'
  209. * @return 0 on success, otherwise desired number of bytes is stored in 'bufsize'
  210. */
  211. typedef int (*MyNSGetExecutablePathProto) (char *buf, size_t * bufsize);
  212. /**
  213. * Try to obtain the path of our executable using '_NSGetExecutablePath'.
  214. *
  215. * @return NULL on error
  216. */
  217. static char *
  218. get_path_from_NSGetExecutablePath ()
  219. {
  220. static char zero = '\0';
  221. char *path;
  222. size_t len;
  223. MyNSGetExecutablePathProto func;
  224. path = NULL;
  225. if (NULL == (func =
  226. (MyNSGetExecutablePathProto) dlsym (RTLD_DEFAULT, "_NSGetExecutablePath")))
  227. return NULL;
  228. path = &zero;
  229. len = 0;
  230. /* get the path len, including the trailing \0 */
  231. (void) func (path, &len);
  232. if (0 == len)
  233. return NULL;
  234. path = GNUNET_malloc (len);
  235. if (0 != func (path, &len))
  236. {
  237. GNUNET_free (path);
  238. return NULL;
  239. }
  240. len = strlen (path);
  241. while ((path[len] != '/') && (len > 0))
  242. len--;
  243. path[len] = '\0';
  244. return path;
  245. }
  246. /**
  247. * Try to obtain the path of our executable using '_dyld_image' API.
  248. *
  249. * @return NULL on error
  250. */
  251. static char *
  252. get_path_from_dyld_image ()
  253. {
  254. const char *path;
  255. char *p;
  256. char *s;
  257. unsigned int i;
  258. int c;
  259. c = _dyld_image_count ();
  260. for (i = 0; i < c; i++)
  261. {
  262. if (((const void *) _dyld_get_image_header (i)) != (const void *)&_mh_dylib_header)
  263. continue;
  264. path = _dyld_get_image_name (i);
  265. if ( (NULL == path) || (0 == strlen (path)) )
  266. continue;
  267. p = GNUNET_strdup (path);
  268. s = p + strlen (p);
  269. while ((s > p) && ('/' != *s))
  270. s--;
  271. s++;
  272. *s = '\0';
  273. return p;
  274. }
  275. return NULL;
  276. }
  277. #endif
  278. /**
  279. * Return the actual path to a file found in the current
  280. * PATH environment variable.
  281. *
  282. * @param binary the name of the file to find
  283. * @return path to binary, NULL if not found
  284. */
  285. static char *
  286. get_path_from_PATH (const char *binary)
  287. {
  288. char *path;
  289. char *pos;
  290. char *end;
  291. char *buf;
  292. const char *p;
  293. if (NULL == (p = getenv ("PATH")))
  294. return NULL;
  295. #if WINDOWS
  296. /* On W32 look in CWD first. */
  297. GNUNET_asprintf (&path, ".%c%s", PATH_SEPARATOR, p);
  298. #else
  299. path = GNUNET_strdup (p); /* because we write on it */
  300. #endif
  301. buf = GNUNET_malloc (strlen (path) + strlen (binary) + 1 + 1);
  302. pos = path;
  303. while (NULL != (end = strchr (pos, PATH_SEPARATOR)))
  304. {
  305. *end = '\0';
  306. sprintf (buf, "%s/%s", pos, binary);
  307. if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
  308. {
  309. pos = GNUNET_strdup (pos);
  310. GNUNET_free (buf);
  311. GNUNET_free (path);
  312. return pos;
  313. }
  314. pos = end + 1;
  315. }
  316. sprintf (buf, "%s/%s", pos, binary);
  317. if (GNUNET_YES == GNUNET_DISK_file_test (buf))
  318. {
  319. pos = GNUNET_strdup (pos);
  320. GNUNET_free (buf);
  321. GNUNET_free (path);
  322. return pos;
  323. }
  324. GNUNET_free (buf);
  325. GNUNET_free (path);
  326. return NULL;
  327. }
  328. /**
  329. * Try to obtain the installation path using the "GNUNET_PREFIX" environment
  330. * variable.
  331. *
  332. * @return NULL on error (environment variable not set)
  333. */
  334. static char *
  335. get_path_from_GNUNET_PREFIX ()
  336. {
  337. const char *p;
  338. if (NULL != (p = getenv ("GNUNET_PREFIX")))
  339. return GNUNET_strdup (p);
  340. return NULL;
  341. }
  342. /**
  343. * @brief get the path to GNUnet bin/ or lib/, prefering the lib/ path
  344. * @author Milan
  345. *
  346. * @return a pointer to the executable path, or NULL on error
  347. */
  348. static char *
  349. os_get_gnunet_path ()
  350. {
  351. char *ret;
  352. if (NULL != (ret = get_path_from_GNUNET_PREFIX ()))
  353. return ret;
  354. #if LINUX
  355. if (NULL != (ret = get_path_from_proc_maps ()))
  356. return ret;
  357. /* try path *first*, before /proc/exe, as /proc/exe can be wrong */
  358. if (NULL != (ret = get_path_from_PATH ("gnunet-arm")))
  359. return ret;
  360. if (NULL != (ret = get_path_from_proc_exe ()))
  361. return ret;
  362. #endif
  363. #if WINDOWS
  364. if (NULL != (ret = get_path_from_module_filename ()))
  365. return ret;
  366. #endif
  367. #if DARWIN
  368. if (NULL != (ret = get_path_from_dyld_image ()))
  369. return ret;
  370. if (NULL != (ret = get_path_from_NSGetExecutablePath ()))
  371. return ret;
  372. #endif
  373. if (NULL != (ret = get_path_from_PATH ("gnunet-arm")))
  374. return ret;
  375. /* other attempts here */
  376. LOG (GNUNET_ERROR_TYPE_ERROR,
  377. _("Could not determine installation path for %s. Set `%s' environment variable.\n"),
  378. "GNUnet", "GNUNET_PREFIX");
  379. return NULL;
  380. }
  381. /**
  382. * @brief get the path to current app's bin/
  383. * @author Milan
  384. *
  385. * @return a pointer to the executable path, or NULL on error
  386. */
  387. static char *
  388. os_get_exec_path ()
  389. {
  390. char *ret = NULL;
  391. #if LINUX
  392. if (NULL != (ret = get_path_from_proc_exe ()))
  393. return ret;
  394. #endif
  395. #if WINDOWS
  396. if (NULL != (ret = get_path_from_module_filename ()))
  397. return ret;
  398. #endif
  399. #if DARWIN
  400. if (NULL != (ret = get_path_from_NSGetExecutablePath ()))
  401. return ret;
  402. #endif
  403. /* other attempts here */
  404. return ret;
  405. }
  406. /**
  407. * @brief get the path to a specific GNUnet installation directory or,
  408. * with #GNUNET_OS_IPK_SELF_PREFIX, the current running apps installation directory
  409. * @author Milan
  410. * @return a pointer to the dir path (to be freed by the caller)
  411. */
  412. char *
  413. GNUNET_OS_installation_get_path (enum GNUNET_OS_InstallationPathKind dirkind)
  414. {
  415. size_t n;
  416. const char *dirname;
  417. char *execpath = NULL;
  418. char *tmp;
  419. char *multiarch;
  420. char *libdir;
  421. int isbasedir;
  422. /* if wanted, try to get the current app's bin/ */
  423. if (dirkind == GNUNET_OS_IPK_SELF_PREFIX)
  424. execpath = os_get_exec_path ();
  425. /* try to get GNUnet's bin/ or lib/, or if previous was unsuccessful some
  426. * guess for the current app */
  427. if (NULL == execpath)
  428. execpath = os_get_gnunet_path ();
  429. if (NULL == execpath)
  430. return NULL;
  431. n = strlen (execpath);
  432. if (0 == n)
  433. {
  434. /* should never happen, but better safe than sorry */
  435. GNUNET_free (execpath);
  436. return NULL;
  437. }
  438. /* remove filename itself */
  439. while ((n > 1) && (DIR_SEPARATOR == execpath[n - 1]))
  440. execpath[--n] = '\0';
  441. isbasedir = 1;
  442. if ((n > 6) &&
  443. ((0 == strcasecmp (&execpath[n - 6], "/lib32")) ||
  444. (0 == strcasecmp (&execpath[n - 6], "/lib64"))))
  445. {
  446. if ( (GNUNET_OS_IPK_LIBDIR != dirkind) &&
  447. (GNUNET_OS_IPK_LIBEXECDIR != dirkind) )
  448. {
  449. /* strip '/lib32' or '/lib64' */
  450. execpath[n - 6] = '\0';
  451. n -= 6;
  452. }
  453. else
  454. isbasedir = 0;
  455. }
  456. else if ((n > 4) &&
  457. ((0 == strcasecmp (&execpath[n - 4], "/bin")) ||
  458. (0 == strcasecmp (&execpath[n - 4], "/lib"))))
  459. {
  460. /* strip '/bin' or '/lib' */
  461. execpath[n - 4] = '\0';
  462. n -= 4;
  463. }
  464. multiarch = NULL;
  465. if (NULL != (libdir = strstr (execpath, "/lib/")))
  466. {
  467. /* test for multi-arch path of the form "PREFIX/lib/MULTIARCH/";
  468. here we need to re-add 'multiarch' to lib and libexec paths later! */
  469. multiarch = &libdir[5];
  470. if (NULL == strchr (multiarch, '/'))
  471. libdir[0] = '\0'; /* Debian multiarch format, cut of from 'execpath' but preserve in multicarch */
  472. else
  473. multiarch = NULL; /* maybe not, multiarch still has a '/', which is not OK */
  474. }
  475. /* in case this was a directory named foo-bin, remove "foo-" */
  476. while ((n > 1) && (execpath[n - 1] == DIR_SEPARATOR))
  477. execpath[--n] = '\0';
  478. switch (dirkind)
  479. {
  480. case GNUNET_OS_IPK_PREFIX:
  481. case GNUNET_OS_IPK_SELF_PREFIX:
  482. dirname = DIR_SEPARATOR_STR;
  483. break;
  484. case GNUNET_OS_IPK_BINDIR:
  485. dirname = DIR_SEPARATOR_STR "bin" DIR_SEPARATOR_STR;
  486. break;
  487. case GNUNET_OS_IPK_LIBDIR:
  488. if (isbasedir)
  489. {
  490. GNUNET_asprintf (&tmp,
  491. "%s%s%s%s%s",
  492. execpath,
  493. DIR_SEPARATOR_STR "lib",
  494. (NULL != multiarch) ? DIR_SEPARATOR_STR : "",
  495. (NULL != multiarch) ? multiarch : "",
  496. DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR);
  497. if (GNUNET_YES ==
  498. GNUNET_DISK_directory_test (tmp, GNUNET_YES))
  499. {
  500. GNUNET_free (execpath);
  501. return tmp;
  502. }
  503. GNUNET_free (tmp);
  504. tmp = NULL;
  505. if (4 == sizeof (void *))
  506. {
  507. dirname =
  508. DIR_SEPARATOR_STR "lib32" DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR;
  509. GNUNET_asprintf (&tmp,
  510. "%s%s",
  511. execpath,
  512. dirname);
  513. }
  514. if (8 == sizeof (void *))
  515. {
  516. dirname =
  517. DIR_SEPARATOR_STR "lib64" DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR;
  518. GNUNET_asprintf (&tmp,
  519. "%s%s",
  520. execpath,
  521. dirname);
  522. }
  523. if ( (NULL != tmp) &&
  524. (GNUNET_YES ==
  525. GNUNET_DISK_directory_test (tmp, GNUNET_YES)) )
  526. {
  527. GNUNET_free (execpath);
  528. return tmp;
  529. }
  530. GNUNET_free (tmp);
  531. }
  532. dirname = DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR;
  533. break;
  534. case GNUNET_OS_IPK_DATADIR:
  535. dirname =
  536. DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR;
  537. break;
  538. case GNUNET_OS_IPK_LOCALEDIR:
  539. dirname =
  540. DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "locale" DIR_SEPARATOR_STR;
  541. break;
  542. case GNUNET_OS_IPK_ICONDIR:
  543. dirname =
  544. DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "icons" DIR_SEPARATOR_STR;
  545. break;
  546. case GNUNET_OS_IPK_DOCDIR:
  547. dirname =
  548. DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "doc" DIR_SEPARATOR_STR \
  549. "gnunet" DIR_SEPARATOR_STR;
  550. break;
  551. case GNUNET_OS_IPK_LIBEXECDIR:
  552. if (isbasedir)
  553. {
  554. dirname =
  555. DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR "libexec" DIR_SEPARATOR_STR;
  556. GNUNET_asprintf (&tmp,
  557. "%s%s%s%s",
  558. execpath,
  559. DIR_SEPARATOR_STR "lib" DIR_SEPARATOR_STR,
  560. (NULL != multiarch) ? multiarch : "",
  561. dirname);
  562. if (GNUNET_YES ==
  563. GNUNET_DISK_directory_test (tmp, GNUNET_YES))
  564. {
  565. GNUNET_free (execpath);
  566. return tmp;
  567. }
  568. GNUNET_free (tmp);
  569. tmp = NULL;
  570. if (4 == sizeof (void *))
  571. {
  572. dirname =
  573. DIR_SEPARATOR_STR "lib32" DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR \
  574. "libexec" DIR_SEPARATOR_STR;
  575. GNUNET_asprintf (&tmp,
  576. "%s%s",
  577. execpath,
  578. dirname);
  579. }
  580. if (8 == sizeof (void *))
  581. {
  582. dirname =
  583. DIR_SEPARATOR_STR "lib64" DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR \
  584. "libexec" DIR_SEPARATOR_STR;
  585. GNUNET_asprintf (&tmp,
  586. "%s%s",
  587. execpath,
  588. dirname);
  589. }
  590. if ( (NULL != tmp) &&
  591. (GNUNET_YES ==
  592. GNUNET_DISK_directory_test (tmp, GNUNET_YES)) )
  593. {
  594. GNUNET_free (execpath);
  595. return tmp;
  596. }
  597. GNUNET_free (tmp);
  598. }
  599. dirname =
  600. DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR \
  601. "libexec" DIR_SEPARATOR_STR;
  602. break;
  603. default:
  604. GNUNET_free (execpath);
  605. return NULL;
  606. }
  607. GNUNET_asprintf (&tmp,
  608. "%s%s",
  609. execpath,
  610. dirname);
  611. GNUNET_free (execpath);
  612. return tmp;
  613. }
  614. /**
  615. * Given the name of a gnunet-helper, gnunet-service or gnunet-daemon
  616. * binary, try to prefix it with the libexec/-directory to get the
  617. * full path.
  618. *
  619. * @param progname name of the binary
  620. * @return full path to the binary, if possible, otherwise copy of 'progname'
  621. */
  622. char *
  623. GNUNET_OS_get_libexec_binary_path (const char *progname)
  624. {
  625. static char *cache;
  626. char *libexecdir;
  627. char *binary;
  628. if ( (DIR_SEPARATOR == progname[0]) ||
  629. (GNUNET_YES == GNUNET_STRINGS_path_is_absolute (progname, GNUNET_NO, NULL, NULL)) )
  630. return GNUNET_strdup (progname);
  631. if (NULL != cache)
  632. libexecdir = cache;
  633. else
  634. libexecdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBEXECDIR);
  635. if (NULL == libexecdir)
  636. return GNUNET_strdup (progname);
  637. GNUNET_asprintf (&binary,
  638. "%s%s",
  639. libexecdir,
  640. progname);
  641. cache = libexecdir;
  642. return binary;
  643. }
  644. /**
  645. * Check whether an executable exists and possibly
  646. * if the suid bit is set on the file.
  647. * Attempts to find the file using the current
  648. * PATH environment variable as a search path.
  649. *
  650. * @param binary the name of the file to check.
  651. * W32: must not have an .exe suffix.
  652. * @param check_suid input true if the binary should be checked for SUID (*nix)
  653. * W32: checks if the program has sufficient privileges by executing this
  654. * binary with the -d flag. -d omits a programs main loop and only
  655. * executes all privileged operations in an binary.
  656. * @param params parameters used for w32 privilege checking (can be NULL for != w32 )
  657. * @return #GNUNET_YES if the file is SUID (*nix) or can be executed with current privileges (W32),
  658. * #GNUNET_NO if not SUID (but binary exists),
  659. * #GNUNET_SYSERR on error (no such binary or not executable)
  660. */
  661. int
  662. GNUNET_OS_check_helper_binary (const char *binary, int check_suid, const char *params)
  663. {
  664. struct stat statbuf;
  665. char *p;
  666. char *pf;
  667. #ifdef MINGW
  668. char *binaryexe;
  669. GNUNET_asprintf (&binaryexe, "%s.exe", binary);
  670. if ( (GNUNET_YES == GNUNET_STRINGS_path_is_absolute (binaryexe, GNUNET_NO,
  671. NULL, NULL)) ||
  672. (0 == strncmp (binary, "./", 2)) )
  673. p = GNUNET_strdup (binaryexe);
  674. else
  675. {
  676. p = get_path_from_PATH (binaryexe);
  677. if (NULL != p)
  678. {
  679. GNUNET_asprintf (&pf, "%s/%s", p, binaryexe);
  680. GNUNET_free (p);
  681. p = pf;
  682. }
  683. }
  684. GNUNET_free (binaryexe);
  685. #else
  686. if ( (GNUNET_YES == GNUNET_STRINGS_path_is_absolute (binary, GNUNET_NO,
  687. NULL, NULL)) ||
  688. (0 == strncmp (binary, "./", 2)) )
  689. p = GNUNET_strdup (binary);
  690. else
  691. {
  692. p = get_path_from_PATH (binary);
  693. if (NULL != p)
  694. {
  695. GNUNET_asprintf (&pf, "%s/%s", p, binary);
  696. GNUNET_free (p);
  697. p = pf;
  698. }
  699. }
  700. #endif
  701. if (NULL == p)
  702. {
  703. LOG (GNUNET_ERROR_TYPE_INFO,
  704. _("Could not find binary `%s' in PATH!\n"),
  705. binary);
  706. return GNUNET_SYSERR;
  707. }
  708. if (0 != ACCESS (p, X_OK))
  709. {
  710. LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", p);
  711. GNUNET_free (p);
  712. return GNUNET_SYSERR;
  713. }
  714. #ifndef MINGW
  715. if (0 == getuid ())
  716. {
  717. /* as we run as root, we don't insist on SUID */
  718. GNUNET_free (p);
  719. return GNUNET_OK;
  720. }
  721. #endif
  722. if (0 != STAT (p, &statbuf))
  723. {
  724. LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", p);
  725. GNUNET_free (p);
  726. return GNUNET_SYSERR;
  727. }
  728. if (check_suid){
  729. #ifndef MINGW
  730. if ((0 != (statbuf.st_mode & S_ISUID)) && (0 == statbuf.st_uid))
  731. {
  732. GNUNET_free (p);
  733. return GNUNET_YES;
  734. }
  735. /* binary exists, but not SUID */
  736. #else
  737. STARTUPINFO start;
  738. char parameters[512];
  739. PROCESS_INFORMATION proc;
  740. DWORD exit_value;
  741. GNUNET_snprintf (parameters,
  742. sizeof (parameters),
  743. "-d %s", params);
  744. memset (&start, 0, sizeof (start));
  745. start.cb = sizeof (start);
  746. memset (&proc, 0, sizeof (proc));
  747. // Start the child process.
  748. if ( ! (CreateProcess( p, // current windows (2k3 and up can handle / instead of \ in paths))
  749. parameters, // execute dryrun/priviliege checking mode
  750. NULL, // Process handle not inheritable
  751. NULL, // Thread handle not inheritable
  752. FALSE, // Set handle inheritance to FALSE
  753. CREATE_DEFAULT_ERROR_MODE, // No creation flags
  754. NULL, // Use parent's environment block
  755. NULL, // Use parent's starting directory
  756. &start, // Pointer to STARTUPINFO structure
  757. &proc ) // Pointer to PROCESS_INFORMATION structure
  758. ))
  759. {
  760. LOG (GNUNET_ERROR_TYPE_ERROR,
  761. _("CreateProcess failed for binary %s (%d).\n"),
  762. p, GetLastError());
  763. return GNUNET_SYSERR;
  764. }
  765. // Wait until child process exits.
  766. WaitForSingleObject( proc.hProcess, INFINITE );
  767. if ( ! GetExitCodeProcess (proc.hProcess, &exit_value)){
  768. LOG (GNUNET_ERROR_TYPE_ERROR,
  769. _("GetExitCodeProcess failed for binary %s (%d).\n"),
  770. p, GetLastError() );
  771. return GNUNET_SYSERR;
  772. }
  773. // Close process and thread handles.
  774. CloseHandle( proc.hProcess );
  775. CloseHandle( proc.hThread );
  776. if (!exit_value)
  777. return GNUNET_YES;
  778. #endif
  779. }
  780. GNUNET_free (p);
  781. return GNUNET_NO;
  782. }
  783. /* end of os_installation.c */