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: stable/11/usr.bin/procstat/procstat.c 330449 2018-03-05 07:26:05Z eadler $
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
47static int aflag, bflag, cflag, eflag, fflag, iflag, jflag, kflag;
48static int lflag, Lflag, rflag, sflag, tflag, vflag, xflag, Sflag;
49int	hflag, nflag, Cflag, Hflag;
50
51static void
52usage(void)
53{
54
55	xo_error("usage: procstat [--libxo] [-CHhn] [-M core] "
56	    "[-N system] [-w interval]\n"
57	    "                [-b | -c | -e | -f | -i | -j | -k | "
58	    "-l | -r | -s | \n"
59	    "                 -S | -t | -v | -x]\n"
60	    "                [-a | pid | core ...]\n");
61	xo_finish();
62	exit(EX_USAGE);
63}
64
65static void
66procstat(struct procstat *prstat, struct kinfo_proc *kipp)
67{
68	char *pidstr = NULL;
69
70	asprintf(&pidstr, "%d", kipp->ki_pid);
71	if (pidstr == NULL)
72		xo_errc(1, ENOMEM, "Failed to allocate memory in procstat()");
73	xo_open_container(pidstr);
74
75	if (bflag)
76		procstat_bin(prstat, kipp);
77	else if (cflag)
78		procstat_args(prstat, kipp);
79	else if (eflag)
80		procstat_env(prstat, kipp);
81	else if (fflag)
82		procstat_files(prstat, kipp);
83	else if (iflag)
84		procstat_sigs(prstat, kipp);
85	else if (jflag)
86		procstat_threads_sigs(prstat, kipp);
87	else if (kflag)
88		procstat_kstack(prstat, kipp, kflag);
89	else if (lflag)
90		procstat_rlimit(prstat, kipp);
91	else if (Lflag)
92		procstat_ptlwpinfo(prstat);
93	else if (rflag)
94		procstat_rusage(prstat, kipp);
95	else if (sflag)
96		procstat_cred(prstat, kipp);
97	else if (tflag)
98		procstat_threads(prstat, kipp);
99	else if (vflag)
100		procstat_vm(prstat, kipp);
101	else if (xflag)
102		procstat_auxv(prstat, kipp);
103	else if (Sflag)
104		procstat_cs(prstat, kipp);
105	else
106		procstat_basic(kipp);
107
108	xo_close_container(pidstr);
109	free(pidstr);
110}
111
112/*
113 * Sort processes first by pid and then tid.
114 */
115static int
116kinfo_proc_compare(const void *a, const void *b)
117{
118	int i;
119
120	i = ((const struct kinfo_proc *)a)->ki_pid -
121	    ((const struct kinfo_proc *)b)->ki_pid;
122	if (i != 0)
123		return (i);
124	i = ((const struct kinfo_proc *)a)->ki_tid -
125	    ((const struct kinfo_proc *)b)->ki_tid;
126	return (i);
127}
128
129void
130kinfo_proc_sort(struct kinfo_proc *kipp, int count)
131{
132
133	qsort(kipp, count, sizeof(*kipp), kinfo_proc_compare);
134}
135
136const char *
137kinfo_proc_thread_name(const struct kinfo_proc *kipp)
138{
139	static char name[MAXCOMLEN+1];
140
141	strlcpy(name, kipp->ki_tdname, sizeof(name));
142	strlcat(name, kipp->ki_moretdname, sizeof(name));
143	if (name[0] == '\0' || strcmp(kipp->ki_comm, name) == 0) {
144		name[0] = '-';
145		name[1] = '\0';
146	}
147
148	return (name);
149}
150
151int
152main(int argc, char *argv[])
153{
154	int ch, interval, tmp;
155	int i;
156	struct kinfo_proc *p;
157	struct procstat *prstat, *cprstat;
158	long l;
159	pid_t pid;
160	char *dummy;
161	char *nlistf, *memf;
162	const char *xocontainer;
163	int cnt;
164
165	interval = 0;
166	memf = nlistf = NULL;
167	argc = xo_parse_args(argc, argv);
168	xocontainer = "basic";
169
170	while ((ch = getopt(argc, argv, "abCcefHhijklLM:N:nrSstvw:x")) != -1) {
171		switch (ch) {
172		case 'C':
173			Cflag++;
174			break;
175
176		case 'H':
177			Hflag++;
178			break;
179
180		case 'M':
181			memf = optarg;
182			break;
183		case 'N':
184			nlistf = optarg;
185			break;
186		case 'S':
187			Sflag++;
188			xocontainer = "cs";
189			break;
190		case 'a':
191			aflag++;
192			break;
193
194		case 'b':
195			bflag++;
196			xocontainer = "binary";
197			break;
198
199		case 'c':
200			cflag++;
201			xocontainer = "arguments";
202			break;
203
204		case 'e':
205			eflag++;
206			xocontainer = "environment";
207			break;
208
209		case 'f':
210			fflag++;
211			xocontainer = "files";
212			break;
213
214		case 'i':
215			iflag++;
216			xocontainer = "signals";
217			break;
218
219		case 'j':
220			jflag++;
221			xocontainer = "thread_signals";
222			break;
223
224		case 'k':
225			kflag++;
226			xocontainer = "kstack";
227			break;
228
229		case 'l':
230			lflag++;
231			xocontainer = "rlimit";
232			break;
233
234		case 'L':
235			Lflag++;
236			xocontainer = "ptlwpinfo";
237			break;
238
239		case 'n':
240			nflag++;
241			break;
242
243		case 'h':
244			hflag++;
245			break;
246
247		case 'r':
248			rflag++;
249			xocontainer = "rusage";
250			break;
251
252		case 's':
253			sflag++;
254			xocontainer = "credentials";
255			break;
256
257		case 't':
258			tflag++;
259			xocontainer = "threads";
260			break;
261
262		case 'v':
263			vflag++;
264			xocontainer = "vm";
265			break;
266
267		case 'w':
268			l = strtol(optarg, &dummy, 10);
269			if (*dummy != '\0')
270				usage();
271			if (l < 1 || l > INT_MAX)
272				usage();
273			interval = l;
274			break;
275
276		case 'x':
277			xflag++;
278			xocontainer = "auxv";
279			break;
280
281		case '?':
282		default:
283			usage();
284		}
285
286	}
287	argc -= optind;
288	argv += optind;
289
290	/* We require that either 0 or 1 mode flags be set. */
291	tmp = bflag + cflag + eflag + fflag + iflag + jflag + (kflag ? 1 : 0) +
292	    lflag + rflag + sflag + tflag + vflag + xflag + Sflag;
293	if (!(tmp == 0 || tmp == 1))
294		usage();
295
296	/* We allow -k to be specified up to twice, but not more. */
297	if (kflag > 2)
298		usage();
299
300	/* Must specify either the -a flag or a list of pids. */
301	if (!(aflag == 1 && argc == 0) && !(aflag == 0 && argc > 0))
302		usage();
303
304	/* Only allow -C with -f. */
305	if (Cflag && !fflag)
306		usage();
307
308	if (memf != NULL)
309		prstat = procstat_open_kvm(nlistf, memf);
310	else
311		prstat = procstat_open_sysctl();
312	if (prstat == NULL)
313		xo_errx(1, "procstat_open()");
314	do {
315		xo_set_version(PROCSTAT_XO_VERSION);
316		xo_open_container("procstat");
317		xo_open_container(xocontainer);
318
319		if (aflag) {
320			p = procstat_getprocs(prstat, KERN_PROC_PROC, 0, &cnt);
321			if (p == NULL)
322				xo_errx(1, "procstat_getprocs()");
323			kinfo_proc_sort(p, cnt);
324			for (i = 0; i < cnt; i++) {
325				procstat(prstat, &p[i]);
326
327				/* Suppress header after first process. */
328				hflag = 1;
329				xo_flush();
330			}
331			procstat_freeprocs(prstat, p);
332		}
333		for (i = 0; i < argc; i++) {
334			l = strtol(argv[i], &dummy, 10);
335			if (*dummy == '\0') {
336				if (l < 0)
337					usage();
338				pid = l;
339
340				p = procstat_getprocs(prstat, KERN_PROC_PID,
341				    pid, &cnt);
342				if (p == NULL)
343					xo_errx(1, "procstat_getprocs()");
344				if (cnt != 0)
345					procstat(prstat, p);
346				procstat_freeprocs(prstat, p);
347			} else {
348				cprstat = procstat_open_core(argv[i]);
349				if (cprstat == NULL) {
350					warnx("procstat_open()");
351					continue;
352				}
353				p = procstat_getprocs(cprstat, KERN_PROC_PID,
354				    -1, &cnt);
355				if (p == NULL)
356					xo_errx(1, "procstat_getprocs()");
357				if (cnt != 0)
358					procstat(cprstat, p);
359				procstat_freeprocs(cprstat, p);
360				procstat_close(cprstat);
361			}
362			/* Suppress header after first process. */
363			hflag = 1;
364		}
365
366		xo_close_container(xocontainer);
367		xo_close_container("procstat");
368		xo_finish();
369		if (interval)
370			sleep(interval);
371	} while (interval);
372
373	procstat_close(prstat);
374
375	exit(0);
376}
377