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