rearranged inclusion of <sys/socket.h> to fix compilation problems
[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 "pathconfig.h"
24 #include "util.h"
25 #include "sys_clone.h"
26
27 #include <vserver.h>
28
29 #include <sys/socket.h>
30 #include <dlfcn.h>
31 #include <stdlib.h>
32 #include <stdint.h>
33 #include <stdio.h>
34 #include <unistd.h>
35 #include <asm/unistd.h>
36 #include <string.h>
37 #include <stdbool.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <stdarg.h>
41 #include <sys/mount.h>
42 #include <linux/fs.h>
43 #include <sched.h>
44 #include <signal.h>
45 #include <sys/types.h>
46 #include <sys/wait.h>
47 #include <sys/un.h>
48 #include <fcntl.h>
49 #include <pwd.h>
50 #include <grp.h>
51
52 #ifndef CLONE_NEWNS
53 #  define CLONE_NEWNS   0x00020000
54 #endif
55
56 #define LIBNAME         "rpm-fake.so"
57 #define PLATFORM_FILE   "/etc/rpm/platform"
58
59 #define INIT(FILE,FUNC) FUNC##_func = ((__typeof__(FUNC) *) (xdlsym(FILE, #FUNC)))
60 #define DECLARE(FUNC)   static __typeof__(FUNC) *       FUNC##_func = 0
61
62
63 #define DBG_INIT        0x0001
64 #define DBG_VARIABLES   0x0002
65 #define DBG_RESOLVER    0x0004
66 #define DBG_EXECV       0x0008
67 #define DBG_VERBOSE0    0x8000
68 #define DBG_VERBOSE1    (0x4000 | DBG_VERBOSE0)
69 #define DBG_VERBOSE2    (0x2000 | DBG_VERBOSE1)
70
71 static char const *     ctx_s = 0;
72 static xid_t            ctx   = VC_NOCTX;
73 static uint32_t         caps  = ~0;
74 static int              flags = 0;
75 static char const *     mnts  = 0;
76 static char const *     root  = 0;
77 static int              pw_sock   = -1;
78 static int              sync_sock = -1;
79 static unsigned int     debug_level = 0;
80
81 static bool             is_initialized = false;
82
83 DECLARE(execv);
84 DECLARE(getpwnam);
85 DECLARE(getgrnam);
86 DECLARE(endpwent);
87 DECLARE(endgrent);
88
89 static void             initRPMFake() __attribute__((__constructor__));
90 static void             exitRPMFake() __attribute__((__destructor__));
91
92 static inline bool
93 isDbgLevel(unsigned int level)
94 {
95   return ((debug_level&level)==level);
96 }
97
98 static void *
99 xdlsym(void *handle, const char *symbol)
100 {
101   void  *res = dlsym(handle, symbol);
102   if (res==0) {
103     char const  *error = dlerror();
104     write(2, symbol, strlen(symbol));
105     write(2, ": ", 2);
106     write(2, error, strlen(error));
107     write(2, "\n", 2);
108
109     _exit(255);
110   }
111
112   return res;
113 }
114
115 static void
116 showHelp()
117 {
118   WRITE_MSG(1,
119             "Usage: LD_PRELOAD=" LIBNAME " <executable> <args>*\n\n"
120             LIBNAME " unterstands the following environment variables:\n"
121             "  $RPM_FAKE_RESOLVER     ...  program which does the NSS resolving (defaults\n"
122             "                              to " RESOLVER_PROG ")\n"
123             "  $RPM_FAKE_RESOLVER_UID ...  uid of the resolver program\n"
124             "  $RPM_FAKE_RESOLVER_GID ...  gid of the resolver program\n"
125             "  $RPM_FAKE_CTX          ...  vserver context which shall be used for resolver\n"
126             "                              and scriptlets\n"
127             "  $RPM_FAKE_CAP          ...  linux capability remove-mask for the context\n"
128             "  $RPM_FAKE_FLAGS        ...  vserver flags of the context\n"
129             "  $RPM_FAKE_CHROOT       ...  directory of the chroot environment\n"
130             "  $RPM_FAKE_NAMESPACE_MOUNTS\n"
131             "                          ... colon separated list of directories which will\n"
132             "                              umounted before scriptlet execution\n\n"
133             "  $RPM_FAKE_HELP          ... shows this message\n"
134             "  $RPM_FAKE_VERSION       ... shows the version of this program\n\n"
135             "  $RPM_FAKE_DEBUG         ... sets the debuglevel bitmask\n\n"
136             "Please report bugs to " PACKAGE_BUGREPORT "\n");
137   exit(0);
138 }
139
140 static void
141 showVersion()
142 {
143   WRITE_MSG(1,
144             LIBNAME " " VERSION " -- wrapper around rpm\n"
145             "This program is part of " PACKAGE_STRING "\n\n"
146             "Copyright (C) 2003 Enrico Scholz\n"
147             VERSION_COPYRIGHT_DISCLAIMER);
148   exit(0);
149 }
150
151 static int
152 getAndClearEnv(char const *key, int dflt)
153 {
154   char          *env = getenv(key);
155   int           res;
156
157   if (env==0 || env[0]=='\0') res = dflt;
158   else {
159     res = atoi(env);
160     unsetenv(key);
161   }
162
163   return res;
164 }
165
166 #if 0
167 static void
168 initPwSocket()
169 {
170   char const *  sock_name = getenv("RPM_FAKE_PWSOCKET");
171   if (sock_name!=0) {
172     int flag;
173     struct sockaddr_un  addr = {
174       .sun_family = AF_UNIX,
175     };
176
177     strncpy(addr.sun_path, sock_name, sizeof(addr.sun_path)-1);
178     addr.sun_path[sizeof(addr.sun_path)-1]='\0';
179     
180     if ((pw_sock=socket(AF_UNIX, SOCK_STREAM, 0))==-1 ||
181         connect(pw_sock, (struct sockaddr *)(&addr), sizeof addr)==-1 ||
182         (flag=fcntl(pw_sock, F_GETFD))==-1 ||
183         fcntl(pw_sock, F_SETFD, flag | FD_CLOEXEC)==-1) {
184       perror("error while initializing pw-socket");
185       exit(255);
186     }
187   }
188   unsetenv("RPM_FAKE_PWSOCKET");
189 }
190 #else
191 static void
192 initPwSocket()
193 {
194   char const *  resolver = getenv("RPM_FAKE_RESOLVER");
195   if (resolver==0) resolver=RESOLVER_PROG;
196   
197   if (resolver!=0 && *resolver!='\0') {
198     int                 res_sock[2];
199     int                 sync_pipe[2];
200     pid_t               pid;
201     char const *        uid=0;
202     char const *        gid=0;
203
204     uid=getenv("RPM_FAKE_RESOLVER_UID");
205     gid=getenv("RPM_FAKE_RESOLVER_GID");
206
207     if (socketpair(AF_UNIX, SOCK_STREAM, 0, res_sock)==-1 ||
208         pipe(sync_pipe)==-1 ||
209         fcntl(res_sock[0],  F_SETFD, FD_CLOEXEC)==-1 ||
210         fcntl(sync_pipe[0], F_SETFD, FD_CLOEXEC)==-1) {
211       perror("rpm-fake.so: failed to create/initialize resolver-socket or pipe");
212       exit(255);
213     }
214
215     pid = fork();
216     if (pid==-1) {
217       perror("rpm-fake.so: fork()");
218       exit(255);
219     }
220
221     if (pid==0) {
222       char const        *args[10];
223       char const        **ptr  = args;
224       char const        *env[] = { "HOME=/", "PATH=/bin:/usr/bin", 0 };
225       
226       setsid();
227       dup2(res_sock[1],  0);
228       dup2(res_sock[1],  1);
229       if (sync_pipe[1]!=3) {
230         close(3);
231         dup2(sync_pipe[1], 3);
232         close(sync_pipe[1]);
233       }
234       close(res_sock[1]);
235         /* ... *socket[0] are marked as close-on-exec ...*/
236
237       *ptr++ = resolver;
238       if (root)  { *ptr++ = "-r"; *ptr++ = root;  }
239       if (uid)   { *ptr++ = "-u"; *ptr++ = uid;   }
240       if (gid)   { *ptr++ = "-g"; *ptr++ = gid;   }
241       if (ctx_s) { *ptr++ = "-c"; *ptr++ = ctx_s; }
242       *ptr++ = 0;
243       
244       execve(resolver, (char **)args, (char **)env);
245       perror("rpm-fake.so: failed to exec resolver");
246       exit(255);
247     }
248     else {
249       uint8_t           c;
250
251       close(res_sock[1]);
252       close(sync_pipe[1]);
253       pw_sock   = res_sock[0];
254       sync_sock = sync_pipe[0];
255
256       if (read(sync_sock, &c, 1)!=1 ||
257           write(pw_sock, ".", 1)!=1 ||
258           read(pw_sock, &c,   1)!=1 ||
259           c!='.') {
260         WRITE_MSG(2, "rpm-fake.so: failed to initialize communication with resolver\n");
261         exit(255);
262       }
263
264       if (wait4(pid, 0, WNOHANG,0)==-1) {
265         WRITE_MSG(2, "rpm-fake.so: unexpected initialization-error of resolver\n");
266         exit(255);
267       }
268     }
269
270     free((char *)ctx_s);
271     unsetenv("RPM_FAKE_RESOLVER_GID");
272     unsetenv("RPM_FAKE_RESOLVER_UID");
273     unsetenv("RPM_FAKE_RESOLVER");    
274   }
275 }
276 #endif
277
278 static void
279 initEnvironment()
280 {
281   int           syscall_rev;
282   int           syscall_nr;
283   
284   if (is_initialized) return;
285
286   syscall_rev = getAndClearEnv("RPM_FAKE_S_CONTEXT_REV", 0);
287   syscall_nr  = getAndClearEnv("RPM_FAKE_S_CONTEXT_NR",  273);
288   
289 #ifdef VC_ENABLE_API_LEGACY
290   {
291     extern void vc_init_internal_legacy(int ctx_rev, int ctx_number,
292                                         int ipv4_rev, int ipv4_number);
293   
294     vc_init_internal_legacy(syscall_rev, syscall_nr, 3, 274);
295   }
296 #endif
297
298   ctx_s = getenv("RPM_FAKE_CTX");
299   if (ctx_s && *ctx_s) ctx_s = strdup(ctx_s);
300   else                 ctx_s = 0;
301
302   ctx       = getAndClearEnv("RPM_FAKE_CTX",  VC_RANDCTX);
303   caps      = getAndClearEnv("RPM_FAKE_CAP",  ~0x3404040f);
304   flags     = getAndClearEnv("RPM_FAKE_FLAGS", 0);
305   root      = getenv("RPM_FAKE_CHROOT");
306   mnts      = getenv("RPM_FAKE_NAMESPACE_MOUNTS");
307   if (mnts && *mnts) mnts = strdup(mnts);
308   else               mnts = 0;
309
310   unsetenv("RPM_FAKE_CHROOT");
311   unsetenv("RPM_FAKE_NAMESPACE_MOUNTS");
312
313   
314   is_initialized = true;
315 }
316
317 static void
318 initSymbols()
319 {
320   INIT(RTLD_NEXT, execv);
321   INIT(RTLD_NEXT, getgrnam);
322   INIT(RTLD_NEXT, getpwnam);
323   INIT(RTLD_NEXT, endpwent);
324   INIT(RTLD_NEXT, endgrent);
325 }
326
327 static void
328 fixPreloadEnv()
329 {
330   char                  *env = getenv("LD_PRELOAD");
331   char                  *pos;
332
333     // the const <-> non-const assignment is not an issue since the following
334     // modifying operations will not be executed in the const-case
335   env = env ? env : "";
336   pos = strstr(env, LIBNAME);
337
338   if (pos!=0) {
339     char        *end_pos = pos + sizeof(LIBNAME);
340     bool        is_end = (end_pos[-1]=='\0');
341     char        *start_pos;
342
343     end_pos[-1] = '\0';
344     start_pos   = strrchr(env, ':');
345     if (start_pos==0) start_pos = env;
346     else if (!is_end) ++start_pos;
347
348     if (is_end) *start_pos = '\0';
349     else        memmove(start_pos, end_pos, strlen(end_pos)+1);
350   }
351
352 #ifdef DEBUG
353   if (isDbgLevel(DBG_VERBOSE1|DBG_VARIABLES)) {
354     WRITE_MSG(2, "env='");
355     WRITE_STR(2, env);
356     WRITE_MSG(2, "'\n");
357   }
358 #endif
359
360   if (*env=='\0') unsetenv("LD_PRELOAD");
361 }
362
363 void
364 initRPMFake()
365 {
366   if (getenv("RPM_FAKE_VERSION")) showVersion();
367   if (getenv("RPM_FAKE_HELP"))    showHelp();
368   
369   debug_level = getAndClearEnv("RPM_FAKE_DEBUG", 0);
370
371   if (isDbgLevel(DBG_INIT)) WRITE_MSG(2, ">>>>> initRPMFake <<<<<\n");
372   
373   fixPreloadEnv();
374   initSymbols();
375   initEnvironment();
376   initPwSocket();
377
378 #if 0
379   if (isDbgLevel(DBG_VARIABLES|DBG_VERBOSE2)) {
380     
381   }
382 #endif
383 }
384
385 void
386 exitRPMFake()
387
388   if (isDbgLevel(DBG_INIT)) WRITE_MSG(2, ">>>>> exitRPMFake <<<<<\n");
389   if (pw_sock!=-1) {
390     uint8_t     c;
391     read(sync_sock, &c, 1);
392     write(pw_sock, "Q", 1);
393   }
394 }
395
396
397   //============   the worker part   ===========
398
399
400 static bool
401 doPwStringRequest(uint32_t *result, char style, char const *name)
402 {
403   uint32_t      len = strlen(name);
404   uint8_t       code;
405   uint8_t       c;
406
407     // read the token...
408   read(sync_sock, &c, 1);
409
410   write(pw_sock, &style, 1);
411   write(pw_sock, &len,   sizeof len);
412   write(pw_sock, name,   len);
413   read (pw_sock, &code,  sizeof code);
414   read (pw_sock, result, sizeof *result);
415
416   return code!=0;
417 }
418
419 struct passwd *
420 getpwnam(const char * name)
421 {
422   if (pw_sock==-1) return getpwnam_func(name);
423   else {
424     static struct passwd        res = {
425       .pw_passwd = "*",
426       .pw_gid    = -1,
427       .pw_gecos  = "",
428       .pw_dir    = "/",
429       .pw_shell  = "/bin/false"
430     };
431
432     res.pw_name = (char *)(name);
433     if (!doPwStringRequest(&res.pw_uid, 'P', name)) return 0;
434     
435     return &res;
436   }
437 }
438
439 struct group *
440 getgrnam(const char * name)
441 {
442   if (pw_sock==-1) return getgrnam_func(name);
443   else {
444     static struct group         res = {
445       .gr_passwd = "*",
446       .gr_mem    = 0
447     };
448
449     res.gr_name = (char *)(name);
450     if (!doPwStringRequest(&res.gr_gid, 'G', name)) return 0;
451
452     return &res;
453   }
454 }
455
456 void
457 endgrent()
458 {
459   if (pw_sock==-1) endgrent_func();
460 }
461
462 void
463 endpwent()
464 {
465   if (pw_sock==-1) endpwent_func();
466 }
467
468
469 static int
470 execvWorker(char const *path, char * const argv[])
471 {
472   int           res;
473
474   if ((res=vc_new_s_context(ctx,caps,flags))!=-1 &&
475       (res=execv_func(path, argv)!=-1) ) {}
476
477   return res;
478 }
479
480 struct ExecvParams
481 {
482     char const *        path;
483     char * const *      argv;
484     char const *        mnts;
485 };
486
487 static int
488 removeNamespaceMountsChild(struct ExecvParams const *params)
489 {
490   char                  buf[strlen(params->mnts)+1], *ptr;
491
492   strcpy(buf, params->mnts);
493   ptr = strtok(buf, ":");
494   while (ptr) {
495     if (umount2(ptr, 0)==-1) {
496         // FIXME: What is the semantic for CLONE_NEWNS? Is it ok that mounts in
497         // chroots are visible only, when chroot is on /dev/root?
498         //
499         // For now, ignore any errors, but future versions should handle them.
500
501         //return -1;
502     }
503     ptr = strtok(0, ":");
504   }
505
506   return execvWorker(params->path, params->argv);
507 }
508
509 static int
510 removeNamespaceMounts(char const *path, char * const argv[])
511 {
512   if (mnts==0) return execvWorker(path, argv);
513
514   {
515     int                         status;
516     pid_t                       p, pid;
517     struct ExecvParams          params;
518
519     params.path = path;
520     params.argv = argv;
521     params.mnts = mnts;
522
523       // the rpmlib signal-handler is still active; use the default one to
524       // make wait4() working...
525     signal(SIGCHLD, SIG_DFL);
526
527 #ifdef NDEBUG
528     pid = sys_clone(CLONE_NEWNS|SIGCHLD|CLONE_VFORK, 0);
529 #else
530     pid = sys_clone(CLONE_NEWNS|SIGCHLD, 0);
531 #endif
532
533     switch (pid) {
534       case -1   :  return -1;
535       case 0    :  _exit(removeNamespaceMountsChild(&params));
536       default   :  break;
537     }
538         
539     while ((p=wait4(pid, &status, 0,0))==-1 &&
540            (errno==EINTR || errno==EAGAIN)) ;
541
542     if (p==-1)   return -1;
543
544     if (WIFEXITED(status))   _exit(WEXITSTATUS(status));
545     if (WIFSIGNALED(status)) kill(getpid(), WTERMSIG(status));
546
547     return -1;
548   }
549 }
550
551
552 int
553 execv(char const *path, char * const argv[]) __THROW
554 {
555   return removeNamespaceMounts(path, argv);
556 }