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