]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/jls/jls.c
zfs: merge openzfs/zfs@95f71c019
[FreeBSD/FreeBSD.git] / usr.sbin / jls / jls.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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 #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, spc;
386 #if (defined INET || defined INET6)
387         int n;
388 #endif
389
390         jid = jailparam_get(params, nparams, jflags);
391         if (jid < 0)
392                 return jid;
393
394         xo_open_instance("jail");
395
396         if (pflags & PRINT_VERBOSE) {
397                 xo_emit("{:jid/%6d}{P:  }{:hostname/%-29.29s/%s}{P: }"
398                     "{:path/%.74s/%s}\n",
399                     *(int *)params[0].jp_value,
400                     (char *)params[1].jp_value,
401                     (char *)params[2].jp_value);
402                 xo_emit("{P:        }{:name/%-29.29s/%s}{P: }{:state/%.74s}\n",
403                     (char *)params[3].jp_value,
404                     *(int *)params[4].jp_value ? "DYING" : "ACTIVE");
405                 xo_emit("{P:        }{:cpusetid/%d}\n", *(int *)params[5].jp_value);
406 #if (defined INET || defined INET6)
407                 n = 6;
408 #endif
409 #ifdef INET
410                 if (ip4_ok && !strcmp(params[n].jp_name, "ip4.addr")) {
411                         emit_ip_addr_list(AF_INET, "ipv4_addrs", params + n);
412                         n++;
413                 }
414 #endif
415 #ifdef INET6
416                 if (ip6_ok && !strcmp(params[n].jp_name, "ip6.addr")) {
417                         emit_ip_addr_list(AF_INET6, "ipv6_addrs", params + n);
418                         n++;
419                 }
420 #endif
421         } else if (pflags & PRINT_DEFAULT) {
422                 if (pflags & PRINT_JAIL_NAME)
423                         xo_emit("{P: }{:name/%-15s/%s}{P: }",
424                             (char *)params[0].jp_value);
425                 else
426                         xo_emit("{:jid/%6d}{P:  }", *(int *)params[0].jp_value);
427                 xo_emit("{:ipv4/%-15.15s/%s}{P: }{:hostname/%-29.29s/%s}{P: }{:path/%.74s/%s}\n",
428 #ifdef INET
429                     (!ip4_ok || params[1].jp_valuelen == 0) ? ""
430                     : inet_ntoa(*(struct in_addr *)params[1].jp_value),
431                     (char *)params[2-!ip4_ok].jp_value,
432                     (char *)params[3-!ip4_ok].jp_value);
433 #else
434                     "-",
435                     (char *)params[1].jp_value,
436                     (char *)params[2].jp_value);
437 #endif
438         } else {
439                 param_values = alloca(nparams * sizeof(*param_values));
440                 for (i = 0; i < nparams; i++) {
441                         if (!(params[i].jp_flags & JP_USER))
442                                 continue;
443                         param_values[i] = jailparam_export(params + i);
444                         if (param_values[i] == NULL)
445                                 xo_errx(1, "%s", jail_errmsg);
446                 }
447                 for (i = spc = 0; i < nparams; i++) {
448                         if (!(params[i].jp_flags & JP_USER))
449                                 continue;
450                         if ((pflags & PRINT_SKIP) &&
451                             ((!(params[i].jp_ctltype &
452                                 (CTLFLAG_WR | CTLFLAG_TUN))) ||
453                              (param_parent[i] >= 0 &&
454                               *(int *)params[param_parent[i]].jp_value !=
455                               JAIL_SYS_NEW)))
456                                 continue;
457                         if (spc)
458                                 xo_emit("{P: }");
459                         else
460                                 spc = 1;
461                         if (pflags & PRINT_NAMEVAL) {
462                                 /*
463                                  * Generally "name=value", but for booleans
464                                  * either "name" or "noname".
465                                  */
466                                 if (params[i].jp_flags &
467                                     (JP_BOOL | JP_NOBOOL)) {
468                                         if (*(int *)params[i].jp_value) {
469                                                 asprintf(&xo_nname, "{en:%s/true}", params[i].jp_name);
470                                                 xo_emit(xo_nname);
471                                                 xo_emit("{d:/%s}", params[i].jp_name);
472                                         }
473                                         else {
474                                                 nname = (params[i].jp_flags &
475                                                     JP_NOBOOL) ?
476                                                     nononame(params[i].jp_name)
477                                                     : noname(params[i].jp_name);
478                                                 if (params[i].jp_flags & JP_NOBOOL) {
479                                                         asprintf(&xo_nname, "{en:%s/true}", params[i].jp_name);
480                                                         xo_emit(xo_nname);
481                                                 } else {
482                                                         asprintf(&xo_nname, "{en:%s/false}", params[i].jp_name);
483                                                         xo_emit(xo_nname);
484                                                 }
485                                                 xo_emit("{d:/%s}", nname);
486                                                 free(nname);
487                                         }
488                                         free(xo_nname);
489                                         continue;
490                                 }
491                                 xo_emit("{d:%s}=", params[i].jp_name);
492                         }
493                         if (!special_print(pflags, params + i))
494                                 quoted_print(pflags, params[i].jp_name, param_values[i]);
495                 }
496                 xo_emit("{P:\n}");
497                 for (i = 0; i < nparams; i++)
498                         if (params[i].jp_flags & JP_USER)
499                                 free(param_values[i]);
500         }
501
502         xo_close_instance("jail");
503         return (jid);
504 }
505
506 static void
507 quoted_print(int pflags, char *name, char *value)
508 {
509         int qc;
510         char *p = value;
511
512         /* An empty string needs quoting. */
513         if (!*p) {
514                 xo_emit("{ea:/%s}{da:/\"\"}", name, value, name);
515                 return;
516         }
517
518         /*
519          * The value will be surrounded by quotes if it contains spaces
520          * or quotes.
521          */
522         qc = strchr(p, '\'') ? '"'
523                 : strchr(p, '"') ? '\''
524                 : strchr(p, ' ') || strchr(p, '\t') ? '"'
525                 : 0;
526
527         if (qc && pflags & PRINT_QUOTED)
528                 xo_emit("{P:/%c}", qc);
529
530         xo_emit("{a:/%s}", name, value);
531
532         if (qc && pflags & PRINT_QUOTED)
533                 xo_emit("{P:/%c}", qc);
534 }
535
536 static int
537 special_print(int pflags, struct jailparam *param)
538 {
539         int ip_as_list;
540
541         switch (xo_get_style(NULL)) {
542         case XO_STYLE_JSON:
543         case XO_STYLE_XML:
544                 ip_as_list = 1;
545                 break;
546         default:
547                 ip_as_list = 0;
548         }
549
550         if (!ip_as_list && param->jp_valuelen == 0) {
551                 if (pflags & PRINT_QUOTED)
552                         xo_emit("{P:\"\"}");
553                 else if (!(pflags & PRINT_NAMEVAL))
554                         xo_emit("{P:-}");
555         } else if (ip_as_list && !strcmp(param->jp_name, "ip4.addr")) {
556                 emit_ip_addr_list(AF_INET, param->jp_name, param);
557         } else if (ip_as_list && !strcmp(param->jp_name, "ip6.addr")) {
558                 emit_ip_addr_list(AF_INET6, param->jp_name, param);
559         } else {
560                 return 0;
561         }
562
563         return 1;
564 }
565
566 static void
567 emit_ip_addr_list(int af_family, const char *list_name, struct jailparam *param)
568 {
569         char ipbuf[INET6_ADDRSTRLEN];
570         size_t addr_len;
571         const char *emit_str;
572         int ai, count;
573
574         switch (af_family) {
575         case AF_INET:
576                 addr_len = sizeof(struct in_addr);
577                 emit_str = "{P:        }{ql:ipv4_addr}{P:\n}";
578                 break;
579         case AF_INET6:
580                 addr_len = sizeof(struct in6_addr);
581                 emit_str = "{P:        }{ql:ipv6_addr}{P:\n}";
582                 break;
583         default:
584                 xo_err(1, "unsupported af_family");
585                 return;
586         }
587
588         count = param->jp_valuelen / addr_len;
589
590         xo_open_list(list_name);
591         for (ai = 0; ai < count; ai++) {
592                 if (inet_ntop(af_family,
593                     ((uint8_t *)param->jp_value) + addr_len * ai,
594                     ipbuf, sizeof(ipbuf)) == NULL) {
595                         xo_err(1, "inet_ntop");
596                 } else {
597                         xo_emit(emit_str, ipbuf);
598                 }
599         }
600         xo_close_list(list_name);
601 }