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