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 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 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 49 #define JP_USER 0x01000000 50 #define JP_OPT 0x02000000 51 52 #define PRINT_DEFAULT 0x01 53 #define PRINT_HEADER 0x02 54 #define PRINT_NAMEVAL 0x04 55 #define PRINT_QUOTED 0x08 56 #define PRINT_SKIP 0x10 57 #define PRINT_VERBOSE 0x20 58 59 static struct jailparam *params; 60 static int *param_parent; 61 static int nparams; 62 #ifdef INET6 63 static int ip6_ok; 64 #endif 65 #ifdef INET 66 static int ip4_ok; 67 #endif 68 69 static int add_param(const char *name, void *value, size_t valuelen, 70 struct jailparam *source, unsigned flags); 71 static int sort_param(const void *a, const void *b); 72 static char *noname(const char *name); 73 static char *nononame(const char *name); 74 static int print_jail(int pflags, int jflags); 75 static void quoted_print(char *str); 76 77 int 78 main(int argc, char **argv) 79 { 80 char *dot, *ep, *jname; 81 int c, i, jflags, jid, lastjid, pflags, spc; 82 83 jname = NULL; 84 pflags = jflags = jid = 0; 85 while ((c = getopt(argc, argv, "adj:hnqsv")) >= 0) 86 switch (c) { 87 case 'a': 88 case 'd': 89 jflags |= JAIL_DYING; 90 break; 91 case 'j': 92 jid = strtoul(optarg, &ep, 10); 93 if (!jid || *ep) { 94 jid = 0; 95 jname = optarg; 96 } 97 break; 98 case 'h': 99 pflags = (pflags & ~(PRINT_SKIP | PRINT_VERBOSE)) | 100 PRINT_HEADER; 101 break; 102 case 'n': 103 pflags = (pflags & ~PRINT_VERBOSE) | PRINT_NAMEVAL; 104 break; 105 case 'q': 106 pflags |= PRINT_QUOTED; 107 break; 108 case 's': 109 pflags = (pflags & ~(PRINT_HEADER | PRINT_VERBOSE)) | 110 PRINT_NAMEVAL | PRINT_QUOTED | PRINT_SKIP; 111 break; 112 case 'v': 113 pflags = (pflags & 114 ~(PRINT_HEADER | PRINT_NAMEVAL | PRINT_SKIP)) | 115 PRINT_VERBOSE; 116 break; 117 default: 118 errx(1, "usage: jls [-dhnqv] [-j jail] [param ...]"); 119 } 120 121 #ifdef INET6 122 ip6_ok = feature_present("inet6"); 123 #endif 124 #ifdef INET 125 ip4_ok = feature_present("inet"); 126 #endif 127 128 /* Add the parameters to print. */ 129 if (optind == argc) { 130 if (pflags & (PRINT_HEADER | PRINT_NAMEVAL)) 131 add_param("all", NULL, (size_t)0, NULL, JP_USER); 132 else if (pflags & PRINT_VERBOSE) { 133 add_param("jid", NULL, (size_t)0, NULL, JP_USER); 134 add_param("host.hostname", NULL, (size_t)0, NULL, 135 JP_USER); 136 add_param("path", NULL, (size_t)0, NULL, JP_USER); 137 add_param("name", NULL, (size_t)0, NULL, JP_USER); 138 add_param("dying", NULL, (size_t)0, NULL, JP_USER); 139 add_param("cpuset.id", NULL, (size_t)0, NULL, JP_USER); 140 #ifdef INET 141 if (ip4_ok) 142 add_param("ip4.addr", NULL, (size_t)0, NULL, 143 JP_USER); 144 #endif 145 #ifdef INET6 146 if (ip6_ok) 147 add_param("ip6.addr", NULL, (size_t)0, NULL, 148 JP_USER | JP_OPT); 149 #endif 150 } else { 151 pflags |= PRINT_DEFAULT; 152 add_param("jid", 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 add_param("host.hostname", NULL, (size_t)0, NULL, 159 JP_USER); 160 add_param("path", NULL, (size_t)0, NULL, JP_USER); 161 } 162 } else 163 while (optind < argc) 164 add_param(argv[optind++], NULL, (size_t)0, NULL, 165 JP_USER); 166 167 if (pflags & PRINT_SKIP) { 168 /* Check for parameters with jailsys parents. */ 169 for (i = 0; i < nparams; i++) { 170 if ((params[i].jp_flags & JP_USER) && 171 (dot = strchr(params[i].jp_name, '.'))) { 172 *dot = 0; 173 param_parent[i] = add_param(params[i].jp_name, 174 NULL, (size_t)0, NULL, JP_OPT); 175 *dot = '.'; 176 } 177 } 178 } 179 180 /* Add the index key parameters. */ 181 if (jid != 0) 182 add_param("jid", &jid, sizeof(jid), NULL, 0); 183 else if (jname != NULL) 184 add_param("name", jname, strlen(jname), NULL, 0); 185 else 186 add_param("lastjid", &lastjid, sizeof(lastjid), NULL, 0); 187 188 /* Print a header line if requested. */ 189 if (pflags & PRINT_VERBOSE) 190 printf(" JID Hostname Path\n" 191 " Name State\n" 192 " CPUSetID\n" 193 " IP Address(es)\n"); 194 else if (pflags & PRINT_DEFAULT) 195 printf(" JID IP Address " 196 "Hostname Path\n"); 197 else if (pflags & PRINT_HEADER) { 198 for (i = spc = 0; i < nparams; i++) 199 if (params[i].jp_flags & JP_USER) { 200 if (spc) 201 putchar(' '); 202 else 203 spc = 1; 204 fputs(params[i].jp_name, stdout); 205 } 206 putchar('\n'); 207 } 208 209 /* Fetch the jail(s) and print the paramters. */ 210 if (jid != 0 || jname != NULL) { 211 if (print_jail(pflags, jflags) < 0) 212 errx(1, "%s", jail_errmsg); 213 } else { 214 for (lastjid = 0; 215 (lastjid = print_jail(pflags, jflags)) >= 0; ) 216 ; 217 if (errno != 0 && errno != ENOENT) 218 errx(1, "%s", jail_errmsg); 219 } 220 221 return (0); 222 } 223 224 static int 225 add_param(const char *name, void *value, size_t valuelen, 226 struct jailparam *source, unsigned flags) 227 { 228 struct jailparam *param, *tparams; 229 int i, tnparams; 230 231 static int paramlistsize; 232 233 /* The pseudo-parameter "all" scans the list of available parameters. */ 234 if (!strcmp(name, "all")) { 235 tnparams = jailparam_all(&tparams); 236 if (tnparams < 0) 237 errx(1, "%s", jail_errmsg); 238 qsort(tparams, (size_t)tnparams, sizeof(struct jailparam), 239 sort_param); 240 for (i = 0; i < tnparams; i++) 241 add_param(tparams[i].jp_name, NULL, (size_t)0, 242 tparams + i, flags); 243 free(tparams); 244 return -1; 245 } 246 247 /* Check for repeat parameters. */ 248 for (i = 0; i < nparams; i++) 249 if (!strcmp(name, params[i].jp_name)) { 250 if (value != NULL && jailparam_import_raw(params + i, 251 value, valuelen) < 0) 252 errx(1, "%s", jail_errmsg); 253 params[i].jp_flags |= flags; 254 if (source != NULL) 255 jailparam_free(source, 1); 256 return i; 257 } 258 259 /* Make sure there is room for the new param record. */ 260 if (!nparams) { 261 paramlistsize = 32; 262 params = malloc(paramlistsize * sizeof(*params)); 263 param_parent = malloc(paramlistsize * sizeof(*param_parent)); 264 if (params == NULL || param_parent == NULL) 265 err(1, "malloc"); 266 } else if (nparams >= paramlistsize) { 267 paramlistsize *= 2; 268 params = realloc(params, paramlistsize * sizeof(*params)); 269 param_parent = realloc(param_parent, 270 paramlistsize * sizeof(*param_parent)); 271 if (params == NULL || param_parent == NULL) 272 err(1, "realloc"); 273 } 274 275 /* Look up the parameter. */ 276 param_parent[nparams] = -1; 277 param = params + nparams++; 278 if (source != NULL) { 279 *param = *source; 280 param->jp_flags |= flags; 281 return param - params; 282 } 283 if (jailparam_init(param, name) < 0) 284 errx(1, "%s", jail_errmsg); 285 param->jp_flags = flags; 286 if ((value != NULL ? jailparam_import_raw(param, value, valuelen) 287 : jailparam_import(param, value)) < 0) { 288 if (flags & JP_OPT) { 289 nparams--; 290 return (-1); 291 } 292 errx(1, "%s", jail_errmsg); 293 } 294 return param - params; 295 } 296 297 static int 298 sort_param(const void *a, const void *b) 299 { 300 const struct jailparam *parama, *paramb; 301 char *ap, *bp; 302 303 /* Put top-level parameters first. */ 304 parama = a; 305 paramb = b; 306 ap = strchr(parama->jp_name, '.'); 307 bp = strchr(paramb->jp_name, '.'); 308 if (ap && !bp) 309 return (1); 310 if (bp && !ap) 311 return (-1); 312 return (strcmp(parama->jp_name, paramb->jp_name)); 313 } 314 315 static char * 316 noname(const char *name) 317 { 318 char *nname, *p; 319 320 nname = malloc(strlen(name) + 3); 321 if (nname == NULL) 322 err(1, "malloc"); 323 p = strrchr(name, '.'); 324 if (p != NULL) 325 sprintf(nname, "%.*s.no%s", (int)(p - name), name, p + 1); 326 else 327 sprintf(nname, "no%s", name); 328 return nname; 329 } 330 331 static char * 332 nononame(const char *name) 333 { 334 char *nname, *p; 335 336 p = strrchr(name, '.'); 337 if (strncmp(p ? p + 1 : name, "no", 2)) 338 return NULL; 339 nname = malloc(strlen(name) - 1); 340 if (nname == NULL) 341 err(1, "malloc"); 342 if (p != NULL) 343 sprintf(nname, "%.*s.%s", (int)(p - name), name, p + 3); 344 else 345 strcpy(nname, name + 2); 346 return nname; 347 } 348 349 static int 350 print_jail(int pflags, int jflags) 351 { 352 char *nname; 353 char **param_values; 354 int i, ai, jid, count, n, spc; 355 char ipbuf[INET6_ADDRSTRLEN]; 356 357 jid = jailparam_get(params, nparams, jflags); 358 if (jid < 0) 359 return jid; 360 if (pflags & PRINT_VERBOSE) { 361 printf("%6d %-29.29s %.74s\n" 362 "%6s %-29.29s %.74s\n" 363 "%6s %-6d\n", 364 *(int *)params[0].jp_value, 365 (char *)params[1].jp_value, 366 (char *)params[2].jp_value, 367 "", 368 (char *)params[3].jp_value, 369 *(int *)params[4].jp_value ? "DYING" : "ACTIVE", 370 "", 371 *(int *)params[5].jp_value); 372 n = 6; 373 #ifdef INET 374 if (ip4_ok && !strcmp(params[n].jp_name, "ip4.addr")) { 375 count = params[n].jp_valuelen / sizeof(struct in_addr); 376 for (ai = 0; ai < count; ai++) 377 if (inet_ntop(AF_INET, 378 &((struct in_addr *)params[n].jp_value)[ai], 379 ipbuf, sizeof(ipbuf)) == NULL) 380 err(1, "inet_ntop"); 381 else 382 printf("%6s %-15.15s\n", "", ipbuf); 383 n++; 384 } 385 #endif 386 #ifdef INET6 387 if (ip6_ok && !strcmp(params[n].jp_name, "ip6.addr")) { 388 count = params[n].jp_valuelen / sizeof(struct in6_addr); 389 for (ai = 0; ai < count; ai++) 390 if (inet_ntop(AF_INET6, 391 &((struct in6_addr *) 392 params[n].jp_value)[ai], 393 ipbuf, sizeof(ipbuf)) == NULL) 394 err(1, "inet_ntop"); 395 else 396 printf("%6s %s\n", "", ipbuf); 397 n++; 398 } 399 #endif 400 } else if (pflags & PRINT_DEFAULT) 401 printf("%6d %-15.15s %-29.29s %.74s\n", 402 *(int *)params[0].jp_value, 403 #ifdef INET 404 (!ip4_ok || params[1].jp_valuelen == 0) ? "-" 405 : inet_ntoa(*(struct in_addr *)params[1].jp_value), 406 #else 407 "-", 408 #endif 409 (char *)params[2-!ip4_ok].jp_value, 410 (char *)params[3-!ip4_ok].jp_value); 411 else { 412 param_values = alloca(nparams * sizeof(*param_values)); 413 for (i = 0; i < nparams; i++) { 414 if (!(params[i].jp_flags & JP_USER)) 415 continue; 416 param_values[i] = jailparam_export(params + i); 417 if (param_values[i] == NULL) 418 errx(1, "%s", jail_errmsg); 419 } 420 for (i = spc = 0; i < nparams; i++) { 421 if (!(params[i].jp_flags & JP_USER)) 422 continue; 423 if ((pflags & PRINT_SKIP) && 424 ((!(params[i].jp_ctltype & 425 (CTLFLAG_WR | CTLFLAG_TUN))) || 426 (param_parent[i] >= 0 && 427 *(int *)params[param_parent[i]].jp_value != 428 JAIL_SYS_NEW))) 429 continue; 430 if (spc) 431 putchar(' '); 432 else 433 spc = 1; 434 if (pflags & PRINT_NAMEVAL) { 435 /* 436 * Generally "name=value", but for booleans 437 * either "name" or "noname". 438 */ 439 if (params[i].jp_flags & 440 (JP_BOOL | JP_NOBOOL)) { 441 if (*(int *)params[i].jp_value) 442 printf("%s", params[i].jp_name); 443 else { 444 nname = (params[i].jp_flags & 445 JP_NOBOOL) ? 446 nononame(params[i].jp_name) 447 : noname(params[i].jp_name); 448 printf("%s", nname); 449 free(nname); 450 } 451 continue; 452 } 453 printf("%s=", params[i].jp_name); 454 } 455 if (params[i].jp_valuelen == 0) { 456 if (pflags & PRINT_QUOTED) 457 printf("\"\""); 458 else if (!(pflags & PRINT_NAMEVAL)) 459 putchar('-'); 460 } else 461 quoted_print(param_values[i]); 462 } 463 putchar('\n'); 464 for (i = 0; i < nparams; i++) 465 if (params[i].jp_flags & JP_USER) 466 free(param_values[i]); 467 } 468 return (jid); 469 } 470 471 static void 472 quoted_print(char *str) 473 { 474 int c, qc; 475 char *p = str; 476 477 /* An empty string needs quoting. */ 478 if (!*p) { 479 fputs("\"\"", stdout); 480 return; 481 } 482 483 /* 484 * The value will be surrounded by quotes if it contains spaces 485 * or quotes. 486 */ 487 qc = strchr(p, '\'') ? '"' 488 : strchr(p, '"') ? '\'' 489 : strchr(p, ' ') || strchr(p, '\t') ? '"' 490 : 0; 491 if (qc) 492 putchar(qc); 493 while ((c = *p++)) { 494 if (c == '\\' || c == qc) 495 putchar('\\'); 496 putchar(c); 497 } 498 if (qc) 499 putchar(qc); 500 } 501