Index: head/usr.sbin/jls/Makefile =================================================================== --- head/usr.sbin/jls/Makefile (revision 292579) +++ head/usr.sbin/jls/Makefile (revision 292580) @@ -1,16 +1,16 @@ # $FreeBSD$ .include PROG= jls MAN= jls.8 -LIBADD= jail +LIBADD= jail xo .if ${MK_INET6_SUPPORT} != "no" CFLAGS+= -DINET6 .endif .if ${MK_INET_SUPPORT} != "no" CFLAGS+= -DINET .endif .include Index: head/usr.sbin/jls/jls.8 =================================================================== --- head/usr.sbin/jls/jls.8 (revision 292579) +++ head/usr.sbin/jls/jls.8 (revision 292580) @@ -1,116 +1,128 @@ .\" .\" Copyright (c) 2003 Mike Barcroft .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" .Dd July 20, 2012 .Dt JLS 8 .Os .Sh NAME .Nm jls .Nd "list jails" .Sh SYNOPSIS .Nm +.Op Fl -libxo .Op Fl dhNnqsv .Op Fl j Ar jail .Op Ar parameter ... .Sh DESCRIPTION The .Nm utility lists all active jails, or the specified jail. Each jail is represented by one row which contains space-separated values of the listed .Ar parameters , including the pseudo-parameter .Va all which will show all available jail parameters. A list of available parameters can be retrieved via .Dq Nm sysctl Fl d Va security.jail.param . See .Xr jail 8 for a description of some core parameters. .Pp If no .Ar parameters or any of the options .Fl hns are given, the following four columns will be printed: jail identifier (jid), IP address (ip4.addr), hostname (host.hostname), and path (path). .Pp The following options are available: .Bl -tag -width indent +.It Fl -libxo +Generate output via +.Xr libxo 3 +in a selection of different human and machine readable formats. +See +.Xr xo_parse_args 3 +for details on command line arguments. .It Fl d List .Va dying as well as active jails. .It Fl h Print a header line containing the parameters listed. If no parameters are given on the command line, .Va all is assumed. .It Fl N In the standard display mode, print each jail's name instead of its numeric ID. If the jail does not have a name, the numeric ID is printed instead. .It Fl n Print parameters in .Dq name=value format, where each parameter is preceded by its name. If no parameters are given on the command line, .Va all is assumed. .It Fl q Put quotes around parameters if they contain spaces or quotes, or are the empty string. .It Fl s Print parameters suitable for passing to .Xr jail 8 , skipping read-only and unused parameters. Implies .Fl nq . .It Fl v Extend the standard display with a multiple-line summary per jail, containing the following parameters: jail identifier (jid), hostname (host.hostname), path (path), jail name (name), jail state (dying), cpuset ID (cpuset), IP address(es) (ip4.addr and ip6.addr). .It Fl j Ar jail The jid or name of the .Ar jail to list. Without this option, all active jails will be listed. .El .Sh SEE ALSO .Xr jail_get 2 , .Xr jail 8 , -.Xr jexec 8 +.Xr jexec 8 , +.Xr libxo 3 , +.Xr xo_parse_args 3 .Sh HISTORY The .Nm utility was added in .Fx 5.1 . Extensible jail parameters were introduced in .Fx 8.0 . +libxo support was added in +.Fx 11.0 . Index: head/usr.sbin/jls/jls.c =================================================================== --- head/usr.sbin/jls/jls.c (revision 292579) +++ head/usr.sbin/jls/jls.c (revision 292580) @@ -1,518 +1,555 @@ /*- * Copyright (c) 2003 Mike Barcroft * Copyright (c) 2008 Bjoern A. Zeeb * Copyright (c) 2009 James Gritton + * Copyright (c) 2015 Emmanuel Vadot * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #define JP_USER 0x01000000 #define JP_OPT 0x02000000 +#define JLS_XO_VERSION "1" + #define PRINT_DEFAULT 0x01 #define PRINT_HEADER 0x02 #define PRINT_NAMEVAL 0x04 #define PRINT_QUOTED 0x08 #define PRINT_SKIP 0x10 #define PRINT_VERBOSE 0x20 #define PRINT_JAIL_NAME 0x40 static struct jailparam *params; static int *param_parent; static int nparams; #ifdef INET6 static int ip6_ok; #endif #ifdef INET static int ip4_ok; #endif static int add_param(const char *name, void *value, size_t valuelen, struct jailparam *source, unsigned flags); static int sort_param(const void *a, const void *b); static char *noname(const char *name); static char *nononame(const char *name); static int print_jail(int pflags, int jflags); -static void quoted_print(char *str); +static void quoted_print(int pflags, char *name, char *value); int main(int argc, char **argv) { char *dot, *ep, *jname, *pname; int c, i, jflags, jid, lastjid, pflags, spc; + argc = xo_parse_args(argc, argv); + if (argc < 0) + exit(1); + + xo_set_version(JLS_XO_VERSION); jname = NULL; pflags = jflags = jid = 0; while ((c = getopt(argc, argv, "adj:hNnqsv")) >= 0) switch (c) { case 'a': case 'd': jflags |= JAIL_DYING; break; case 'j': jid = strtoul(optarg, &ep, 10); if (!jid || *ep) { jid = 0; jname = optarg; } break; case 'h': pflags = (pflags & ~(PRINT_SKIP | PRINT_VERBOSE)) | PRINT_HEADER; break; case 'N': pflags |= PRINT_JAIL_NAME; break; case 'n': pflags = (pflags & ~PRINT_VERBOSE) | PRINT_NAMEVAL; break; case 'q': pflags |= PRINT_QUOTED; break; case 's': pflags = (pflags & ~(PRINT_HEADER | PRINT_VERBOSE)) | PRINT_NAMEVAL | PRINT_QUOTED | PRINT_SKIP; break; case 'v': pflags = (pflags & ~(PRINT_HEADER | PRINT_NAMEVAL | PRINT_SKIP)) | PRINT_VERBOSE; break; default: - errx(1, "usage: jls [-dhNnqv] [-j jail] [param ...]"); + xo_errx(1, "usage: jls [-dhNnqv] [-j jail] [param ...]"); } #ifdef INET6 ip6_ok = feature_present("inet6"); #endif #ifdef INET ip4_ok = feature_present("inet"); #endif /* Add the parameters to print. */ if (optind == argc) { if (pflags & (PRINT_HEADER | PRINT_NAMEVAL)) add_param("all", NULL, (size_t)0, NULL, JP_USER); else if (pflags & PRINT_VERBOSE) { add_param("jid", NULL, (size_t)0, NULL, JP_USER); add_param("host.hostname", NULL, (size_t)0, NULL, JP_USER); add_param("path", NULL, (size_t)0, NULL, JP_USER); add_param("name", NULL, (size_t)0, NULL, JP_USER); add_param("dying", NULL, (size_t)0, NULL, JP_USER); add_param("cpuset.id", NULL, (size_t)0, NULL, JP_USER); #ifdef INET if (ip4_ok) add_param("ip4.addr", NULL, (size_t)0, NULL, JP_USER); #endif #ifdef INET6 if (ip6_ok) add_param("ip6.addr", NULL, (size_t)0, NULL, JP_USER | JP_OPT); #endif } else { pflags |= PRINT_DEFAULT; if (pflags & PRINT_JAIL_NAME) add_param("name", NULL, (size_t)0, NULL, JP_USER); else add_param("jid", NULL, (size_t)0, NULL, JP_USER); #ifdef INET if (ip4_ok) add_param("ip4.addr", NULL, (size_t)0, NULL, JP_USER); #endif add_param("host.hostname", NULL, (size_t)0, NULL, JP_USER); add_param("path", NULL, (size_t)0, NULL, JP_USER); } } else { pflags &= ~PRINT_VERBOSE; while (optind < argc) add_param(argv[optind++], NULL, (size_t)0, NULL, JP_USER); } if (pflags & PRINT_SKIP) { /* Check for parameters with jailsys parents. */ for (i = 0; i < nparams; i++) { if ((params[i].jp_flags & JP_USER) && (dot = strchr(params[i].jp_name, '.'))) { pname = alloca((dot - params[i].jp_name) + 1); strlcpy(pname, params[i].jp_name, (dot - params[i].jp_name) + 1); param_parent[i] = add_param(pname, NULL, (size_t)0, NULL, JP_OPT); } } } /* Add the index key parameters. */ if (jid != 0) add_param("jid", &jid, sizeof(jid), NULL, 0); else if (jname != NULL) add_param("name", jname, strlen(jname), NULL, 0); else add_param("lastjid", &lastjid, sizeof(lastjid), NULL, 0); /* Print a header line if requested. */ - if (pflags & PRINT_VERBOSE) - printf(" JID Hostname Path\n" - " Name State\n" - " CPUSetID\n" - " IP Address(es)\n"); + if (pflags & PRINT_VERBOSE) { + xo_emit("{T:/%3s}{T:JID}{P: }{T:Hostname}{Pd:/%22s}{T:Path}\n", + "", ""); + xo_emit("{P:/%8s}{T:Name}{Pd:/%26s}{T:State}\n", "", ""); + xo_emit("{P:/%8s}{T:CPUSetID}\n", ""); + xo_emit("{P:/%8s}{T:IP Address(es)}\n", ""); + } else if (pflags & PRINT_DEFAULT) if (pflags & PRINT_JAIL_NAME) - printf(" JID IP Address " - "Hostname Path\n"); + xo_emit("{P: }{T:JID/%-15s}{P: }{T:IP Address/%-15s}" + "{P: }{T:Hostname/%-29s}{P: }{T:Path}\n"); else - printf(" JID IP Address " - "Hostname Path\n"); + xo_emit("{T:JID/%6s}{P: }{T:IP Address}{P:/%6s}" + "{T:Hostname}{P:/%22s}{T:Path}\n", "", ""); else if (pflags & PRINT_HEADER) { for (i = spc = 0; i < nparams; i++) if (params[i].jp_flags & JP_USER) { if (spc) - putchar(' '); + xo_emit("{P: }"); else spc = 1; - fputs(params[i].jp_name, stdout); + xo_emit(params[i].jp_name); } - putchar('\n'); + xo_emit("{P:\n}"); } + xo_open_container("jail-information"); + xo_open_list("jail"); /* Fetch the jail(s) and print the parameters. */ if (jid != 0 || jname != NULL) { if (print_jail(pflags, jflags) < 0) - errx(1, "%s", jail_errmsg); + xo_errx(1, "%s", jail_errmsg); } else { for (lastjid = 0; (lastjid = print_jail(pflags, jflags)) >= 0; ) ; if (errno != 0 && errno != ENOENT) - errx(1, "%s", jail_errmsg); + xo_errx(1, "%s", jail_errmsg); } - + xo_close_list("jail"); + xo_close_container("jail-information"); + xo_finish(); return (0); } static int add_param(const char *name, void *value, size_t valuelen, struct jailparam *source, unsigned flags) { struct jailparam *param, *tparams; int i, tnparams; static int paramlistsize; /* The pseudo-parameter "all" scans the list of available parameters. */ if (!strcmp(name, "all")) { tnparams = jailparam_all(&tparams); if (tnparams < 0) - errx(1, "%s", jail_errmsg); + xo_errx(1, "%s", jail_errmsg); qsort(tparams, (size_t)tnparams, sizeof(struct jailparam), sort_param); for (i = 0; i < tnparams; i++) add_param(tparams[i].jp_name, NULL, (size_t)0, tparams + i, flags); free(tparams); return -1; } /* Check for repeat parameters. */ for (i = 0; i < nparams; i++) if (!strcmp(name, params[i].jp_name)) { if (value != NULL && jailparam_import_raw(params + i, value, valuelen) < 0) - errx(1, "%s", jail_errmsg); + xo_errx(1, "%s", jail_errmsg); params[i].jp_flags |= flags; if (source != NULL) jailparam_free(source, 1); return i; } /* Make sure there is room for the new param record. */ if (!nparams) { paramlistsize = 32; params = malloc(paramlistsize * sizeof(*params)); param_parent = malloc(paramlistsize * sizeof(*param_parent)); if (params == NULL || param_parent == NULL) - err(1, "malloc"); + xo_err(1, "malloc"); } else if (nparams >= paramlistsize) { paramlistsize *= 2; params = realloc(params, paramlistsize * sizeof(*params)); param_parent = realloc(param_parent, paramlistsize * sizeof(*param_parent)); if (params == NULL || param_parent == NULL) - err(1, "realloc"); + xo_err(1, "realloc"); } /* Look up the parameter. */ param_parent[nparams] = -1; param = params + nparams++; if (source != NULL) { *param = *source; param->jp_flags |= flags; return param - params; } if (jailparam_init(param, name) < 0 || (value != NULL ? jailparam_import_raw(param, value, valuelen) : jailparam_import(param, value)) < 0) { if (flags & JP_OPT) { nparams--; return (-1); } - errx(1, "%s", jail_errmsg); + xo_errx(1, "%s", jail_errmsg); } param->jp_flags = flags; return param - params; } static int sort_param(const void *a, const void *b) { const struct jailparam *parama, *paramb; char *ap, *bp; /* Put top-level parameters first. */ parama = a; paramb = b; ap = strchr(parama->jp_name, '.'); bp = strchr(paramb->jp_name, '.'); if (ap && !bp) return (1); if (bp && !ap) return (-1); return (strcmp(parama->jp_name, paramb->jp_name)); } static char * noname(const char *name) { char *nname, *p; nname = malloc(strlen(name) + 3); if (nname == NULL) - err(1, "malloc"); + xo_err(1, "malloc"); p = strrchr(name, '.'); if (p != NULL) sprintf(nname, "%.*s.no%s", (int)(p - name), name, p + 1); else sprintf(nname, "no%s", name); return nname; } static char * nononame(const char *name) { char *nname, *p; p = strrchr(name, '.'); if (strncmp(p ? p + 1 : name, "no", 2)) return NULL; nname = malloc(strlen(name) - 1); if (nname == NULL) - err(1, "malloc"); + xo_err(1, "malloc"); if (p != NULL) sprintf(nname, "%.*s.%s", (int)(p - name), name, p + 3); else strcpy(nname, name + 2); return nname; } static int print_jail(int pflags, int jflags) { - char *nname; + char *nname, *xo_nname; char **param_values; int i, ai, jid, count, n, spc; char ipbuf[INET6_ADDRSTRLEN]; jid = jailparam_get(params, nparams, jflags); if (jid < 0) return jid; + + xo_open_instance("jail"); + if (pflags & PRINT_VERBOSE) { - printf("%6d %-29.29s %.74s\n" - "%6s %-29.29s %.74s\n" - "%6s %-6d\n", + xo_emit("{:jid/%6d}{P: }{:hostname/%-29.29s/%s}{P: }" + "{:path/%.74s/%s}\n", *(int *)params[0].jp_value, (char *)params[1].jp_value, - (char *)params[2].jp_value, - "", + (char *)params[2].jp_value); + xo_emit("{P: }{:name/%-29.29s/%s}{P: }{:state/%.74s}\n", (char *)params[3].jp_value, - *(int *)params[4].jp_value ? "DYING" : "ACTIVE", - "", - *(int *)params[5].jp_value); + *(int *)params[4].jp_value ? "DYING" : "ACTIVE"); + xo_emit("{P: }{:cpusetid/%d}\n", *(int *)params[5].jp_value); n = 6; #ifdef INET if (ip4_ok && !strcmp(params[n].jp_name, "ip4.addr")) { count = params[n].jp_valuelen / sizeof(struct in_addr); for (ai = 0; ai < count; ai++) if (inet_ntop(AF_INET, &((struct in_addr *)params[n].jp_value)[ai], ipbuf, sizeof(ipbuf)) == NULL) - err(1, "inet_ntop"); - else - printf("%6s %-15.15s\n", "", ipbuf); + xo_err(1, "inet_ntop"); + else { + xo_emit("{P: }{l:ipv4_addrs}{P:\n}", ipbuf); + } n++; } #endif #ifdef INET6 if (ip6_ok && !strcmp(params[n].jp_name, "ip6.addr")) { count = params[n].jp_valuelen / sizeof(struct in6_addr); for (ai = 0; ai < count; ai++) if (inet_ntop(AF_INET6, &((struct in6_addr *) params[n].jp_value)[ai], ipbuf, sizeof(ipbuf)) == NULL) - err(1, "inet_ntop"); + xo_err(1, "inet_ntop"); else - printf("%6s %s\n", "", ipbuf); + xo_emit("{P: }{l:ipv6_addrs}{P:\n}", ipbuf); n++; } #endif } else if (pflags & PRINT_DEFAULT) { if (pflags & PRINT_JAIL_NAME) - printf(" %-15s ", (char *)params[0].jp_value); + xo_emit("{P: }{:name/%-15s/%s}{P: }", + (char *)params[0].jp_value); else - printf("%6d ", *(int *)params[0].jp_value); - printf("%-15.15s %-29.29s %.74s\n", + xo_emit("{:jid/%6d}{P: }", *(int *)params[0].jp_value); + xo_emit("{:ipv4/%-15.15s/%s}{P: }{:hostname/%-29.29s/%s}{P: }{:path/%.74s/%s}\n", #ifdef INET - (!ip4_ok || params[1].jp_valuelen == 0) ? "-" + (!ip4_ok || params[1].jp_valuelen == 0) ? "" : inet_ntoa(*(struct in_addr *)params[1].jp_value), (char *)params[2-!ip4_ok].jp_value, (char *)params[3-!ip4_ok].jp_value); #else "-", (char *)params[1].jp_value, (char *)params[2].jp_value); #endif } else { param_values = alloca(nparams * sizeof(*param_values)); for (i = 0; i < nparams; i++) { if (!(params[i].jp_flags & JP_USER)) continue; param_values[i] = jailparam_export(params + i); if (param_values[i] == NULL) - errx(1, "%s", jail_errmsg); + xo_errx(1, "%s", jail_errmsg); } for (i = spc = 0; i < nparams; i++) { if (!(params[i].jp_flags & JP_USER)) continue; if ((pflags & PRINT_SKIP) && ((!(params[i].jp_ctltype & (CTLFLAG_WR | CTLFLAG_TUN))) || (param_parent[i] >= 0 && *(int *)params[param_parent[i]].jp_value != JAIL_SYS_NEW))) continue; if (spc) - putchar(' '); + xo_emit("{P: }"); else spc = 1; if (pflags & PRINT_NAMEVAL) { /* * Generally "name=value", but for booleans * either "name" or "noname". */ if (params[i].jp_flags & (JP_BOOL | JP_NOBOOL)) { - if (*(int *)params[i].jp_value) - printf("%s", params[i].jp_name); + if (*(int *)params[i].jp_value) { + asprintf(&xo_nname, "{en:%s/true}", params[i].jp_name); + xo_emit(xo_nname); + xo_emit("{d:/%s}", params[i].jp_name); + } else { nname = (params[i].jp_flags & JP_NOBOOL) ? nononame(params[i].jp_name) : noname(params[i].jp_name); - printf("%s", nname); + if (params[i].jp_flags & JP_NOBOOL) { + asprintf(&xo_nname, "{en:%s/true}", params[i].jp_name); + xo_emit(xo_nname); + } else { + asprintf(&xo_nname, "{en:%s/false}", params[i].jp_name); + xo_emit(xo_nname); + } + xo_emit("{d:/%s}", nname); free(nname); } + free(xo_nname); continue; } - printf("%s=", params[i].jp_name); + xo_emit("{d:%s}=", params[i].jp_name); } if (params[i].jp_valuelen == 0) { if (pflags & PRINT_QUOTED) - printf("\"\""); + xo_emit("{P:\"\"}"); else if (!(pflags & PRINT_NAMEVAL)) - putchar('-'); - } else - quoted_print(param_values[i]); + xo_emit("{P:-}"); + } else { + quoted_print(pflags, params[i].jp_name, param_values[i]); + } } - putchar('\n'); + xo_emit("{P:\n}"); for (i = 0; i < nparams; i++) if (params[i].jp_flags & JP_USER) free(param_values[i]); } + + xo_close_instance("jail"); return (jid); } static void -quoted_print(char *str) +quoted_print(int pflags, char *name, char *value) { - int c, qc; - char *p = str; + int qc; + char *p = value; + char *param_name_value; /* An empty string needs quoting. */ if (!*p) { - fputs("\"\"", stdout); + asprintf(¶m_name_value, "{k:%s}{d:%s/\"\"}", name, name); + xo_emit(param_name_value); + free(param_name_value); return; } + asprintf(¶m_name_value, "{:%s/%%s}", name); /* * The value will be surrounded by quotes if it contains spaces * or quotes. */ qc = strchr(p, '\'') ? '"' - : strchr(p, '"') ? '\'' - : strchr(p, ' ') || strchr(p, '\t') ? '"' - : 0; - if (qc) - putchar(qc); - while ((c = *p++)) { - if (c == '\\' || c == qc) - putchar('\\'); - putchar(c); - } - if (qc) - putchar(qc); + : strchr(p, '"') ? '\'' + : strchr(p, ' ') || strchr(p, '\t') ? '"' + : 0; + + if (qc && pflags & PRINT_QUOTED) + xo_emit("{P:/%c}", qc); + + xo_emit(param_name_value, value); + + free(param_name_value); + + if (qc && pflags & PRINT_QUOTED) + xo_emit("{P:/%c}", qc); }