Since using a destructor doesn't seem to work all that well, try using atexit instead.
[util-vserver.git] / 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
26 #include <lib/vserver.h>
27 #include <lib/internal.h>
28 #include <lib_internal/sys_clone.h>
29
30 #include <sys/socket.h>
31 #include <dlfcn.h>
32 #include <stdlib.h>
33 #include <stdint.h>
34 #include <stdio.h>
35 #include <unistd.h>
36 #include <asm/unistd.h>
37 #include <string.h>
38 #include <stdbool.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <stdarg.h>
42 #include <sys/mount.h>
43 #include <linux/fs.h>
44 #include <sched.h>
45 #include <signal.h>
46 #include <sys/types.h>
47 #include <sys/wait.h>
48 #include <sys/un.h>
49 #include <fcntl.h>
50 #include <pwd.h>
51 #include <grp.h>
52
53
54   // from selinux.h
55   // FIXME: add configure autodetection and include <selinux.h> directly
56 int rpm_execcon(unsigned int verified,
57                 const char *filename,
58                 char *const argv[], char *const envp[]);
59
60
61 #define ENSC_WRAPPERS_PREFIX    "rpm-fake.so: "
62 #define ENSC_WRAPPERS_VSERVER   1
63 #define ENSC_WRAPPERS_UNISTD    1
64 #include <wrappers.h>
65
66 #undef _POSIX_SOURCE
67 #include "capability-compat.h"
68
69 #define LIBNAME         "rpm-fake.so"
70 #define PLATFORM_FILE   "/etc/rpm/platform"
71
72 #define INIT(FILE,FUNC) FUNC##_func = ((__typeof__(FUNC) *) (xdlsym(FILE, #FUNC)))
73 #define DECLARE(FUNC)   static __typeof__(FUNC) *       FUNC##_func = 0
74
75 #define DEBUG 1
76
77 #define DBG_INIT        0x0001
78 #define DBG_VARIABLES   0x0002
79 #define DBG_RESOLVER    0x0004
80 #define DBG_EXECV       0x0008
81 #define DBG_ENV         0x0010
82 #define DBG_VERBOSE0    0x8000
83 #define DBG_VERBOSE1    (0x4000 | DBG_VERBOSE0)
84 #define DBG_VERBOSE2    (0x2000 | DBG_VERBOSE1)
85
86 int                     wrapper_exit_code = 255;
87
88 static xid_t            ctx   = VC_NOCTX;
89 static uint32_t         caps  = ~0;
90 static int              flags = 0;
91 static char const *     mnts  = 0;
92 static char const *     root  = 0;
93 static int              pw_sock   = -1;
94 static int              sync_sock = -1;
95 static unsigned int     debug_level = 0;
96
97 static bool             is_initialized = false;
98
99 static bool             ctx_created = false;
100
101   //DECLARE(rpm_execcon);
102   //DECLARE(execv);
103 DECLARE(getpwnam);
104 DECLARE(getgrnam);
105 DECLARE(endpwent);
106 DECLARE(endgrent);
107
108 static void             initRPMFake() __attribute__((__constructor__));
109 static void             exitRPMFake() __attribute__((__destructor__));
110
111 static inline bool
112 isDbgLevel(unsigned int level)
113 {
114   return ((debug_level&level)==level);
115 }
116
117 static void *
118 xdlsym(void *handle, const char *symbol)
119 {
120   void  *res = dlsym(handle, symbol);
121   if (res==0) {
122     char const  *error = dlerror();
123     Vwrite(2, symbol, strlen(symbol));
124     Vwrite(2, ": ", 2);
125     Vwrite(2, error, strlen(error));
126     Vwrite(2, "\n", 2);
127
128     _exit(255);
129   }
130
131   return res;
132 }
133
134 static void
135 showHelp()
136 {
137   WRITE_MSG(1,
138             "Usage: LD_PRELOAD=" LIBNAME " <executable> <args>*\n\n"
139             LIBNAME " unterstands the following environment variables:\n"
140             "  $RPM_FAKE_RESOLVER     ...  program which does the NSS resolving (defaults\n"
141             "                              to " RESOLVER_PROG ")\n"
142             "  $RPM_FAKE_RESOLVER_UID ...  uid of the resolver program\n"
143             "  $RPM_FAKE_RESOLVER_GID ...  gid of the resolver program\n"
144             "  $RPM_FAKE_CTX          ...  vserver context which shall be used for resolver\n"
145             "                              and scriptlets\n"
146             "  $RPM_FAKE_CAP          ...  linux capability remove-mask for the context\n"
147             "  $RPM_FAKE_FLAGS        ...  vserver flags of the context\n"
148             "  $RPM_FAKE_CHROOT       ...  directory of the chroot environment\n"
149             "  $RPM_FAKE_NAMESPACE_MOUNTS\n"
150             "                          ... colon separated list of directories which will\n"
151             "                              umounted before scriptlet execution\n\n"
152             "  $RPM_FAKE_HELP          ... shows this message\n"
153             "  $RPM_FAKE_VERSION       ... shows the version of this program\n\n"
154             "  $RPM_FAKE_DEBUG         ... sets the debuglevel bitmask\n\n"
155             "Please report bugs to " PACKAGE_BUGREPORT "\n");
156   exit(0);
157 }
158
159 static void
160 showVersion()
161 {
162   WRITE_MSG(1,
163             LIBNAME " " VERSION " -- wrapper around rpm\n"
164             "This program is part of " PACKAGE_STRING "\n\n"
165             "Copyright (C) 2003 Enrico Scholz\n"
166             VERSION_COPYRIGHT_DISCLAIMER);
167   exit(0);
168 }
169
170 static void
171 unsetPreloadEnv()
172 {
173   char                  *env = getenv("LD_PRELOAD");
174   char                  *pos;
175
176     // the const <-> non-const assignment is not an issue since the following
177     // modifying operations will not be executed in the const-case
178   env = env ? env : "";
179   pos = strstr(env, LIBNAME);
180
181   if (pos!=0) {
182     char        *end_pos = pos + sizeof(LIBNAME);
183     bool        is_end = (end_pos[-1]=='\0');
184     char        *start_pos;
185
186     end_pos[-1] = '\0';
187     start_pos   = strrchr(env, ':');
188     if (start_pos==0) start_pos = env;
189     else if (!is_end) ++start_pos;
190
191     if (is_end) *start_pos = '\0';
192     else        memmove(start_pos, end_pos, strlen(end_pos)+1);
193   }
194
195 #ifdef DEBUG
196   if (isDbgLevel(DBG_VERBOSE1|DBG_VARIABLES)) {
197     WRITE_MSG(2, "env='");
198     WRITE_STR(2, env);
199     WRITE_MSG(2, "'\n");
200   }
201 #endif
202
203   if (*env=='\0') unsetenv("LD_PRELOAD");
204 }
205
206 static void
207 clearEnv()
208 {
209   if (isDbgLevel(DBG_ENV)) WRITE_MSG(2, "clearEnv()\n");
210   
211   unsetenv("RPM_FAKE_S_CONTEXT_REV");
212   unsetenv("RPM_FAKE_S_CONTEXT_NR");
213   unsetenv("RPM_FAKE_CTX");
214   unsetenv("RPM_FAKE_FLAGS");
215   unsetenv("RPM_FAKE_CHROOT");
216   unsetenv("RPM_FAKE_NAMESPACE_MOUNTS");
217
218   unsetenv("RPM_FAKE_RESOLVER_GID");
219   unsetenv("RPM_FAKE_RESOLVER_UID");
220   unsetenv("RPM_FAKE_RESOLVER");    
221   unsetenv("RPM_FAKE_PWSOCKET");
222
223   unsetenv("RPM_FAKE_DEBUG");
224
225   unsetPreloadEnv();
226 }
227
228 static int
229 getDefaultEnv(char const *key, int dflt)
230 {
231   char          *env = getenv(key);
232   int           res;
233
234   if (env==0 || env[0]=='\0') res = dflt;
235   else                        res = atoi(env);
236
237   return res;
238 }
239
240   /// \returns true iff we are in ctx after leaving this function
241 static bool
242 setupContext(xid_t xid, char const **xid_str)
243 {
244   bool          res = false;
245   
246   if (vc_isSupported(vcFEATURE_MIGRATE)) {
247     xid_t       rc=VC_NOCTX;
248
249     if ((xid==VC_DYNAMIC_XID || !vc_is_dynamic_xid(xid)) &&
250         (rc=vc_ctx_create(xid, NULL))==VC_NOCTX &&
251         errno!=EEXIST) {
252       perror(ENSC_WRAPPERS_PREFIX "vc_ctx_create()");
253       exit(255);
254     }
255
256     if (rc!=VC_NOCTX) {
257       char                      buf[sizeof(xid_t)*3 + 128];
258       size_t                    l;
259       struct vc_ctx_caps        caps;
260       struct vc_ctx_flags       flags;
261       
262       strcpy(buf, "rpm-fake.so #");
263       l = utilvserver_fmt_uint(buf+sizeof("rpm-fake.so #")-1, getppid());
264       Evc_set_vhi_name(rc, vcVHI_CONTEXT, buf, sizeof("rpm-fake.so #")+l-1);
265
266       caps.ccaps =  0ull;
267       caps.cmask = ~0ull;
268       caps.bcaps = ~vc_get_insecurebcaps();
269       caps.bmask = ~0ull;
270       Evc_set_ccaps(rc, &caps);
271
272       flags.flagword = 0;
273       flags.mask = VC_VXF_SC_HELPER;
274       Evc_set_cflags(rc, &flags);
275       
276         // context will be activated later...
277
278       xid = rc;
279       res = true;
280       ctx_created = true;
281     }
282   }
283
284   if (xid==VC_DYNAMIC_XID)
285     *xid_str = 0;
286   else {
287     char                buf[sizeof(xid_t)*3 + 2];
288     size_t              l;
289     
290     l        = utilvserver_fmt_uint(buf, xid); buf[l] = '\0';
291     *xid_str = strdup(buf);
292   }
293  
294   Ewrite(3, &xid, sizeof xid);
295   return res;
296 }
297
298 #if 0
299 static void
300 initPwSocket()
301 {
302   char const *  sock_name = getenv("RPM_FAKE_PWSOCKET");
303   if (sock_name!=0) {
304     int flag;
305     struct sockaddr_un  addr = {
306       .sun_family = AF_UNIX,
307     };
308
309     strncpy(addr.sun_path, sock_name, sizeof(addr.sun_path)-1);
310     addr.sun_path[sizeof(addr.sun_path)-1]='\0';
311     
312     if ((pw_sock=socket(AF_UNIX, SOCK_STREAM, 0))==-1 ||
313         connect(pw_sock, (struct sockaddr *)(&addr), sizeof addr)==-1 ||
314         (flag=fcntl(pw_sock, F_GETFD))==-1 ||
315         fcntl(pw_sock, F_SETFD, flag | FD_CLOEXEC)==-1) {
316       perror(ENSC_WRAPPERS_PREFIX "error while initializing pw-socket");
317       exit(255);
318     }
319   }
320 }
321 #else
322 static void
323 initPwSocket()
324 {
325   char const *  resolver = getenv("RPM_FAKE_RESOLVER");
326   if (resolver==0) resolver=RESOLVER_PROG;
327   
328   if (resolver!=0 && *resolver!='\0') {
329     int                 res_sock[2];
330     int                 sync_pipe[2];
331     pid_t               pid;
332     char const *        uid=0;
333     char const *        gid=0;
334
335     uid=getenv("RPM_FAKE_RESOLVER_UID");
336     gid=getenv("RPM_FAKE_RESOLVER_GID");
337
338     if (socketpair(AF_UNIX, SOCK_STREAM, 0, res_sock)==-1 ||
339         pipe(sync_pipe)==-1 ||
340         fcntl(res_sock[0],  F_SETFD, FD_CLOEXEC)==-1 ||
341         fcntl(sync_pipe[0], F_SETFD, FD_CLOEXEC)==-1) {
342       perror(ENSC_WRAPPERS_PREFIX "failed to create/initialize resolver-socket or pipe");
343       exit(255);
344     }
345
346     pid = fork();
347     if (pid==-1) {
348       perror(ENSC_WRAPPERS_PREFIX "fork()");
349       exit(255);
350     }
351
352     if (pid==0) {
353       char const        *args[20];
354       char const        **ptr  = args;
355       char const        *env[] = { "HOME=/", "PATH=/bin:/usr/bin", 0 };
356       char const        *xid_str;
357       char              flag_str[ sizeof(flags)*3 + 2];
358       char              caps_str[ sizeof(caps)*3  + 2];
359
360       clearEnv();
361       
362       setsid();
363       dup2(res_sock[1],  0);
364       dup2(res_sock[1],  1);
365       if (sync_pipe[1]!=3) {
366         close(3);
367         dup2(sync_pipe[1], 3);
368         close(sync_pipe[1]);
369       }
370       close(res_sock[1]);
371         /* ... *socket[0] are marked as close-on-exec ...*/
372
373       flag_str[utilvserver_fmt_uint(flag_str, flags)] = '\0';
374       caps_str[utilvserver_fmt_uint(caps_str, caps)]  = '\0';
375
376       *ptr++ = resolver;
377       *ptr++ = "-F"; *ptr++ = flag_str;
378       *ptr++ = "-C"; *ptr++ = caps_str;
379       if (root)  { *ptr++ = "-r"; *ptr++ = ".";   }
380       if (uid)   { *ptr++ = "-u"; *ptr++ = uid;   }
381       if (gid)   { *ptr++ = "-g"; *ptr++ = gid;   }
382
383       if (root) Echdir(root);
384
385       if (setupContext(ctx, &xid_str)) { *ptr++ = "-s"; }
386       else if (xid_str)                { *ptr++ = "-c"; *ptr++ = xid_str; }
387       
388       *ptr++ = 0;
389       execve(resolver, (char **)args, (char **)env);
390       perror(ENSC_WRAPPERS_PREFIX "failed to exec resolver");
391       exit(255);
392     }
393     else {
394       uint8_t           c;
395
396       close(res_sock[1]);
397       close(sync_pipe[1]);
398       pw_sock   = res_sock[0];
399       sync_sock = sync_pipe[0];
400
401       if (read(sync_sock, &ctx, sizeof ctx)!=sizeof(ctx) ||
402           read(sync_sock, &c, 1)!=1 ||
403           write(pw_sock, ".", 1)!=1 ||
404           read(pw_sock, &c,   1)!=1 ||
405           c!='.') {
406         WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "failed to initialize communication with resolver\n");
407         exit(255);
408       }
409
410       if (wait4(pid, 0, WNOHANG,0)==-1) {
411         WRITE_MSG(2, ENSC_WRAPPERS_PREFIX" unexpected initialization-error of resolver\n");
412         exit(255);
413       }
414     }
415   }
416 }
417 #endif
418
419 static void
420 reduceCapabilities()
421 {
422   int retried = 0;
423   struct __user_cap_header_struct header;
424   struct __user_cap_data_struct user[2];
425   
426   header.version = _LINUX_CAPABILITY_VERSION_2;
427   header.pid     = 0;
428
429 retry:
430   if (capget(&header, user)==-1) {
431     if (!retried &&
432         header.version != _LINUX_CAPABILITY_VERSION_2) {
433       header.version = _LINUX_CAPABILITY_VERSION_1;
434       retried = 1;
435       goto retry;
436     }
437     perror("capget()");
438     exit(wrapper_exit_code);
439   }
440
441   user[0].effective   &= ~(1<<CAP_MKNOD);
442   user[0].permitted   &= ~(1<<CAP_MKNOD);
443   user[0].inheritable &= ~(1<<CAP_MKNOD);
444
445   if (capset(&header, user)==-1) {
446     perror("capset()");
447     exit(wrapper_exit_code);
448   }
449 }
450
451 static void
452 initEnvironment()
453 {
454   int           syscall_rev;
455   int           syscall_nr;
456   
457   if (is_initialized) return;
458
459   syscall_rev = getDefaultEnv("RPM_FAKE_S_CONTEXT_REV", 0);
460   syscall_nr  = getDefaultEnv("RPM_FAKE_S_CONTEXT_NR",  273);
461   
462 #ifdef VC_ENABLE_API_LEGACY
463   {
464     extern void vc_init_internal_legacy(int ctx_rev, int ctx_number,
465                                         int ipv4_rev, int ipv4_number);
466   
467     vc_init_internal_legacy(syscall_rev, syscall_nr, 3, 274);
468   }
469 #endif
470
471   ctx       = getDefaultEnv("RPM_FAKE_CTX",  VC_DYNAMIC_XID);
472   caps      = getDefaultEnv("RPM_FAKE_CAP",  ~0x3404040f);
473   flags     = getDefaultEnv("RPM_FAKE_FLAGS", 0);
474   root      = getenv("RPM_FAKE_CHROOT");
475   mnts      = getenv("RPM_FAKE_NAMESPACE_MOUNTS");
476   if (mnts && *mnts) mnts = strdup(mnts);
477   else               mnts = 0;
478
479 #if DEBUG
480   if (isDbgLevel(DBG_VERBOSE1))
481     dprintf(2, "ctx=%u, caps=%016x, flags=%016x,\nroot='%s',\nmnts='%s'\n",
482             ctx, caps, flags, root, mnts);
483 #endif
484   
485   is_initialized = true;
486 }
487
488 static void
489 initSymbols()
490 {
491     //INIT(RTLD_NEXT, rpm_execcon);
492     //INIT(RTLD_NEXT, execv);
493   INIT(RTLD_NEXT, getgrnam);
494   INIT(RTLD_NEXT, getpwnam);
495   INIT(RTLD_NEXT, endpwent);
496   INIT(RTLD_NEXT, endgrent);
497 }
498
499 void
500 initRPMFake()
501 {
502   if (getenv("RPM_FAKE_VERSION")) showVersion();
503   if (getenv("RPM_FAKE_HELP"))    showHelp();
504   
505   debug_level = getDefaultEnv("RPM_FAKE_DEBUG", 0);
506
507   if (isDbgLevel(DBG_INIT)) WRITE_MSG(2, ">>>>> initRPMFake <<<<<\n");
508   
509   reduceCapabilities();
510   initSymbols();
511   initEnvironment();
512   initPwSocket();
513
514   atexit(exitRPMFake);
515
516 #if 0
517   if (isDbgLevel(DBG_VARIABLES|DBG_VERBOSE2)) {
518     
519   }
520 #endif
521 }
522
523 void
524 exitRPMFake()
525
526   if (isDbgLevel(DBG_INIT)) WRITE_MSG(2, ">>>>> exitRPMFake <<<<<\n");
527   if (pw_sock!=-1) {
528     uint8_t     c;
529     if (read(sync_sock, &c, 1)!=1) { /*...*/ }
530     if (write(pw_sock, "Q", 1)!=1) { /*...*/ }
531     if (ctx_created) {
532       if (vc_isSupported(vcFEATURE_VWAIT)) {
533         if (vc_wait_exit(ctx)==-1) { /*...*/ }
534       }
535       else {
536         /* this can race */
537         if (read(sync_sock, &c, 1)!=0) { /*...*/}
538       }
539     }
540   }
541 }
542
543
544   //============   the worker part   ===========
545
546
547 static bool
548 doPwStringRequest(uint32_t *result, char style, char const *name)
549 {
550   uint32_t      len = strlen(name);
551   uint8_t       code;
552   uint8_t       c;
553
554   return (TEMP_FAILURE_RETRY(read (sync_sock, &c, 1))==1 &&
555           TEMP_FAILURE_RETRY(write(pw_sock, &style, 1))==1 &&
556           TEMP_FAILURE_RETRY(write(pw_sock, &len,   sizeof len))==sizeof(len) &&
557           TEMP_FAILURE_RETRY(write(pw_sock, name,   len))==(ssize_t)(len) &&
558           TEMP_FAILURE_RETRY(read (pw_sock, &code,  sizeof code))==sizeof(code) &&
559           TEMP_FAILURE_RETRY(read (pw_sock, result, sizeof *result))==sizeof(*result) &&
560           code!=0);
561 }
562
563 struct passwd *
564 getpwnam(const char * name)
565 {
566   if (pw_sock==-1) return getpwnam_func(name);
567   else {
568     uint32_t                    id;
569     static struct passwd        res = {
570       .pw_passwd = "*",
571       .pw_gid    = -1,
572       .pw_gecos  = "",
573       .pw_dir    = "/",
574       .pw_shell  = "/bin/false"
575     };
576
577     res.pw_name = (char *)(name);
578     if (!doPwStringRequest(&id, 'P', name)) return 0;
579     res.pw_uid = id;
580     
581     return &res;
582   }
583 }
584
585 struct group *
586 getgrnam(const char * name)
587 {
588   if (pw_sock==-1) return getgrnam_func(name);
589   else {
590     uint32_t                    id;
591     static struct group         res = {
592       .gr_passwd = "*",
593       .gr_mem    = 0
594     };
595
596     res.gr_name = (char *)(name);
597     if (!doPwStringRequest(&id, 'G', name)) return 0;
598     res.gr_gid = id;
599
600     return &res;
601   }
602 }
603
604 void
605 endgrent()
606 {
607   if (pw_sock==-1) endgrent_func();
608   TEMP_FAILURE_RETRY(write(pw_sock, "Cg", 2));
609 }
610
611 void
612 endpwent()
613 {
614   if (pw_sock==-1) endpwent_func();
615   TEMP_FAILURE_RETRY(write(pw_sock, "Cp", 2));
616 }
617
618
619 static int
620 execvWorker(char const *path, char * const argv[], char * const envp[])
621 {
622   int           res = -1;
623
624   if (vc_isSupported(vcFEATURE_MIGRATE))
625     res = vc_ctx_migrate(ctx, 0);
626   else {
627 #ifdef VC_ENABLE_API_COMPAT  
628     res = vc_new_s_context(ctx,caps,flags);
629 #else
630     WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "can not change context: migrate kernel feature missing and 'compat' API disabled\n");
631 #endif
632   }
633
634   clearEnv();    
635     
636   if (res!=-1)
637     res=execve(path, argv, envp);
638
639   return res;
640 }
641
642 struct ExecvParams
643 {
644     char const *        path;
645     char * const *      argv;
646     char * const *      envp;
647     char const *        mnts;
648 };
649
650 static int
651 removeNamespaceMountsChild(struct ExecvParams const *params)
652 {
653   char                  buf[strlen(params->mnts)+1], *ptr;
654
655   strcpy(buf, params->mnts);
656   ptr = strtok(buf, ":");
657   while (ptr) {
658     if (umount2(ptr, 0)==-1) {
659         // FIXME: What is the semantic for CLONE_NEWNS? Is it ok that mounts in
660         // chroots are visible only, when chroot is on /dev/root?
661         //
662         // For now, ignore any errors, but future versions should handle them.
663
664         //return -1;
665     }
666     ptr = strtok(0, ":");
667   }
668
669   return execvWorker(params->path, params->argv, params->envp);
670 }
671
672 static int
673 removeNamespaceMounts(char const *path,
674                       char * const argv[], char * const envp[])
675 {
676   if (mnts==0) return execvWorker(path, argv, envp);
677
678   {
679     int                         status;
680     pid_t                       p, pid;
681     struct ExecvParams          params;
682
683     params.path = path;
684     params.argv = argv;
685     params.envp = envp;
686     params.mnts = mnts;
687
688       // the rpmlib signal-handler is still active; use the default one to
689       // make wait4() working...
690     signal(SIGCHLD, SIG_DFL);
691
692 #ifdef NDEBUG
693     pid = sys_clone(CLONE_NEWNS|SIGCHLD|CLONE_VFORK, 0);
694 #else
695     pid = sys_clone(CLONE_NEWNS|SIGCHLD, 0);
696 #endif
697
698     switch (pid) {
699       case -1   :  return -1;
700       case 0    :  _exit(removeNamespaceMountsChild(&params));
701       default   :  break;
702     }
703         
704     while ((p=wait4(pid, &status, 0,0))==-1 &&
705            (errno==EINTR || errno==EAGAIN)) ;
706
707     if (p==-1)   return -1;
708
709     if (WIFEXITED(status))   _exit(WEXITSTATUS(status));
710     if (WIFSIGNALED(status)) kill(getpid(), WTERMSIG(status));
711
712     return -1;
713   }
714 }
715
716
717 int
718 execv(char const *path, char * const argv[])
719 {
720   extern char **environ;
721
722   if (isDbgLevel(DBG_EXECV)) {
723     WRITE_MSG(2, "execv('");
724     WRITE_STR(2, path);
725     WRITE_MSG(2, "', ...)\n");
726   }
727
728   return removeNamespaceMounts(path, argv, environ);
729 }
730
731 int
732 rpm_execcon(unsigned int UNUSED verified,
733             const char *filename,
734             char *const argv[], char *const envp[])
735 {
736   if (isDbgLevel(DBG_EXECV)) {
737     WRITE_MSG(2, "rpm_execcon(..., '");
738     WRITE_STR(2, filename);
739     WRITE_MSG(2, "', ...)\n");
740   }
741
742   return removeNamespaceMounts(filename, argv, envp);
743 }
744
745 int
746 is_selinux_enabled()
747 {
748   return 0;
749 }