Update and rename MantenerFIFO to MantenerFIFO.md
[vsorcdistro/.git] / mininet / mnexec.c
1 /* mnexec: execution utility for mininet
2  *
3  * Starts up programs and does things that are slow or
4  * difficult in Python, including:
5  *
6  *  - closing all file descriptors except stdin/out/error
7  *  - detaching from a controlling tty using setsid
8  *  - running in network and mount namespaces
9  *  - printing out the pid of a process so we can identify it later
10  *  - attaching to a namespace and cgroup
11  *  - setting RT scheduling
12  *
13  * Partially based on public domain setsid(1)
14 */
15
16 #define _GNU_SOURCE
17 #include <stdio.h>
18 #include <linux/sched.h>
19 #include <unistd.h>
20 #include <limits.h>
21 #include <syscall.h>
22 #include <fcntl.h>
23 #include <stdlib.h>
24 #include <sched.h>
25 #include <ctype.h>
26 #include <sys/mount.h>
27
28 #if !defined(VERSION)
29 #define VERSION "(devel)"
30 #endif
31
32 void usage(char *name)
33 {
34     printf("Execution utility for Mininet\n\n"
35            "Usage: %s [-cdnp] [-a pid] [-g group] [-r rtprio] cmd args...\n\n"
36            "Options:\n"
37            "  -c: close all file descriptors except stdin/out/error\n"
38            "  -d: detach from tty by calling setsid()\n"
39            "  -n: run in new network and mount namespaces\n"
40            "  -p: print ^A + pid\n"
41            "  -a pid: attach to pid's network and mount namespaces\n"
42            "  -g group: add to cgroup\n"
43            "  -r rtprio: run with SCHED_RR (usually requires -g)\n"
44            "  -v: print version\n",
45            name);
46 }
47
48
49 int setns(int fd, int nstype)
50 {
51     return syscall(__NR_setns, fd, nstype);
52 }
53
54 /* Validate alphanumeric path foo1/bar2/baz */
55 void validate(char *path)
56 {
57     char *s;
58     for (s=path; *s; s++) {
59         if (!isalnum(*s) && *s != '/') {
60             fprintf(stderr, "invalid path: %s\n", path);
61             exit(1);
62         }
63     }
64 }
65
66 /* Add our pid to cgroup */
67 void cgroup(char *gname)
68 {
69     static char path[PATH_MAX];
70     static char *groups[] = {
71         "cpu", "cpuacct", "cpuset", NULL
72     };
73     char **gptr;
74     pid_t pid = getpid();
75     int count = 0;
76     validate(gname);
77     for (gptr = groups; *gptr; gptr++) {
78         FILE *f;
79         snprintf(path, PATH_MAX, "/sys/fs/cgroup/%s/%s/tasks",
80                  *gptr, gname);
81         f = fopen(path, "w");
82         if (f) {
83             count++;
84             fprintf(f, "%d\n", pid);
85             fclose(f);
86         }
87     }
88     if (!count) {
89         fprintf(stderr, "cgroup: could not add to cgroup %s\n",
90             gname);
91         exit(1);
92     }
93 }
94
95 int main(int argc, char *argv[])
96 {
97     int c;
98     int fd;
99     char path[PATH_MAX];
100     int nsid;
101     int pid;
102     char *cwd = get_current_dir_name();
103
104     static struct sched_param sp;
105     while ((c = getopt(argc, argv, "+cdnpa:g:r:vh")) != -1)
106         switch(c) {
107         case 'c':
108             /* close file descriptors except stdin/out/error */
109             for (fd = getdtablesize(); fd > 2; fd--)
110                 close(fd);
111             break;
112         case 'd':
113             /* detach from tty */
114             if (getpgrp() == getpid()) {
115                 switch(fork()) {
116                     case -1:
117                         perror("fork");
118                         return 1;
119                     case 0:     /* child */
120                         break;
121                     default:    /* parent */
122                         return 0;
123                 }
124             }
125             setsid();
126             break;
127         case 'n':
128             /* run in network and mount namespaces */
129             if (unshare(CLONE_NEWNET|CLONE_NEWNS) == -1) {
130                 perror("unshare");
131                 return 1;
132             }
133
134             /* Mark our whole hierarchy recursively as private, so that our
135              * mounts do not propagate to other processes.
136              */
137
138             if (mount("none", "/", NULL, MS_REC|MS_PRIVATE, NULL) == -1) {
139                 perror("remount");
140                 return 1;
141             }
142
143             /* mount sysfs to pick up the new network namespace */
144             if (mount("sysfs", "/sys", "sysfs", MS_MGC_VAL, NULL) == -1) {
145                 perror("mount");
146                 return 1;
147             }
148             break;
149         case 'p':
150             /* print pid */
151             printf("\001%d\n", getpid());
152             fflush(stdout);
153             break;
154         case 'a':
155             /* Attach to pid's network namespace and mount namespace */
156             pid = atoi(optarg);
157             sprintf(path, "/proc/%d/ns/net", pid);
158             nsid = open(path, O_RDONLY);
159             if (nsid < 0) {
160                 perror(path);
161                 return 1;
162             }
163             if (setns(nsid, 0) != 0) {
164                 perror("setns");
165                 return 1;
166             }
167             /* Plan A: call setns() to attach to mount namespace */
168             sprintf(path, "/proc/%d/ns/mnt", pid);
169             nsid = open(path, O_RDONLY);
170             if (nsid < 0 || setns(nsid, 0) != 0) {
171                 /* Plan B: chroot/chdir into pid's root file system */
172                 sprintf(path, "/proc/%d/root", pid);
173                 if (chroot(path) < 0) {
174                     perror(path);
175                     return 1;
176                 }
177             }
178             /* chdir to correct working directory */
179             if (chdir(cwd) != 0) {
180                 perror(cwd);
181                 return 1;
182             }
183             break;
184         case 'g':
185             /* Attach to cgroup */
186             cgroup(optarg);
187             break;
188         case 'r':
189             /* Set RT scheduling priority */
190             sp.sched_priority = atoi(optarg);
191             if (sched_setscheduler(getpid(), SCHED_RR, &sp) < 0) {
192                 perror("sched_setscheduler");
193                 return 1;
194             }
195             break;
196         case 'v':
197             printf("%s\n", VERSION);
198             exit(0);
199         case 'h':
200             usage(argv[0]);
201             exit(0);
202         default:
203             usage(argv[0]);
204             exit(1);
205         }
206
207     if (optind < argc) {
208         execvp(argv[optind], &argv[optind]);
209         perror(argv[optind]);
210         return 1;
211     }
212
213     usage(argv[0]);
214
215     return 0;
216 }