Use a consistent style for including <config.h>.
[gnulib.git] / lib / progreloc.c
1 /* Provide relocatable programs.
2    Copyright (C) 2003-2004 Free Software Foundation, Inc.
3    Written by Bruno Haible <bruno@clisp.org>, 2003.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software Foundation,
17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 /* Specification.  */
25 #include "progname.h"
26
27 #include <stdbool.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <fcntl.h>
32 #if HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
35 #include <sys/stat.h>
36
37 #if defined _WIN32 || defined __WIN32__
38 # undef WIN32   /* avoid warning on mingw32 */
39 # define WIN32
40 #endif
41
42 #ifdef WIN32
43 # define WIN32_LEAN_AND_MEAN
44 # include <windows.h>
45 #endif
46
47 #include "xreadlink.h"
48 #include "canonicalize.h"
49 #include "relocatable.h"
50
51 #ifdef NO_XMALLOC
52 # define xmalloc malloc
53 # define xstrdup strdup
54 #else
55 # include "xalloc.h"
56 #endif
57
58 /* Pathname support.
59    ISSLASH(C)           tests whether C is a directory separator character.
60    IS_PATH_WITH_DIR(P)  tests whether P contains a directory specification.
61  */
62 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
63   /* Win32, Cygwin, OS/2, DOS */
64 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
65 # define HAS_DEVICE(P) \
66     ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
67      && (P)[1] == ':')
68 # define IS_PATH_WITH_DIR(P) \
69     (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
70 # define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
71 #else
72   /* Unix */
73 # define ISSLASH(C) ((C) == '/')
74 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
75 # define FILE_SYSTEM_PREFIX_LEN(P) 0
76 #endif
77
78 #undef set_program_name
79
80
81 #if ENABLE_RELOCATABLE
82
83 #ifdef __linux__
84 /* File descriptor of the executable.
85    (Only used to verify that we find the correct executable.)  */
86 static int executable_fd = -1;
87 #endif
88
89 /* Tests whether a given pathname may belong to the executable.  */
90 static bool
91 maybe_executable (const char *filename)
92 {
93 #if !defined WIN32
94   if (access (filename, X_OK) < 0)
95     return false;
96
97 #ifdef __linux__
98   if (executable_fd >= 0)
99     {
100       /* If we already have an executable_fd, check that filename points to
101          the same inode.  */
102       struct stat statexe;
103       struct stat statfile;
104
105       if (fstat (executable_fd, &statexe) >= 0)
106         {
107           if (stat (filename, &statfile) < 0)
108             return false;
109           if (!(statfile.st_dev
110                 && statfile.st_dev == statexe.st_dev
111                 && statfile.st_ino == statexe.st_ino))
112             return false;
113         }
114     }
115 #endif
116 #endif
117
118   return true;
119 }
120
121 /* Determine the full pathname of the current executable, freshly allocated.
122    Return NULL if unknown.
123    Guaranteed to work on Linux and Woe32.  Likely to work on the other
124    Unixes (maybe except BeOS), under most conditions.  */
125 static char *
126 find_executable (const char *argv0)
127 {
128 #ifdef WIN32
129   char buf[1024];
130   int length = GetModuleFileName (NULL, buf, sizeof (buf));
131   if (length < 0)
132     return NULL;
133   if (!IS_PATH_WITH_DIR (buf))
134     /* Shouldn't happen.  */
135     return NULL;
136   return xstrdup (buf);
137 #else /* Unix */
138 #ifdef __linux__
139   /* The executable is accessible as /proc/<pid>/exe.  In newer Linux
140      versions, also as /proc/self/exe.  Linux >= 2.1 provides a symlink
141      to the true pathname; older Linux versions give only device and ino,
142      enclosed in brackets, which we cannot use here.  */
143   {
144     char *link;
145
146     link = xreadlink ("/proc/self/exe");
147     if (link != NULL && link[0] != '[')
148       return link;
149     if (executable_fd < 0)
150       executable_fd = open ("/proc/self/exe", O_RDONLY, 0);
151
152     {
153       char buf[6+10+5];
154       sprintf (buf, "/proc/%d/exe", getpid ());
155       link = xreadlink (buf);
156       if (link != NULL && link[0] != '[')
157         return link;
158       if (executable_fd < 0)
159         executable_fd = open (buf, O_RDONLY, 0);
160     }
161   }
162 #endif
163   /* Guess the executable's full path.  We assume the executable has been
164      called via execlp() or execvp() with properly set up argv[0].  The
165      login(1) convention to add a '-' prefix to argv[0] is not supported.  */
166   {
167     bool has_slash = false;
168     {
169       const char *p;
170       for (p = argv0; *p; p++)
171         if (*p == '/')
172           {
173             has_slash = true;
174             break;
175           }
176     }
177     if (!has_slash)
178       {
179         /* exec searches paths without slashes in the directory list given
180            by $PATH.  */
181         const char *path = getenv ("PATH");
182
183         if (path != NULL)
184           {
185             const char *p;
186             const char *p_next;
187
188             for (p = path; *p; p = p_next)
189               {
190                 const char *q;
191                 size_t p_len;
192                 char *concat_name;
193
194                 for (q = p; *q; q++)
195                   if (*q == ':')
196                     break;
197                 p_len = q - p;
198                 p_next = (*q == '\0' ? q : q + 1);
199
200                 /* We have a path item at p, of length p_len.
201                    Now concatenate the path item and argv0.  */
202                 concat_name = (char *) xmalloc (p_len + strlen (argv0) + 2);
203 #ifdef NO_XMALLOC
204                 if (concat_name == NULL)
205                   return NULL;
206 #endif
207                 if (p_len == 0)
208                   /* An empty PATH element designates the current directory.  */
209                   strcpy (concat_name, argv0);
210                 else
211                   {
212                     memcpy (concat_name, p, p_len);
213                     concat_name[p_len] = '/';
214                     strcpy (concat_name + p_len + 1, argv0);
215                   }
216                 if (maybe_executable (concat_name))
217                   return canonicalize_file_name (concat_name);
218                 free (concat_name);
219               }
220           }
221         /* Not found in the PATH, assume the current directory.  */
222       }
223     /* exec treats paths containing slashes as relative to the current
224        directory.  */
225     if (maybe_executable (argv0))
226       return canonicalize_file_name (argv0);
227   }
228   /* No way to find the executable.  */
229   return NULL;
230 #endif
231 }
232
233 /* Full pathname of executable, or NULL.  */
234 static char *executable_fullname;
235
236 static void
237 prepare_relocate (const char *orig_installprefix, const char *orig_installdir,
238                   const char *argv0)
239 {
240   const char *curr_prefix;
241
242   /* Determine the full pathname of the current executable.  */
243   executable_fullname = find_executable (argv0);
244
245   /* Determine the current installation prefix from it.  */
246   curr_prefix = compute_curr_prefix (orig_installprefix, orig_installdir,
247                                      executable_fullname);
248   if (curr_prefix != NULL)
249     /* Now pass this prefix to all copies of the relocate.c source file.  */
250     set_relocation_prefix (orig_installprefix, curr_prefix);
251 }
252
253 /* Set program_name, based on argv[0], and original installation prefix and
254    directory, for relocatability.  */
255 void
256 set_program_name_and_installdir (const char *argv0,
257                                  const char *orig_installprefix,
258                                  const char *orig_installdir)
259 {
260   const char *argv0_stripped = argv0;
261
262   /* Relocatable programs are renamed to .bin by install-reloc.  Remove
263      this suffix here.  */
264   {
265     size_t argv0_len = strlen (argv0);
266     if (argv0_len > 4 && memcmp (argv0 + argv0_len - 4, ".bin", 4) == 0)
267       {
268         char *shorter = (char *) xmalloc (argv0_len - 4 + 1);
269 #ifdef NO_XMALLOC
270         if (shorter != NULL)
271 #endif
272           {
273             memcpy (shorter, argv0, argv0_len - 4);
274             shorter[argv0_len - 4] = '\0';
275             argv0_stripped = shorter;
276           }
277       }
278   }
279
280   set_program_name (argv0_stripped);
281
282   prepare_relocate (orig_installprefix, orig_installdir, argv0);
283 }
284
285 /* Return the full pathname of the current executable, based on the earlier
286    call to set_program_name_and_installdir.  Return NULL if unknown.  */
287 char *
288 get_full_program_name (void)
289 {
290   return executable_fullname;
291 }
292
293 #endif