]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/jls/jls.c
THIS BRANCH IS OBSOLETE, PLEASE READ:
[FreeBSD/FreeBSD.git] / usr.sbin / jls / jls.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2003 Mike Barcroft <mike@FreeBSD.org>
5  * Copyright (c) 2008 Bjoern A. Zeeb <bz@FreeBSD.org>
6  * Copyright (c) 2009 James Gritton <jamie@FreeBSD.org>
7  * Copyright (c) 2015 Emmanuel Vadot <manu@bocal.org>
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/param.h>
36 #include <sys/jail.h>
37 #include <sys/socket.h>
38 #include <sys/sysctl.h>
39
40 #include <arpa/inet.h>
41 #include <netinet/in.h>
42
43 #include <err.h>
44 #include <errno.h>
45 #include <jail.h>
46 #include <limits.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51 #include <libxo/xo.h>
52
53 #define JP_USER         0x01000000
54 #define JP_OPT          0x02000000
55
56 #define JLS_XO_VERSION  "2"
57
58 #define PRINT_DEFAULT   0x01
59 #define PRINT_HEADER    0x02
60 #define PRINT_NAMEVAL   0x04
61 #define PRINT_QUOTED    0x08
62 #define PRINT_SKIP      0x10
63 #define PRINT_VERBOSE   0x20
64 #define PRINT_JAIL_NAME 0x40
65
66 static struct jailparam *params;
67 static int *param_parent;
68 static int nparams;
69 #ifdef INET6
70 static int ip6_ok;
71 #endif
72 #ifdef INET
73 static int ip4_ok;
74 #endif
75
76 static int add_param(const char *name, void *value, size_t valuelen,
77                 struct jailparam *source, unsigned flags);
78 static int sort_param(const void *a, const void *b);
79 static char *noname(const char *name);
80 static char *nononame(const char *name);
81 static int print_jail(int pflags, int jflags);
82 static int special_print(int pflags, struct jailparam *param);
83 static void quoted_print(int pflags, char *name, char *value);
84 static void emit_ip_addr_list(int af_family, const char *list_name,
85                 struct jailparam *param);
86
87 int
88 main(int argc, char **argv)
89 {
90         char *dot, *ep, *jname, *pname;
91         int c, i, jflags, jid, lastjid, pflags, spc;
92
93         argc = xo_parse_args(argc, argv);
94         if (argc < 0)
95                 exit(1);
96
97         xo_set_version(JLS_XO_VERSION);
98         jname = NULL;
99         pflags = jflags = jid = 0;
100         while ((c = getopt(argc, argv, "adj:hNnqsv")) >= 0)
101                 switch (c) {
102                 case 'a':
103                 case 'd':
104                         jflags |= JAIL_DYING;
105                         break;
106                 case 'j':
107                         jid = strtoul(optarg, &ep, 10);
108                         if (!jid || *ep) {
109                                 jid = 0;
110                                 jname = optarg;
111                         }
112                         break;
113                 case 'h':
114                         pflags = (pflags & ~(PRINT_SKIP | PRINT_VERBOSE)) |
115                             PRINT_HEADER;
116                         break;
117                 case 'N':
118                         pflags |= PRINT_JAIL_NAME;
119                         break;
120                 case 'n':
121                         pflags = (pflags & ~PRINT_VERBOSE) | PRINT_NAMEVAL;
122                         break;
123                 case 'q':
124                         pflags |= PRINT_QUOTED;
125                         break;
126                 case 's':
127                         pflags = (pflags & ~(PRINT_HEADER | PRINT_VERBOSE)) |
128                             PRINT_NAMEVAL | PRINT_QUOTED | PRINT_SKIP;
129                         break;
130                 case 'v':
131                         pflags = (pflags &
132                             ~(PRINT_HEADER | PRINT_NAMEVAL | PRINT_SKIP)) |
133                             PRINT_VERBOSE;
134                         break;
135                 default:
136                         xo_errx(1, "usage: jls [-dhNnqv] [-j jail] [param ...]");
137                 }
138
139 #ifdef INET6
140         ip6_ok = feature_present("inet6");
141 #endif
142 #ifdef INET
143         ip4_ok = feature_present("inet");
144 #endif
145
146         /* Add the parameters to print. */
147         if (optind == argc) {
148                 if (pflags & (PRINT_HEADER | PRINT_NAMEVAL))
149                         add_param("all", NULL, (size_t)0, NULL, JP_USER);
150                 else if (pflags & PRINT_VERBOSE) {
151                         add_param("jid", NULL, (size_t)0, NULL, JP_USER);
152                         add_param("host.hostname", NULL, (size_t)0, NULL,
153                             JP_USER);
154                         add_param("path", NULL, (size_t)0, NULL, JP_USER);
155                         add_param("name", NULL, (size_t)0, NULL, JP_USER);
156                         add_param("dying", NULL, (size_t)0, NULL, JP_USER);
157                         add_param("cpuset.id", NULL, (size_t)0, NULL, JP_USER);
158 #ifdef INET
159                         if (ip4_ok)
160                                 add_param("ip4.addr", NULL, (size_t)0, NULL,
161                                     JP_USER);
162 #endif
163 #ifdef INET6
164                         if (ip6_ok)
165                                 add_param("ip6.addr", NULL, (size_t)0, NULL,
166                                     JP_USER | JP_OPT);
167 #endif
168                 } else {
169                         pflags |= PRINT_DEFAULT;
170                         if (pflags & PRINT_JAIL_NAME)
171                                 add_param("name", NULL, (size_t)0, NULL, JP_USER);
172                         else
173                                 add_param("jid", NULL, (size_t)0, NULL, JP_USER);
174 #ifdef INET
175                         if (ip4_ok)
176                                 add_param("ip4.addr", NULL, (size_t)0, NULL,
177                                     JP_USER);
178 #endif
179                         add_param("host.hostname", NULL, (size_t)0, NULL,
180                             JP_USER);
181                         add_param("path", NULL, (size_t)0, NULL, JP_USER);
182                 }
183         } else {
184                 pflags &= ~PRINT_VERBOSE;
185                 while (optind < argc)
186                         add_param(argv[optind++], NULL, (size_t)0, NULL,
187                             JP_USER);
188         }
189
190         if (pflags & PRINT_SKIP) {
191                 /* Check for parameters with jailsys parents. */
192                 for (i = 0; i < nparams; i++) {
193                         if ((params[i].jp_flags & JP_USER) &&
194                             (dot = strchr(params[i].jp_name, '.'))) {
195                                 pname = alloca((dot - params[i].jp_name) + 1);
196                                 strlcpy(pname, params[i].jp_name,
197                                     (dot - params[i].jp_name) + 1);
198                                 param_parent[i] = add_param(pname,
199                                     NULL, (size_t)0, NULL, JP_OPT);
200                         }
201                 }
202         }
203
204         /* Add the index key parameters. */
205         if (jid != 0)
206                 add_param("jid", &jid, sizeof(jid), NULL, 0);
207         else if (jname != NULL)
208                 add_param("name", jname, strlen(jname), NULL, 0);
209         else
210                 add_param("lastjid", &lastjid, sizeof(lastjid), NULL, 0);
211
212         /* Print a header line if requested. */
213         if (pflags & PRINT_VERBOSE) {
214                 xo_emit("{T:/%3s}{T:JID}{P:  }{T:Hostname}{Pd:/%22s}{T:Path}\n",
215                         "", "");
216                 xo_emit("{P:/%8s}{T:Name}{Pd:/%26s}{T:State}\n", "", "");
217                 xo_emit("{P:/%8s}{T:CPUSetID}\n", "");
218                 xo_emit("{P:/%8s}{T:IP Address(es)}\n", "");
219         }
220         else if (pflags & PRINT_DEFAULT)
221                 if (pflags & PRINT_JAIL_NAME)
222                         xo_emit("{P: }{T:JID/%-15s}{P: }{T:IP Address/%-15s}"
223                                 "{P: }{T:Hostname/%-29s}{P: }{T:Path}\n");
224                 else
225                         xo_emit("{T:JID/%6s}{P:  }{T:IP Address}{P:/%6s}"
226                                 "{T:Hostname}{P:/%22s}{T:Path}\n", "", "");
227         else if (pflags & PRINT_HEADER) {
228                 for (i = spc = 0; i < nparams; i++)
229                         if (params[i].jp_flags & JP_USER) {
230                                 if (spc)
231                                         xo_emit("{P: }");
232                                 else
233                                         spc = 1;
234                                 xo_emit(params[i].jp_name);
235                         }
236                 xo_emit("{P:\n}");
237         }
238
239         xo_open_container("jail-information");
240         xo_open_list("jail");
241         /* Fetch the jail(s) and print the parameters. */
242         if (jid != 0 || jname != NULL) {
243                 if (print_jail(pflags, jflags) < 0)
244                         xo_errx(1, "%s", jail_errmsg);
245         } else {
246                 for (lastjid = 0;
247                      (lastjid = print_jail(pflags, jflags)) >= 0; )
248                         ;
249                 if (errno != 0 && errno != ENOENT)
250                         xo_errx(1, "%s", jail_errmsg);
251         }
252         xo_close_list("jail");
253         xo_close_container("jail-information");
254         xo_finish();
255         return (0);
256 }
257
258 static int
259 add_param(const char *name, void *value, size_t valuelen,
260     struct jailparam *source, unsigned flags)
261 {
262         struct jailparam *param, *tparams;
263         int i, tnparams;
264
265         static int paramlistsize;
266
267         /* The pseudo-parameter "all" scans the list of available parameters. */
268         if (!strcmp(name, "all")) {
269                 tnparams = jailparam_all(&tparams);
270                 if (tnparams < 0)
271                         xo_errx(1, "%s", jail_errmsg);
272                 qsort(tparams, (size_t)tnparams, sizeof(struct jailparam),
273                     sort_param);
274                 for (i = 0; i < tnparams; i++)
275                         add_param(tparams[i].jp_name, NULL, (size_t)0,
276                             tparams + i, flags);
277                 free(tparams);
278                 return -1;
279         }
280
281         /* Check for repeat parameters. */
282         for (i = 0; i < nparams; i++)
283                 if (!strcmp(name, params[i].jp_name)) {
284                         if (value != NULL && jailparam_import_raw(params + i,
285                             value, valuelen) < 0)
286                                 xo_errx(1, "%s", jail_errmsg);
287                         params[i].jp_flags |= flags;
288                         if (source != NULL)
289                                 jailparam_free(source, 1);
290                         return i;
291                 }
292
293         /* Make sure there is room for the new param record. */
294         if (!nparams) {
295                 paramlistsize = 32;
296                 params = malloc(paramlistsize * sizeof(*params));
297                 param_parent = malloc(paramlistsize * sizeof(*param_parent));
298                 if (params == NULL || param_parent == NULL)
299                         xo_err(1, "malloc");
300         } else if (nparams >= paramlistsize) {
301                 paramlistsize *= 2;
302                 params = realloc(params, paramlistsize * sizeof(*params));
303                 param_parent = realloc(param_parent,
304                     paramlistsize * sizeof(*param_parent));
305                 if (params == NULL || param_parent == NULL)
306                         xo_err(1, "realloc");
307         }
308
309         /* Look up the parameter. */
310         param_parent[nparams] = -1;
311         param = params + nparams++;
312         if (source != NULL) {
313                 *param = *source;
314                 param->jp_flags |= flags;
315                 return param - params;
316         }
317         if (jailparam_init(param, name) < 0 ||
318             (value != NULL ? jailparam_import_raw(param, value, valuelen)
319              : jailparam_import(param, value)) < 0) {
320                 if (flags & JP_OPT) {
321                         nparams--;
322                         return (-1);
323                 }
324                 xo_errx(1, "%s", jail_errmsg);
325         }
326         param->jp_flags |= flags;
327         return param - params;
328 }
329
330 static int
331 sort_param(const void *a, const void *b)
332 {
333         const struct jailparam *parama, *paramb;
334         char *ap, *bp;
335
336         /* Put top-level parameters first. */
337         parama = a;
338         paramb = b;
339         ap = strchr(parama->jp_name, '.');
340         bp = strchr(paramb->jp_name, '.');
341         if (ap && !bp)
342                 return (1);
343         if (bp && !ap)
344                 return (-1);
345         return (strcmp(parama->jp_name, paramb->jp_name));
346 }
347
348 static char *
349 noname(const char *name)
350 {
351         char *nname, *p;
352
353         nname = malloc(strlen(name) + 3);
354         if (nname == NULL)
355                 xo_err(1, "malloc");
356         p = strrchr(name, '.');
357         if (p != NULL)
358                 sprintf(nname, "%.*s.no%s", (int)(p - name), name, p + 1);
359         else
360                 sprintf(nname, "no%s", name);
361         return nname;
362 }
363
364 static char *
365 nononame(const char *name)
366 {
367         char *nname, *p;
368
369         p = strrchr(name, '.');
370         if (strncmp(p ? p + 1 : name, "no", 2))
371                 return NULL;
372         nname = malloc(strlen(name) - 1);
373         if (nname == NULL)
374                 xo_err(1, "malloc");
375         if (p != NULL)
376                 sprintf(nname, "%.*s.%s", (int)(p - name), name, p + 3);
377         else
378                 strcpy(nname, name + 2);
379         return nname;
380 }
381
382 static int
383 print_jail(int pflags, int jflags)
384 {
385         char *nname, *xo_nname;
386         char **param_values;
387         int i, jid, n, spc;
388
389         jid = jailparam_get(params, nparams, jflags);
390         if (jid < 0)
391                 return jid;
392
393         xo_open_instance("jail");
394
395         if (pflags & PRINT_VERBOSE) {
396                 xo_emit("{:jid/%6d}{P:  }{:hostname/%-29.29s/%s}{P: }"
397                     "{:path/%.74s/%s}\n",
398                     *(int *)params[0].jp_value,
399                     (char *)params[1].jp_value,
400                     (char *)params[2].jp_value);
401                 xo_emit("{P:        }{:name/%-29.29s/%s}{P: }{:state/%.74s}\n",
402                     (char *)params[3].jp_value,
403                     *(int *)params[4].jp_value ? "DYING" : "ACTIVE");
404                 xo_emit("{P:        }{:cpusetid/%d}\n", *(int *)params[5].jp_value);
405                 n = 6;
406 #ifdef INET
407                 if (ip4_ok && !strcmp(params[n].jp_name, "ip4.addr")) {
408                         emit_ip_addr_list(AF_INET, "ipv4_addrs", params + n);
409                         n++;
410                 }
411 #endif
412 #ifdef INET6
413                 if (ip6_ok && !strcmp(params[n].jp_name, "ip6.addr")) {
414                         emit_ip_addr_list(AF_INET6, "ipv6_addrs", params + n);
415                         n++;
416                 }
417 #endif
418         } else if (pflags & PRINT_DEFAULT) {
419                 if (pflags & PRINT_JAIL_NAME)
420                         xo_emit("{P: }{:name/%-15s/%s}{P: }",
421                             (char *)params[0].jp_value);
422                 else
423                         xo_emit("{:jid/%6d}{P:  }", *(int *)params[0].jp_value);
424                 xo_emit("{:ipv4/%-15.15s/%s}{P: }{:hostname/%-29.29s/%s}{P: }{:path/%.74s/%s}\n",
425 #ifdef INET
426                     (!ip4_ok || params[1].jp_valuelen == 0) ? ""
427                     : inet_ntoa(*(struct in_addr *)params[1].jp_value),
428                     (char *)params[2-!ip4_ok].jp_value,
429                     (char *)params[3-!ip4_ok].jp_value);
430 #else
431                     "-",
432                     (char *)params[1].jp_value,
433                     (char *)params[2].jp_value);
434 #endif
435         } else {
436                 param_values = alloca(nparams * sizeof(*param_values));
437                 for (i = 0; i < nparams; i++) {
438                         if (!(params[i].jp_flags & JP_USER))
439                                 continue;
440                         param_values[i] = jailparam_export(params + i);
441                         if (param_values[i] == NULL)
442                                 xo_errx(1, "%s", jail_errmsg);
443                 }
444                 for (i = spc = 0; i < nparams; i++) {
445                         if (!(params[i].jp_flags & JP_USER))
446                                 continue;
447                         if ((pflags & PRINT_SKIP) &&
448                             ((!(params[i].jp_ctltype &
449                                 (CTLFLAG_WR | CTLFLAG_TUN))) ||
450                              (param_parent[i] >= 0 &&
451                               *(int *)params[param_parent[i]].jp_value !=
452                               JAIL_SYS_NEW)))
453                                 continue;
454                         if (spc)
455                                 xo_emit("{P: }");
456                         else
457                                 spc = 1;
458                         if (pflags & PRINT_NAMEVAL) {
459                                 /*
460                                  * Generally "name=value", but for booleans
461                                  * either "name" or "noname".
462                                  */
463                                 if (params[i].jp_flags &
464                                     (JP_BOOL | JP_NOBOOL)) {
465                                         if (*(int *)params[i].jp_value) {
466                                                 asprintf(&xo_nname, "{en:%s/true}", params[i].jp_name);
467                                                 xo_emit(xo_nname);
468                                                 xo_emit("{d:/%s}", params[i].jp_name);
469                                         }
470                                         else {
471                                                 nname = (params[i].jp_flags &
472                                                     JP_NOBOOL) ?
473                                                     nononame(params[i].jp_name)
474                                                     : noname(params[i].jp_name);
475                                                 if (params[i].jp_flags & JP_NOBOOL) {
476                                                         asprintf(&xo_nname, "{en:%s/true}", params[i].jp_name);
477                                                         xo_emit(xo_nname);
478                                                 } else {
479                                                         asprintf(&xo_nname, "{en:%s/false}", params[i].jp_name);
480                                                         xo_emit(xo_nname);
481                                                 }
482                                                 xo_emit("{d:/%s}", nname);
483                                                 free(nname);
484                                         }
485                                         free(xo_nname);
486                                         continue;
487                                 }
488                                 xo_emit("{d:%s}=", params[i].jp_name);
489                         }
490                         if (!special_print(pflags, params + i))
491                                 quoted_print(pflags, params[i].jp_name, param_values[i]);
492                 }
493                 xo_emit("{P:\n}");
494                 for (i = 0; i < nparams; i++)
495                         if (params[i].jp_flags & JP_USER)
496                                 free(param_values[i]);
497         }
498
499         xo_close_instance("jail");
500         return (jid);
501 }
502
503 static void
504 quoted_print(int pflags, char *name, char *value)
505 {
506         int qc;
507         char *p = value;
508
509         /* An empty string needs quoting. */
510         if (!*p) {
511                 xo_emit("{ea:/%s}{da:/\"\"}", name, value, name);
512                 return;
513         }
514
515         /*
516          * The value will be surrounded by quotes if it contains spaces
517          * or quotes.
518          */
519         qc = strchr(p, '\'') ? '"'
520                 : strchr(p, '"') ? '\''
521                 : strchr(p, ' ') || strchr(p, '\t') ? '"'
522                 : 0;
523
524         if (qc && pflags & PRINT_QUOTED)
525                 xo_emit("{P:/%c}", qc);
526
527         xo_emit("{a:/%s}", name, value);
528
529         if (qc && pflags & PRINT_QUOTED)
530                 xo_emit("{P:/%c}", qc);
531 }
532
533 static int
534 special_print(int pflags, struct jailparam *param)
535 {
536         int ip_as_list;
537
538         switch (xo_get_style(NULL)) {
539         case XO_STYLE_JSON:
540         case XO_STYLE_XML:
541                 ip_as_list = 1;
542                 break;
543         default:
544                 ip_as_list = 0;
545         }
546
547         if (!ip_as_list && param->jp_valuelen == 0) {
548                 if (pflags & PRINT_QUOTED)
549                         xo_emit("{P:\"\"}");
550                 else if (!(pflags & PRINT_NAMEVAL))
551                         xo_emit("{P:-}");
552         } else if (ip_as_list && !strcmp(param->jp_name, "ip4.addr")) {
553                 emit_ip_addr_list(AF_INET, param->jp_name, param);
554         } else if (ip_as_list && !strcmp(param->jp_name, "ip6.addr")) {
555                 emit_ip_addr_list(AF_INET6, param->jp_name, param);
556         } else {
557                 return 0;
558         }
559
560         return 1;
561 }
562
563 static void
564 emit_ip_addr_list(int af_family, const char *list_name, struct jailparam *param)
565 {
566         char ipbuf[INET6_ADDRSTRLEN];
567         size_t addr_len;
568         const char *emit_str;
569         int ai, count;
570
571         switch (af_family) {
572         case AF_INET:
573                 addr_len = sizeof(struct in_addr);
574                 emit_str = "{P:        }{ql:ipv4_addr}{P:\n}";
575                 break;
576         case AF_INET6:
577                 addr_len = sizeof(struct in6_addr);
578                 emit_str = "{P:        }{ql:ipv6_addr}{P:\n}";
579                 break;
580         default:
581                 xo_err(1, "unsupported af_family");
582                 return;
583         }
584
585         count = param->jp_valuelen / addr_len;
586
587         xo_open_list(list_name);
588         for (ai = 0; ai < count; ai++) {
589                 if (inet_ntop(af_family,
590                     ((uint8_t *)param->jp_value) + addr_len * ai,
591                     ipbuf, sizeof(ipbuf)) == NULL) {
592                         xo_err(1, "inet_ntop");
593                 } else {
594                         xo_emit(emit_str, ipbuf);
595                 }
596         }
597         xo_close_list(list_name);
598 }