1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2007, 2011 Robert N. M. Watson 5 * Copyright (c) 2015 Allan Jude <allanjude@freebsd.org> 6 * Copyright (c) 2017 Dell EMC 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 * $FreeBSD$ 31 */ 32 33 #include <sys/param.h> 34 #include <sys/sysctl.h> 35 #include <sys/user.h> 36 37 #include <err.h> 38 #include <libprocstat.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <sysexits.h> 43 #include <unistd.h> 44 45 #include "procstat.h" 46 47 enum { 48 PS_CMP_NORMAL = 0x00, 49 PS_CMP_PLURAL = 0x01, 50 PS_CMP_SUBSTR = 0x02 51 }; 52 53 struct procstat_cmd { 54 const char *command; 55 const char *xocontainer; 56 const char *usage; 57 void (*cmd)(struct procstat *, struct kinfo_proc *); 58 void (*opt)(int, char * const *); 59 int cmp; 60 }; 61 62 int procstat_opts = 0; 63 64 static void cmdopt_none(int argc, char * const argv[]); 65 static void cmdopt_verbose(int argc, char * const argv[]); 66 static void cmdopt_signals(int argc, char * const argv[]); 67 static void cmdopt_rusage(int argc, char * const argv[]); 68 static void cmdopt_files(int argc, char * const argv[]); 69 static void cmdopt_cpuset(int argc, char * const argv[]); 70 71 static const struct procstat_cmd cmd_table[] = { 72 { "argument", "arguments", NULL, &procstat_args, &cmdopt_none, 73 PS_CMP_PLURAL | PS_CMP_SUBSTR }, 74 { "auxv", "auxv", NULL, &procstat_auxv, &cmdopt_none, PS_CMP_NORMAL }, 75 { "basic", "basic", NULL, &procstat_basic, &cmdopt_none, 76 PS_CMP_NORMAL }, 77 { "binary", "binary", NULL, &procstat_bin, &cmdopt_none, 78 PS_CMP_SUBSTR }, 79 { "cpuset", "cs", NULL, &procstat_cs, &cmdopt_cpuset, PS_CMP_NORMAL }, 80 { "cs", "cs", NULL, &procstat_cs, &cmdopt_cpuset, PS_CMP_NORMAL }, 81 { "credential", "credentials", NULL, &procstat_cred, &cmdopt_none, 82 PS_CMP_PLURAL | PS_CMP_SUBSTR }, 83 { "environment", "environment", NULL, &procstat_env, &cmdopt_none, 84 PS_CMP_SUBSTR }, 85 { "fd", "files", "[-C]", &procstat_files, &cmdopt_files, 86 PS_CMP_PLURAL }, 87 { "file", "files", "[-C]", &procstat_files, &cmdopt_files, 88 PS_CMP_PLURAL }, 89 { "kstack", "kstack", "[-v]", &procstat_kstack, &cmdopt_verbose, 90 PS_CMP_NORMAL }, 91 { "ptlwpinfo", "ptlwpinfo", NULL, &procstat_ptlwpinfo, &cmdopt_none, 92 PS_CMP_NORMAL }, 93 { "rlimit", "rlimit", NULL, &procstat_rlimit, &cmdopt_none, 94 PS_CMP_NORMAL }, 95 { "rusage", "rusage", "[-Ht]", &procstat_rusage, &cmdopt_rusage, 96 PS_CMP_NORMAL }, 97 { "sigfastblock", "sigfastblock", NULL, &procstat_sigfastblock, 98 &cmdopt_none, PS_CMP_NORMAL }, 99 { "signal", "signals", "[-n]", &procstat_sigs, &cmdopt_signals, 100 PS_CMP_PLURAL | PS_CMP_SUBSTR }, 101 { "thread", "threads", NULL, &procstat_threads, &cmdopt_none, 102 PS_CMP_PLURAL }, 103 { "tsignal", "thread_signals", "[-n]", &procstat_threads_sigs, 104 &cmdopt_signals, PS_CMP_PLURAL | PS_CMP_SUBSTR }, 105 { "vm", "vm", NULL, &procstat_vm, &cmdopt_none, PS_CMP_NORMAL } 106 }; 107 108 static void 109 usage(void) 110 { 111 size_t i, l; 112 int multi; 113 114 xo_error("usage: procstat [--libxo] [-h] [-M core] [-N system]" 115 " [-w interval] command\n" 116 " [pid ... | core ...]\n" 117 " procstat [--libxo] -a [-h] [-M core] [-N system] " 118 " [-w interval] command\n" 119 " procstat [--libxo] [-h] [-M core] [-N system]" 120 " [-w interval]\n" 121 " [-S | -b | -c | -e | -f [-C] | -i [-n] | " 122 "-j [-n] | -k [-k] |\n" 123 " -l | -r [-H] | -s | -t | -v | -x] " 124 "[pid ... | core ...]\n" 125 " procstat [--libxo] -a [-h] [-M core] [-N system]" 126 " [-w interval]\n" 127 " [-S | -b | -c | -e | -f [-C] | -i [-n] | " 128 "-j [-n] | -k [-k] |\n" 129 " -l | -r [-H] | -s | -t | -v | -x]\n" 130 " procstat [--libxo] -L [-h] [-M core] [-N system] core ...\n" 131 "Available commands:\n"); 132 for (i = 0, l = nitems(cmd_table); i < l; i++) { 133 multi = i + 1 < l && cmd_table[i].cmd == cmd_table[i + 1].cmd; 134 xo_error(" %s%s%s", multi ? "[" : "", 135 cmd_table[i].command, (cmd_table[i].cmp & PS_CMP_PLURAL) ? 136 "(s)" : ""); 137 for (; i + 1 < l && cmd_table[i].cmd == cmd_table[i + 1].cmd; 138 i++) 139 xo_error(" | %s%s", cmd_table[i + 1].command, 140 (cmd_table[i].cmp & PS_CMP_PLURAL) ? "(s)" : ""); 141 if (multi) 142 xo_error("]"); 143 if (cmd_table[i].usage != NULL) 144 xo_error(" %s", cmd_table[i].usage); 145 xo_error("\n"); 146 } 147 xo_finish(); 148 exit(EX_USAGE); 149 } 150 151 static void 152 procstat(const struct procstat_cmd *cmd, struct procstat *prstat, 153 struct kinfo_proc *kipp) 154 { 155 char *pidstr = NULL; 156 157 asprintf(&pidstr, "%d", kipp->ki_pid); 158 if (pidstr == NULL) 159 xo_errc(1, ENOMEM, "Failed to allocate memory in procstat()"); 160 xo_open_container(pidstr); 161 cmd->cmd(prstat, kipp); 162 xo_close_container(pidstr); 163 free(pidstr); 164 } 165 166 /* 167 * Sort processes first by pid and then tid. 168 */ 169 static int 170 kinfo_proc_compare(const void *a, const void *b) 171 { 172 int i; 173 174 i = ((const struct kinfo_proc *)a)->ki_pid - 175 ((const struct kinfo_proc *)b)->ki_pid; 176 if (i != 0) 177 return (i); 178 i = ((const struct kinfo_proc *)a)->ki_tid - 179 ((const struct kinfo_proc *)b)->ki_tid; 180 return (i); 181 } 182 183 void 184 kinfo_proc_sort(struct kinfo_proc *kipp, int count) 185 { 186 187 qsort(kipp, count, sizeof(*kipp), kinfo_proc_compare); 188 } 189 190 const char * 191 kinfo_proc_thread_name(const struct kinfo_proc *kipp) 192 { 193 static char name[MAXCOMLEN+1]; 194 195 strlcpy(name, kipp->ki_tdname, sizeof(name)); 196 strlcat(name, kipp->ki_moretdname, sizeof(name)); 197 if (name[0] == '\0' || strcmp(kipp->ki_comm, name) == 0) { 198 name[0] = '-'; 199 name[1] = '\0'; 200 } 201 202 return (name); 203 } 204 205 static const struct procstat_cmd * 206 getcmd(const char *str) 207 { 208 const struct procstat_cmd *cmd; 209 size_t i, l; 210 int cmp, s; 211 212 if (str == NULL) 213 return (NULL); 214 cmd = NULL; 215 if ((l = strlen(str)) == 0) 216 return (getcmd("basic")); 217 s = l > 1 && strcasecmp(str + l - 1, "s") == 0; 218 for (i = 0; i < nitems(cmd_table); i++) { 219 /* 220 * After the first match substring matches are disabled, 221 * allowing subsequent full matches to take precedence. 222 */ 223 if (cmd == NULL && (cmd_table[i].cmp & PS_CMP_SUBSTR)) 224 cmp = strncasecmp(str, cmd_table[i].command, l - 225 ((cmd_table[i].cmp & PS_CMP_PLURAL) && s ? 1 : 0)); 226 else if ((cmd_table[i].cmp & PS_CMP_PLURAL) && s && 227 l == strlen(cmd_table[i].command) + 1) 228 cmp = strncasecmp(str, cmd_table[i].command, l - 1); 229 else 230 cmp = strcasecmp(str, cmd_table[i].command); 231 if (cmp == 0) 232 cmd = &cmd_table[i]; 233 } 234 return (cmd); 235 } 236 237 int 238 main(int argc, char *argv[]) 239 { 240 int ch, interval; 241 int i; 242 struct kinfo_proc *p; 243 const struct procstat_cmd *cmd; 244 struct procstat *prstat, *cprstat; 245 long l; 246 pid_t pid; 247 char *dummy; 248 char *nlistf, *memf; 249 int aflag; 250 int cnt; 251 252 interval = 0; 253 cmd = NULL; 254 memf = nlistf = NULL; 255 aflag = 0; 256 argc = xo_parse_args(argc, argv); 257 258 while ((ch = getopt(argc, argv, "abCcefHhijkLlM:N:nrSstvw:x")) != -1) { 259 switch (ch) { 260 case 'a': 261 aflag++; 262 break; 263 case 'b': 264 if (cmd != NULL) 265 usage(); 266 cmd = getcmd("binary"); 267 break; 268 case 'C': 269 procstat_opts |= PS_OPT_CAPABILITIES; 270 break; 271 case 'c': 272 if (cmd != NULL) 273 usage(); 274 cmd = getcmd("arguments"); 275 break; 276 case 'e': 277 if (cmd != NULL) 278 usage(); 279 cmd = getcmd("environment"); 280 break; 281 case 'f': 282 if (cmd != NULL) 283 usage(); 284 cmd = getcmd("files"); 285 break; 286 case 'H': 287 procstat_opts |= PS_OPT_PERTHREAD; 288 break; 289 case 'h': 290 procstat_opts |= PS_OPT_NOHEADER; 291 break; 292 case 'i': 293 if (cmd != NULL) 294 usage(); 295 cmd = getcmd("signals"); 296 break; 297 case 'j': 298 if (cmd != NULL) 299 usage(); 300 cmd = getcmd("tsignals"); 301 break; 302 case 'k': 303 if (cmd != NULL && cmd->cmd == procstat_kstack) { 304 if ((procstat_opts & PS_OPT_VERBOSE) != 0) 305 usage(); 306 procstat_opts |= PS_OPT_VERBOSE; 307 } else { 308 if (cmd != NULL) 309 usage(); 310 cmd = getcmd("kstack"); 311 } 312 break; 313 case 'L': 314 if (cmd != NULL) 315 usage(); 316 cmd = getcmd("ptlwpinfo"); 317 break; 318 case 'l': 319 if (cmd != NULL) 320 usage(); 321 cmd = getcmd("rlimit"); 322 break; 323 case 'M': 324 memf = optarg; 325 break; 326 case 'N': 327 nlistf = optarg; 328 break; 329 case 'n': 330 procstat_opts |= PS_OPT_SIGNUM; 331 break; 332 case 'r': 333 if (cmd != NULL) 334 usage(); 335 cmd = getcmd("rusage"); 336 break; 337 case 'S': 338 if (cmd != NULL) 339 usage(); 340 cmd = getcmd("cpuset"); 341 break; 342 case 's': 343 if (cmd != NULL) 344 usage(); 345 cmd = getcmd("credentials"); 346 break; 347 case 't': 348 if (cmd != NULL) 349 usage(); 350 cmd = getcmd("threads"); 351 break; 352 case 'v': 353 if (cmd != NULL) 354 usage(); 355 cmd = getcmd("vm"); 356 break; 357 case 'w': 358 l = strtol(optarg, &dummy, 10); 359 if (*dummy != '\0') 360 usage(); 361 if (l < 1 || l > INT_MAX) 362 usage(); 363 interval = l; 364 break; 365 case 'x': 366 if (cmd != NULL) 367 usage(); 368 cmd = getcmd("auxv"); 369 break; 370 case '?': 371 default: 372 usage(); 373 } 374 375 } 376 argc -= optind; 377 argv += optind; 378 379 if (cmd == NULL && argv[0] != NULL && (cmd = getcmd(argv[0])) != NULL) { 380 if ((procstat_opts & PS_SUBCOMMAND_OPTS) != 0) 381 usage(); 382 if (cmd->opt != NULL) { 383 optreset = 1; 384 optind = 1; 385 cmd->opt(argc, argv); 386 argc -= optind; 387 argv += optind; 388 } else { 389 argc -= 1; 390 argv += 1; 391 } 392 } else { 393 if (cmd == NULL) 394 cmd = getcmd("basic"); 395 if (cmd->cmd != procstat_files && 396 (procstat_opts & PS_OPT_CAPABILITIES) != 0) 397 usage(); 398 } 399 400 /* Must specify either the -a flag or a list of pids. */ 401 if (!(aflag == 1 && argc == 0) && !(aflag == 0 && argc > 0)) 402 usage(); 403 404 if (memf != NULL) 405 prstat = procstat_open_kvm(nlistf, memf); 406 else 407 prstat = procstat_open_sysctl(); 408 if (prstat == NULL) 409 xo_errx(1, "procstat_open()"); 410 do { 411 xo_set_version(PROCSTAT_XO_VERSION); 412 xo_open_container("procstat"); 413 xo_open_container(cmd->xocontainer); 414 415 if (aflag) { 416 p = procstat_getprocs(prstat, KERN_PROC_PROC, 0, &cnt); 417 if (p == NULL) 418 xo_errx(1, "procstat_getprocs()"); 419 kinfo_proc_sort(p, cnt); 420 for (i = 0; i < cnt; i++) { 421 procstat(cmd, prstat, &p[i]); 422 423 /* Suppress header after first process. */ 424 procstat_opts |= PS_OPT_NOHEADER; 425 xo_flush(); 426 } 427 procstat_freeprocs(prstat, p); 428 } 429 for (i = 0; i < argc; i++) { 430 l = strtol(argv[i], &dummy, 10); 431 if (*dummy == '\0') { 432 if (l < 0) 433 usage(); 434 pid = l; 435 436 p = procstat_getprocs(prstat, KERN_PROC_PID, 437 pid, &cnt); 438 if (p == NULL) 439 xo_errx(1, "procstat_getprocs()"); 440 if (cnt != 0) 441 procstat(cmd, prstat, p); 442 procstat_freeprocs(prstat, p); 443 } else { 444 cprstat = procstat_open_core(argv[i]); 445 if (cprstat == NULL) { 446 warnx("procstat_open()"); 447 continue; 448 } 449 p = procstat_getprocs(cprstat, KERN_PROC_PID, 450 -1, &cnt); 451 if (p == NULL) 452 xo_errx(1, "procstat_getprocs()"); 453 if (cnt != 0) 454 procstat(cmd, cprstat, p); 455 procstat_freeprocs(cprstat, p); 456 procstat_close(cprstat); 457 } 458 /* Suppress header after first process. */ 459 procstat_opts |= PS_OPT_NOHEADER; 460 } 461 462 xo_close_container(cmd->xocontainer); 463 xo_close_container("procstat"); 464 xo_finish(); 465 if (interval) 466 sleep(interval); 467 } while (interval); 468 469 procstat_close(prstat); 470 471 exit(0); 472 } 473 474 void 475 cmdopt_none(int argc, char * const argv[]) 476 { 477 int ch; 478 479 while ((ch = getopt(argc, argv, "")) != -1) { 480 switch (ch) { 481 case '?': 482 default: 483 usage(); 484 } 485 } 486 } 487 488 void 489 cmdopt_verbose(int argc, char * const argv[]) 490 { 491 int ch; 492 493 while ((ch = getopt(argc, argv, "v")) != -1) { 494 switch (ch) { 495 case 'v': 496 procstat_opts |= PS_OPT_VERBOSE; 497 break; 498 case '?': 499 default: 500 usage(); 501 } 502 } 503 } 504 505 void 506 cmdopt_signals(int argc, char * const argv[]) 507 { 508 int ch; 509 510 while ((ch = getopt(argc, argv, "n")) != -1) { 511 switch (ch) { 512 case 'n': 513 procstat_opts |= PS_OPT_SIGNUM; 514 break; 515 case '?': 516 default: 517 usage(); 518 } 519 } 520 } 521 522 void 523 cmdopt_rusage(int argc, char * const argv[]) 524 { 525 int ch; 526 527 while ((ch = getopt(argc, argv, "Ht")) != -1) { 528 switch (ch) { 529 case 'H': 530 /* FALLTHROUGH */ 531 case 't': 532 procstat_opts |= PS_OPT_PERTHREAD; 533 break; 534 case '?': 535 default: 536 usage(); 537 } 538 } 539 } 540 541 void 542 cmdopt_files(int argc, char * const argv[]) 543 { 544 int ch; 545 546 while ((ch = getopt(argc, argv, "C")) != -1) { 547 switch (ch) { 548 case 'C': 549 procstat_opts |= PS_OPT_CAPABILITIES; 550 break; 551 case '?': 552 default: 553 usage(); 554 } 555 } 556 } 557 558 void 559 cmdopt_cpuset(int argc, char * const argv[]) 560 { 561 562 procstat_opts |= PS_OPT_PERTHREAD; 563 cmdopt_none(argc, argv); 564 } 565