Merge branch 'cadet-new-options'
[oweals/gnunet.git] / src / util / os_installation.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2006-2018 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 <sys/stat.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <unistr.h> /* for u16_to_u8 */
36
37 #include "platform.h"
38 #include "gnunet_util_lib.h"
39 #if DARWIN
40 #include <mach-o/ldsyms.h>
41 #include <mach-o/dyld.h>
42 #elif WINDOWS
43 #include <windows.h>
44 #endif
45
46
47 #define LOG(kind,...) GNUNET_log_from (kind, "util-os-installation", __VA_ARGS__)
48
49 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util-os-installation", syscall, filename)
50
51
52 /**
53  * Default project data used for installation path detection
54  * for GNUnet (core).
55  */
56 static const struct GNUNET_OS_ProjectData default_pd = {
57   .libname = "libgnunetutil",
58   .project_dirname = "gnunet",
59   .binary_name = "gnunet-arm",
60   .env_varname = "GNUNET_PREFIX",
61   .base_config_varname = "GNUNET_BASE_CONFIG",
62   .bug_email = "gnunet-developers@gnu.org",
63   .homepage = "http://www.gnu.org/s/gnunet/",
64   .config_file = "gnunet.conf",
65   .user_config_file = "~/.config/gnunet.conf",
66 };
67
68 /**
69  * Which project data do we currently use for installation
70  * path detection? Never NULL.
71  */
72 static const struct GNUNET_OS_ProjectData *current_pd = &default_pd;
73
74 /**
75  * Return default project data used by 'libgnunetutil' for GNUnet.
76  */
77 const struct GNUNET_OS_ProjectData *
78 GNUNET_OS_project_data_default (void)
79 {
80   return &default_pd;
81 }
82
83
84 /**
85  * @return current project data.
86  */
87 const struct GNUNET_OS_ProjectData *
88 GNUNET_OS_project_data_get ()
89 {
90   return current_pd;
91 }
92
93
94 /**
95  * Setup OS subsystem with project data.
96  *
97  * @param pd project data used to determine paths
98  */
99 void
100 GNUNET_OS_init (const struct GNUNET_OS_ProjectData *pd)
101 {
102   GNUNET_assert (NULL != pd);
103   current_pd = pd;
104 }
105
106
107 #if LINUX
108 /**
109  * Try to determine path by reading /proc/PID/exe
110  *
111  * @return NULL on error
112  */
113 static char *
114 get_path_from_proc_maps ()
115 {
116   char fn[64];
117   char line[1024];
118   char dir[1024];
119   FILE *f;
120   char *lgu;
121
122   GNUNET_snprintf (fn, sizeof (fn), "/proc/%u/maps", getpid ());
123   if (NULL == (f = FOPEN (fn, "r")))
124     return NULL;
125   while (NULL != fgets (line, sizeof (line), f))
126   {
127     if ((1 ==
128          SSCANF (line, "%*x-%*x %*c%*c%*c%*c %*x %*2x:%*2x %*u%*[ ]%1023s", dir)) &&
129         (NULL != (lgu = strstr (dir,
130                                 current_pd->libname))))
131     {
132       lgu[0] = '\0';
133       FCLOSE (f);
134       return GNUNET_strdup (dir);
135     }
136   }
137   FCLOSE (f);
138   return NULL;
139 }
140
141
142 /**
143  * Try to determine path by reading /proc/PID/exe
144  *
145  * @return NULL on error
146  */
147 static char *
148 get_path_from_proc_exe ()
149 {
150   char fn[64];
151   char lnk[1024];
152   ssize_t size;
153   char *lep;
154
155   GNUNET_snprintf (fn,
156                    sizeof (fn),
157                    "/proc/%u/exe",
158                    getpid ());
159   size = readlink (fn,
160                    lnk,
161                    sizeof (lnk) - 1);
162   if (size <= 0)
163   {
164     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR,
165                        "readlink",
166                        fn);
167     return NULL;
168   }
169   GNUNET_assert ( ((size_t) size) < sizeof (lnk));
170   lnk[size] = '\0';
171   while ((lnk[size] != '/') && (size > 0))
172     size--;
173   GNUNET_asprintf (&lep,
174                    "/%s/libexec/",
175                    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,
179                      &lnk[size - strlen (lep)])) )
180     size -= strlen (lep) - 1;
181   GNUNET_free (lep);
182   if ( (size < 4) ||
183        (lnk[size - 4] != '/') )
184   {
185     /* not installed in "/bin/" -- binary path probably useless */
186     return NULL;
187   }
188   lnk[size] = '\0';
189   return GNUNET_strdup (lnk);
190 }
191 #endif
192
193
194 #if WINDOWS
195 static HINSTANCE dll_instance;
196
197
198 /**
199  * GNUNET_util_cl_init() in common_logging.c is preferred.
200  * This function is only for thread-local storage (not used in GNUnet)
201  * and hInstance saving.
202  */
203 BOOL WINAPI
204 DllMain (HINSTANCE hinstDLL,
205          DWORD fdwReason,
206          LPVOID lpvReserved)
207 {
208   switch (fdwReason)
209   {
210     case DLL_PROCESS_ATTACH:
211       dll_instance = hinstDLL;
212       break;
213     case DLL_THREAD_ATTACH:
214       break;
215     case DLL_THREAD_DETACH:
216       break;
217     case DLL_PROCESS_DETACH:
218       break;
219   }
220   return TRUE;
221 }
222
223
224 /**
225  * Try to determine path with win32-specific function
226  *
227  * @return NULL on error
228  */
229 static char *
230 get_path_from_module_filename ()
231 {
232   size_t pathlen = 512;
233   DWORD real_pathlen;
234   wchar_t *idx;
235   wchar_t *modulepath = NULL;
236   char *upath;
237   uint8_t *u8_string;
238   size_t u8_string_length;
239
240   /* This braindead function won't tell us how much space it needs, so
241    * we start at 1024 and double the space up if it doesn't fit, until
242    * it fits, or we exceed the threshold.
243    */
244   do
245   {
246     pathlen = pathlen * 2;
247     modulepath = GNUNET_realloc (modulepath,
248                                  pathlen * sizeof (wchar_t));
249     SetLastError (0);
250     real_pathlen = GetModuleFileNameW (dll_instance,
251                                        modulepath,
252                                        pathlen * sizeof (wchar_t));
253   } while (real_pathlen >= pathlen && pathlen < 16*1024);
254   if (real_pathlen >= pathlen)
255     GNUNET_assert (0);
256   /* To be safe */
257   modulepath[real_pathlen] = '\0';
258
259   idx = modulepath + real_pathlen;
260   while ((idx > modulepath) && (*idx != L'\\') && (*idx != L'/'))
261     idx--;
262   *idx = L'\0';
263
264   /* Now modulepath holds full path to the directory where libgnunetutil is.
265    * This directory should look like <GNUNET_PREFIX>/bin or <GNUNET_PREFIX>.
266    */
267   if (wcschr (modulepath, L'/') || wcschr (modulepath, L'\\'))
268   {
269     /* At least one directory component (i.e. we're not in a root directory) */
270     wchar_t *dirname = idx;
271     while ((dirname > modulepath) && (*dirname != L'\\') && (*dirname != L'/'))
272       dirname--;
273     *dirname = L'\0';
274     if (dirname > modulepath)
275     {
276       dirname++;
277       /* Now modulepath holds full path to the parent directory of the directory
278        * where libgnunetutil is.
279        * dirname holds the name of the directory where libgnunetutil is.
280        */
281       if (wcsicmp (dirname, L"bin") == 0)
282       {
283         /* pass */
284       }
285       else
286       {
287         /* Roll back our changes to modulepath */
288         dirname--;
289         *dirname = L'/';
290       }
291     }
292   }
293
294   /* modulepath is GNUNET_PREFIX */
295   u8_string = u16_to_u8 (modulepath, wcslen (modulepath), NULL, &u8_string_length);
296   if (NULL == u8_string)
297     GNUNET_assert (0);
298
299   upath = GNUNET_malloc (u8_string_length + 1);
300   GNUNET_memcpy (upath, u8_string, u8_string_length);
301   upath[u8_string_length] = '\0';
302
303   free (u8_string);
304   GNUNET_free (modulepath);
305
306   return upath;
307 }
308 #endif
309
310
311 #if DARWIN
312 /**
313  * Signature of the '_NSGetExecutablePath" function.
314  *
315  * @param buf where to write the path
316  * @param number of bytes available in @a buf
317  * @return 0 on success, otherwise desired number of bytes is stored in 'bufsize'
318  */
319 typedef int
320 (*MyNSGetExecutablePathProto) (char *buf,
321                                size_t *bufsize);
322
323
324 /**
325  * Try to obtain the path of our executable using '_NSGetExecutablePath'.
326  *
327  * @return NULL on error
328  */
329 static char *
330 get_path_from_NSGetExecutablePath ()
331 {
332   static char zero = '\0';
333   char *path;
334   size_t len;
335   MyNSGetExecutablePathProto func;
336
337   path = NULL;
338   if (NULL == (func =
339                (MyNSGetExecutablePathProto) dlsym (RTLD_DEFAULT, "_NSGetExecutablePath")))
340     return NULL;
341   path = &zero;
342   len = 0;
343   /* get the path len, including the trailing \0 */
344   (void) func (path, &len);
345   if (0 == len)
346     return NULL;
347   path = GNUNET_malloc (len);
348   if (0 != func (path, &len))
349   {
350     GNUNET_free (path);
351     return NULL;
352   }
353   len = strlen (path);
354   while ((path[len] != '/') && (len > 0))
355     len--;
356   path[len] = '\0';
357   return path;
358 }
359
360
361 /**
362  * Try to obtain the path of our executable using '_dyld_image' API.
363  *
364  * @return NULL on error
365  */
366 static char *
367 get_path_from_dyld_image ()
368 {
369   const char *path;
370   char *p;
371   char *s;
372   unsigned int i;
373   int c;
374
375   c = _dyld_image_count ();
376   for (i = 0; i < c; i++)
377   {
378     if (((const void *) _dyld_get_image_header (i)) !=
379         ((const void *) &_mh_dylib_header) )
380       continue;
381     path = _dyld_get_image_name (i);
382     if ( (NULL == path) || (0 == strlen (path)) )
383       continue;
384     p = GNUNET_strdup (path);
385     s = p + strlen (p);
386     while ((s > p) && ('/' != *s))
387       s--;
388     s++;
389     *s = '\0';
390     return p;
391   }
392   return NULL;
393 }
394 #endif
395
396
397 /**
398  * Return the actual path to a file found in the current
399  * PATH environment variable.
400  *
401  * @param binary the name of the file to find
402  * @return path to binary, NULL if not found
403  */
404 static char *
405 get_path_from_PATH (const char *binary)
406 {
407   char *path;
408   char *pos;
409   char *end;
410   char *buf;
411   const char *p;
412
413   if (NULL == (p = getenv ("PATH")))
414     return NULL;
415 #if WINDOWS
416   /* On W32 look in CWD first. */
417   GNUNET_asprintf (&path, ".%c%s", PATH_SEPARATOR, p);
418 #else
419   path = GNUNET_strdup (p);     /* because we write on it */
420 #endif
421   buf = GNUNET_malloc (strlen (path) + strlen (binary) + 1 + 1);
422   pos = path;
423   while (NULL != (end = strchr (pos, PATH_SEPARATOR)))
424   {
425     *end = '\0';
426     sprintf (buf, "%s/%s", pos, binary);
427     if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
428     {
429       pos = GNUNET_strdup (pos);
430       GNUNET_free (buf);
431       GNUNET_free (path);
432       return pos;
433     }
434     pos = end + 1;
435   }
436   sprintf (buf, "%s/%s", pos, binary);
437   if (GNUNET_YES == GNUNET_DISK_file_test (buf))
438   {
439     pos = GNUNET_strdup (pos);
440     GNUNET_free (buf);
441     GNUNET_free (path);
442     return pos;
443   }
444   GNUNET_free (buf);
445   GNUNET_free (path);
446   return NULL;
447 }
448
449
450 /**
451  * Try to obtain the installation path using the "GNUNET_PREFIX" environment
452  * variable.
453  *
454  * @return NULL on error (environment variable not set)
455  */
456 static char *
457 get_path_from_GNUNET_PREFIX ()
458 {
459   const char *p;
460
461   if ( (NULL != current_pd->env_varname) &&
462        (NULL != (p = getenv (current_pd->env_varname))) )
463     return GNUNET_strdup (p);
464   if ( (NULL != current_pd->env_varname_alt) &&
465        (NULL != (p = getenv (current_pd->env_varname_alt))) )
466     return GNUNET_strdup (p);
467   return NULL;
468 }
469
470
471 /**
472  * @brief get the path to GNUnet bin/ or lib/, prefering the lib/ path
473  * @author Milan
474  *
475  * @return a pointer to the executable path, or NULL on error
476  */
477 static char *
478 os_get_gnunet_path ()
479 {
480   char *ret;
481
482   if (NULL != (ret = get_path_from_GNUNET_PREFIX ()))
483     return ret;
484 #if LINUX
485   if (NULL != (ret = get_path_from_proc_maps ()))
486     return ret;
487   /* try path *first*, before /proc/exe, as /proc/exe can be wrong */
488   if ( (NULL != current_pd->binary_name) &&
489        (NULL != (ret = get_path_from_PATH (current_pd->binary_name))) )
490     return ret;
491   if (NULL != (ret = get_path_from_proc_exe ()))
492     return ret;
493 #endif
494 #if WINDOWS
495   if (NULL != (ret = get_path_from_module_filename ()))
496     return ret;
497 #endif
498 #if DARWIN
499   if (NULL != (ret = get_path_from_dyld_image ()))
500     return ret;
501   if (NULL != (ret = get_path_from_NSGetExecutablePath ()))
502     return ret;
503 #endif
504   if ( (NULL != current_pd->binary_name) &&
505        (NULL != (ret = get_path_from_PATH (current_pd->binary_name))) )
506     return ret;
507   /* other attempts here */
508   LOG (GNUNET_ERROR_TYPE_ERROR,
509        _("Could not determine installation path for %s.  Set `%s' environment variable.\n"),
510        current_pd->project_dirname,
511        current_pd->env_varname);
512   return NULL;
513 }
514
515
516 /**
517  * @brief get the path to current app's bin/
518  * @return a pointer to the executable path, or NULL on error
519  */
520 static char *
521 os_get_exec_path ()
522 {
523   char *ret = NULL;
524
525 #if LINUX
526   if (NULL != (ret = get_path_from_proc_exe ()))
527     return ret;
528 #endif
529 #if WINDOWS
530   if (NULL != (ret = get_path_from_module_filename ()))
531     return ret;
532 #endif
533 #if DARWIN
534   if (NULL != (ret = get_path_from_NSGetExecutablePath ()))
535     return ret;
536 #endif
537   /* other attempts here */
538   return ret;
539 }
540
541
542 /**
543  * @brief get the path to a specific GNUnet installation directory or,
544  * with #GNUNET_OS_IPK_SELF_PREFIX, the current running apps installation directory
545  * @return a pointer to the dir path (to be freed by the caller)
546  */
547 char *
548 GNUNET_OS_installation_get_path (enum GNUNET_OS_InstallationPathKind dirkind)
549 {
550   size_t n;
551   char *dirname;
552   char *execpath = NULL;
553   char *tmp;
554   char *multiarch;
555   char *libdir;
556   int isbasedir;
557
558   /* if wanted, try to get the current app's bin/ */
559   if (dirkind == GNUNET_OS_IPK_SELF_PREFIX)
560     execpath = os_get_exec_path ();
561
562   /* try to get GNUnet's bin/ or lib/, or if previous was unsuccessful some
563    * guess for the current app */
564   if (NULL == execpath)
565     execpath = os_get_gnunet_path ();
566
567   if (NULL == execpath)
568     return NULL;
569
570   n = strlen (execpath);
571   if (0 == n)
572   {
573     /* should never happen, but better safe than sorry */
574     GNUNET_free (execpath);
575     return NULL;
576   }
577   /* remove filename itself */
578   while ((n > 1) && (DIR_SEPARATOR == execpath[n - 1]))
579     execpath[--n] = '\0';
580
581   isbasedir = 1;
582   if ((n > 6) &&
583       ((0 == strcasecmp (&execpath[n - 6], "/lib32")) ||
584        (0 == strcasecmp (&execpath[n - 6], "/lib64"))))
585   {
586     if ( (GNUNET_OS_IPK_LIBDIR != dirkind) &&
587          (GNUNET_OS_IPK_LIBEXECDIR != dirkind) )
588     {
589       /* strip '/lib32' or '/lib64' */
590       execpath[n - 6] = '\0';
591       n -= 6;
592     }
593     else
594       isbasedir = 0;
595   }
596   else if ((n > 4) &&
597            ((0 == strcasecmp (&execpath[n - 4], "/bin")) ||
598             (0 == strcasecmp (&execpath[n - 4], "/lib"))))
599   {
600     /* strip '/bin' or '/lib' */
601     execpath[n - 4] = '\0';
602     n -= 4;
603   }
604   multiarch = NULL;
605   if (NULL != (libdir = strstr (execpath, "/lib/")))
606   {
607     /* test for multi-arch path of the form "PREFIX/lib/MULTIARCH/";
608        here we need to re-add 'multiarch' to lib and libexec paths later! */
609     multiarch = &libdir[5];
610     if (NULL == strchr (multiarch, '/'))
611       libdir[0] = '\0'; /* Debian multiarch format, cut of from 'execpath' but preserve in multicarch */
612     else
613       multiarch = NULL; /* maybe not, multiarch still has a '/', which is not OK */
614   }
615   /* in case this was a directory named foo-bin, remove "foo-" */
616   while ((n > 1) && (execpath[n - 1] == DIR_SEPARATOR))
617     execpath[--n] = '\0';
618   switch (dirkind)
619   {
620   case GNUNET_OS_IPK_PREFIX:
621   case GNUNET_OS_IPK_SELF_PREFIX:
622     dirname = GNUNET_strdup (DIR_SEPARATOR_STR);
623     break;
624   case GNUNET_OS_IPK_BINDIR:
625     dirname = GNUNET_strdup (DIR_SEPARATOR_STR "bin" DIR_SEPARATOR_STR);
626     break;
627   case GNUNET_OS_IPK_LIBDIR:
628     if (isbasedir)
629     {
630       GNUNET_asprintf (&tmp,
631                        "%s%s%s%s%s%s%s",
632                        execpath,
633                        DIR_SEPARATOR_STR "lib",
634                        (NULL != multiarch) ? DIR_SEPARATOR_STR : "",
635                        (NULL != multiarch) ? multiarch : "",
636                        DIR_SEPARATOR_STR,
637                        current_pd->project_dirname,
638                        DIR_SEPARATOR_STR);
639       if (GNUNET_YES ==
640           GNUNET_DISK_directory_test (tmp, GNUNET_YES))
641       {
642         GNUNET_free (execpath);
643         return tmp;
644       }
645       GNUNET_free (tmp);
646       tmp = NULL;
647       dirname = NULL;
648       if (4 == sizeof (void *))
649       {
650         GNUNET_asprintf (&dirname,
651                          DIR_SEPARATOR_STR "lib32" DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR,
652                          current_pd->project_dirname);
653         GNUNET_asprintf (&tmp,
654                          "%s%s",
655                          execpath,
656                          dirname);
657       }
658       if (8 == sizeof (void *))
659       {
660         GNUNET_asprintf (&dirname,
661                          DIR_SEPARATOR_STR "lib64" DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR,
662                          current_pd->project_dirname);
663         GNUNET_asprintf (&tmp,
664                          "%s%s",
665                          execpath,
666                          dirname);
667       }
668
669       if ( (NULL != tmp) &&
670            (GNUNET_YES ==
671             GNUNET_DISK_directory_test (tmp, GNUNET_YES)) )
672       {
673         GNUNET_free (execpath);
674         GNUNET_free_non_null (dirname);
675         return tmp;
676       }
677       GNUNET_free (tmp);
678       GNUNET_free_non_null (dirname);
679     }
680     GNUNET_asprintf (&dirname,
681                      DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR,
682                      current_pd->project_dirname);
683     break;
684   case GNUNET_OS_IPK_DATADIR:
685     GNUNET_asprintf (&dirname,
686                      DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR,
687                      current_pd->project_dirname);
688     break;
689   case GNUNET_OS_IPK_LOCALEDIR:
690     dirname = GNUNET_strdup (DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "locale" DIR_SEPARATOR_STR);
691     break;
692   case GNUNET_OS_IPK_ICONDIR:
693     dirname = GNUNET_strdup (DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "icons" DIR_SEPARATOR_STR);
694     break;
695   case GNUNET_OS_IPK_DOCDIR:
696     GNUNET_asprintf (&dirname,
697                      DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "doc" DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR,
698                      current_pd->project_dirname);
699     break;
700   case GNUNET_OS_IPK_LIBEXECDIR:
701     if (isbasedir)
702     {
703       GNUNET_asprintf (&dirname,
704                        DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR "libexec" DIR_SEPARATOR_STR,
705                        current_pd->project_dirname);
706       GNUNET_asprintf (&tmp,
707                        "%s%s%s%s",
708                        execpath,
709                        DIR_SEPARATOR_STR "lib" DIR_SEPARATOR_STR,
710                        (NULL != multiarch) ? multiarch : "",
711                        dirname);
712       if (GNUNET_YES ==
713           GNUNET_DISK_directory_test (tmp, GNUNET_YES))
714       {
715         GNUNET_free (execpath);
716         GNUNET_free (dirname);
717         return tmp;
718       }
719       GNUNET_free (tmp);
720       tmp = NULL;
721       dirname = NULL;
722       if (4 == sizeof (void *))
723       {
724         GNUNET_asprintf (&dirname,
725                          DIR_SEPARATOR_STR "lib32" DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR "libexec" DIR_SEPARATOR_STR,
726                          current_pd->project_dirname);
727         GNUNET_asprintf (&tmp,
728                          "%s%s",
729                          execpath,
730                          dirname);
731       }
732       if (8 == sizeof (void *))
733       {
734         GNUNET_asprintf (&dirname,
735                          DIR_SEPARATOR_STR "lib64" DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR "libexec" DIR_SEPARATOR_STR,
736                          current_pd->project_dirname);
737         GNUNET_asprintf (&tmp,
738                          "%s%s",
739                          execpath,
740                          dirname);
741       }
742       if ( (NULL != tmp) &&
743            (GNUNET_YES ==
744             GNUNET_DISK_directory_test (tmp, GNUNET_YES)) )
745       {
746         GNUNET_free (execpath);
747         GNUNET_free_non_null (dirname);
748         return tmp;
749       }
750       GNUNET_free (tmp);
751       GNUNET_free_non_null (dirname);
752     }
753     GNUNET_asprintf (&dirname,
754                      DIR_SEPARATOR_STR "%s" DIR_SEPARATOR_STR "libexec" DIR_SEPARATOR_STR,
755                      current_pd->project_dirname);
756     break;
757   default:
758     GNUNET_free (execpath);
759     return NULL;
760   }
761   GNUNET_asprintf (&tmp,
762                    "%s%s",
763                    execpath,
764                    dirname);
765   GNUNET_free (dirname);
766   GNUNET_free (execpath);
767   return tmp;
768 }
769
770
771 /**
772  * Given the name of a gnunet-helper, gnunet-service or gnunet-daemon
773  * binary, try to prefix it with the libexec/-directory to get the
774  * full path.
775  *
776  * @param progname name of the binary
777  * @return full path to the binary, if possible, otherwise copy of 'progname'
778  */
779 char *
780 GNUNET_OS_get_libexec_binary_path (const char *progname)
781 {
782   static char *cache;
783   char *libexecdir;
784   char *binary;
785
786   if ( (DIR_SEPARATOR == progname[0]) ||
787        (GNUNET_YES ==
788         GNUNET_STRINGS_path_is_absolute (progname,
789                                          GNUNET_NO,
790                                          NULL, NULL)) )
791     return GNUNET_strdup (progname);
792   if (NULL != cache)
793     libexecdir = cache;
794   else
795     libexecdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBEXECDIR);
796   if (NULL == libexecdir)
797     return GNUNET_strdup (progname);
798   GNUNET_asprintf (&binary,
799                    "%s%s",
800                    libexecdir,
801                    progname);
802   cache = libexecdir;
803   return binary;
804 }
805
806
807 /**
808  * Given the name of a helper, service or daemon binary construct the full
809  * path to the binary using the SUID_BINARY_PATH in the PATHS section of the
810  * configuration. If that option is not present, fall back to
811  * GNUNET_OS_get_libexec_binary_path. If @a progname is an absolute path, a
812  * copy of this path is returned.
813  *
814  * @param cfg configuration to inspect
815  * @param progname name of the binary
816  * @return full path to the binary, if possible, a copy of @a progname
817  *         otherwise
818  */
819 char *
820 GNUNET_OS_get_suid_binary_path (const struct GNUNET_CONFIGURATION_Handle *cfg,
821                                 const char *progname)
822 {
823   static char *cache;
824   char *binary = NULL;
825   char *path = NULL;
826   size_t path_len;
827
828   if (GNUNET_YES ==
829        GNUNET_STRINGS_path_is_absolute (progname,
830                                         GNUNET_NO,
831                                         NULL, NULL))
832   {
833     return GNUNET_strdup (progname);
834   }
835   if (NULL != cache)
836     path = cache;
837   else
838     GNUNET_CONFIGURATION_get_value_string (cfg,
839                                            "PATHS",
840                                            "SUID_BINARY_PATH",
841                                            &path);
842   if (NULL == path || 0 == strlen (path))
843     return GNUNET_OS_get_libexec_binary_path (progname);
844   path_len = strlen (path);
845   GNUNET_asprintf (&binary,
846                    "%s%s%s",
847                    path,
848                    (path[path_len - 1] == DIR_SEPARATOR) ? "" : DIR_SEPARATOR_STR,
849                    progname);
850   cache = path;
851   return binary;
852 }
853
854
855 /**
856  * Check whether an executable exists and possibly if the suid bit is
857  * set on the file.  Attempts to find the file using the current PATH
858  * environment variable as a search path.
859  *
860  * @param binary the name of the file to check.
861  *        W32: must not have an .exe suffix.
862  * @param check_suid input true if the binary should be checked for SUID (*nix)
863  *        W32: checks if the program has sufficient privileges by executing this
864  *             binary with the -d flag. -d omits a programs main loop and only
865  *             executes all privileged operations in an binary.
866  * @param params parameters used for w32 privilege checking (can be NULL for != w32 )
867  * @return #GNUNET_YES if the file is SUID (*nix) or can be executed with current privileges (W32),
868  *         #GNUNET_NO if not SUID (but binary exists),
869  *         #GNUNET_SYSERR on error (no such binary or not executable)
870  */
871 int
872 GNUNET_OS_check_helper_binary (const char *binary,
873                                int check_suid,
874                                const char *params)
875 {
876   struct stat statbuf;
877   char *p;
878   char *pf;
879 #ifdef MINGW
880   char *binaryexe;
881
882   GNUNET_asprintf (&binaryexe,
883                    "%s.exe",
884                    binary);
885   if ( (GNUNET_YES ==
886         GNUNET_STRINGS_path_is_absolute (binaryexe,
887                                          GNUNET_NO,
888                                          NULL, NULL)) ||
889        (0 == strncmp (binary, "./", 2)) )
890     p = GNUNET_strdup (binaryexe);
891   else
892   {
893     p = get_path_from_PATH (binaryexe);
894     if (NULL != p)
895     {
896       GNUNET_asprintf (&pf, "%s/%s", p, binaryexe);
897       GNUNET_free (p);
898       p = pf;
899     }
900   }
901   GNUNET_free (binaryexe);
902 #else
903   if ( (GNUNET_YES ==
904         GNUNET_STRINGS_path_is_absolute (binary,
905                                          GNUNET_NO,
906                                          NULL,
907                                          NULL)) ||
908        (0 == strncmp (binary, "./", 2)) )
909   {
910     p = GNUNET_strdup (binary);
911   }
912   else
913   {
914     p = get_path_from_PATH (binary);
915     if (NULL != p)
916     {
917       GNUNET_asprintf (&pf,
918                        "%s/%s",
919                        p,
920                        binary);
921       GNUNET_free (p);
922       p = pf;
923     }
924   }
925 #endif
926   if (NULL == p)
927   {
928     LOG (GNUNET_ERROR_TYPE_INFO,
929          _("Could not find binary `%s' in PATH!\n"),
930          binary);
931     return GNUNET_SYSERR;
932   }
933   if (0 != ACCESS (p,
934                    X_OK))
935   {
936     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
937                        "access",
938                        p);
939     GNUNET_free (p);
940     return GNUNET_SYSERR;
941   }
942 #ifndef MINGW
943   if (0 == getuid ())
944   {
945     /* as we run as root, we don't insist on SUID */
946     GNUNET_free (p);
947     return GNUNET_YES;
948   }
949 #endif
950   if (0 != STAT (p,
951                  &statbuf))
952   {
953     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING,
954                        "stat",
955                        p);
956     GNUNET_free (p);
957     return GNUNET_SYSERR;
958   }
959   if (check_suid)
960   {
961 #ifndef MINGW
962     (void) params;
963     if ( (0 != (statbuf.st_mode & S_ISUID)) &&
964          (0 == statbuf.st_uid) )
965     {
966       GNUNET_free (p);
967       return GNUNET_YES;
968     }
969     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
970                 _("Binary `%s' exists, but is not SUID\n"),
971                 p);
972     /* binary exists, but not SUID */
973 #else
974     STARTUPINFO start;
975     char parameters[512];
976     PROCESS_INFORMATION proc;
977     DWORD exit_value;
978
979     GNUNET_snprintf (parameters,
980                      sizeof (parameters),
981                      "-d %s", params);
982     memset (&start, 0, sizeof (start));
983     start.cb = sizeof (start);
984     memset (&proc, 0, sizeof (proc));
985
986
987     // Start the child process.
988     if ( ! (CreateProcess( p,   // current windows (2k3 and up can handle / instead of \ in paths))
989         parameters,           // execute dryrun/priviliege checking mode
990         NULL,           // Process handle not inheritable
991         NULL,           // Thread handle not inheritable
992         FALSE,          // Set handle inheritance to FALSE
993         CREATE_DEFAULT_ERROR_MODE, // No creation flags
994         NULL,           // Use parent's environment block
995         NULL,           // Use parent's starting directory
996         &start,            // Pointer to STARTUPINFO structure
997         &proc )           // Pointer to PROCESS_INFORMATION structure
998                                ))
999       {
1000         LOG (GNUNET_ERROR_TYPE_ERROR,
1001              _("CreateProcess failed for binary %s (%d).\n"),
1002              p, GetLastError());
1003         return GNUNET_SYSERR;
1004     }
1005
1006     // Wait until child process exits.
1007     WaitForSingleObject( proc.hProcess, INFINITE );
1008
1009     if ( ! GetExitCodeProcess (proc.hProcess, &exit_value)){
1010         LOG (GNUNET_ERROR_TYPE_ERROR,
1011              _("GetExitCodeProcess failed for binary %s (%d).\n"),
1012              p, GetLastError() );
1013         return GNUNET_SYSERR;
1014       }
1015     // Close process and thread handles.
1016     CloseHandle( proc.hProcess );
1017     CloseHandle( proc.hThread );
1018
1019     if (!exit_value)
1020       return GNUNET_YES;
1021 #endif
1022     }
1023   GNUNET_free (p);
1024   return GNUNET_NO;
1025 }
1026
1027
1028 /* end of os_installation.c */