849c242bd68924a37fb11e76e9d6894cf4c156d7
[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 #include "compat.h"
23
24 #include <vserver.h>
25
26 #include <dlfcn.h>
27 #include <stdlib.h>
28 #include <stdint.h>
29 #include <stdio.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <stdbool.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <stdarg.h>
36 #include <sys/mount.h>
37 #include <linux/fs.h>
38 #include <sched.h>
39 #include <signal.h>
40 #include <sys/types.h>
41 #include <sys/wait.h>
42
43 #define LIBNAME         "rpm-fake.so"
44 #define PLATFORM_FILE   "/etc/rpm/platform"
45
46 #define INIT(FILE,FUNC) FUNC##_func = ((__typeof__(FUNC) *) (dlsym(FILE, #FUNC)))
47 #define DECLARE(FUNC)   static __typeof__(FUNC) *       FUNC##_func = 0
48
49 static bool             is_initialized = false;
50 DECLARE(execv);
51   //DECLARE(open);
52
53 static int
54 getAndClearEnv(char const *key, int dflt)
55 {
56   char          *env = getenv(key);
57   int           res;
58
59   if (env==0 || env[0]=='\0') res = dflt;
60   else {
61     res = atoi(env);
62     unsetenv(key);
63   }
64
65   return res;
66 }
67
68 static void
69 initLib()
70 {
71   if (is_initialized) return;
72
73   INIT(RTLD_NEXT, execv);
74     //INIT(RTLD_NEXT, open);
75
76   is_initialized = true;
77 }
78
79 static void
80 fixPreloadEnv()
81 {
82   char                  *env = getenv("LD_PRELOAD");
83   char                  *pos;
84
85     // the const <-> non-const assignment is not an issue since the following modifying operations
86     // will not be executed in the const-case
87   env = env ? env : "";
88   pos = strstr(env, LIBNAME);
89
90   if (pos!=0) {
91     char        *end_pos = pos + sizeof(LIBNAME);
92     bool        is_end = (end_pos[-1]=='\0');
93     char        *start_pos;
94
95     end_pos[-1] = '\0';
96     start_pos   = strrchr(env, ':');
97     if (start_pos==0) start_pos = env;
98     else if (!is_end) ++start_pos;
99
100     if (is_end) *start_pos = '\0';
101     else        memmove(start_pos, end_pos, strlen(end_pos)+1);
102   }
103
104 #ifdef DEBUG
105   printf("env='%s'\n", env);
106 #endif
107
108   if (*env=='\0') unsetenv("LD_PRELOAD");
109 }
110
111 static int
112 execvWorker(char const *path, char * const argv[])
113 {
114   int           res;
115   int           ctx;
116
117   ctx = getAndClearEnv("RPM_FAKE_CTX",  -1);
118   if ( (res=vc_new_s_context(ctx,
119                              getAndClearEnv("RPM_FAKE_CAP",  ~0x3404040f),
120                              getAndClearEnv("RPM_FAKE_FLAGS", 0)))!=-1 &&
121        (res=execv_func(path, argv)!=-1) ) {}
122
123   return res;
124 }
125
126 struct ExecvParams
127 {
128     char const *        path;
129     char * const *      argv;
130     char const *        mnts;
131 };
132
133 static int
134 removeNamespaceMountsChild(void *params_v)
135 {
136   struct ExecvParams *  params = params_v;
137   char                  buf[strlen(params->mnts)+1], *ptr;
138
139   strcpy(buf, params->mnts);
140   ptr = strtok(buf, ":");
141   while (ptr) {
142     if (umount2(ptr, 0)==-1) {
143         // FIXME: What is the semantic for CLONE_NEWNS? Is it ok that mounts in
144         // chroots are visible only, when chroot is on /dev/root?
145         //
146         // For now, ignore any errors, but future versions should handle them.
147
148         //return -1;
149     }
150     ptr = strtok(0, ":");
151   }
152
153   unsetenv("RPM_FAKE_NAMESPACE_MOUNTS");
154   return execvWorker(params->path, params->argv);
155 }
156
157 static int
158 removeNamespaceMounts(char const *path, char * const argv[])
159 {
160   char const *  mnts = getenv("RPM_FAKE_NAMESPACE_MOUNTS");
161
162   if (mnts==0) return execvWorker(path, argv);
163
164   {
165     char        buf[512 + 2*strlen(mnts)];
166     int         status;
167     pid_t       p;
168     struct ExecvParams          params = {
169       .path = path,
170       .argv = argv,
171       .mnts = mnts,
172     };
173
174       // the rpmlib signal-handler is still active; use the default one to
175       // make wait4() working...
176     signal(SIGCHLD, SIG_DFL);
177
178     pid_t       pid = clone(removeNamespaceMountsChild, buf+sizeof(buf)/2,
179                             CLONE_NEWNS|SIGCHLD|CLONE_VFORK, &params);
180
181     if (pid==-1) return -1;
182     while ((p=wait4(pid, &status, 0,0))==-1 &&
183            (errno==EINTR || errno==EAGAIN)) ;
184
185     if (p==-1)   return -1;
186
187     if (WIFEXITED(status))   exit(WEXITSTATUS(status));
188     if (WIFSIGNALED(status)) kill(getpid(), WTERMSIG(status));
189
190     return -1;
191   }
192 }
193
194 int
195 execv(char const *path, char * const argv[]) __THROW
196 {
197   initLib();
198   fixPreloadEnv();
199   return removeNamespaceMounts(path, argv);
200 }