1 /* gcc -Wall -O2 -g chpid.c -o chpid */
3 #define _XOPEN_SOURCE_EXTENDED
10 #include <sys/types.h>
12 #include <sys/syscall.h>
15 #include <sys/resource.h>
16 #include <sys/mount.h>
25 #define MNT_FORCE 0x00000001 /* Attempt to forcibily umount */
26 #endif /* MNT_FORCE */
28 #define MNT_DETACH 0x00000002 /* Just detach from the tree */
29 #endif /* MNT_DETACH */
31 #define MNT_EXPIRE 0x00000004 /* Mark for expiry */
32 #endif /* MNT_EXPIRE */
42 # ifndef CLONE_NPSPACE
43 # define CLONE_NPSPACE 0x04000000 /* New process space */
45 # ifndef CLONE_NSYSVIPC
46 # define CLONE_NSYSVIPC 0x08000000 /* New sysvipc space */
49 # define CLONE_NHOST 0x10000000 /* New networking host context (lo, hostname, etc) */
52 # define CLONE_NDEV 0x20000000 /* New hash of devices I can touch */
55 # define CLONE_NUSERS 0x40000000 /* New users */
58 # define CLONE_NTIME 0x80000000 /* New time context??? */
60 # ifndef CLONE_NS_NOSUID
61 # define CLONE_NS_NOSUID 0x00001000 /* Force MNT_NOSUID on all mounts in the namespace */
66 #define CLONE_NEWUTS 0x04000000 /* New uts namespace (uname) */
69 #define CLONE_NEWIPC 0x08000000 /* New sysvipc namespace */
72 #define CLONE_NEWUID 0x10000000 /* New users */
75 #define CLONE_NEWNET 0x40000000 /* New network namespace (lo, device, names sockets, etc) */
78 #define CLONE_NEWPID 0x80000000 /* New process space */
81 #define CLONE_NEWTIME 0x40000000 /* New time context??? */
84 #define CLONE_NEWDEV 0x00001000 /* New has of devices I can touch */
88 #ifndef PROC_SUPER_MAGIC
89 #define PROC_SUPER_MAGIC 0x9fa0
90 #endif /* PROC_SUPER_MAGIC */
93 static pid_t raw_clone(int flags, void *child_stack,
94 int *parent_tidptr, struct user_desc *newtls, int *child_tidptr)
96 return syscall(__NR_clone, flags, child_stack, parent_tidptr, newtls, child_tidptr);
99 static int raw_pivot_root(const char *new_root, const char *old_root)
101 return syscall(__NR_pivot_root, new_root, old_root);
104 static void (*my_exit)(int status) = exit;
106 static void die(char *fmt, ...)
110 vfprintf(stderr, fmt, ap);
117 static void *xmalloc(size_t size)
121 if (!ptr) die("malloc of %d bytes failed: %s\n", size, strerror(errno));
125 static void umount_buffer(char *line)
127 char *next, *mnt, *end, *type;
131 next = strchr(line, '\n');
134 mnt = strchr(line, ' ');
136 end = strchr(mnt, ' ');
141 /* For debugging list what I am unounting */
142 write(STDOUT_FILENO, mnt, strlen(mnt));
143 write(STDOUT_FILENO, "\n", 1);
146 if (strcmp(type, "rootfs"))
148 result = umount2(mnt, MNT_DETACH);
150 die("umount of '%s' failed: %d: %s\n",
151 mnt, errno, strerror(errno));
154 static void umount_all(void)
156 static const char mounts[] = "/proc/self/mounts";
158 ssize_t bufsz, bytes;
163 /* Temporarily mount /proc at / so I know I have it */
164 result = mount("proc", "/proc", "proc", 0, NULL);
166 die("mount of proc failed: %d: %s\n",
167 errno, strerror(errno));
170 struct dirent *entry;
172 while((entry = readdir(dir)) != NULL) {
173 write(STDOUT_FILENO, entry->d_name, strlen(entry->d_name));
174 write(STDOUT_FILENO, "\n", 1);
178 fd = open(mounts, O_RDONLY);
180 die("open of %s failed: %d: %s\n",
181 mounts, errno, strerror(errno));
183 /* Read in all of the mount points */
185 buf = xmalloc(bufsz);
186 result = lseek(fd, 0, SEEK_SET);
188 die("lseek failed: %d:%s\n",
189 errno, strerror(errno));
190 bytes = read(fd, buf, bufsz);
192 die("Read of %s failed: %d:%s\n",
193 mounts, errno, strerror(errno));
194 if (bytes != bufsz) {
195 /* Terminate the buffer */
198 /* Free the buffer and try again */
204 /* Be good and close the file descriptor */
207 fprintf(stderr, "close of %s failed: %d:%s\n",
208 mounts, errno, strerror(errno));
211 /* For debugging print the list of mounts */
212 write(STDOUT_FILENO, buf, bufsz);
213 write(STDOUT_FILENO, "\n\n", 2);
219 int main(int argc, char **argv, char **envp)
225 char **cmd_argv, *shell_argv[2];
226 char *root = "/", *old = "/mnt";
227 char *label = "none";
233 clone_flags = SIGCHLD;
235 for (i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
236 if (strcmp(argv[i], "--") == 0) {
239 else if (((argc - i) >= 2) && (strcmp(argv[i], "-r") == 0)) {
240 clone_flags |= CLONE_NEWNS;
244 else if (((argc - i) >= 2) && (strcmp(argv[i], "-o") == 0)) {
248 else if (((argc - i) >= 2) && (strcmp(argv[i], "-l") == 0)) {
249 clone_flags |= CLONE_NEWNS;
253 else if (strcmp(argv[i], "-m") == 0) {
254 clone_flags |= CLONE_NEWNS;
256 else if (strcmp(argv[i], "-h") == 0) {
257 clone_flags |= CLONE_NEWUTS;
259 else if (strcmp(argv[i], "-i") == 0) {
260 clone_flags |= CLONE_NEWIPC;
262 else if (strcmp(argv[i], "-n") == 0) {
263 clone_flags |= CLONE_NEWNET;
265 else if (strcmp(argv[i], "-p") == 0) {
266 clone_flags |= CLONE_NEWPID;
268 else if (strcmp(argv[i], "-u") == 0) {
269 clone_flags |= CLONE_NEWUID;
271 else if (strcmp(argv[i], "-d") == 0) {
272 clone_flags |= CLONE_NEWDEV;
274 else if (strcmp(argv[i], "-t") == 0) {
275 clone_flags |= CLONE_NEWTIME;
277 else if (strcmp(argv[i], "--tty") == 0) {
280 else if (strcmp(argv[i], "--tty-force") == 0) {
281 tty = 1; tty_force = 1;
284 die("Bad argument %s\n", argv[i]);
288 if (cmd_argv[0] == NULL) {
289 cmd_argv = shell_argv;
290 shell_argv[0] = getenv("SHELL");
291 shell_argv[1] = NULL;
293 if (cmd_argv[0] == NULL) {
294 die("No command specified\n");
297 fprintf(stderr, "cmd_argv: %s\n", cmd_argv[0]);
299 if (root[0] != '/') {
300 die("root path: '%s' not absolute\n", root);
303 die("old path: '%s' not absolute\n", old);
307 status = getrlimit(RLIMIT_NPROC, &rlim);
309 fprintf(stderr, "getrlimit RLIMIT_NPROC failed: %s\n",
313 fprintf(stderr, "RLIMIT_NPROC: %llu %llu\n",
314 (unsigned long long)rlim.rlim_cur, (unsigned long long)rlim.rlim_max);
318 status = setrlimit(RLIMIT_NPROC, &rlim);
320 fprintf(stderr, "setrlimit RLIMIT_NPROC %lu %lu failed: %s\n",
321 (unsigned long)rlim.rlim_cur, (unsigned long)rlim.rlim_max,
326 printf("Clone flags: %lx\n", clone_flags);
327 pid = raw_clone(clone_flags, NULL, NULL, NULL, NULL);
329 fprintf(stderr, "clone_failed: pid: %d %d:%s\n",
330 pid, errno, strerror(errno));
338 /* FIXME allocate a process inside for controlling the new process space */
339 if (clone_flags & CLONE_NEWPID) {
340 fprintf(stderr, "pid: %d, ppid: %d pgrp: %d sid: %d\n",
341 getpid(), getppid(), getpgid(0), getsid(0));
342 /* If CLONE_NEWPID isn't implemented exit */
344 die("CLONE_NEWPID not implemented\n");
347 if (clone_flags & CLONE_NEWNS) {
350 if (strcmp(root, "/") != 0) {
351 /* FIXME find a way to remove unreachable filesystems
352 * from the namespace.
354 result = chdir(root);
356 die("chdir to '%s' failed: %d:%s\n",
357 root, errno, strerror(errno));
358 /* Convert the current working directory into a mount point */
359 result = mount(".", ".", NULL, MS_BIND | MS_REC, NULL);
361 die("bind of '%s' failed: %d: %s\n",
362 root, errno, strerror(errno));
363 /* Update the current working directory */
364 result = chdir(root);
366 die("chdir to '%s' failed: %d:%s\n",
367 ".", errno, strerror(errno));
368 result = mount(".", "/", NULL, MS_MOVE, NULL);
370 die("mount of '%s' failed: %d:%s\n",
371 root, errno, strerror(errno));
372 result = chroot(".");
374 die("chroot to '%s' failed: %d:%s\n",
375 root, errno, strerror(errno));
379 if (strcmp(root, "/") != 0) {
380 char put_old[PATH_MAX];
381 result = snprintf(put_old, sizeof(put_old), "%s%s", root, old);
382 if (result >= sizeof(put_old))
383 die("path name to long\n");
385 die("snprintf failed: %d:%s\n",
386 errno, strerror(errno));
388 /* Ensure I have a mount point at the directory I want to export */
389 result = mount(root, root, NULL, MS_BIND | MS_REC, NULL);
391 die("bind of '%s' failed: %d:%s\n",
392 root, errno, strerror(errno));
394 /* Switch the mount points */
395 result = raw_pivot_root(root, put_old);
397 die("pivot_root('%s', '%s') failed: %d:%s\n",
398 root, put_old, errno, strerror(errno));
400 /* Unmount all of the old mounts */
401 result = umount2(old, MNT_DETACH);
403 die("umount2 of '%s' failed: %d:%s\n",
404 put_old, errno, strerror(errno));
408 result = statfs("/proc", &stfs);
409 if ((result == 0) && (stfs.f_type == PROC_SUPER_MAGIC)) {
410 printf ("Umounting proc\n");
411 /* Unmount and remount proc so it reflects the new pid space */
412 result = umount2("/proc", MNT_DETACH);
414 die("umount failed: %d:%s\n", errno, strerror(errno));
416 result = mount(label, "/proc", "proc", 0, NULL);
418 die("mount failed: %d:%s\n",
419 errno, strerror(errno));
426 die("setsid failed: %d:%s\n",
427 errno, strerror(errno));
428 fprintf(stderr, "pid: %d, ppid: %d pgrp: %d sid: %d\n",
429 getpid(), getppid(), getpgid(0), getsid(0));
431 result = ioctl(STDIN_FILENO, TIOCSCTTY, tty_force);
433 die("tiocsctty failed: %d:%s\n",
434 errno, strerror(errno));
436 pgrp = tcgetpgrp(STDIN_FILENO);
438 fprintf(stderr, "pgrp: %d\n", pgrp);
440 fprintf(stderr, "pid: %d, ppid: %d pgrp: %d sid: %d\n",
441 getpid(), getppid(), getpgid(0), getsid(0));
444 result = execve(cmd_argv[0], cmd_argv, envp);
445 die("execve of %s failed: %d:%s\n",
446 cmd_argv[0], errno, strerror(errno));
449 fprintf(stderr, "child pid: %d\n", pid);
450 pid = waitpid(pid, &status, 0);
451 fprintf(stderr, "pid: %d exited status: %d\n",
454 fprintf(stderr, "waitpid failed: %d %s\n",
455 errno, strerror(errno));
459 fprintf(stderr, "waitpid returned no pid!\n");
462 if (WIFEXITED(status)) {
463 fprintf(stderr, "pid: %d exited: %d\n",
464 pid, WEXITSTATUS(status));
466 if (WIFSIGNALED(status)) {
467 fprintf(stderr, "pid: %d exited with a uncaught signal: %d %s\n",
468 pid, WTERMSIG(status), strsignal(WTERMSIG(status)));
470 if (WIFSTOPPED(status)) {
471 fprintf(stderr, "pid: %d stopped with signal: %d\n",
472 pid, WSTOPSIG(status));