1 /* mnexec: execution utility for mininet
3 * Starts up programs and does things that are slow or
4 * difficult in Python, including:
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
13 * Partially based on public domain setsid(1)
18 #include <linux/sched.h>
26 #include <sys/mount.h>
29 #define VERSION "(devel)"
32 void usage(char *name)
34 printf("Execution utility for Mininet\n\n"
35 "Usage: %s [-cdnp] [-a pid] [-g group] [-r rtprio] cmd args...\n\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",
49 int setns(int fd, int nstype)
51 return syscall(__NR_setns, fd, nstype);
54 /* Validate alphanumeric path foo1/bar2/baz */
55 void validate(char *path)
58 for (s=path; *s; s++) {
59 if (!isalnum(*s) && *s != '/') {
60 fprintf(stderr, "invalid path: %s\n", path);
66 /* Add our pid to cgroup */
67 void cgroup(char *gname)
69 static char path[PATH_MAX];
70 static char *groups[] = {
71 "cpu", "cpuacct", "cpuset", NULL
77 for (gptr = groups; *gptr; gptr++) {
79 snprintf(path, PATH_MAX, "/sys/fs/cgroup/%s/%s/tasks",
84 fprintf(f, "%d\n", pid);
89 fprintf(stderr, "cgroup: could not add to cgroup %s\n",
95 int main(int argc, char *argv[])
102 char *cwd = get_current_dir_name();
104 static struct sched_param sp;
105 while ((c = getopt(argc, argv, "+cdnpa:g:r:vh")) != -1)
108 /* close file descriptors except stdin/out/error */
109 for (fd = getdtablesize(); fd > 2; fd--)
113 /* detach from tty */
114 if (getpgrp() == getpid()) {
121 default: /* parent */
128 /* run in network and mount namespaces */
129 if (unshare(CLONE_NEWNET|CLONE_NEWNS) == -1) {
134 /* Mark our whole hierarchy recursively as private, so that our
135 * mounts do not propagate to other processes.
138 if (mount("none", "/", NULL, MS_REC|MS_PRIVATE, NULL) == -1) {
143 /* mount sysfs to pick up the new network namespace */
144 if (mount("sysfs", "/sys", "sysfs", MS_MGC_VAL, NULL) == -1) {
151 printf("\001%d\n", getpid());
155 /* Attach to pid's network namespace and mount namespace */
157 sprintf(path, "/proc/%d/ns/net", pid);
158 nsid = open(path, O_RDONLY);
163 if (setns(nsid, 0) != 0) {
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) {
178 /* chdir to correct working directory */
179 if (chdir(cwd) != 0) {
185 /* Attach to cgroup */
189 /* Set RT scheduling priority */
190 sp.sched_priority = atoi(optarg);
191 if (sched_setscheduler(getpid(), SCHED_RR, &sp) < 0) {
192 perror("sched_setscheduler");
197 printf("%s\n", VERSION);
208 execvp(argv[optind], &argv[optind]);
209 perror(argv[optind]);