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