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