]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - tools/tools/vimage/vimage.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / tools / tools / vimage / vimage.c
1 /*
2  * Copyright (c) 2002-2004 Marko Zec <zec@fer.hr>
3  * Copyright (c) 2009 University of Zagreb
4  * Copyright (c) 2009 FreeBSD Foundation
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD$
28  */
29
30 #include <sys/param.h>
31 #include <sys/ioctl.h>
32 #include <sys/jail.h>
33 #include <sys/socket.h>
34
35 #include <net/if.h>
36
37 #include <ctype.h>
38 #include <jail.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43
44 typedef enum {
45         VI_SWITCHTO,
46         VI_CREATE,
47         VI_MODIFY,
48         VI_DESTROY,
49         VI_IFMOVE,
50         VI_GET
51 } vi_cmd_t;
52
53 typedef struct vimage_status {
54         char name[MAXPATHLEN];          /* Must be first field for strcmp(). */
55         char path[MAXPATHLEN];
56         char hostname[MAXPATHLEN];
57         char domainname[MAXPATHLEN];
58         int jid;
59         int parentjid;
60         int vnet;
61         int childcnt;
62         int childmax;
63         int cpuset;
64         int rawsock;
65         int socket_af;
66         int mount;
67 } vstat_t;
68
69 #define VST_SIZE_STEP   1024
70 #define MAXPARAMS       32
71
72 static int getjail(vstat_t *, int, int);
73
74 static char *invocname;
75
76 static void
77 usage(void)
78 {
79
80         fprintf(stderr,
81             "usage: %s [-c | -m] vname [param=value ...]\n"
82             "       %s -d vname\n"
83             "       %s -l[rvj] [vname]\n"
84             "       %s -i vname ifname [newifname]\n"
85             "       %s vname [command ...]\n",
86             invocname, invocname, invocname, invocname, invocname);
87         exit(1);
88 }
89
90 int
91 main(int argc, char **argv)
92 {
93         struct jailparam params[MAXPARAMS];
94         char ifname[IFNAMSIZ];
95         struct ifreq ifreq;
96         vi_cmd_t newcmd, cmd;
97         int recurse = 0;
98         int verbose = 0;
99         int jid, i, s, namelen;
100         int vst_size, vst_last;
101         vstat_t *vst;
102         char *str;
103         char ch;
104
105         invocname = argv[0];
106
107         newcmd = cmd = VI_SWITCHTO; /* Default if no modifiers specified. */
108         while ((ch = getopt(argc, argv, "cdijlmrv")) != -1) {
109                 switch (ch) {
110                 case 'c':
111                         newcmd = VI_CREATE;
112                         break;
113                 case 'm':
114                         newcmd = VI_MODIFY;
115                         break;
116                 case 'd':
117                         newcmd = VI_DESTROY;
118                         break;
119                 case 'l':
120                         newcmd = VI_GET;
121                         break;
122                 case 'i':
123                         newcmd = VI_IFMOVE;
124                         break;
125                 case 'r':
126                         recurse = 1;
127                         break;
128                 case 'v':
129                         verbose++;
130                         break;
131                 case 'j':
132                         verbose = 2;
133                         break;
134                 default:
135                         usage();
136                 }
137                 if (cmd == VI_SWITCHTO || cmd == newcmd)
138                         cmd = newcmd;
139                 else
140                         usage();
141         }
142         argc -= optind;
143         argv += optind;
144
145         if ((cmd != VI_GET && (argc == 0 || recurse != 0 || verbose != 0)) ||
146             (cmd == VI_IFMOVE && (argc < 2 || argc > 3)) ||
147             (cmd == VI_MODIFY && argc < 2) || argc >= MAXPARAMS)
148                 usage();
149
150         switch (cmd) {
151         case VI_GET:
152                 vst_last = 0;
153                 vst_size = VST_SIZE_STEP;
154                 if ((vst = malloc(vst_size * sizeof(*vst))) == NULL)
155                         break;
156                 if (argc == 1)
157                         namelen = strlen(argv[0]);
158                 else
159                         namelen = 0;
160                 jid = 0;
161                 while ((jid = getjail(&vst[vst_last], jid, verbose)) > 0) {
162                         /* Skip jails which do not own vnets. */
163                         if (vst[vst_last].vnet != 1)
164                                 continue;
165                         /* Skip non-matching vnames / hierarchies. */
166                         if (namelen &&
167                             ((strlen(vst[vst_last].name) < namelen ||
168                             strncmp(vst[vst_last].name, argv[0], namelen) != 0)
169                             || (strlen(vst[vst_last].name) > namelen &&
170                             vst[vst_last].name[namelen] != '.')))
171                                 continue;
172                         /* Skip any sub-trees if -r not requested. */
173                         if (!recurse &&
174                             (strlen(vst[vst_last].name) < namelen ||
175                             strchr(&vst[vst_last].name[namelen], '.') != NULL))
176                                 continue;
177                         /* Grow vst table if necessary. */
178                         if (++vst_last == vst_size) {
179                                 vst_size += VST_SIZE_STEP;
180                                 vst = realloc(vst, vst_size * sizeof(*vst));
181                                 if (vst == NULL)
182                                         break;
183                         }
184                 }
185                 if (vst == NULL)
186                         break;
187                 /* Sort: the key is the 1st field in *vst, i.e. vimage name. */
188                 qsort(vst, vst_last, sizeof(*vst), (void *) strcmp);
189                 for (i = 0; i < vst_last; i++) {
190                         if (!verbose) {
191                                 printf("%s\n", vst[i].name);
192                                 continue;
193                         }
194
195                         printf("%s:\n", vst[i].name);
196                         printf("    Path: %s\n", vst[i].path);
197                         printf("    Hostname: %s\n", vst[i].hostname);
198                         printf("    Domainname: %s\n", vst[i].domainname);
199                         printf("    Children: %d\n", vst[i].childcnt);
200
201                         if (verbose < 2)
202                                 continue;
203
204                         printf("    Children limit: %d\n", vst[i].childmax);
205                         printf("    CPUsetID: %d\n", vst[i].cpuset);
206                         printf("    JID: %d\n", vst[i].jid);
207                         printf("    PJID: %d\n", vst[i].parentjid);
208                         printf("    Raw sockets allowed: %d\n", vst[i].rawsock);
209                         printf("    All AF allowed: %d\n", vst[i].socket_af);
210                         printf("    Mount allowed: %d\n", vst[i].mount);
211                 }
212                 free(vst);
213                 exit(0);
214
215         case VI_IFMOVE:
216                 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
217                         break;
218                 if ((jid = jail_getid(argv[0])) < 0)
219                         break;
220                 ifreq.ifr_jid = jid;
221                 strncpy(ifreq.ifr_name, argv[1], sizeof(ifreq.ifr_name));
222                 if (ioctl(s, SIOCSIFVNET, (caddr_t)&ifreq) < 0)
223                         break;
224                 close(s);
225                 if (argc == 3)
226                         snprintf(ifname, sizeof(ifname), "%s", argv[2]);
227                 else
228                         snprintf(ifname, sizeof(ifname), "eth0");
229                 ifreq.ifr_data = ifname;
230                 /* Do we need to rename the ifnet? */
231                 if (strcmp(ifreq.ifr_name, ifname) != 0) {
232                         /* Switch to the context of the target vimage. */
233                         if (jail_attach(jid) < 0)
234                                 break;
235                         if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
236                                 break;
237                         for (namelen = 0; isalpha(ifname[namelen]); namelen++);
238                         i = 0;
239                         /* Search for a free ifunit in target vnet.  Unsafe. */
240                         while (ioctl(s, SIOCSIFNAME, (caddr_t)&ifreq) < 0) {
241                                 snprintf(&ifname[namelen],
242                                     sizeof(ifname) - namelen, "%d", i);
243                                 /* Emergency brake. */
244                                 if (i++ == IF_MAXUNIT)
245                                         break;
246                         }
247                 }
248                 if (i < IF_MAXUNIT)
249                         printf("%s@%s\n", ifname, argv[0]);
250                 else
251                         printf("%s@%s\n", ifreq.ifr_name, argv[0]);
252                 exit(0);
253
254         case VI_CREATE:
255                 if (jail_setv(JAIL_CREATE,
256                     "name", argv[0],
257                     "vnet", NULL,
258                     "host", NULL,
259                     "persist", NULL,
260                     "allow.raw_sockets", "true",
261                     "allow.socket_af", "true",
262                     "allow.mount", "true",
263                     NULL) < 0)
264                         break;
265                 if (argc == 1)
266                         exit(0);
267                 /* Not done yet, proceed to apply non-default parameters. */
268
269         case VI_MODIFY:
270                 jailparam_init(&params[0], "name");
271                 jailparam_import(&params[0], argv[0]);
272                 for (i = 1; i < argc; i++) {
273                         for (str = argv[i]; *str != '=' && *str != 0; str++) {
274                                 /* Do nothing - search for '=' delimeter. */
275                         }
276                         if (*str == 0)
277                                 break;
278                         *str++ = 0;
279                         if (*str == 0)
280                                 break;
281                         jailparam_init(&params[i], argv[i]);
282                         jailparam_import(&params[i], str);
283                 }
284                 if (i != argc)
285                         break;
286                 if (jailparam_set(params, i, JAIL_UPDATE) < 0)
287                         break;
288                 exit(0);
289
290         case VI_DESTROY:
291                 if ((jid = jail_getid(argv[0])) < 0)
292                         break;
293                 if (jail_remove(jid) < 0)
294                         break;
295                 exit(0);
296
297         case VI_SWITCHTO:
298                 if ((jid = jail_getid(argv[0])) < 0)
299                         break;
300                 if (jail_attach(jid) < 0)
301                         break;
302                 if (argc == 1) {
303                         printf("Switched to vimage %s\n", argv[0]);
304                         if ((str = getenv("SHELL")) == NULL)
305                                 execlp("/bin/sh", invocname, NULL);
306                         else
307                                 execlp(str, invocname, NULL);
308                 } else 
309                         execvp(argv[1], &argv[1]);
310                 break;
311
312         default:
313                 /* Should be unreachable. */
314                 break;
315         }
316
317         if (jail_errmsg[0])
318                 fprintf(stderr, "Error: %s\n", jail_errmsg);
319         else
320                 perror("Error");
321         exit(1);
322 }
323
324 static int
325 getjail(vstat_t *vs, int lastjid, int verbose)
326 {
327         struct jailparam params[32];    /* Must be > max(psize). */
328         int psize = 0;
329
330         bzero(params, sizeof(params));
331         bzero(vs, sizeof(*vs));
332
333         jailparam_init(&params[psize], "lastjid");
334         jailparam_import_raw(&params[psize++], &lastjid, sizeof lastjid);
335
336         jailparam_init(&params[psize], "vnet");
337         jailparam_import_raw(&params[psize++], &vs->vnet, sizeof(vs->vnet));
338
339         jailparam_init(&params[psize], "name");
340         jailparam_import_raw(&params[psize++], &vs->name, sizeof(vs->name));
341
342         if (verbose == 0)
343                 goto done;
344
345         jailparam_init(&params[psize], "path");
346         jailparam_import_raw(&params[psize++], &vs->path, sizeof(vs->path));
347
348         jailparam_init(&params[psize], "host.hostname");
349         jailparam_import_raw(&params[psize++], &vs->hostname,
350             sizeof(vs->hostname));
351
352         jailparam_init(&params[psize], "host.domainname");
353         jailparam_import_raw(&params[psize++], &vs->domainname,
354             sizeof(vs->domainname));
355
356         jailparam_init(&params[psize], "children.cur");
357         jailparam_import_raw(&params[psize++], &vs->childcnt,
358             sizeof(vs->childcnt));
359
360         if (verbose == 1)
361                 goto done;
362
363         jailparam_init(&params[psize], "children.max");
364         jailparam_import_raw(&params[psize++], &vs->childmax,
365             sizeof(vs->childmax));
366
367         jailparam_init(&params[psize], "cpuset.id");
368         jailparam_import_raw(&params[psize++], &vs->cpuset,
369             sizeof(vs->cpuset));
370
371         jailparam_init(&params[psize], "parent");
372         jailparam_import_raw(&params[psize++], &vs->parentjid,
373             sizeof(vs->parentjid));
374
375         jailparam_init(&params[psize], "allow.raw_sockets");
376         jailparam_import_raw(&params[psize++], &vs->rawsock,
377             sizeof(vs->rawsock));
378
379         jailparam_init(&params[psize], "allow.socket_af");
380         jailparam_import_raw(&params[psize++], &vs->socket_af,
381             sizeof(vs->socket_af));
382
383         jailparam_init(&params[psize], "allow.mount");
384         jailparam_import_raw(&params[psize++], &vs->mount, sizeof(vs->mount));
385
386 done:
387         vs->jid = jailparam_get(params, psize, 0);
388         jailparam_free(params, psize);
389         return (vs->jid);
390 }