Reset to the original
[linux-2.6.git] / chpid.c
1 /* gcc -Wall -O2 -g chpid.c -o chpid */
2 #define _XOPEN_SOURCE
3 #define _XOPEN_SOURCE_EXTENDED
4 #define _SVID_SOURCE
5 #define _GNU_SOURCE
6 #include <stdio.h>
7 #include <errno.h>
8 #include <string.h>
9 #include <stdlib.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <sys/syscall.h>
13 #include <sys/wait.h>
14 #include <sys/time.h>
15 #include <sys/resource.h>
16 #include <sys/mount.h>
17 #include <sys/vfs.h>
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <sched.h>
21 #include <stdarg.h>
22 #include <dirent.h>
23
24 #ifndef MNT_FORCE
25 #define MNT_FORCE       0x00000001      /* Attempt to forcibily umount */
26 #endif /* MNT_FORCE */
27 #ifndef MNT_DETACH
28 #define MNT_DETACH      0x00000002      /* Just detach from the tree */
29 #endif /* MNT_DETACH */
30 #ifndef MNT_EXPIRE
31 #define MNT_EXPIRE      0x00000004      /* Mark for expiry */
32 #endif /* MNT_EXPIRE */
33
34 #ifndef MS_MOVE
35 #define MS_MOVE 8192
36 #endif
37 #ifndef MS_REC
38 #define MS_REC 16384
39 #endif
40
41 #ifdef OLD
42 # ifndef CLONE_NPSPACE
43 # define CLONE_NPSPACE  0x04000000      /* New process space */
44 # endif
45 # ifndef CLONE_NSYSVIPC
46 # define CLONE_NSYSVIPC 0x08000000      /* New sysvipc space */
47 # endif
48 # ifndef CLONE_NHOST
49 # define CLONE_NHOST    0x10000000      /* New networking host context (lo, hostname, etc) */
50 # endif
51 # ifndef CLONE_NDEV
52 # define CLONE_NDEV     0x20000000      /* New hash of devices I can touch */
53 # endif
54 # ifndef CLONE_NUSERS
55 # define CLONE_NUSERS   0x40000000      /* New users */
56 # endif
57 # ifndef CLONE_NTIME
58 # define CLONE_NTIME    0x80000000      /* New time context??? */
59 # endif
60 # ifndef CLONE_NS_NOSUID
61 # define CLONE_NS_NOSUID        0x00001000      /* Force MNT_NOSUID on all mounts in the namespace */
62 # endif
63 #endif
64
65 #ifndef CLONE_NEWUTS
66 #define CLONE_NEWUTS    0x04000000      /* New uts namespace (uname) */
67 #endif
68 #ifndef CLONE_NEWIPC
69 #define CLONE_NEWIPC    0x08000000      /* New sysvipc namespace */
70 #endif
71 #ifndef CLONE_NEWUID
72 #define CLONE_NEWUID    0x10000000      /* New users */
73 #endif
74 #ifndef CLONE_NEWNET
75 #define CLONE_NEWNET    0x40000000      /* New network namespace (lo, device, names sockets, etc) */
76 #endif
77 #ifndef CLONE_NEWPID
78 #define CLONE_NEWPID    0x80000000      /* New process space */
79 #endif
80 #ifndef CLONE_NEWTIME
81 #define CLONE_NEWTIME   0x40000000      /* New time context??? */
82 #endif
83 #ifndef CLONE_NEWDEV
84 #define CLONE_NEWDEV    0x00001000      /* New has of devices I can touch */
85 #endif
86
87
88 #ifndef PROC_SUPER_MAGIC
89 #define PROC_SUPER_MAGIC 0x9fa0
90 #endif /* PROC_SUPER_MAGIC */
91
92 struct user_desc;
93 static pid_t raw_clone(int flags, void *child_stack,
94        int *parent_tidptr, struct user_desc *newtls, int *child_tidptr)
95 {
96        return syscall(__NR_clone, flags, child_stack, parent_tidptr, newtls, child_tidptr);
97 }
98
99 static int raw_pivot_root(const char *new_root, const char *old_root)
100 {
101        return syscall(__NR_pivot_root, new_root, old_root);
102 }
103
104 static void (*my_exit)(int status) = exit;
105
106 static void die(char *fmt, ...)
107 {
108        va_list ap;
109        va_start(ap, fmt);
110        vfprintf(stderr, fmt, ap);
111        va_end(ap);
112        fflush(stderr);
113        fflush(stdout);
114        my_exit(1);
115 }
116
117 static void *xmalloc(size_t size)
118 {
119        void *ptr;
120        ptr = malloc(size);
121        if (!ptr) die("malloc of %d bytes failed: %s\n", size, strerror(errno));
122        return ptr;
123 }
124
125 static void umount_buffer(char *line)
126 {
127        char *next, *mnt, *end, *type;
128        int result;
129        if (!line || !*line)
130                return;
131        next = strchr(line, '\n');
132        *next++ = '0';
133        type = line;
134        mnt = strchr(line, ' ');
135        *mnt++ = '\0';
136        end = strchr(mnt, ' ');
137        *end = '\0';
138        umount_buffer(next);
139
140 #if 0
141        /* For debugging list what I am unounting */
142        write(STDOUT_FILENO, mnt, strlen(mnt));
143        write(STDOUT_FILENO, "\n", 1);
144 #endif
145
146        if (strcmp(type, "rootfs"))
147                return;
148        result = umount2(mnt, MNT_DETACH);
149        if (result < 0)
150                die("umount of '%s' failed: %d: %s\n",
151                        mnt, errno, strerror(errno));
152 }
153
154 static void umount_all(void)
155 {
156        static const char mounts[] = "/proc/self/mounts";
157        char *buf;
158        ssize_t bufsz, bytes;
159        int fd, result;
160        buf = NULL;
161        bufsz = 4096;
162
163        /* Temporarily mount /proc at / so I know I have it */
164        result = mount("proc", "/proc", "proc", 0, NULL);
165        if (result < 0)
166                die("mount of proc failed: %d: %s\n",
167                        errno, strerror(errno));
168 #if 0
169        DIR *dir;
170        struct dirent *entry;
171        dir = opendir("/");
172        while((entry = readdir(dir)) != NULL) {
173                write(STDOUT_FILENO, entry->d_name, strlen(entry->d_name));
174                write(STDOUT_FILENO, "\n", 1);
175        }
176        closedir(dir);
177 #endif
178        fd = open(mounts, O_RDONLY);
179        if (fd < 0)
180                die("open of %s failed: %d: %s\n",
181                        mounts, errno, strerror(errno));
182
183        /* Read in all of the mount points */
184        do {
185                buf = xmalloc(bufsz);
186                result = lseek(fd, 0, SEEK_SET);
187                if (result != 0)
188                        die("lseek failed: %d:%s\n",
189                                errno, strerror(errno));
190                bytes = read(fd, buf, bufsz);
191                if (bytes < 0)
192                        die("Read of %s failed: %d:%s\n",
193                                mounts, errno, strerror(errno));
194                if (bytes != bufsz) {
195                        /* Terminate the buffer */
196                        buf[bytes] = '\0';
197                } else {
198                        /* Free the buffer and try again */
199                        free(buf);
200                        buf = NULL;
201                        bufsz <<= 1;
202                }
203        } while(!buf);
204        /* Be good and close the file descriptor */
205        result = close(fd);
206        if (result < 0)
207                fprintf(stderr, "close of %s failed: %d:%s\n",
208                        mounts, errno, strerror(errno));
209
210 #if 0
211        /* For debugging print the list of mounts */
212        write(STDOUT_FILENO, buf, bufsz);
213        write(STDOUT_FILENO, "\n\n", 2);
214 #endif
215
216        umount_buffer(buf);
217 }
218
219 int main(int argc, char **argv, char **envp)
220 {
221        pid_t pid;
222        int status;
223        struct rlimit rlim;
224        int clone_flags;
225        char **cmd_argv, *shell_argv[2];
226        char *root = "/", *old = "/mnt";
227        char *label = "none";
228        int i;
229        int tty, tty_force;
230
231        tty = 0;
232        tty_force = 0;
233        clone_flags = SIGCHLD;
234
235        for (i = 1; (i < argc) && (argv[i][0] == '-'); i++) {
236                if (strcmp(argv[i], "--") == 0) {
237                        break;
238                }
239                else if (((argc - i) >= 2) && (strcmp(argv[i], "-r") == 0)) {
240                        clone_flags |= CLONE_NEWNS;
241                        root = argv[i + 1];
242                        i++;
243                }
244                else if (((argc - i) >= 2) && (strcmp(argv[i], "-o") == 0)) {
245                        old = argv[i + 1];
246                        i++;
247                }
248                else if (((argc - i) >= 2) && (strcmp(argv[i], "-l") == 0)) {
249                        clone_flags |= CLONE_NEWNS;
250                        label = argv[i + 1];
251                        i++;
252                }
253                else if (strcmp(argv[i], "-m") == 0) {
254                        clone_flags |= CLONE_NEWNS;
255                }
256                else if (strcmp(argv[i], "-h") == 0) {
257                        clone_flags |= CLONE_NEWUTS;
258                }
259                else if (strcmp(argv[i], "-i") == 0) {
260                        clone_flags |= CLONE_NEWIPC;
261                }
262                else if (strcmp(argv[i], "-n") == 0) {
263                        clone_flags |= CLONE_NEWNET;
264                }
265                else if (strcmp(argv[i], "-p") == 0) {
266                        clone_flags |= CLONE_NEWPID;
267                }
268                else if (strcmp(argv[i], "-u") == 0) {
269                        clone_flags |= CLONE_NEWUID;
270                }
271                else if (strcmp(argv[i], "-d") == 0) {
272                        clone_flags |= CLONE_NEWDEV;
273                }
274                else if (strcmp(argv[i], "-t") == 0) {
275                        clone_flags |= CLONE_NEWTIME;
276                }
277                else if (strcmp(argv[i], "--tty") == 0) {
278                        tty = 1;
279                }
280                else if (strcmp(argv[i], "--tty-force") == 0) {
281                        tty = 1; tty_force = 1;
282                }
283                else {
284                        die("Bad argument %s\n", argv[i]);
285                }
286        }
287        cmd_argv = argv + i;
288        if (cmd_argv[0] == NULL) {
289                cmd_argv = shell_argv;
290                shell_argv[0] = getenv("SHELL");
291                shell_argv[1] = NULL;
292        }
293        if (cmd_argv[0] == NULL) {
294                die("No command specified\n");
295        }
296 #if 1
297        fprintf(stderr, "cmd_argv: %s\n", cmd_argv[0]);
298 #endif
299        if (root[0] != '/') {
300                die("root path: '%s' not absolute\n", root);
301        }
302        if (old[0] != '/') {
303                die("old path: '%s' not absolute\n", old);
304        }
305
306 #if 0
307        status = getrlimit(RLIMIT_NPROC, &rlim);
308        if (status < 0) {
309                fprintf(stderr, "getrlimit RLIMIT_NPROC failed: %s\n",
310                        strerror(errno));
311                exit(1);
312        }
313        fprintf(stderr, "RLIMIT_NPROC: %llu %llu\n",
314                (unsigned long long)rlim.rlim_cur, (unsigned long long)rlim.rlim_max);
315 #endif
316 #if 0
317        rlim.rlim_cur = 256;
318        status = setrlimit(RLIMIT_NPROC, &rlim);
319        if (status < 0) {
320                fprintf(stderr, "setrlimit RLIMIT_NPROC %lu %lu failed: %s\n",
321                        (unsigned long)rlim.rlim_cur, (unsigned long)rlim.rlim_max,
322                        strerror(errno));
323                exit(2);
324        }
325 #endif
326        printf("Clone flags: %lx\n", clone_flags);
327        pid = raw_clone(clone_flags, NULL, NULL, NULL, NULL);
328        if (pid < 0) {
329                fprintf(stderr, "clone_failed: pid: %d %d:%s\n",
330                        pid, errno, strerror(errno));
331                exit(2);
332        }
333        if (pid == 0) {
334                /* In the child */
335                int result;
336                my_exit = _exit;
337
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 */
343                        if (getpid() != 1)
344                                die("CLONE_NEWPID not implemented\n");
345                }
346
347                if (clone_flags & CLONE_NEWNS) {
348                        struct statfs stfs;
349 #if 0
350                        if (strcmp(root, "/") != 0) {
351                                /* FIXME find a way to remove unreachable filesystems
352                                 * from the namespace.
353                                 */
354                                result = chdir(root);
355                                if (result < 0)
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);
360                                if (result < 0)
361                                        die("bind of '%s' failed: %d: %s\n",
362                                                root, errno, strerror(errno));
363                                /* Update the current working directory */
364                                result = chdir(root);
365                                if (result < 0)
366                                        die("chdir to '%s' failed: %d:%s\n",
367                                                ".", errno, strerror(errno));
368                                result = mount(".", "/", NULL, MS_MOVE, NULL);
369                                if (result < 0)
370                                        die("mount of '%s' failed: %d:%s\n",
371                                                root, errno, strerror(errno));
372                                result = chroot(".");
373                                if (result < 0)
374                                        die("chroot to '%s' failed: %d:%s\n",
375                                                root, errno, strerror(errno));
376                        }
377 #endif
378 #if 1
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");
384                                if (result < 0)
385                                        die("snprintf failed: %d:%s\n",
386                                                errno, strerror(errno));
387
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);
390                                if (result < 0)
391                                        die("bind of '%s' failed: %d:%s\n",
392                                                root, errno, strerror(errno));
393
394                                /* Switch the mount points */
395                                result = raw_pivot_root(root, put_old);
396                                if (result < 0)
397                                        die("pivot_root('%s', '%s') failed: %d:%s\n",
398                                                root, put_old, errno, strerror(errno));
399
400                                /* Unmount all of the old mounts */
401                                result = umount2(old, MNT_DETACH);
402                                if (result < 0)
403                                        die("umount2 of '%s' failed: %d:%s\n",
404                                                put_old, errno, strerror(errno));
405                        }
406 #endif
407
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);
413                                if (result < 0)
414                                        die("umount failed: %d:%s\n", errno, strerror(errno));
415
416                                result = mount(label, "/proc", "proc", 0, NULL);
417                                if (result < 0)
418                                        die("mount failed: %d:%s\n",
419                                                errno, strerror(errno));
420                        }
421                }
422                if (tty) {
423                        pid_t sid, pgrp;
424                        sid = setsid();
425                        if (sid < 0)
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));
430
431                        result = ioctl(STDIN_FILENO, TIOCSCTTY, tty_force);
432                        if (result < 0)
433                                die("tiocsctty failed: %d:%s\n",
434                                        errno, strerror(errno));
435
436                        pgrp = tcgetpgrp(STDIN_FILENO);
437
438                        fprintf(stderr, "pgrp: %d\n", pgrp);
439
440                        fprintf(stderr, "pid: %d, ppid: %d pgrp: %d sid: %d\n",
441                                getpid(), getppid(), getpgid(0), getsid(0));
442
443                }
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));
447        }
448        /* In the parent */
449        fprintf(stderr, "child pid: %d\n", pid);
450        pid = waitpid(pid, &status, 0);
451        fprintf(stderr, "pid: %d exited status: %d\n",
452                pid, status);
453        if (pid < 0) {
454                fprintf(stderr, "waitpid failed: %d %s\n",
455                        errno, strerror(errno));
456                exit(9);
457        }
458        if (pid == 0) {
459                fprintf(stderr, "waitpid returned no pid!\n");
460                exit(10);
461        }
462        if (WIFEXITED(status)) {
463                fprintf(stderr, "pid: %d exited: %d\n",
464                        pid, WEXITSTATUS(status));
465        }
466        if (WIFSIGNALED(status)) {
467                fprintf(stderr, "pid: %d exited with a uncaught signal: %d %s\n",
468                        pid, WTERMSIG(status), strsignal(WTERMSIG(status)));
469        }
470        if (WIFSTOPPED(status)) {
471                fprintf(stderr, "pid: %d stopped with signal: %d\n",
472                        pid, WSTOPSIG(status));
473        }
474        return 0;
475 }