blob: 42a9cf6e7e682c50da7109d6bd618ba33555047a [file] [log] [blame]
carlosmscabralf40ecd12013-02-01 18:15:58 -02001/* 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 a network namespace
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#include <stdio.h>
17#include <linux/sched.h>
18#include <unistd.h>
19#include <limits.h>
20#include <syscall.h>
21#include <fcntl.h>
22#include <stdlib.h>
23#include <limits.h>
24#include <sched.h>
25
26#if !defined(VERSION)
27#define VERSION "(devel)"
28#endif
29
30void usage(char *name)
31{
32 printf("Execution utility for Mininet\n\n"
33 "Usage: %s [-cdnp] [-a pid] [-g group] [-r rtprio] cmd args...\n\n"
34 "Options:\n"
35 " -c: close all file descriptors except stdin/out/error\n"
36 " -d: detach from tty by calling setsid()\n"
37 " -n: run in new network namespace\n"
38 " -p: print ^A + pid\n"
39 " -a pid: attach to pid's network namespace\n"
40 " -g group: add to cgroup\n"
41 " -r rtprio: run with SCHED_RR (usually requires -g)\n"
42 " -v: print version\n",
43 name);
44}
45
46
47int setns(int fd, int nstype)
48{
49 return syscall(308, fd, nstype);
50}
51
52/* Validate alphanumeric path foo1/bar2/baz */
53void validate(char *path)
54{
55 char *s;
56 for (s=path; *s; s++) {
57 if (!isalnum(*s) && *s != '/') {
58 fprintf(stderr, "invalid path: %s\n", path);
59 exit(1);
60 }
61 }
62}
63
64/* Add our pid to cgroup */
65int cgroup(char *gname)
66{
67 static char path[PATH_MAX];
68 static char *groups[] = {
69 "cpu", "cpuacct", "cpuset", NULL
70 };
71 char **gptr;
72 pid_t pid = getpid();
73 int count = 0;
74 validate(gname);
75 for (gptr = groups; *gptr; gptr++) {
76 FILE *f;
77 snprintf(path, PATH_MAX, "/sys/fs/cgroup/%s/%s/tasks",
78 *gptr, gname);
79 f = fopen(path, "w");
80 if (f) {
81 count++;
82 fprintf(f, "%d\n", pid);
83 fclose(f);
84 }
85 }
86 if (!count) {
87 fprintf(stderr, "cgroup: could not add to cgroup %s\n",
88 gname);
89 exit(1);
90 }
91}
92
93int main(int argc, char *argv[])
94{
95 char c;
96 int fd;
97 char path[PATH_MAX];
98 int nsid;
99 int pid;
100 static struct sched_param sp;
101 while ((c = getopt(argc, argv, "+cdnpa:g:r:vh")) != -1)
102 switch(c) {
103 case 'c':
104 /* close file descriptors except stdin/out/error */
105 for (fd = getdtablesize(); fd > 2; fd--)
106 close(fd);
107 break;
108 case 'd':
109 /* detach from tty */
110 if (getpgrp() == getpid()) {
111 switch(fork()) {
112 case -1:
113 perror("fork");
114 return 1;
115 case 0: /* child */
116 break;
117 default: /* parent */
118 return 0;
119 }
120 }
121 setsid();
122 break;
123 case 'n':
124 /* run in network namespace */
125 if (unshare(CLONE_NEWNET) == -1) {
126 perror("unshare");
127 return 1;
128 }
129 break;
130 case 'p':
131 /* print pid */
132 printf("\001%d\n", getpid());
133 fflush(stdout);
134 break;
135 case 'a':
136 /* Attach to pid's network namespace */
137 pid = atoi(optarg);
138 sprintf(path, "/proc/%d/ns/net", pid );
139 nsid = open(path, O_RDONLY);
140 if (nsid < 0) {
141 perror(path);
142 return 1;
143 }
144 if (setns(nsid, 0) != 0) {
145 perror("setns");
146 return 1;
147 }
148 break;
149 case 'g':
150 /* Attach to cgroup */
151 cgroup(optarg);
152 break;
153 case 'r':
154 /* Set RT scheduling priority */
155 sp.sched_priority = atoi(optarg);
156 if (sched_setscheduler(getpid(), SCHED_RR, &sp) < 0) {
157 perror("sched_setscheduler");
158 return 1;
159 }
160 break;
161 case 'v':
162 printf("%s\n", VERSION);
163 exit(0);
164 case 'h':
165 usage(argv[0]);
166 exit(0);
167 default:
168 usage(argv[0]);
169 exit(1);
170 }
171
172 if (optind < argc) {
173 execvp(argv[optind], &argv[optind]);
174 perror(argv[optind]);
175 return 1;
176 }
177
178 usage(argv[0]);
179}