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