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