]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/jls/jls.c
MFV r294491: ntp 4.2.8p6.
[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  "1"
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 void quoted_print(int pflags, char *name, char *value);
81
82 int
83 main(int argc, char **argv)
84 {
85         char *dot, *ep, *jname, *pname;
86         int c, i, jflags, jid, lastjid, pflags, spc;
87
88         argc = xo_parse_args(argc, argv);
89         if (argc < 0)
90                 exit(1);
91
92         xo_set_version(JLS_XO_VERSION);
93         jname = NULL;
94         pflags = jflags = jid = 0;
95         while ((c = getopt(argc, argv, "adj:hNnqsv")) >= 0)
96                 switch (c) {
97                 case 'a':
98                 case 'd':
99                         jflags |= JAIL_DYING;
100                         break;
101                 case 'j':
102                         jid = strtoul(optarg, &ep, 10);
103                         if (!jid || *ep) {
104                                 jid = 0;
105                                 jname = optarg;
106                         }
107                         break;
108                 case 'h':
109                         pflags = (pflags & ~(PRINT_SKIP | PRINT_VERBOSE)) |
110                             PRINT_HEADER;
111                         break;
112                 case 'N':
113                         pflags |= PRINT_JAIL_NAME;
114                         break;
115                 case 'n':
116                         pflags = (pflags & ~PRINT_VERBOSE) | PRINT_NAMEVAL;
117                         break;
118                 case 'q':
119                         pflags |= PRINT_QUOTED;
120                         break;
121                 case 's':
122                         pflags = (pflags & ~(PRINT_HEADER | PRINT_VERBOSE)) |
123                             PRINT_NAMEVAL | PRINT_QUOTED | PRINT_SKIP;
124                         break;
125                 case 'v':
126                         pflags = (pflags &
127                             ~(PRINT_HEADER | PRINT_NAMEVAL | PRINT_SKIP)) |
128                             PRINT_VERBOSE;
129                         break;
130                 default:
131                         xo_errx(1, "usage: jls [-dhNnqv] [-j jail] [param ...]");
132                 }
133
134 #ifdef INET6
135         ip6_ok = feature_present("inet6");
136 #endif
137 #ifdef INET
138         ip4_ok = feature_present("inet");
139 #endif
140
141         /* Add the parameters to print. */
142         if (optind == argc) {
143                 if (pflags & (PRINT_HEADER | PRINT_NAMEVAL))
144                         add_param("all", NULL, (size_t)0, NULL, JP_USER);
145                 else if (pflags & PRINT_VERBOSE) {
146                         add_param("jid", NULL, (size_t)0, NULL, JP_USER);
147                         add_param("host.hostname", NULL, (size_t)0, NULL,
148                             JP_USER);
149                         add_param("path", NULL, (size_t)0, NULL, JP_USER);
150                         add_param("name", NULL, (size_t)0, NULL, JP_USER);
151                         add_param("dying", NULL, (size_t)0, NULL, JP_USER);
152                         add_param("cpuset.id", NULL, (size_t)0, NULL, JP_USER);
153 #ifdef INET
154                         if (ip4_ok)
155                                 add_param("ip4.addr", NULL, (size_t)0, NULL,
156                                     JP_USER);
157 #endif
158 #ifdef INET6
159                         if (ip6_ok)
160                                 add_param("ip6.addr", NULL, (size_t)0, NULL,
161                                     JP_USER | JP_OPT);
162 #endif
163                 } else {
164                         pflags |= PRINT_DEFAULT;
165                         if (pflags & PRINT_JAIL_NAME)
166                                 add_param("name", NULL, (size_t)0, NULL, JP_USER);
167                         else
168                                 add_param("jid", NULL, (size_t)0, NULL, JP_USER);
169 #ifdef INET
170                         if (ip4_ok)
171                                 add_param("ip4.addr", NULL, (size_t)0, NULL,
172                                     JP_USER);
173 #endif
174                         add_param("host.hostname", NULL, (size_t)0, NULL,
175                             JP_USER);
176                         add_param("path", NULL, (size_t)0, NULL, JP_USER);
177                 }
178         } else {
179                 pflags &= ~PRINT_VERBOSE;
180                 while (optind < argc)
181                         add_param(argv[optind++], NULL, (size_t)0, NULL,
182                             JP_USER);
183         }
184
185         if (pflags & PRINT_SKIP) {
186                 /* Check for parameters with jailsys parents. */
187                 for (i = 0; i < nparams; i++) {
188                         if ((params[i].jp_flags & JP_USER) &&
189                             (dot = strchr(params[i].jp_name, '.'))) {
190                                 pname = alloca((dot - params[i].jp_name) + 1);
191                                 strlcpy(pname, params[i].jp_name,
192                                     (dot - params[i].jp_name) + 1);
193                                 param_parent[i] = add_param(pname,
194                                     NULL, (size_t)0, NULL, JP_OPT);
195                         }
196                 }
197         }
198
199         /* Add the index key parameters. */
200         if (jid != 0)
201                 add_param("jid", &jid, sizeof(jid), NULL, 0);
202         else if (jname != NULL)
203                 add_param("name", jname, strlen(jname), NULL, 0);
204         else
205                 add_param("lastjid", &lastjid, sizeof(lastjid), NULL, 0);
206
207         /* Print a header line if requested. */
208         if (pflags & PRINT_VERBOSE) {
209                 xo_emit("{T:/%3s}{T:JID}{P:  }{T:Hostname}{Pd:/%22s}{T:Path}\n",
210                         "", "");
211                 xo_emit("{P:/%8s}{T:Name}{Pd:/%26s}{T:State}\n", "", "");
212                 xo_emit("{P:/%8s}{T:CPUSetID}\n", "");
213                 xo_emit("{P:/%8s}{T:IP Address(es)}\n", "");
214         }
215         else if (pflags & PRINT_DEFAULT)
216                 if (pflags & PRINT_JAIL_NAME)
217                         xo_emit("{P: }{T:JID/%-15s}{P: }{T:IP Address/%-15s}"
218                                 "{P: }{T:Hostname/%-29s}{P: }{T:Path}\n");
219                 else
220                         xo_emit("{T:JID/%6s}{P:  }{T:IP Address}{P:/%6s}"
221                                 "{T:Hostname}{P:/%22s}{T:Path}\n", "", "");
222         else if (pflags & PRINT_HEADER) {
223                 for (i = spc = 0; i < nparams; i++)
224                         if (params[i].jp_flags & JP_USER) {
225                                 if (spc)
226                                         xo_emit("{P: }");
227                                 else
228                                         spc = 1;
229                                 xo_emit(params[i].jp_name);
230                         }
231                 xo_emit("{P:\n}");
232         }
233
234         xo_open_container("jail-information");
235         xo_open_list("jail");
236         /* Fetch the jail(s) and print the parameters. */
237         if (jid != 0 || jname != NULL) {
238                 if (print_jail(pflags, jflags) < 0)
239                         xo_errx(1, "%s", jail_errmsg);
240         } else {
241                 for (lastjid = 0;
242                      (lastjid = print_jail(pflags, jflags)) >= 0; )
243                         ;
244                 if (errno != 0 && errno != ENOENT)
245                         xo_errx(1, "%s", jail_errmsg);
246         }
247         xo_close_list("jail");
248         xo_close_container("jail-information");
249         xo_finish();
250         return (0);
251 }
252
253 static int
254 add_param(const char *name, void *value, size_t valuelen,
255     struct jailparam *source, unsigned flags)
256 {
257         struct jailparam *param, *tparams;
258         int i, tnparams;
259
260         static int paramlistsize;
261
262         /* The pseudo-parameter "all" scans the list of available parameters. */
263         if (!strcmp(name, "all")) {
264                 tnparams = jailparam_all(&tparams);
265                 if (tnparams < 0)
266                         xo_errx(1, "%s", jail_errmsg);
267                 qsort(tparams, (size_t)tnparams, sizeof(struct jailparam),
268                     sort_param);
269                 for (i = 0; i < tnparams; i++)
270                         add_param(tparams[i].jp_name, NULL, (size_t)0,
271                             tparams + i, flags);
272                 free(tparams);
273                 return -1;
274         }
275
276         /* Check for repeat parameters. */
277         for (i = 0; i < nparams; i++)
278                 if (!strcmp(name, params[i].jp_name)) {
279                         if (value != NULL && jailparam_import_raw(params + i,
280                             value, valuelen) < 0)
281                                 xo_errx(1, "%s", jail_errmsg);
282                         params[i].jp_flags |= flags;
283                         if (source != NULL)
284                                 jailparam_free(source, 1);
285                         return i;
286                 }
287
288         /* Make sure there is room for the new param record. */
289         if (!nparams) {
290                 paramlistsize = 32;
291                 params = malloc(paramlistsize * sizeof(*params));
292                 param_parent = malloc(paramlistsize * sizeof(*param_parent));
293                 if (params == NULL || param_parent == NULL)
294                         xo_err(1, "malloc");
295         } else if (nparams >= paramlistsize) {
296                 paramlistsize *= 2;
297                 params = realloc(params, paramlistsize * sizeof(*params));
298                 param_parent = realloc(param_parent,
299                     paramlistsize * sizeof(*param_parent));
300                 if (params == NULL || param_parent == NULL)
301                         xo_err(1, "realloc");
302         }
303
304         /* Look up the parameter. */
305         param_parent[nparams] = -1;
306         param = params + nparams++;
307         if (source != NULL) {
308                 *param = *source;
309                 param->jp_flags |= flags;
310                 return param - params;
311         }
312         if (jailparam_init(param, name) < 0 ||
313             (value != NULL ? jailparam_import_raw(param, value, valuelen)
314              : jailparam_import(param, value)) < 0) {
315                 if (flags & JP_OPT) {
316                         nparams--;
317                         return (-1);
318                 }
319                 xo_errx(1, "%s", jail_errmsg);
320         }
321         param->jp_flags = flags;
322         return param - params;
323 }
324
325 static int
326 sort_param(const void *a, const void *b)
327 {
328         const struct jailparam *parama, *paramb;
329         char *ap, *bp;
330
331         /* Put top-level parameters first. */
332         parama = a;
333         paramb = b;
334         ap = strchr(parama->jp_name, '.');
335         bp = strchr(paramb->jp_name, '.');
336         if (ap && !bp)
337                 return (1);
338         if (bp && !ap)
339                 return (-1);
340         return (strcmp(parama->jp_name, paramb->jp_name));
341 }
342
343 static char *
344 noname(const char *name)
345 {
346         char *nname, *p;
347
348         nname = malloc(strlen(name) + 3);
349         if (nname == NULL)
350                 xo_err(1, "malloc");
351         p = strrchr(name, '.');
352         if (p != NULL)
353                 sprintf(nname, "%.*s.no%s", (int)(p - name), name, p + 1);
354         else
355                 sprintf(nname, "no%s", name);
356         return nname;
357 }
358
359 static char *
360 nononame(const char *name)
361 {
362         char *nname, *p;
363
364         p = strrchr(name, '.');
365         if (strncmp(p ? p + 1 : name, "no", 2))
366                 return NULL;
367         nname = malloc(strlen(name) - 1);
368         if (nname == NULL)
369                 xo_err(1, "malloc");
370         if (p != NULL)
371                 sprintf(nname, "%.*s.%s", (int)(p - name), name, p + 3);
372         else
373                 strcpy(nname, name + 2);
374         return nname;
375 }
376
377 static int
378 print_jail(int pflags, int jflags)
379 {
380         char *nname, *xo_nname;
381         char **param_values;
382         int i, ai, jid, count, n, spc;
383         char ipbuf[INET6_ADDRSTRLEN];
384
385         jid = jailparam_get(params, nparams, jflags);
386         if (jid < 0)
387                 return jid;
388
389         xo_open_instance("jail");
390
391         if (pflags & PRINT_VERBOSE) {
392                 xo_emit("{:jid/%6d}{P:  }{:hostname/%-29.29s/%s}{P: }"
393                     "{:path/%.74s/%s}\n",
394                     *(int *)params[0].jp_value,
395                     (char *)params[1].jp_value,
396                     (char *)params[2].jp_value);
397                 xo_emit("{P:        }{:name/%-29.29s/%s}{P: }{:state/%.74s}\n",
398                     (char *)params[3].jp_value,
399                     *(int *)params[4].jp_value ? "DYING" : "ACTIVE");
400                 xo_emit("{P:        }{:cpusetid/%d}\n", *(int *)params[5].jp_value);
401                 n = 6;
402 #ifdef INET
403                 if (ip4_ok && !strcmp(params[n].jp_name, "ip4.addr")) {
404                         count = params[n].jp_valuelen / sizeof(struct in_addr);
405                         for (ai = 0; ai < count; ai++)
406                                 if (inet_ntop(AF_INET,
407                                     &((struct in_addr *)params[n].jp_value)[ai],
408                                     ipbuf, sizeof(ipbuf)) == NULL)
409                                         xo_err(1, "inet_ntop");
410                                 else {
411                                         xo_emit("{P:        }{l:ipv4_addrs}{P:\n}", ipbuf);
412                                 }
413                         n++;
414                 }
415 #endif
416 #ifdef INET6
417                 if (ip6_ok && !strcmp(params[n].jp_name, "ip6.addr")) {
418                         count = params[n].jp_valuelen / sizeof(struct in6_addr);
419                         for (ai = 0; ai < count; ai++)
420                                 if (inet_ntop(AF_INET6,
421                                     &((struct in6_addr *)
422                                         params[n].jp_value)[ai],
423                                     ipbuf, sizeof(ipbuf)) == NULL)
424                                         xo_err(1, "inet_ntop");
425                                 else
426                                         xo_emit("{P:        }{l:ipv6_addrs}{P:\n}", ipbuf);
427                         n++;
428                 }
429 #endif
430         } else if (pflags & PRINT_DEFAULT) {
431                 if (pflags & PRINT_JAIL_NAME)
432                         xo_emit("{P: }{:name/%-15s/%s}{P: }",
433                             (char *)params[0].jp_value);
434                 else
435                         xo_emit("{:jid/%6d}{P:  }", *(int *)params[0].jp_value);
436                 xo_emit("{:ipv4/%-15.15s/%s}{P: }{:hostname/%-29.29s/%s}{P: }{:path/%.74s/%s}\n",
437 #ifdef INET
438                     (!ip4_ok || params[1].jp_valuelen == 0) ? ""
439                     : inet_ntoa(*(struct in_addr *)params[1].jp_value),
440                     (char *)params[2-!ip4_ok].jp_value,
441                     (char *)params[3-!ip4_ok].jp_value);
442 #else
443                     "-",
444                     (char *)params[1].jp_value,
445                     (char *)params[2].jp_value);
446 #endif
447         } else {
448                 param_values = alloca(nparams * sizeof(*param_values));
449                 for (i = 0; i < nparams; i++) {
450                         if (!(params[i].jp_flags & JP_USER))
451                                 continue;
452                         param_values[i] = jailparam_export(params + i);
453                         if (param_values[i] == NULL)
454                                 xo_errx(1, "%s", jail_errmsg);
455                 }
456                 for (i = spc = 0; i < nparams; i++) {
457                         if (!(params[i].jp_flags & JP_USER))
458                                 continue;
459                         if ((pflags & PRINT_SKIP) &&
460                             ((!(params[i].jp_ctltype &
461                                 (CTLFLAG_WR | CTLFLAG_TUN))) ||
462                              (param_parent[i] >= 0 &&
463                               *(int *)params[param_parent[i]].jp_value !=
464                               JAIL_SYS_NEW)))
465                                 continue;
466                         if (spc)
467                                 xo_emit("{P: }");
468                         else
469                                 spc = 1;
470                         if (pflags & PRINT_NAMEVAL) {
471                                 /*
472                                  * Generally "name=value", but for booleans
473                                  * either "name" or "noname".
474                                  */
475                                 if (params[i].jp_flags &
476                                     (JP_BOOL | JP_NOBOOL)) {
477                                         if (*(int *)params[i].jp_value) {
478                                                 asprintf(&xo_nname, "{en:%s/true}", params[i].jp_name);
479                                                 xo_emit(xo_nname);
480                                                 xo_emit("{d:/%s}", params[i].jp_name);
481                                         }
482                                         else {
483                                                 nname = (params[i].jp_flags &
484                                                     JP_NOBOOL) ?
485                                                     nononame(params[i].jp_name)
486                                                     : noname(params[i].jp_name);
487                                                 if (params[i].jp_flags & JP_NOBOOL) {
488                                                         asprintf(&xo_nname, "{en:%s/true}", params[i].jp_name);
489                                                         xo_emit(xo_nname);
490                                                 } else {
491                                                         asprintf(&xo_nname, "{en:%s/false}", params[i].jp_name);
492                                                         xo_emit(xo_nname);
493                                                 }
494                                                 xo_emit("{d:/%s}", nname);
495                                                 free(nname);
496                                         }
497                                         free(xo_nname);
498                                         continue;
499                                 }
500                                 xo_emit("{d:%s}=", params[i].jp_name);
501                         }
502                         if (params[i].jp_valuelen == 0) {
503                                 if (pflags & PRINT_QUOTED)
504                                         xo_emit("{P:\"\"}");
505                                 else if (!(pflags & PRINT_NAMEVAL))
506                                         xo_emit("{P:-}");
507                         } else {
508                                 quoted_print(pflags, params[i].jp_name, param_values[i]);
509                         }
510                 }
511                 xo_emit("{P:\n}");
512                 for (i = 0; i < nparams; i++)
513                         if (params[i].jp_flags & JP_USER)
514                                 free(param_values[i]);
515         }
516
517         xo_close_instance("jail");
518         return (jid);
519 }
520
521 static void
522 quoted_print(int pflags, char *name, char *value)
523 {
524         int qc;
525         char *p = value;
526         char *param_name_value;
527
528         /* An empty string needs quoting. */
529         if (!*p) {
530                 asprintf(&param_name_value, "{k:%s}{d:%s/\"\"}", name, name);
531                 xo_emit(param_name_value);
532                 free(param_name_value);
533                 return;
534         }
535
536         asprintf(&param_name_value, "{:%s/%%s}", name);
537         /*
538          * The value will be surrounded by quotes if it contains spaces
539          * or quotes.
540          */
541         qc = strchr(p, '\'') ? '"'
542                 : strchr(p, '"') ? '\''
543                 : strchr(p, ' ') || strchr(p, '\t') ? '"'
544                 : 0;
545
546         if (qc && pflags & PRINT_QUOTED)
547                 xo_emit("{P:/%c}", qc);
548
549         xo_emit(param_name_value, value);
550
551         free(param_name_value);
552
553         if (qc && pflags & PRINT_QUOTED)
554                 xo_emit("{P:/%c}", qc);
555 }