installing all service, daemon and helper binaries to lib/gnunet/libexec/; updating...
[oweals/gnunet.git] / src / util / os_installation.c
1 /*
2      This file is part of GNUnet.
3      (C) 2006 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 2, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file src/util/os_installation.c
23  * @brief get paths used by the program
24  * @author Milan
25  */
26 #include <sys/stat.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include "platform.h"
32 #include "gnunet_common.h"
33 #include "gnunet_configuration_lib.h"
34 #include "gnunet_disk_lib.h"
35 #include "gnunet_os_lib.h"
36 #include "gnunet_strings_lib.h"
37 #if DARWIN
38 #include <mach-o/ldsyms.h>
39 #include <mach-o/dyld.h>
40 #endif
41
42 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
43
44 #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
45
46 #if LINUX
47 /**
48  * Try to determine path by reading /proc/PID/exe
49  *
50  * @return NULL on error
51  */
52 static char *
53 get_path_from_proc_maps ()
54 {
55   char fn[64];
56   char line[1024];
57   char dir[1024];
58   FILE *f;
59   char *lgu;
60
61   GNUNET_snprintf (fn, sizeof (fn), "/proc/%u/maps", getpid ());
62   if (NULL == (f = FOPEN (fn, "r")))
63     return NULL;
64   while (NULL != fgets (line, sizeof (line), f))
65   {
66     if ((1 ==
67          SSCANF (line, "%*x-%*x %*c%*c%*c%*c %*x %*2u:%*2u %*u%*[ ]%s", dir)) &&
68         (NULL != (lgu = strstr (dir, "libgnunetutil"))))
69     {
70       lgu[0] = '\0';
71       FCLOSE (f);
72       return GNUNET_strdup (dir);
73     }
74   }
75   FCLOSE (f);
76   return NULL;
77 }
78
79
80 /**
81  * Try to determine path by reading /proc/PID/exe
82  *
83  * @return NULL on error
84  */
85 static char *
86 get_path_from_proc_exe ()
87 {
88   char fn[64];
89   char lnk[1024];
90   ssize_t size;
91
92   GNUNET_snprintf (fn, sizeof (fn), "/proc/%u/exe", getpid ());
93   size = readlink (fn, lnk, sizeof (lnk) - 1);
94   if (size <= 0)
95   {
96     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "readlink", fn);
97     return NULL;
98   }
99   GNUNET_assert (size < sizeof (lnk));
100   lnk[size] = '\0';
101   while ((lnk[size] != '/') && (size > 0))
102     size--;
103   if ((size < 4) || (lnk[size - 4] != '/'))
104   {
105     /* not installed in "/bin/" -- binary path probably useless */
106     return NULL;
107   }
108   lnk[size] = '\0';
109   return GNUNET_strdup (lnk);
110 }
111 #endif
112
113 #if WINDOWS
114 /**
115  * Try to determine path with win32-specific function
116  *
117  * @return NULL on error
118  */
119 static char *
120 get_path_from_module_filename ()
121 {
122   wchar_t path[4097];
123   char upath[4097];
124   wchar_t *idx;
125
126   GetModuleFileNameW (NULL, path, sizeof (path) - 1);
127   idx = path + wcslen (path);
128   while ((idx > path) && (*idx != L'\\') && (*idx != L'/'))
129     idx--;
130   *idx = L'\0';
131   upath[0] = '\0';
132   WideCharToMultiByte (CP_UTF8, 0, path, -1, upath, 4097, NULL, NULL);
133
134   return GNUNET_strdup (upath);
135 }
136 #endif
137
138 #if DARWIN
139 /**
140  * Signature of the '_NSGetExecutablePath" function.
141  *
142  * @param buf where to write the path
143  * @param number of bytes available in 'buf'
144  * @return 0 on success, otherwise desired number of bytes is stored in 'bufsize'
145  */
146 typedef int (*MyNSGetExecutablePathProto) (char *buf, size_t * bufsize);
147
148
149 /**
150  * Try to obtain the path of our executable using '_NSGetExecutablePath'.
151  *
152  * @return NULL on error
153  */
154 static char *
155 get_path_from_NSGetExecutablePath ()
156 {
157   static char zero = '\0';
158   char *path;
159   size_t len;
160   MyNSGetExecutablePathProto func;
161
162   path = NULL;
163   if (NULL == (func =
164                (MyNSGetExecutablePathProto) dlsym (RTLD_DEFAULT, "_NSGetExecutablePath")))
165     return NULL;
166   path = &zero;
167   len = 0;
168   /* get the path len, including the trailing \0 */
169   (void) func (path, &len);
170   if (0 == len)
171     return NULL;
172   path = GNUNET_malloc (len);
173   if (0 != func (path, &len))
174   {
175     GNUNET_free (path);
176     return NULL;
177   }
178   len = strlen (path);
179   while ((path[len] != '/') && (len > 0))
180     len--;
181   path[len] = '\0';
182   return path;
183 }
184
185
186 /**
187  * Try to obtain the path of our executable using '_dyld_image' API.
188  *
189  * @return NULL on error
190  */
191 static char *
192 get_path_from_dyld_image ()
193 {
194   const char *path;
195   char *p;
196   char *s;
197   unsigned int i;
198   int c;
199
200   c = _dyld_image_count ();
201   for (i = 0; i < c; i++)
202   {
203     if (_dyld_get_image_header (i) != &_mh_dylib_header)
204       continue;
205     path = _dyld_get_image_name (i);
206     if ( (NULL == path) || (0 == strlen (path)) )
207       continue;
208     p = GNUNET_strdup (path);
209     s = p + strlen (p);
210     while ((s > p) && ('/' != *s))
211       s--;
212     s++;
213     *s = '\0';
214     return p;
215   }
216   return NULL;
217 }
218 #endif
219
220
221 /**
222  * Return the actual path to a file found in the current
223  * PATH environment variable.
224  *
225  * @param binary the name of the file to find
226  * @return path to binary, NULL if not found
227  */
228 static char *
229 get_path_from_PATH (const char *binary)
230 {
231   char *path;
232   char *pos;
233   char *end;
234   char *buf;
235   const char *p;
236
237   if (NULL == (p = getenv ("PATH")))
238     return NULL;
239 #if WINDOWS
240   /* On W32 look in CWD first. */
241   GNUNET_asprintf (&path, ".%c%s", PATH_SEPARATOR, p);
242 #else
243   path = GNUNET_strdup (p);     /* because we write on it */
244 #endif
245   buf = GNUNET_malloc (strlen (path) + strlen (binary) + 1 + 1);
246   pos = path;
247   while (NULL != (end = strchr (pos, PATH_SEPARATOR)))
248   {
249     *end = '\0';
250     sprintf (buf, "%s/%s", pos, binary);
251     if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
252     {
253       pos = GNUNET_strdup (pos);
254       GNUNET_free (buf);
255       GNUNET_free (path);
256       return pos;
257     }
258     pos = end + 1;
259   }
260   sprintf (buf, "%s/%s", pos, binary);
261   if (GNUNET_YES == GNUNET_DISK_file_test (buf))
262   {
263     pos = GNUNET_strdup (pos);
264     GNUNET_free (buf);
265     GNUNET_free (path);
266     return pos;
267   }
268   GNUNET_free (buf);
269   GNUNET_free (path);
270   return NULL;
271 }
272
273
274 /**
275  * Try to obtain the installation path using the "GNUNET_PREFIX" environment
276  * variable.
277  *
278  * @return NULL on error (environment variable not set)
279  */
280 static char *
281 get_path_from_GNUNET_PREFIX ()
282 {
283   const char *p;
284
285   if (NULL != (p = getenv ("GNUNET_PREFIX")))
286     return GNUNET_strdup (p);
287   return NULL;
288 }
289
290
291 /**
292  * @brief get the path to GNUnet bin/ or lib/, prefering the lib/ path
293  * @author Milan
294  *
295  * @return a pointer to the executable path, or NULL on error
296  */
297 static char *
298 os_get_gnunet_path ()
299 {
300   char *ret;
301
302   if (NULL != (ret = get_path_from_GNUNET_PREFIX ()))
303     return ret;
304 #if LINUX
305   if (NULL != (ret = get_path_from_proc_maps ()))
306     return ret;
307   if (NULL != (ret = get_path_from_proc_exe ()))
308     return ret;
309 #endif
310 #if WINDOWS
311   if (NULL != (ret = get_path_from_module_filename ()))
312     return ret;
313 #endif
314 #if DARWIN
315   if (NULL != (ret = get_path_from_dyld_image ()))
316     return ret;
317   if (NULL != (ret = get_path_from_NSGetExecutablePath ()))
318     return ret;
319 #endif
320   if (NULL != (ret = get_path_from_PATH ("gnunet-arm")))
321     return ret;
322   /* other attempts here */
323   LOG (GNUNET_ERROR_TYPE_ERROR,
324        _
325        ("Could not determine installation path for %s.  Set `%s' environment variable.\n"),
326        "GNUnet", "GNUNET_PREFIX");
327   return NULL;
328 }
329
330
331 /**
332  * @brief get the path to current app's bin/
333  * @author Milan
334  *
335  * @return a pointer to the executable path, or NULL on error
336  */
337 static char *
338 os_get_exec_path ()
339 {
340   char *ret;
341
342 #if LINUX
343   if (NULL != (ret = get_path_from_proc_exe ()))
344     return ret;
345 #endif
346 #if WINDOWS
347   if (NULL != (ret = get_path_from_module_filename ()))
348     return ret;
349 #endif
350 #if DARWIN
351   if (NULL != (ret = get_path_from_NSGetExecutablePath ()))
352     return ret;
353 #endif
354   /* other attempts here */
355   return NULL;
356 }
357
358
359 /**
360  * @brief get the path to a specific GNUnet installation directory or,
361  * with GNUNET_IPK_SELF_PREFIX, the current running apps installation directory
362  * @author Milan
363  * @return a pointer to the dir path (to be freed by the caller)
364  */
365 char *
366 GNUNET_OS_installation_get_path (enum GNUNET_OS_InstallationPathKind dirkind)
367 {
368   size_t n;
369   const char *dirname;
370   char *execpath = NULL;
371   char *tmp;
372   int isbasedir;
373
374   /* if wanted, try to get the current app's bin/ */
375   if (dirkind == GNUNET_OS_IPK_SELF_PREFIX)
376     execpath = os_get_exec_path ();
377
378   /* try to get GNUnet's bin/ or lib/, or if previous was unsuccessful some
379    * guess for the current app */
380   if (NULL == execpath)
381     execpath = os_get_gnunet_path ();
382
383   if (NULL == execpath)
384     return NULL;
385
386   n = strlen (execpath);
387   if (0 == n)
388   {
389     /* should never happen, but better safe than sorry */
390     GNUNET_free (execpath);
391     return NULL;
392   }
393   /* remove filename itself */
394   while ((n > 1) && (DIR_SEPARATOR == execpath[n - 1]))
395     execpath[--n] = '\0';
396
397   isbasedir = 1;
398   if ((n > 5) &&
399       ((0 == strcasecmp (&execpath[n - 5], "lib32")) ||
400        (0 == strcasecmp (&execpath[n - 5], "lib64"))))
401   {
402     if (GNUNET_OS_IPK_LIBDIR != dirkind)
403     {
404       /* strip '/lib32' or '/lib64' */
405       execpath[n - 5] = '\0';
406       n -= 5;
407     }
408     else
409       isbasedir = 0;
410   }
411   else if ((n > 3) &&
412            ((0 == strcasecmp (&execpath[n - 3], "bin")) ||
413             (0 == strcasecmp (&execpath[n - 3], "lib"))))
414   {
415     /* strip '/bin' or '/lib' */
416     execpath[n - 3] = '\0';
417     n -= 3;
418   }
419   /* in case this was a directory named foo-bin, remove "foo-" */
420   while ((n > 1) && (execpath[n - 1] == DIR_SEPARATOR))
421     execpath[--n] = '\0';
422   switch (dirkind)
423   {
424   case GNUNET_OS_IPK_PREFIX:
425   case GNUNET_OS_IPK_SELF_PREFIX:
426     dirname = DIR_SEPARATOR_STR;
427     break;
428   case GNUNET_OS_IPK_BINDIR:
429     dirname = DIR_SEPARATOR_STR "bin" DIR_SEPARATOR_STR;
430     break;
431   case GNUNET_OS_IPK_LIBDIR:
432     if (isbasedir)
433       dirname =
434           DIR_SEPARATOR_STR "lib" DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR;
435     else
436       dirname = DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR;
437     break;
438   case GNUNET_OS_IPK_DATADIR:
439     dirname =
440         DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR;
441     break;
442   case GNUNET_OS_IPK_LOCALEDIR:
443     dirname =
444         DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "locale" DIR_SEPARATOR_STR;
445     break;
446   case GNUNET_OS_IPK_ICONDIR:
447     dirname =
448         DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "icons" DIR_SEPARATOR_STR;
449     break;
450   case GNUNET_OS_IPK_DOCDIR:
451     dirname =
452         DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "doc" DIR_SEPARATOR_STR \
453         "gnunet" DIR_SEPARATOR_STR;
454     break;
455   case GNUNET_OS_IPK_LIBEXECDIR:
456     dirname =
457         DIR_SEPARATOR_STR "lib" DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR \
458         "libexec" DIR_SEPARATOR_STR;
459     break;
460   default:
461     GNUNET_free (execpath);
462     return NULL;
463   }
464   tmp = GNUNET_malloc (strlen (execpath) + strlen (dirname) + 1);
465   sprintf (tmp, "%s%s", execpath, dirname);
466   GNUNET_free (execpath);
467   return tmp;
468 }
469
470
471 /**
472  * Given the name of a gnunet-helper, gnunet-service or gnunet-daemon
473  * binary, try to prefix it with the libexec/-directory to get the
474  * full path.
475  *
476  * @param progname name of the binary
477  * @return full path to the binary, if possible, otherwise copy of 'progname'
478  */
479 char *
480 GNUNET_OS_get_libexec_binary_path (const char *progname)
481 {
482   char *libexecdir;
483   char *binary;
484
485   libexecdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBEXECDIR);
486   if (NULL == libexecdir)
487     return GNUNET_strdup (progname);
488   GNUNET_asprintf (&binary,
489                    "%s%s",
490                    libexecdir,
491                    progname);
492   GNUNET_free (libexecdir);
493   return binary;
494 }
495
496
497 /**
498  * Check whether an executable exists and possibly
499  * if the suid bit is set on the file.
500  * Attempts to find the file using the current
501  * PATH environment variable as a search path.
502  *
503  * @param binary the name of the file to check.
504  *        W32: must not have an .exe suffix.
505  * @return GNUNET_YES if the file is SUID,
506  *         GNUNET_NO if not SUID (but binary exists)
507  *         GNUNET_SYSERR on error (no such binary or not executable)
508  */
509 int
510 GNUNET_OS_check_helper_binary (const char *binary)
511 {
512   struct stat statbuf;
513   char *p;
514   char *pf;
515 #ifdef MINGW
516   SOCKET rawsock;
517   char *binaryexe;
518
519   GNUNET_asprintf (&binaryexe, "%s.exe", binary);
520   if ( (GNUNET_YES == GNUNET_STRINGS_path_is_absolute (binaryexe, GNUNET_NO,
521                                                        NULL, NULL)) ||
522        (0 == strncmp (binary, "./", 2)) )
523     p = GNUNET_strdup (binaryexe);
524   else
525   {
526     p = get_path_from_PATH (binaryexe);
527     if (NULL != p)
528     {
529       GNUNET_asprintf (&pf, "%s/%s", p, binaryexe);
530       GNUNET_free (p);
531       p = pf;
532     }
533   }
534   GNUNET_free (binaryexe);
535 #else
536   if ( (GNUNET_YES == GNUNET_STRINGS_path_is_absolute (binary, GNUNET_NO,
537                                                        NULL, NULL)) ||
538        (0 == strncmp (binary, "./", 2)) )
539     p = GNUNET_strdup (binary);
540   else
541   {
542     p = get_path_from_PATH (binary);
543     if (NULL != p)
544     {
545       GNUNET_asprintf (&pf, "%s/%s", p, binary);
546       GNUNET_free (p);
547       p = pf;
548     }
549   }
550 #endif
551   if (NULL == p)
552   {
553     LOG (GNUNET_ERROR_TYPE_INFO, _("Could not find binary `%s' in PATH!\n"),
554          binary);
555     return GNUNET_SYSERR;
556   }
557   if (0 != ACCESS (p, X_OK))
558   {
559     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "access", p);
560     GNUNET_free (p);
561     return GNUNET_SYSERR;
562   }
563 #ifndef MINGW
564   if (0 == getuid ())
565   {
566     /* as we run as root, we don't insist on SUID */
567     GNUNET_free (p);
568     return GNUNET_OK;
569   }
570 #endif
571   if (0 != STAT (p, &statbuf))
572   {
573     LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", p);
574     GNUNET_free (p);
575     return GNUNET_SYSERR;
576   }
577 #ifndef MINGW
578   if ((0 != (statbuf.st_mode & S_ISUID)) && (0 == statbuf.st_uid))
579   {
580     GNUNET_free (p);
581     return GNUNET_YES;
582   }
583   /* binary exists, but not SUID */
584   GNUNET_free (p);
585   return GNUNET_NO;
586 #else
587   GNUNET_free (p);
588   {
589     static int once; /* remember result from previous runs... */
590
591     if (0 == once)
592     {
593       rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
594       if (INVALID_SOCKET == rawsock)
595         {
596           DWORD err = GetLastError ();
597           
598           LOG (GNUNET_ERROR_TYPE_DEBUG,
599                "socket (AF_INET, SOCK_RAW, IPPROTO_ICMP) failed! GLE = %d\n", err);
600           once = -1;
601           return GNUNET_NO;           /* not running as administrator */
602         }
603       once = 1;
604       closesocket (rawsock);
605     }
606     if (-1 == once)
607       return GNUNET_NO;
608     return GNUNET_YES;
609   }
610 #endif
611 }
612
613
614 /* end of os_installation.c */