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