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(¶m_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(¶m_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 } 556