1 /* Relocating wrapper program.
2    Copyright (C) 2003, 2005-2007, 2009-2020 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 3 of the License, or
8    (at your option) 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, see <https://www.gnu.org/licenses/>.  */
17 
18 /* Dependencies:
19    relocwrapper
20     -> progname
21     -> progreloc
22        -> stat
23           -> filename
24           -> pathmax
25           -> verify
26        -> areadlink
27           -> careadlinkat
28              -> allocator
29           -> readlink
30              -> stat
31        -> canonicalize-lgpl
32           -> filename
33           -> malloca
34           -> lstat
35           -> readlink
36     -> relocatable
37     -> setenv
38        -> malloca
39     -> fprintf-posix [ignore, cut dependency tree here]
40     -> strerror [ignore, cut dependency tree here]
41     -> c-ctype
42 
43    Macros that need to be set while compiling this file:
44      - ENABLE_RELOCATABLE 1
45      - INSTALLPREFIX the base installation directory
46      - INSTALLDIR the directory into which this program is installed
47      - LIBPATHVAR the platform dependent runtime library path variable
48      - LIBDIRS a comma-terminated list of strings representing the list of
49        directories that contain the libraries at installation time
50 
51    We don't want to internationalize this wrapper because then it would
52    depend on libintl and therefore need relocation itself.  So use only
53    libc functions, no gettext(), no error(), no xmalloc(), no xsetenv().
54  */
55 
56 #define _GL_USE_STDLIB_ALLOC 1
57 #include <config.h>
58 
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
63 #include <errno.h>
64 
65 #include "progname.h"
66 #include "relocatable.h"
67 #include "c-ctype.h"
68 #include "verify.h"
69 
70 /* Use the system functions, not the gnulib overrides in this file.  */
71 #undef fprintf
72 #undef strerror
73 
74 /* Return a copy of the filename, with an extra ".bin" at the end.
75    More generally, it replaces "${EXEEXT}" at the end with ".bin${EXEEXT}".  */
76 static char *
add_dotbin(const char * filename)77 add_dotbin (const char *filename)
78 {
79   size_t filename_len = strlen (filename);
80   char *result = (char *) malloc (filename_len + 4 + 1);
81 
82   if (result != NULL)
83     {
84       if (sizeof (EXEEXT) > sizeof (""))
85         {
86           /* EXEEXT handling.  */
87           const size_t exeext_len = sizeof (EXEEXT) - sizeof ("");
88           static const char exeext[] = EXEEXT;
89           if (filename_len > exeext_len)
90             {
91               /* Compare using an inlined copy of c_strncasecmp(), because
92                  the filenames may have undergone a case conversion since
93                  they were packaged.  In other words, EXEEXT may be ".exe"
94                  on one system and ".EXE" on another.  */
95               const char *s1 = filename + filename_len - exeext_len;
96               const char *s2 = exeext;
97               for (; *s1 != '\0'; s1++, s2++)
98                 {
99                   unsigned char c1 = *s1;
100                   unsigned char c2 = *s2;
101                   if (c_tolower (c1) != c_tolower (c2))
102                     goto simple_append;
103                 }
104               /* Insert ".bin" before EXEEXT or its equivalent.  */
105               memcpy (result, filename, filename_len - exeext_len);
106               memcpy (result + filename_len - exeext_len, ".bin", 4);
107               memcpy (result + filename_len - exeext_len + 4,
108                       filename + filename_len - exeext_len,
109                       exeext_len + 1);
110               return result;
111             }
112         }
113      simple_append:
114       /* Simply append ".bin".  */
115       memcpy (result, filename, filename_len);
116       memcpy (result + filename_len, ".bin", 4 + 1);
117       return result;
118     }
119   else
120     {
121       fprintf (stderr, "%s: %s\n", program_name, "memory exhausted");
122       exit (1);
123     }
124 }
125 
126 /* List of directories that contain the libraries.  */
127 static const char *libdirs[] = { LIBDIRS NULL };
128 /* Verify that at least one directory is given.  */
129 verify (sizeof (libdirs) / sizeof (libdirs[0]) > 1);
130 
131 /* Relocate the list of directories that contain the libraries.  */
132 static void
relocate_libdirs()133 relocate_libdirs ()
134 {
135   size_t i;
136 
137   for (i = 0; i < sizeof (libdirs) / sizeof (libdirs[0]) - 1; i++)
138     libdirs[i] = relocate (libdirs[i]);
139 }
140 
141 /* Activate the list of directories in the LIBPATHVAR.  */
142 static void
activate_libdirs()143 activate_libdirs ()
144 {
145   const char *old_value;
146   size_t total;
147   size_t i;
148   char *value;
149   char *p;
150 
151   old_value = getenv (LIBPATHVAR);
152   if (old_value == NULL)
153     old_value = "";
154 
155   total = 0;
156   for (i = 0; i < sizeof (libdirs) / sizeof (libdirs[0]) - 1; i++)
157     total += strlen (libdirs[i]) + 1;
158   total += strlen (old_value) + 1;
159 
160   value = (char *) malloc (total);
161   if (value == NULL)
162     {
163       fprintf (stderr, "%s: %s\n", program_name, "memory exhausted");
164       exit (1);
165     }
166   p = value;
167   for (i = 0; i < sizeof (libdirs) / sizeof (libdirs[0]) - 1; i++)
168     {
169       size_t len = strlen (libdirs[i]);
170       memcpy (p, libdirs[i], len);
171       p += len;
172       *p++ = ':';
173     }
174   if (old_value[0] != '\0')
175     strcpy (p, old_value);
176   else
177     p[-1] = '\0';
178 
179   if (setenv (LIBPATHVAR, value, 1) < 0)
180     {
181       fprintf (stderr, "%s: %s\n", program_name, "memory exhausted");
182       exit (1);
183     }
184 }
185 
186 int
main(int argc,char * argv[])187 main (int argc, char *argv[])
188 {
189   char *full_program_name;
190 
191   /* Set the program name and perform preparations for
192      get_full_program_name() and relocate().  */
193   set_program_name_and_installdir (argv[0], INSTALLPREFIX, INSTALLDIR);
194 
195   /* Get the full program path.  (Important if accessed through a symlink.)  */
196   full_program_name = get_full_program_name ();
197   if (full_program_name == NULL)
198     full_program_name = argv[0];
199 
200   /* Invoke the real program, with suffix ".bin".  */
201   argv[0] = add_dotbin (full_program_name);
202   relocate_libdirs ();
203   activate_libdirs ();
204   execv (argv[0], argv);
205   fprintf (stderr, "%s: could not execute %s: %s\n",
206            program_name, argv[0], strerror (errno));
207   exit (127);
208 }
209