fix
[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
27 #ifdef __cplusplus
28 extern "C"
29 {
30 #if 0                           /* keep Emacsens' auto-indent happy */
31 }
32 #endif
33 #endif
34
35 #include <sys/stat.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39
40 #include "platform.h"
41 #include "gnunet_common.h"
42 #include "gnunet_configuration_lib.h"
43 #include "gnunet_disk_lib.h"
44 #include "gnunet_os_lib.h"
45 #if DARWIN
46 #include <mach-o/ldsyms.h>
47 #include <mach-o/dyld.h>
48 #endif
49
50 #if LINUX
51 /**
52  * Try to determine path by reading /proc/PID/exe
53  */
54 static char *
55 get_path_from_proc_maps ()
56 {
57   char fn[64];
58   char line[1024];
59   char dir[1024];
60   FILE *f;
61   char *lgu;
62
63   GNUNET_snprintf (fn,
64                    sizeof(fn), 
65                    "/proc/%u/maps", 
66                    getpid ());
67   f = fopen (fn, "r");
68   if (f == NULL)
69     return NULL;
70   while (NULL != fgets (line, sizeof(line), f))
71     {
72       if ((1 == sscanf (line,
73                         "%*x-%*x %*c%*c%*c%*c %*x %*2u:%*2u %*u%*[ ]%s",
74                         dir)) &&
75           (NULL != (lgu = strstr (dir, "libgnunetutil"))))
76         {
77           lgu[0] = '\0';
78           fclose (f);
79           return GNUNET_strdup (dir);
80         }
81     }
82   fclose (f);
83   return NULL;
84 }
85
86 /**
87  * Try to determine path by reading /proc/PID/exe
88  */
89 static char *
90 get_path_from_proc_exe ()
91 {
92   char fn[64];
93   char lnk[1024];
94   ssize_t size;
95
96   GNUNET_snprintf (fn, 
97                    sizeof(fn), "/proc/%u/exe", getpid ());
98   size = readlink (fn, lnk, sizeof (lnk)-1);
99   if ((size == 0) || (size >= sizeof(lnk)-1))
100     {
101       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "readlink", fn);
102       return NULL;
103     }
104   lnk[size] = '\0';
105   while ((lnk[size] != '/') && (size > 0))
106     size--;
107   if ((size < 4) || (lnk[size - 4] != '/'))
108     {
109       /* not installed in "/bin/" -- binary path probably useless */
110       return NULL;
111     }
112   lnk[size] = '\0';
113   return GNUNET_strdup (lnk);
114 }
115 #endif
116
117 #if WINDOWS
118 /**
119  * Try to determine path with win32-specific function
120  */
121 static char *
122 get_path_from_module_filename ()
123 {
124   char path[4097];
125   char *idx;
126
127   GetModuleFileName (NULL, path, sizeof(path)-1);
128   idx = path + strlen (path);
129   while ((idx > path) && (*idx != '\\') && (*idx != '/'))
130     idx--;
131   *idx = '\0';
132   return GNUNET_strdup (path);
133 }
134 #endif
135
136 #if DARWIN
137 typedef int (*MyNSGetExecutablePathProto) (char *buf, size_t * bufsize);
138
139 static char *
140 get_path_from_NSGetExecutablePath ()
141 {
142   static char zero = '\0';
143   char *path;
144   size_t len;
145   MyNSGetExecutablePathProto func;
146   int ret;
147
148   path = NULL;
149   func =
150     (MyNSGetExecutablePathProto) dlsym (RTLD_DEFAULT, "_NSGetExecutablePath");
151   if (!func)
152     return NULL;
153   path = &zero;
154   len = 0;
155   /* get the path len, including the trailing \0 */
156   func (path, &len);
157   if (len == 0)
158     return NULL;
159   path = GNUNET_malloc (len);
160   ret = func (path, &len);
161   if (ret != 0)
162     {
163       GNUNET_free (path);
164       return NULL;
165     }
166   len = strlen (path);
167   while ((path[len] != '/') && (len > 0))
168     len--;
169   path[len] = '\0';
170   return path;
171 }
172
173 static char *
174 get_path_from_dyld_image ()
175 {
176   const char *path;
177   char *p, *s;
178   int i;
179   int c;
180
181   p = NULL;
182   c = _dyld_image_count ();
183   for (i = 0; i < c; i++)
184     {
185       if (_dyld_get_image_header (i) == &_mh_dylib_header)
186         {
187           path = _dyld_get_image_name (i);
188           if (path != NULL && strlen (path) > 0)
189             {
190               p = strdup (path);
191               s = p + strlen (p);
192               while ((s > p) && (*s != '/'))
193                 s--;
194               s++;
195               *s = '\0';
196             }
197           break;
198         }
199     }
200   return p;
201 }
202 #endif
203
204 static char *
205 get_path_from_PATH ()
206 {
207   char *path;
208   char *pos;
209   char *end;
210   char *buf;
211   const char *p;
212
213   p = getenv ("PATH");
214   if (p == NULL)
215     return NULL;
216   path = GNUNET_strdup (p);     /* because we write on it */
217   buf = GNUNET_malloc (strlen (path) + 20);
218   pos = path;
219
220   while (NULL != (end = strchr (pos, ':')))
221     {
222       *end = '\0';
223       sprintf (buf, "%s/%s", pos, "gnunetd");
224       if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
225         {
226           pos = GNUNET_strdup (pos);
227           GNUNET_free (buf);
228           GNUNET_free (path);
229           return pos;
230         }
231       pos = end + 1;
232     }
233   sprintf (buf, "%s/%s", pos, "gnunetd");
234   if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
235     {
236       pos = GNUNET_strdup (pos);
237       GNUNET_free (buf);
238       GNUNET_free (path);
239       return pos;
240     }
241   GNUNET_free (buf);
242   GNUNET_free (path);
243   return NULL;
244 }
245
246 static char *
247 get_path_from_GNUNET_PREFIX ()
248 {
249   const char *p;
250
251   p = getenv ("GNUNET_PREFIX");
252   if (p != NULL)
253     return GNUNET_strdup (p);
254   return NULL;
255 }
256
257 /*
258  * @brief get the path to GNUnet bin/ or lib/, prefering the lib/ path
259  * @author Milan
260  *
261  * @return a pointer to the executable path, or NULL on error
262  */
263 static char *
264 os_get_gnunet_path ()
265 {
266   char *ret;
267
268   ret = get_path_from_GNUNET_PREFIX ();
269   if (ret != NULL)
270     return ret;
271 #if LINUX
272   ret = get_path_from_proc_maps ();
273   if (ret != NULL)
274     return ret;
275   ret = get_path_from_proc_exe ();
276   if (ret != NULL)
277     return ret;
278 #endif
279 #if WINDOWS
280   ret = get_path_from_module_filename ();
281   if (ret != NULL)
282     return ret;
283 #endif
284 #if DARWIN
285   ret = get_path_from_dyld_image ();
286   if (ret != NULL)
287     return ret;
288   ret = get_path_from_NSGetExecutablePath ();
289   if (ret != NULL)
290     return ret;
291 #endif
292   ret = get_path_from_PATH ();
293   if (ret != NULL)
294     return ret;
295   /* other attempts here */
296   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
297               _
298               ("Could not determine installation path for GNUnet.  Set `%s' environment variable.\n"),
299               "GNUNET_PREFIX");
300   return NULL;
301 }
302
303 /*
304  * @brief get the path to current app's bin/
305  * @author Milan
306  *
307  * @return a pointer to the executable path, or NULL on error
308  */
309 static char *
310 os_get_exec_path ()
311 {
312   char *ret;
313
314   ret = NULL;
315 #if LINUX
316   ret = get_path_from_proc_exe ();
317   if (ret != NULL)
318     return ret;
319 #endif
320 #if WINDOWS
321   ret = get_path_from_module_filename ();
322   if (ret != NULL)
323     return ret;
324 #endif
325 #if DARWIN
326   ret = get_path_from_NSGetExecutablePath ();
327   if (ret != NULL)
328     return ret;
329 #endif
330   /* other attempts here */
331   return ret;
332 }
333
334
335
336 /**
337  * @brief get the path to a specific GNUnet installation directory or,
338  * with GNUNET_IPK_SELF_PREFIX, the current running apps installation directory
339  * @author Milan
340  * @return a pointer to the dir path (to be freed by the caller)
341  */
342 char *
343 GNUNET_OS_installation_get_path (enum GNUNET_OS_InstallationPathKind dirkind)
344 {
345   size_t n;
346   const char *dirname;
347   char *execpath = NULL;
348   char *tmp;
349   int isbasedir;
350
351   /* if wanted, try to get the current app's bin/ */
352   if (dirkind == GNUNET_OS_IPK_SELF_PREFIX)
353     execpath = os_get_exec_path ();
354
355   /* try to get GNUnet's bin/ or lib/, or if previous was unsuccessful some
356    * guess for the current app */
357   if (execpath == NULL)
358     execpath = os_get_gnunet_path ();
359
360   if (execpath == NULL)
361     return NULL;
362
363   n = strlen (execpath);
364   if (n == 0)
365     {
366       /* should never happen, but better safe than sorry */
367       GNUNET_free (execpath);
368       return NULL;
369     }
370   /* remove filename itself */
371   while ((n > 1) && (execpath[n - 1] == DIR_SEPARATOR))
372     execpath[--n] = '\0';
373
374   isbasedir = 1;
375   if ((n > 5) &&
376       ((0 == strcasecmp (&execpath[n - 5], "lib32")) ||
377        (0 == strcasecmp (&execpath[n - 5], "lib64"))))
378     {
379       if (dirkind != GNUNET_OS_IPK_LIBDIR)
380         {
381           /* strip '/lib32' or '/lib64' */
382           execpath[n - 5] = '\0';
383           n -= 5;
384         }
385       else
386         isbasedir = 0;
387     }
388   else if ((n > 3) &&
389            ((0 == strcasecmp (&execpath[n - 3], "bin")) ||
390             (0 == strcasecmp (&execpath[n - 3], "lib"))))
391     {
392       /* strip '/bin' or '/lib' */
393       execpath[n - 3] = '\0';
394       n -= 3;
395     }
396   /* in case this was a directory named foo-bin, remove "foo-" */
397   while ((n > 1) && (execpath[n - 1] == DIR_SEPARATOR))
398     execpath[--n] = '\0';
399   switch (dirkind)
400     {
401     case GNUNET_OS_IPK_PREFIX:
402     case GNUNET_OS_IPK_SELF_PREFIX:
403       dirname = DIR_SEPARATOR_STR;
404       break;
405     case GNUNET_OS_IPK_BINDIR:
406       dirname = DIR_SEPARATOR_STR "bin" DIR_SEPARATOR_STR;
407       break;
408     case GNUNET_OS_IPK_LIBDIR:
409       if (isbasedir)
410         dirname =
411           DIR_SEPARATOR_STR "lib" DIR_SEPARATOR_STR "gnunet"
412           DIR_SEPARATOR_STR;
413       else
414         dirname = DIR_SEPARATOR_STR "gnunet" DIR_SEPARATOR_STR;
415       break;
416     case GNUNET_OS_IPK_DATADIR:
417       dirname =
418         DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "gnunet"
419         DIR_SEPARATOR_STR;
420       break;
421     case GNUNET_OS_IPK_LOCALEDIR:
422       dirname =
423         DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "locale"
424         DIR_SEPARATOR_STR;
425       break;
426     default:
427       GNUNET_free (execpath);
428       return NULL;
429     }
430   tmp = GNUNET_malloc (strlen (execpath) + strlen (dirname) + 1);
431   sprintf (tmp, "%s%s", execpath, dirname);
432   GNUNET_free (execpath);
433   return tmp;
434 }
435
436 #if 0                           /* keep Emacsens' auto-indent happy */
437 {
438 #endif
439 #ifdef __cplusplus
440 }
441 #endif
442 /* end of os_installation.c */