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