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/param.h>
33#include <sys/jail.h>
34#include <sys/socket.h>
35#include <sys/sysctl.h>
36
37#include <arpa/inet.h>
38#include <netinet/in.h>
39
40#include <err.h>
41#include <errno.h>
42#include <jail.h>
43#include <limits.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48#include <libxo/xo.h>
49
50#define	JP_USER		0x01000000
51#define	JP_OPT		0x02000000
52
53#define JLS_XO_VERSION	"2"
54
55#define	PRINT_DEFAULT	0x01
56#define	PRINT_HEADER	0x02
57#define	PRINT_NAMEVAL	0x04
58#define	PRINT_QUOTED	0x08
59#define	PRINT_SKIP	0x10
60#define	PRINT_VERBOSE	0x20
61#define	PRINT_JAIL_NAME	0x40
62
63static struct jailparam *params;
64static int *param_parent;
65static int nparams;
66#ifdef INET6
67static int ip6_ok;
68#endif
69#ifdef INET
70static int ip4_ok;
71#endif
72
73static int add_param(const char *name, void *value, size_t valuelen,
74		struct jailparam *source, unsigned flags);
75static int sort_param(const void *a, const void *b);
76static char *noname(const char *name);
77static char *nononame(const char *name);
78static int print_jail(int pflags, int jflags);
79static int special_print(int pflags, struct jailparam *param);
80static void quoted_print(int pflags, char *name, char *value);
81static void emit_ip_addr_list(int af_family, const char *list_name,
82		struct jailparam *param);
83
84int
85main(int argc, char **argv)
86{
87	char *dot, *ep, *jname, *pname;
88	int c, i, jflags, jid, lastjid, pflags, spc;
89
90	argc = xo_parse_args(argc, argv);
91	if (argc < 0)
92		exit(1);
93
94        xo_set_version(JLS_XO_VERSION);
95	jname = NULL;
96	pflags = jflags = jid = 0;
97	while ((c = getopt(argc, argv, "adj:hNnqsv")) >= 0)
98		switch (c) {
99		case 'a':
100		case 'd':
101			jflags |= JAIL_DYING;
102			break;
103		case 'j':
104			jid = strtoul(optarg, &ep, 10);
105			if (!jid || *ep) {
106				jid = 0;
107				jname = optarg;
108			}
109			break;
110		case 'h':
111			pflags = (pflags & ~(PRINT_SKIP | PRINT_VERBOSE)) |
112			    PRINT_HEADER;
113			break;
114		case 'N':
115			pflags |= PRINT_JAIL_NAME;
116			break;
117		case 'n':
118			pflags = (pflags & ~PRINT_VERBOSE) | PRINT_NAMEVAL;
119			break;
120		case 'q':
121			pflags |= PRINT_QUOTED;
122			break;
123		case 's':
124			pflags = (pflags & ~(PRINT_HEADER | PRINT_VERBOSE)) |
125			    PRINT_NAMEVAL | PRINT_QUOTED | PRINT_SKIP;
126			break;
127		case 'v':
128			pflags = (pflags &
129			    ~(PRINT_HEADER | PRINT_NAMEVAL | PRINT_SKIP)) |
130			    PRINT_VERBOSE;
131			break;
132		default:
133			xo_errx(1, "usage: jls [-dhNnqv] [-j jail] [param ...]");
134		}
135
136#ifdef INET6
137	ip6_ok = feature_present("inet6");
138#endif
139#ifdef INET
140	ip4_ok = feature_present("inet");
141#endif
142
143	/* Add the parameters to print. */
144	if (optind == argc) {
145		if (pflags & (PRINT_HEADER | PRINT_NAMEVAL))
146			add_param("all", NULL, (size_t)0, NULL, JP_USER);
147		else if (pflags & PRINT_VERBOSE) {
148			add_param("jid", NULL, (size_t)0, NULL, JP_USER);
149			add_param("host.hostname", NULL, (size_t)0, NULL,
150			    JP_USER);
151			add_param("path", NULL, (size_t)0, NULL, JP_USER);
152			add_param("name", NULL, (size_t)0, NULL, JP_USER);
153			add_param("dying", NULL, (size_t)0, NULL, JP_USER);
154			add_param("cpuset.id", NULL, (size_t)0, NULL, JP_USER);
155#ifdef INET
156			if (ip4_ok)
157				add_param("ip4.addr", NULL, (size_t)0, NULL,
158				    JP_USER);
159#endif
160#ifdef INET6
161			if (ip6_ok)
162				add_param("ip6.addr", NULL, (size_t)0, NULL,
163				    JP_USER | JP_OPT);
164#endif
165		} else {
166			pflags |= PRINT_DEFAULT;
167			if (pflags & PRINT_JAIL_NAME)
168				add_param("name", NULL, (size_t)0, NULL, JP_USER);
169			else
170				add_param("jid", NULL, (size_t)0, NULL, JP_USER);
171#ifdef INET
172			if (ip4_ok)
173				add_param("ip4.addr", NULL, (size_t)0, NULL,
174				    JP_USER);
175#endif
176			add_param("host.hostname", NULL, (size_t)0, NULL,
177			    JP_USER);
178			add_param("path", NULL, (size_t)0, NULL, JP_USER);
179		}
180	} else {
181		pflags &= ~PRINT_VERBOSE;
182		while (optind < argc)
183			add_param(argv[optind++], NULL, (size_t)0, NULL,
184			    JP_USER);
185	}
186
187	if (pflags & PRINT_SKIP) {
188		/* Check for parameters with jailsys parents. */
189		for (i = 0; i < nparams; i++) {
190			if ((params[i].jp_flags & JP_USER) &&
191			    (dot = strchr(params[i].jp_name, '.'))) {
192				pname = alloca((dot - params[i].jp_name) + 1);
193				strlcpy(pname, params[i].jp_name,
194				    (dot - params[i].jp_name) + 1);
195				param_parent[i] = add_param(pname,
196				    NULL, (size_t)0, NULL, JP_OPT);
197			}
198		}
199	}
200
201	/* Add the index key parameters. */
202	if (jid != 0)
203		add_param("jid", &jid, sizeof(jid), NULL, 0);
204	else if (jname != NULL)
205		add_param("name", jname, strlen(jname), NULL, 0);
206	else
207		add_param("lastjid", &lastjid, sizeof(lastjid), NULL, 0);
208
209	/* Print a header line if requested. */
210	if (pflags & PRINT_VERBOSE) {
211		xo_emit("{T:/%3s}{T:JID}{P:  }{T:Hostname}{Pd:/%22s}{T:Path}\n",
212		        "", "");
213		xo_emit("{P:/%8s}{T:Name}{Pd:/%26s}{T:State}\n", "", "");
214		xo_emit("{P:/%8s}{T:CPUSetID}\n", "");
215		xo_emit("{P:/%8s}{T:IP Address(es)}\n", "");
216	}
217	else if (pflags & PRINT_DEFAULT)
218		if (pflags & PRINT_JAIL_NAME)
219			xo_emit("{P: }{T:JID/%-15s}{P: }{T:IP Address/%-15s}"
220			        "{P: }{T:Hostname/%-29s}{P: }{T:Path}\n");
221		else
222			xo_emit("{T:JID/%6s}{P:  }{T:IP Address}{P:/%6s}"
223			        "{T:Hostname}{P:/%22s}{T:Path}\n", "", "");
224	else if (pflags & PRINT_HEADER) {
225		for (i = spc = 0; i < nparams; i++)
226			if (params[i].jp_flags & JP_USER) {
227				if (spc)
228					xo_emit("{P: }");
229				else
230					spc = 1;
231				xo_emit(params[i].jp_name);
232			}
233		xo_emit("{P:\n}");
234	}
235
236	xo_open_container("jail-information");
237	xo_open_list("jail");
238	/* Fetch the jail(s) and print the parameters. */
239	if (jid != 0 || jname != NULL) {
240		if (print_jail(pflags, jflags) < 0)
241			xo_errx(1, "%s", jail_errmsg);
242	} else {
243		for (lastjid = 0;
244		     (lastjid = print_jail(pflags, jflags)) >= 0; )
245			;
246		if (errno != 0 && errno != ENOENT)
247			xo_errx(1, "%s", jail_errmsg);
248	}
249	xo_close_list("jail");
250	xo_close_container("jail-information");
251	xo_finish();
252	return (0);
253}
254
255static int
256add_param(const char *name, void *value, size_t valuelen,
257    struct jailparam *source, unsigned flags)
258{
259	struct jailparam *param, *tparams;
260	int i, tnparams;
261
262	static int paramlistsize;
263
264	/* The pseudo-parameter "all" scans the list of available parameters. */
265	if (!strcmp(name, "all")) {
266		tnparams = jailparam_all(&tparams);
267		if (tnparams < 0)
268			xo_errx(1, "%s", jail_errmsg);
269		qsort(tparams, (size_t)tnparams, sizeof(struct jailparam),
270		    sort_param);
271		for (i = 0; i < tnparams; i++)
272			add_param(tparams[i].jp_name, NULL, (size_t)0,
273			    tparams + i, flags);
274		free(tparams);
275		return -1;
276	}
277
278	/* Check for repeat parameters. */
279	for (i = 0; i < nparams; i++)
280		if (!strcmp(name, params[i].jp_name)) {
281			if (value != NULL && jailparam_import_raw(params + i,
282			    value, valuelen) < 0)
283				xo_errx(1, "%s", jail_errmsg);
284			params[i].jp_flags |= flags;
285			if (source != NULL)
286				jailparam_free(source, 1);
287			return i;
288		}
289
290	/* Make sure there is room for the new param record. */
291	if (!nparams) {
292		paramlistsize = 32;
293		params = malloc(paramlistsize * sizeof(*params));
294		param_parent = malloc(paramlistsize * sizeof(*param_parent));
295		if (params == NULL || param_parent == NULL)
296			xo_err(1, "malloc");
297	} else if (nparams >= paramlistsize) {
298		paramlistsize *= 2;
299		params = realloc(params, paramlistsize * sizeof(*params));
300		param_parent = realloc(param_parent,
301		    paramlistsize * sizeof(*param_parent));
302		if (params == NULL || param_parent == NULL)
303			xo_err(1, "realloc");
304	}
305
306	/* Look up the parameter. */
307	param_parent[nparams] = -1;
308	param = params + nparams++;
309	if (source != NULL) {
310		*param = *source;
311		param->jp_flags |= flags;
312		return param - params;
313	}
314	if (jailparam_init(param, name) < 0 ||
315	    (value != NULL ? jailparam_import_raw(param, value, valuelen)
316	     : jailparam_import(param, value)) < 0) {
317		if (flags & JP_OPT) {
318			nparams--;
319			return (-1);
320		}
321		xo_errx(1, "%s", jail_errmsg);
322	}
323	param->jp_flags |= flags;
324	return param - params;
325}
326
327static int
328sort_param(const void *a, const void *b)
329{
330	const struct jailparam *parama, *paramb;
331	char *ap, *bp;
332
333	/* Put top-level parameters first. */
334	parama = a;
335	paramb = b;
336	ap = strchr(parama->jp_name, '.');
337	bp = strchr(paramb->jp_name, '.');
338	if (ap && !bp)
339		return (1);
340	if (bp && !ap)
341		return (-1);
342	return (strcmp(parama->jp_name, paramb->jp_name));
343}
344
345static char *
346noname(const char *name)
347{
348	char *nname, *p;
349
350	nname = malloc(strlen(name) + 3);
351	if (nname == NULL)
352		xo_err(1, "malloc");
353	p = strrchr(name, '.');
354	if (p != NULL)
355		sprintf(nname, "%.*s.no%s", (int)(p - name), name, p + 1);
356	else
357		sprintf(nname, "no%s", name);
358	return nname;
359}
360
361static char *
362nononame(const char *name)
363{
364	char *nname, *p;
365
366	p = strrchr(name, '.');
367	if (strncmp(p ? p + 1 : name, "no", 2))
368		return NULL;
369	nname = malloc(strlen(name) - 1);
370	if (nname == NULL)
371		xo_err(1, "malloc");
372	if (p != NULL)
373		sprintf(nname, "%.*s.%s", (int)(p - name), name, p + 3);
374	else
375		strcpy(nname, name + 2);
376	return nname;
377}
378
379static int
380print_jail(int pflags, int jflags)
381{
382	char *nname, *xo_nname;
383	char **param_values;
384	int i, jid, spc;
385#if (defined INET || defined INET6)
386	int n;
387#endif
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#if (defined INET || defined INET6)
406		n = 6;
407#endif
408#ifdef INET
409		if (ip4_ok && !strcmp(params[n].jp_name, "ip4.addr")) {
410			emit_ip_addr_list(AF_INET, "ipv4_addrs", params + n);
411			n++;
412		}
413#endif
414#ifdef INET6
415		if (ip6_ok && !strcmp(params[n].jp_name, "ip6.addr")) {
416			emit_ip_addr_list(AF_INET6, "ipv6_addrs", params + n);
417			n++;
418		}
419#endif
420	} else if (pflags & PRINT_DEFAULT) {
421		if (pflags & PRINT_JAIL_NAME)
422			xo_emit("{P: }{:name/%-15s/%s}{P: }",
423			    (char *)params[0].jp_value);
424		else
425			xo_emit("{:jid/%6d}{P:  }", *(int *)params[0].jp_value);
426		xo_emit("{:ipv4/%-15.15s/%s}{P: }{:hostname/%-29.29s/%s}{P: }{:path/%.74s/%s}\n",
427#ifdef INET
428		    (!ip4_ok || params[1].jp_valuelen == 0) ? ""
429		    : inet_ntoa(*(struct in_addr *)params[1].jp_value),
430		    (char *)params[2-!ip4_ok].jp_value,
431		    (char *)params[3-!ip4_ok].jp_value);
432#else
433		    "-",
434		    (char *)params[1].jp_value,
435		    (char *)params[2].jp_value);
436#endif
437	} else {
438		param_values = alloca(nparams * sizeof(*param_values));
439		for (i = 0; i < nparams; i++) {
440			if (!(params[i].jp_flags & JP_USER))
441				continue;
442			param_values[i] = jailparam_export(params + i);
443			if (param_values[i] == NULL)
444				xo_errx(1, "%s", jail_errmsg);
445		}
446		for (i = spc = 0; i < nparams; i++) {
447			if (!(params[i].jp_flags & JP_USER))
448				continue;
449			if ((pflags & PRINT_SKIP) &&
450			    ((!(params[i].jp_ctltype &
451				(CTLFLAG_WR | CTLFLAG_TUN))) ||
452			     (param_parent[i] >= 0 &&
453			      *(int *)params[param_parent[i]].jp_value !=
454			      JAIL_SYS_NEW)))
455				continue;
456			if (spc)
457				xo_emit("{P: }");
458			else
459				spc = 1;
460			if (pflags & PRINT_NAMEVAL) {
461				/*
462				 * Generally "name=value", but for booleans
463				 * either "name" or "noname".
464				 */
465				if (params[i].jp_flags &
466				    (JP_BOOL | JP_NOBOOL)) {
467					if (*(int *)params[i].jp_value) {
468						asprintf(&xo_nname, "{en:%s/true}", params[i].jp_name);
469						xo_emit(xo_nname);
470						xo_emit("{d:/%s}", params[i].jp_name);
471					}
472					else {
473						nname = (params[i].jp_flags &
474						    JP_NOBOOL) ?
475						    nononame(params[i].jp_name)
476						    : noname(params[i].jp_name);
477						if (params[i].jp_flags & JP_NOBOOL) {
478							asprintf(&xo_nname, "{en:%s/true}", params[i].jp_name);
479							xo_emit(xo_nname);
480						} else {
481							asprintf(&xo_nname, "{en:%s/false}", params[i].jp_name);
482							xo_emit(xo_nname);
483						}
484						xo_emit("{d:/%s}", nname);
485						free(nname);
486					}
487					free(xo_nname);
488					continue;
489				}
490				xo_emit("{d:%s}=", params[i].jp_name);
491			}
492			if (!special_print(pflags, params + i))
493				quoted_print(pflags, params[i].jp_name, param_values[i]);
494		}
495		xo_emit("{P:\n}");
496		for (i = 0; i < nparams; i++)
497			if (params[i].jp_flags & JP_USER)
498				free(param_values[i]);
499	}
500
501	xo_close_instance("jail");
502	return (jid);
503}
504
505static void
506quoted_print(int pflags, char *name, char *value)
507{
508	int qc;
509	char *p = value;
510
511	/* An empty string needs quoting. */
512	if (!*p) {
513		xo_emit("{ea:/%s}{da:/\"\"}", name, value, name);
514		return;
515	}
516
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("{a:/%s}", name, value);
530
531	if (qc && pflags & PRINT_QUOTED)
532		xo_emit("{P:/%c}", qc);
533}
534
535static int
536special_print(int pflags, struct jailparam *param)
537{
538	int ip_as_list;
539
540	switch (xo_get_style(NULL)) {
541	case XO_STYLE_JSON:
542	case XO_STYLE_XML:
543		ip_as_list = 1;
544		break;
545	default:
546		ip_as_list = 0;
547	}
548
549	if (!ip_as_list && param->jp_valuelen == 0) {
550		if (pflags & PRINT_QUOTED)
551			xo_emit("{P:\"\"}");
552		else if (!(pflags & PRINT_NAMEVAL))
553			xo_emit("{P:-}");
554	} else if (ip_as_list && !strcmp(param->jp_name, "ip4.addr")) {
555		emit_ip_addr_list(AF_INET, param->jp_name, param);
556	} else if (ip_as_list && !strcmp(param->jp_name, "ip6.addr")) {
557		emit_ip_addr_list(AF_INET6, param->jp_name, param);
558	} else {
559		return 0;
560	}
561
562	return 1;
563}
564
565static void
566emit_ip_addr_list(int af_family, const char *list_name, struct jailparam *param)
567{
568	char ipbuf[INET6_ADDRSTRLEN];
569	size_t addr_len;
570	const char *emit_str;
571	int ai, count;
572
573	switch (af_family) {
574	case AF_INET:
575		addr_len = sizeof(struct in_addr);
576		emit_str = "{P:        }{ql:ipv4_addr}{P:\n}";
577		break;
578	case AF_INET6:
579		addr_len = sizeof(struct in6_addr);
580		emit_str = "{P:        }{ql:ipv6_addr}{P:\n}";
581		break;
582	default:
583		xo_err(1, "unsupported af_family");
584		return;
585	}
586
587	count = param->jp_valuelen / addr_len;
588
589	xo_open_list(list_name);
590	for (ai = 0; ai < count; ai++) {
591		if (inet_ntop(af_family,
592		    ((uint8_t *)param->jp_value) + addr_len * ai,
593		    ipbuf, sizeof(ipbuf)) == NULL) {
594			xo_err(1, "inet_ntop");
595		} else {
596			xo_emit(emit_str, ipbuf);
597		}
598	}
599	xo_close_list(list_name);
600}
601