3 // Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
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.
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.
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.
23 #include "pathconfig.h"
33 #include <asm/unistd.h>
39 #include <sys/mount.h>
43 #include <sys/types.h>
45 #include <sys/socket.h>
52 # define CLONE_NEWNS 0x00020000
55 #define LIBNAME "rpm-fake.so"
56 #define PLATFORM_FILE "/etc/rpm/platform"
58 #define INIT(FILE,FUNC) FUNC##_func = ((__typeof__(FUNC) *) (xdlsym(FILE, #FUNC)))
59 #define DECLARE(FUNC) static __typeof__(FUNC) * FUNC##_func = 0
62 #define DBG_INIT 0x0001
63 #define DBG_VARIABLES 0x0002
64 #define DBG_RESOLVER 0x0004
65 #define DBG_EXECV 0x0008
66 #define DBG_VERBOSE0 0x8000
67 #define DBG_VERBOSE1 (0x4000 | DBG_VERBOSE0)
68 #define DBG_VERBOSE2 (0x2000 | DBG_VERBOSE1)
70 static char const * ctx_s = 0;
71 static xid_t ctx = VC_NOCTX;
72 static uint32_t caps = ~0;
74 static char const * mnts = 0;
75 static char const * root = 0;
76 static int pw_sock = -1;
77 static int sync_sock = -1;
78 static unsigned int debug_level = 0;
80 static bool is_initialized = false;
88 static void initRPMFake() __attribute__((__constructor__));
89 static void exitRPMFake() __attribute__((__destructor__));
92 isDbgLevel(unsigned int level)
94 return ((debug_level&level)==level);
98 xdlsym(void *handle, const char *symbol)
100 void *res = dlsym(handle, symbol);
102 char const *error = dlerror();
103 write(2, symbol, strlen(symbol));
105 write(2, error, strlen(error));
118 "Usage: LD_PRELOAD=" LIBNAME " <executable> <args>*\n\n"
119 LIBNAME " unterstands the following environment variables:\n"
120 " $RPM_FAKE_RESOLVER ... program which does the NSS resolving (defaults\n"
121 " to " RESOLVER_PROG ")\n"
122 " $RPM_FAKE_RESOLVER_UID ... uid of the resolver program\n"
123 " $RPM_FAKE_RESOLVER_GID ... gid of the resolver program\n"
124 " $RPM_FAKE_CTX ... vserver context which shall be used for resolver\n"
126 " $RPM_FAKE_CAP ... linux capability remove-mask for the context\n"
127 " $RPM_FAKE_FLAGS ... vserver flags of the context\n"
128 " $RPM_FAKE_CHROOT ... directory of the chroot environment\n"
129 " $RPM_FAKE_NAMESPACE_MOUNTS\n"
130 " ... colon separated list of directories which will\n"
131 " umounted before scriptlet execution\n\n"
132 " $RPM_FAKE_HELP ... shows this message\n"
133 " $RPM_FAKE_VERSION ... shows the version of this program\n\n"
134 " $RPM_FAKE_DEBUG ... sets the debuglevel bitmask\n\n"
135 "Please report bugs to " PACKAGE_BUGREPORT "\n");
143 LIBNAME " " VERSION " -- wrapper around rpm\n"
144 "This program is part of " PACKAGE_STRING "\n\n"
145 "Copyright (C) 2003 Enrico Scholz\n"
146 VERSION_COPYRIGHT_DISCLAIMER);
151 getAndClearEnv(char const *key, int dflt)
153 char *env = getenv(key);
156 if (env==0 || env[0]=='\0') res = dflt;
169 char const * sock_name = getenv("RPM_FAKE_PWSOCKET");
172 struct sockaddr_un addr = {
173 .sun_family = AF_UNIX,
176 strncpy(addr.sun_path, sock_name, sizeof(addr.sun_path)-1);
177 addr.sun_path[sizeof(addr.sun_path)-1]='\0';
179 if ((pw_sock=socket(AF_UNIX, SOCK_STREAM, 0))==-1 ||
180 connect(pw_sock, (struct sockaddr *)(&addr), sizeof addr)==-1 ||
181 (flag=fcntl(pw_sock, F_GETFD))==-1 ||
182 fcntl(pw_sock, F_SETFD, flag | FD_CLOEXEC)==-1) {
183 perror("error while initializing pw-socket");
187 unsetenv("RPM_FAKE_PWSOCKET");
193 char const * resolver = getenv("RPM_FAKE_RESOLVER");
194 if (resolver==0) resolver=RESOLVER_PROG;
196 if (resolver!=0 && *resolver!='\0') {
203 uid=getenv("RPM_FAKE_RESOLVER_UID");
204 gid=getenv("RPM_FAKE_RESOLVER_GID");
206 if (socketpair(AF_UNIX, SOCK_STREAM, 0, res_sock)==-1 ||
207 pipe(sync_pipe)==-1 ||
208 fcntl(res_sock[0], F_SETFD, FD_CLOEXEC)==-1 ||
209 fcntl(sync_pipe[0], F_SETFD, FD_CLOEXEC)==-1) {
210 perror("rpm-fake.so: failed to create/initialize resolver-socket or pipe");
216 perror("rpm-fake.so: fork()");
221 char const *args[10];
222 char const **ptr = args;
223 char const *env[] = { "HOME=/", "PATH=/bin:/usr/bin", 0 };
226 dup2(res_sock[1], 0);
227 dup2(res_sock[1], 1);
228 if (sync_pipe[1]!=3) {
230 dup2(sync_pipe[1], 3);
234 /* ... *socket[0] are marked as close-on-exec ...*/
237 if (root) { *ptr++ = "-r"; *ptr++ = root; }
238 if (uid) { *ptr++ = "-u"; *ptr++ = uid; }
239 if (gid) { *ptr++ = "-g"; *ptr++ = gid; }
240 if (ctx_s) { *ptr++ = "-c"; *ptr++ = ctx_s; }
243 execve(resolver, (char **)args, (char **)env);
244 perror("rpm-fake.so: failed to exec resolver");
252 pw_sock = res_sock[0];
253 sync_sock = sync_pipe[0];
255 if (read(sync_sock, &c, 1)!=1 ||
256 write(pw_sock, ".", 1)!=1 ||
257 read(pw_sock, &c, 1)!=1 ||
259 WRITE_MSG(2, "rpm-fake.so: failed to initialize communication with resolver\n");
263 if (wait4(pid, 0, WNOHANG,0)==-1) {
264 WRITE_MSG(2, "rpm-fake.so: unexpected initialization-error of resolver\n");
270 unsetenv("RPM_FAKE_RESOLVER_GID");
271 unsetenv("RPM_FAKE_RESOLVER_UID");
272 unsetenv("RPM_FAKE_RESOLVER");
283 if (is_initialized) return;
285 syscall_rev = getAndClearEnv("RPM_FAKE_S_CONTEXT_REV", 0);
286 syscall_nr = getAndClearEnv("RPM_FAKE_S_CONTEXT_NR", 273);
288 #ifdef VC_ENABLE_API_LEGACY
290 extern void vc_init_internal_legacy(int ctx_rev, int ctx_number,
291 int ipv4_rev, int ipv4_number);
293 vc_init_internal_legacy(syscall_rev, syscall_nr, 3, 274);
297 ctx_s = getenv("RPM_FAKE_CTX");
298 if (ctx_s && *ctx_s) ctx_s = strdup(ctx_s);
301 ctx = getAndClearEnv("RPM_FAKE_CTX", VC_RANDCTX);
302 caps = getAndClearEnv("RPM_FAKE_CAP", ~0x3404040f);
303 flags = getAndClearEnv("RPM_FAKE_FLAGS", 0);
304 root = getenv("RPM_FAKE_CHROOT");
305 mnts = getenv("RPM_FAKE_NAMESPACE_MOUNTS");
306 if (mnts && *mnts) mnts = strdup(mnts);
309 unsetenv("RPM_FAKE_CHROOT");
310 unsetenv("RPM_FAKE_NAMESPACE_MOUNTS");
313 is_initialized = true;
319 INIT(RTLD_NEXT, execv);
320 INIT(RTLD_NEXT, getgrnam);
321 INIT(RTLD_NEXT, getpwnam);
322 INIT(RTLD_NEXT, endpwent);
323 INIT(RTLD_NEXT, endgrent);
329 char *env = getenv("LD_PRELOAD");
332 // the const <-> non-const assignment is not an issue since the following
333 // modifying operations will not be executed in the const-case
334 env = env ? env : "";
335 pos = strstr(env, LIBNAME);
338 char *end_pos = pos + sizeof(LIBNAME);
339 bool is_end = (end_pos[-1]=='\0');
343 start_pos = strrchr(env, ':');
344 if (start_pos==0) start_pos = env;
345 else if (!is_end) ++start_pos;
347 if (is_end) *start_pos = '\0';
348 else memmove(start_pos, end_pos, strlen(end_pos)+1);
352 if (isDbgLevel(DBG_VERBOSE1|DBG_VARIABLES)) {
353 WRITE_MSG(2, "env='");
359 if (*env=='\0') unsetenv("LD_PRELOAD");
365 if (getenv("RPM_FAKE_VERSION")) showVersion();
366 if (getenv("RPM_FAKE_HELP")) showHelp();
368 debug_level = getAndClearEnv("RPM_FAKE_DEBUG", 0);
370 if (isDbgLevel(DBG_INIT)) WRITE_MSG(2, ">>>>> initRPMFake <<<<<\n");
378 if (isDbgLevel(DBG_VARIABLES|DBG_VERBOSE2)) {
387 if (isDbgLevel(DBG_INIT)) WRITE_MSG(2, ">>>>> exitRPMFake <<<<<\n");
390 read(sync_sock, &c, 1);
391 write(pw_sock, "Q", 1);
396 //============ the worker part ===========
400 doPwStringRequest(uint32_t *result, char style, char const *name)
402 uint32_t len = strlen(name);
407 read(sync_sock, &c, 1);
409 write(pw_sock, &style, 1);
410 write(pw_sock, &len, sizeof len);
411 write(pw_sock, name, len);
412 read (pw_sock, &code, sizeof code);
413 read (pw_sock, result, sizeof *result);
419 getpwnam(const char * name)
421 if (pw_sock==-1) return getpwnam_func(name);
423 static struct passwd res = {
428 .pw_shell = "/bin/false"
431 res.pw_name = (char *)(name);
432 if (!doPwStringRequest(&res.pw_uid, 'P', name)) return 0;
439 getgrnam(const char * name)
441 if (pw_sock==-1) return getgrnam_func(name);
443 static struct group res = {
448 res.gr_name = (char *)(name);
449 if (!doPwStringRequest(&res.gr_gid, 'G', name)) return 0;
458 if (pw_sock==-1) endgrent_func();
464 if (pw_sock==-1) endpwent_func();
469 execvWorker(char const *path, char * const argv[])
473 if ((res=vc_new_s_context(ctx,caps,flags))!=-1 &&
474 (res=execv_func(path, argv)!=-1) ) {}
487 removeNamespaceMountsChild(struct ExecvParams const *params)
489 char buf[strlen(params->mnts)+1], *ptr;
491 strcpy(buf, params->mnts);
492 ptr = strtok(buf, ":");
494 if (umount2(ptr, 0)==-1) {
495 // FIXME: What is the semantic for CLONE_NEWNS? Is it ok that mounts in
496 // chroots are visible only, when chroot is on /dev/root?
498 // For now, ignore any errors, but future versions should handle them.
502 ptr = strtok(0, ":");
505 return execvWorker(params->path, params->argv);
509 removeNamespaceMounts(char const *path, char * const argv[])
511 if (mnts==0) return execvWorker(path, argv);
516 struct ExecvParams params;
522 // the rpmlib signal-handler is still active; use the default one to
523 // make wait4() working...
524 signal(SIGCHLD, SIG_DFL);
527 pid = syscall(__NR_clone, CLONE_NEWNS|SIGCHLD|CLONE_VFORK, 0);
529 pid = syscall(__NR_clone, CLONE_NEWNS|SIGCHLD, 0);
534 case 0 : _exit(removeNamespaceMountsChild(¶ms));
538 while ((p=wait4(pid, &status, 0,0))==-1 &&
539 (errno==EINTR || errno==EAGAIN)) ;
541 if (p==-1) return -1;
543 if (WIFEXITED(status)) _exit(WEXITSTATUS(status));
544 if (WIFSIGNALED(status)) kill(getpid(), WTERMSIG(status));
552 execv(char const *path, char * const argv[]) __THROW
554 return removeNamespaceMounts(path, argv);