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