Statistics
| Branch: | Tag: | Revision:

mininet / mnexec.c @ 9c3ecfe3

History | View | Annotate | Download (5.23 KB)

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
    static struct sched_param sp;
103
    while ((c = getopt(argc, argv, "+cdnpa:g:r:vh")) != -1)
104
        switch(c) {
105
        case 'c':
106
            /* close file descriptors except stdin/out/error */
107
            for (fd = getdtablesize(); fd > 2; fd--)
108
                close(fd);
109
            break;
110
        case 'd':
111
            /* detach from tty */
112
            if (getpgrp() == getpid()) {
113
                switch(fork()) {
114
                    case -1:
115
                        perror("fork");
116
                        return 1;
117
                    case 0:     /* child */
118
                        break;
119
                    default:    /* parent */
120
                        return 0;
121
                }
122
            }
123
            setsid();
124
            break;
125
        case 'n':
126
            /* run in network and mount namespaces */
127
            if (unshare(CLONE_NEWNET|CLONE_NEWNS) == -1) {
128
                perror("unshare");
129
                return 1;
130
            }
131
            /* mount sysfs to pick up the new network namespace */
132
            if (mount("sysfs", "/sys", "sysfs", MS_MGC_VAL, NULL) == -1) {
133
                perror("mount");
134
                return 1;
135
            }
136
            break;
137
        case 'p':
138
            /* print pid */
139
            printf("\001%d\n", getpid());
140
            fflush(stdout);
141
            break;
142
        case 'a':
143
            /* Attach to pid's network namespace and mount namespace*/
144
            pid = atoi(optarg);
145
            sprintf(path, "/proc/%d/ns/net", pid );
146
            nsid = open(path, O_RDONLY);
147
            if (nsid < 0) {
148
                perror(path);
149
                return 1;
150
            }
151
            if (setns(nsid, 0) != 0) {
152
                perror("setns");
153
                return 1;
154
            }
155
            sprintf(path, "/proc/%d/ns/mnt", pid );
156
            nsid = open(path, O_RDONLY);
157
            if (nsid < 0) {
158
                perror(path);
159
                return 1;
160
            }
161
            if (setns(nsid, 0) != 0) {
162
                perror("setns");
163
                return 1;
164
            }
165
            break;
166
        case 'g':
167
            /* Attach to cgroup */
168
            cgroup(optarg);
169
            break;
170
        case 'r':
171
            /* Set RT scheduling priority */
172
            sp.sched_priority = atoi(optarg);
173
            if (sched_setscheduler(getpid(), SCHED_RR, &sp) < 0) {
174
                perror("sched_setscheduler");
175
                return 1;
176
            }
177
            break;
178
        case 'v':
179
            printf("%s\n", VERSION);
180
            exit(0);
181
        case 'h':
182
            usage(argv[0]);
183
            exit(0);
184
        default:
185
            usage(argv[0]);
186
            exit(1); 
187
        }
188

    
189
    if (optind < argc) {
190
        execvp(argv[optind], &argv[optind]);
191
        perror(argv[optind]);
192
        return 1;
193
    }
194
    
195
    usage(argv[0]);
196

    
197
    return 0;
198
}