Add getopt_.h, getopt_int.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 #else
54 # include "xmalloc.h"
55 #endif
56
57 /* Pathname support.
58    ISSLASH(C)           tests whether C is a directory separator character.
59    IS_PATH_WITH_DIR(P)  tests whether P contains a directory specification.
60  */
61 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
62   /* Win32, Cygwin, OS/2, DOS */
63 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
64 # define HAS_DEVICE(P) \
65     ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
66      && (P)[1] == ':')
67 # define IS_PATH_WITH_DIR(P) \
68     (strchr (P, '/') != NULL || strchr (P, '\\') != NULL || HAS_DEVICE (P))
69 # define FILE_SYSTEM_PREFIX_LEN(P) (HAS_DEVICE (P) ? 2 : 0)
70 #else
71   /* Unix */
72 # define ISSLASH(C) ((C) == '/')
73 # define IS_PATH_WITH_DIR(P) (strchr (P, '/') != NULL)
74 # define FILE_SYSTEM_PREFIX_LEN(P) 0
75 #endif
76
77 #undef set_program_name
78
79
80 #if ENABLE_RELOCATABLE
81
82 #ifdef __linux__
83 /* File descriptor of the executable.
84    (Only used to verify that we find the correct executable.)  */
85 static int executable_fd = -1;
86 #endif
87
88 /* Tests whether a given pathname may belong to the executable.  */
89 static bool
90 maybe_executable (const char *filename)
91 {
92 #if !defined WIN32
93   if (access (filename, X_OK) < 0)
94     return false;
95
96 #ifdef __linux__
97   if (executable_fd >= 0)
98     {
99       /* If we already have an executable_fd, check that filename points to
100          the same inode.  */
101       struct stat statexe;
102       struct stat statfile;
103
104       if (fstat (executable_fd, &statexe) >= 0)
105         {
106           if (stat (filename, &statfile) < 0)
107             return false;
108           if (!(statfile.st_dev
109                 && statfile.st_dev == statexe.st_dev
110                 && statfile.st_ino == statexe.st_ino))
111             return false;
112         }
113     }
114 #endif
115 #endif
116
117   return true;
118 }
119
120 /* Determine the full pathname of the current executable, freshly allocated.
121    Return NULL if unknown.
122    Guaranteed to work on Linux and Woe32.  Likely to work on the other
123    Unixes (maybe except BeOS), under most conditions.  */
124 static char *
125 find_executable (const char *argv0)
126 {
127 #ifdef WIN32
128   char buf[1024];
129   int length = GetModuleFileName (NULL, buf, sizeof (buf));
130   if (length < 0)
131     return NULL;
132   if (!IS_PATH_WITH_DIR (buf))
133     /* Shouldn't happen.  */
134     return NULL;
135   return xstrdup (buf);
136 #else /* Unix */
137 #ifdef __linux__
138   /* The executable is accessible as /proc/<pid>/exe.  In newer Linux
139      versions, also as /proc/self/exe.  Linux >= 2.1 provides a symlink
140      to the true pathname; older Linux versions give only device and ino,
141      enclosed in brackets, which we cannot use here.  */
142   {
143     char *link;
144
145     link = xreadlink ("/proc/self/exe");
146     if (link != NULL && link[0] != '[')
147       return link;
148     if (executable_fd < 0)
149       executable_fd = open ("/proc/self/exe", O_RDONLY, 0);
150
151     {
152       char buf[6+10+5];
153       sprintf (buf, "/proc/%d/exe", getpid ());
154       link = xreadlink (buf);
155       if (link != NULL && link[0] != '[')
156         return link;
157       if (executable_fd < 0)
158         executable_fd = open (buf, O_RDONLY, 0);
159     }
160   }
161 #endif
162   /* Guess the executable's full path.  We assume the executable has been
163      called via execlp() or execvp() with properly set up argv[0].  The
164      login(1) convention to add a '-' prefix to argv[0] is not supported.  */
165   {
166     bool has_slash = false;
167     {
168       const char *p;
169       for (p = argv0; *p; p++)
170         if (*p == '/')
171           {
172             has_slash = true;
173             break;
174           }
175     }
176     if (!has_slash)
177       {
178         /* exec searches paths without slashes in the directory list given
179            by $PATH.  */
180         const char *path = getenv ("PATH");
181
182         if (path != NULL)
183           {
184             const char *p;
185             const char *p_next;
186
187             for (p = path; *p; p = p_next)
188               {
189                 const char *q;
190                 size_t p_len;
191                 char *concat_name;
192
193                 for (q = p; *q; q++)
194                   if (*q == ':')
195                     break;
196                 p_len = q - p;
197                 p_next = (*q == '\0' ? q : q + 1);
198
199                 /* We have a path item at p, of length p_len.
200                    Now concatenate the path item and argv0.  */
201                 concat_name = (char *) xmalloc (p_len + strlen (argv0) + 2);
202 #ifdef NO_XMALLOC
203                 if (concat_name == NULL)
204                   return NULL;
205 #endif
206                 if (p_len == 0)
207                   /* An empty PATH element designates the current directory.  */
208                   strcpy (concat_name, argv0);
209                 else
210                   {
211                     memcpy (concat_name, p, p_len);
212                     concat_name[p_len] = '/';
213                     strcpy (concat_name + p_len + 1, argv0);
214                   }
215                 if (maybe_executable (concat_name))
216                   return canonicalize_file_name (concat_name);
217                 free (concat_name);
218               }
219           }
220         /* Not found in the PATH, assume the current directory.  */
221       }
222     /* exec treats paths containing slashes as relative to the current
223        directory.  */
224     if (maybe_executable (argv0))
225       return canonicalize_file_name (argv0);
226   }
227   /* No way to find the executable.  */
228   return NULL;
229 #endif
230 }
231
232 /* Full pathname of executable, or NULL.  */
233 static char *executable_fullname;
234
235 static void
236 prepare_relocate (const char *orig_installprefix, const char *orig_installdir,
237                   const char *argv0)
238 {
239   const char *curr_prefix;
240
241   /* Determine the full pathname of the current executable.  */
242   executable_fullname = find_executable (argv0);
243
244   /* Determine the current installation prefix from it.  */
245   curr_prefix = compute_curr_prefix (orig_installprefix, orig_installdir,
246                                      executable_fullname);
247   if (curr_prefix != NULL)
248     /* Now pass this prefix to all copies of the relocate.c source file.  */
249     set_relocation_prefix (orig_installprefix, curr_prefix);
250 }
251
252 /* Set program_name, based on argv[0], and original installation prefix and
253    directory, for relocatability.  */
254 void
255 set_program_name_and_installdir (const char *argv0,
256                                  const char *orig_installprefix,
257                                  const char *orig_installdir)
258 {
259   const char *argv0_stripped = argv0;
260
261   /* Relocatable programs are renamed to .bin by install-reloc.  Remove
262      this suffix here.  */
263   {
264     size_t argv0_len = strlen (argv0);
265     if (argv0_len > 4 && memcmp (argv0 + argv0_len - 4, ".bin", 4) == 0)
266       {
267         char *shorter = (char *) xmalloc (argv0_len - 4 + 1);
268 #ifdef NO_XMALLOC
269         if (shorter != NULL)
270 #endif
271           {
272             memcpy (shorter, argv0, argv0_len - 4);
273             shorter[argv0_len - 4] = '\0';
274             argv0_stripped = shorter;
275           }
276       }
277   }
278
279   set_program_name (argv0_stripped);
280
281   prepare_relocate (orig_installprefix, orig_installdir, argv0);
282 }
283
284 /* Return the full pathname of the current executable, based on the earlier
285    call to set_program_name_and_installdir.  Return NULL if unknown.  */
286 char *
287 get_full_program_name (void)
288 {
289   return executable_fullname;
290 }
291
292 #endif