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