explicitly cast result of dlsym() to prevent compiler warnings
[util-vserver.git] / util-vserver / src / rpm-fake.c
1 // $Id$    --*- c++ -*--
2
3 // Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
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; version 2 of the License.
8 //  
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //  
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 //  
18
19 #ifdef HAVE_CONFIG_H
20 #  include <config.h>
21 #endif
22
23 #include <dlfcn.h>
24 #include <stdlib.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <stdbool.h>
30 #include <errno.h>
31 #include <asm/unistd.h>
32 #include <fcntl.h>
33 #include <stdarg.h>
34 #include <sys/mount.h>
35 #include <linux/fs.h>
36 #include <sched.h>
37 #include <signal.h>
38 #include <sys/types.h>
39 #include <sys/wait.h>
40
41 #define LIBNAME         "rpm-fake.so"
42 #define PLATFORM_FILE   "/etc/rpm/platform"
43
44 #define INIT(FILE,FUNC) FUNC##_func = ((__typeof__(FUNC) *) (dlsym(FILE, #FUNC)))
45 #define DECLARE(FUNC)   static __typeof__(FUNC) *       FUNC##_func = 0
46
47 static bool             is_initialized = false;
48 DECLARE(execv);
49   //DECLARE(open);
50
51 static int def_NR_new_s_context = 259;
52 #undef __NR_new_s_context
53 static int __NR_new_s_context_rev0;
54 static int __NR_new_s_context_rev1;
55
56 static int new_s_context_rev0(int, int, int);
57 static int new_s_context_rev1(int, int *, int, int);
58
59 _syscall3(int, new_s_context_rev0, int, newctx,              int, remove_cap, int, flags)
60 _syscall4(int, new_s_context_rev1, int, nbctx,  int *, ctxs, int, remove_cap, int, flags)
61
62 static int
63 new_s_context(int rev,
64               int nbctx,  int *ctxs, int remove_cap, int flags)
65 {
66   int           ret;
67   
68   switch (rev) {
69     case 0      :
70     {
71       int       ctx;
72       
73       switch (nbctx) {
74         case 0  :  ctx = -1;      break;
75         case 1  :  ctx = ctxs[0]; break;
76         default :  errno = EINVAL; return -1;
77       }
78
79       ret = new_s_context_rev0(ctx, remove_cap, flags);
80
81       break;
82     }
83
84     case 1      :
85       ret = new_s_context_rev1(nbctx, ctxs, remove_cap, flags);
86       break;
87
88     default     :
89       errno = EINVAL; return -1;
90   }
91
92   return ret;
93 }
94
95 static void
96 initVserverSyscalls(int s_context_NR)
97 {
98   int   nr = (s_context_NR!=-1) ? s_context_NR : def_NR_new_s_context;
99
100   __NR_new_s_context_rev0 = __NR_new_s_context_rev1 = nr;
101 }
102
103 static int
104 getAndClearEnv(char const *key, int dflt)
105 {
106   char  *env = getenv(key);
107   int           res;
108
109   if (env==0 || env[0]=='\0') res = dflt;
110   else {
111     res = atoi(env);
112     unsetenv(key);
113   }
114
115   return res;
116 }
117
118 static void
119 initLib()
120 {
121   if (is_initialized) return;
122
123   initVserverSyscalls(getAndClearEnv("RPM_FAKE_S_CONTEXT_NR", -1));
124   INIT(RTLD_NEXT, execv);
125     //INIT(RTLD_NEXT, open);
126
127   is_initialized = true;
128 }
129
130 static void
131 fixPreloadEnv()
132 {
133   char                  *env = getenv("LD_PRELOAD");
134   char                  *pos;
135
136     // the const <-> non-const assignment is not an issue since the following modifying operations
137     // will not be executed in the const-case
138   env = env ? env : "";
139   pos = strstr(env, LIBNAME);
140
141   if (pos!=0) {
142     char        *end_pos = pos + sizeof(LIBNAME);
143     bool        is_end = (end_pos[-1]=='\0');
144     char        *start_pos;
145     
146     end_pos[-1] = '\0';
147     start_pos   = strrchr(env, ':');
148     if (start_pos==0) start_pos = env;
149     else if (!is_end) ++start_pos;
150
151     if (is_end) *start_pos = '\0';
152     else        memmove(start_pos, end_pos, strlen(end_pos)+1);
153   }
154
155 #ifdef DEBUG
156   printf("env='%s'\n", env);
157 #endif
158
159   if (*env=='\0') unsetenv("LD_PRELOAD");
160 }
161
162 static int
163 execvWorker(char const *path, char * const argv[])
164 {
165   int           res;
166   int           ctx;
167
168   ctx = getAndClearEnv("RPM_FAKE_CTX",  -1);
169   if ( (res=new_s_context(getAndClearEnv("RPM_FAKE_S_CONTEXT_REV", 0),
170                           ctx==-1 ? 0 : 1,
171                           &ctx,
172                           getAndClearEnv("RPM_FAKE_CAP",  ~0x3404040f),
173                           getAndClearEnv("RPM_FAKE_FLAGS", 0)))!=-1 &&
174        (res=execv_func(path, argv)!=-1) ) {}
175
176   return res;
177 }
178
179 struct ExecvParams
180 {
181     char const *        path;
182     char * const *      argv;
183     char const *        mnts;
184 };
185
186 static int
187 removeNamespaceMountsChild(void *params_v)
188 {
189   struct ExecvParams *  params = params_v;
190   char                  buf[strlen(params->mnts)+1], *ptr;
191
192   strcpy(buf, params->mnts);
193   ptr = strtok(buf, ":");
194   while (ptr) {
195     if (umount2(ptr, 0)==-1) {
196         // FIXME: What is the semantic for CLONE_NEWNS? Is it ok that mounts in
197         // chroots are visible only, when chroot is on /dev/root?
198         //
199         // For now, ignore any errors, but future versions should handle them.
200
201         //return -1;
202     }
203     ptr = strtok(0, ":");
204   }
205
206   unsetenv("RPM_FAKE_NAMESPACE_MOUNTS");
207   return execvWorker(params->path, params->argv);
208 }
209
210 static int
211 removeNamespaceMounts(char const *path, char * const argv[])
212 {
213   char const *  mnts = getenv("RPM_FAKE_NAMESPACE_MOUNTS");
214
215   if (mnts==0) return execvWorker(path, argv);
216
217   {
218     char        buf[512 + 2*strlen(mnts)];
219     int         status;
220     pid_t       p;
221     struct ExecvParams          params = {
222       .path = path,
223       .argv = argv,
224       .mnts = mnts,
225     };
226     
227     pid_t       pid = clone(removeNamespaceMountsChild, buf+sizeof(buf)/2,
228                             CLONE_NEWNS|SIGCHLD|CLONE_VFORK, &params);
229
230     if (pid==-1) return -1;
231     while ((p=waitpid(pid, &status, 0))==-1 &&
232            (errno==EINTR || errno==EAGAIN)) ;
233     if (p==-1)   return -1;
234
235     if (WIFEXITED(status))   exit(WEXITSTATUS(status));
236     if (WIFSIGNALED(status)) kill(getpid(), WTERMSIG(status));
237
238     return -1;
239   }
240 }
241
242 int
243 execv(char const *path, char * const argv[]) __THROW
244 {
245   initLib();
246   fixPreloadEnv();
247   return removeNamespaceMounts(path, argv);
248 }