summaryrefslogtreecommitdiff
path: root/scripts/wrapper.c
blob: 3d815b439300120bac3de7093ccfe11f2b106255 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>


/* Needed for execve */
extern char **environ;

int main( int argc,
          char** argv )
{
  char *fullname;   /* 'fullname' is used to store the absolute path to the
                       tool being executed; it serves as a base to compute
                       the realname of that tool, and the directory holding
                       our runtime libraries */
  char *realname;   /* 'realname' is the real name of the tool, that is what
                       the wrapper is currently impersonating */
  char *basedir;    /* 'libdir' contains our runtime libraries */

  char *lastslash;  /* Temporary variables now */
  char *ldlibpath;
  size_t len;
  int execve_ret;

  /* Avoid the warning-treated-as-error: "error: unused parameter 'argc'" */
  len = argc;

  /* In case we have a relative or absolute pathname (ie. contains a slash),
   * then realpath wll work. But if the tool was found in the PATH, realpath
   * won't work, and we'll have to search ourselves.
   * This if{}else{} block allocates memory for fullname. */
  if( strchr( argv[0], '/' ) ) {
    fullname = (char*) malloc( PATH_MAX * sizeof(char) );
    if( ! realpath( argv[0], fullname ) ) {
      perror( "tool wrapper" );
      exit( 1 );
    }
  } else {
    char *path;
    char *mypath;
    char *colon;
    char *testname;
    struct stat st;

    fullname = NULL;
    colon = mypath = path = strdup( getenv( "PATH" ) );
    while( colon ) {
      colon = strchr( mypath, ':' );
      if( colon ) {
        *colon = '\0';
      }
      testname = strdup( mypath );
      testname = (char*) realloc( testname,   strlen( testname )
                                            + strlen( argv[0] )
                                            + 2 * sizeof(char) );
      memset( testname + strlen( testname ),
              0,
              strlen( argv[0] ) + 2 * sizeof(char) );
      strcat( testname, "/" );
      strcat( testname, argv[0] );
      if( stat( testname, &st ) == 0 ) {
        /* OK, exists. Is it a regular file, or a
         * symlink, which the current user may execute? */
        if( S_ISREG( st.st_mode ) && ! access( testname, X_OK || R_OK ) ) {
          fullname = strdup( testname );
          break;
        }
      }
      free( testname );
      mypath = colon + 1;
    }
    free( path );
    if( ! fullname ) {
      fprintf( stderr, "tool wrapper: %s: command not found\n", argv[0] );
      exit( 1 );
    }
  }

  /* Duplicate my own name to add the 'dot' to tool name */
  realname = strdup( fullname );
  realname = (char*) realloc( realname, strlen( realname) + 2 * sizeof(char) );
  realname[ strlen( realname ) + 1 ] = '\0';

  /* Add the dot after the last '/' */
  lastslash = strrchr( realname, '/' );
  memmove( lastslash + 1, lastslash, strlen( lastslash ) );
  *( lastslash + 1 ) = '.';

  /* Compute the basedir of the tool */
  basedir = strdup( fullname );
  lastslash = strrchr( basedir, '/' );
  *lastslash = '\0';
  lastslash = strrchr( basedir, '/' );
  *lastslash = '\0';

  /* Append '/lib' */
  len = strlen( basedir );
  basedir = (char*) realloc( basedir, len + 5 );
  *( basedir + len ) = '\0';
  strcat( basedir, "/lib" );

  /* Now add the directory with our runtime libraries to the
     front of the library search path, LD_LIBRARY_PATH */
  ldlibpath = getenv( "LD_LIBRARY_PATH" );
  if( ldlibpath ) {
    basedir = (char*) realloc( basedir,   strlen( basedir )
                                        + strlen( ldlibpath )
                                        + 2 * sizeof(char) );
    strcat( basedir, ":" );
    strcat( basedir, ldlibpath );
  }

  if( setenv( "LD_LIBRARY_PATH", basedir, 1 ) ) {
    errno = ENOMEM;
    perror( "tool wrapper" );
    exit( 1 );
  }

  /* Execute the real tool, now */
  execve_ret = execve( realname, argv, environ );

  /* In case something went wrong above, print a
     diagnostic message, and exit with error code 1 */
  perror( "tool wrapper" );
  return 1;
}