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