1/*-
2 * Copyright (c) 2007, 2011 Robert N. M. Watson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD$
27 */
28
29#include <sys/param.h>
30#include <sys/sysctl.h>
31#include <sys/user.h>
32
33#include <err.h>
34#include <libprocstat.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <sysexits.h>
38#include <unistd.h>
39
40#include "procstat.h"
41
42static int aflag, bflag, cflag, eflag, fflag, iflag, jflag, kflag, lflag, sflag;
43static int tflag, vflag, xflag;
44int	hflag, nflag, Cflag;
45
46static void
47usage(void)
48{
49
50	fprintf(stderr, "usage: procstat [-h] [-C] [-M core] [-N system] "
51	    "[-w interval] \n");
52	fprintf(stderr, "                [-b | -c | -e | -f | -i | -j | -k | "
53	    "-l | -s | -t | -v | -x] [-a | pid | core ...]\n");
54	exit(EX_USAGE);
55}
56
57static void
58procstat(struct procstat *prstat, struct kinfo_proc *kipp)
59{
60
61	if (bflag)
62		procstat_bin(prstat, kipp);
63	else if (cflag)
64		procstat_args(prstat, kipp);
65	else if (eflag)
66		procstat_env(prstat, kipp);
67	else if (fflag)
68		procstat_files(prstat, kipp);
69	else if (iflag)
70		procstat_sigs(prstat, kipp);
71	else if (jflag)
72		procstat_threads_sigs(prstat, kipp);
73	else if (kflag)
74		procstat_kstack(prstat, kipp, kflag);
75	else if (lflag)
76		procstat_rlimit(prstat, kipp);
77	else if (sflag)
78		procstat_cred(prstat, kipp);
79	else if (tflag)
80		procstat_threads(prstat, kipp);
81	else if (vflag)
82		procstat_vm(prstat, kipp);
83	else if (xflag)
84		procstat_auxv(prstat, kipp);
85	else
86		procstat_basic(kipp);
87}
88
89/*
90 * Sort processes first by pid and then tid.
91 */
92static int
93kinfo_proc_compare(const void *a, const void *b)
94{
95	int i;
96
97	i = ((const struct kinfo_proc *)a)->ki_pid -
98	    ((const struct kinfo_proc *)b)->ki_pid;
99	if (i != 0)
100		return (i);
101	i = ((const struct kinfo_proc *)a)->ki_tid -
102	    ((const struct kinfo_proc *)b)->ki_tid;
103	return (i);
104}
105
106void
107kinfo_proc_sort(struct kinfo_proc *kipp, int count)
108{
109
110	qsort(kipp, count, sizeof(*kipp), kinfo_proc_compare);
111}
112
113int
114main(int argc, char *argv[])
115{
116	int ch, interval, tmp;
117	int i;
118	struct kinfo_proc *p;
119	struct procstat *prstat, *cprstat;
120	long l;
121	pid_t pid;
122	char *dummy;
123	char *nlistf, *memf;
124	int cnt;
125
126	interval = 0;
127	memf = nlistf = NULL;
128	while ((ch = getopt(argc, argv, "CN:M:abcefijklhstvw:x")) != -1) {
129		switch (ch) {
130		case 'C':
131			Cflag++;
132			break;
133
134		case 'M':
135			memf = optarg;
136			break;
137		case 'N':
138			nlistf = optarg;
139			break;
140		case 'a':
141			aflag++;
142			break;
143
144		case 'b':
145			bflag++;
146			break;
147
148		case 'c':
149			cflag++;
150			break;
151
152		case 'e':
153			eflag++;
154			break;
155
156		case 'f':
157			fflag++;
158			break;
159
160		case 'i':
161			iflag++;
162			break;
163
164		case 'j':
165			jflag++;
166			break;
167
168		case 'k':
169			kflag++;
170			break;
171
172		case 'l':
173			lflag++;
174			break;
175
176		case 'n':
177			nflag++;
178			break;
179
180		case 'h':
181			hflag++;
182			break;
183
184		case 's':
185			sflag++;
186			break;
187
188		case 't':
189			tflag++;
190			break;
191
192		case 'v':
193			vflag++;
194			break;
195
196		case 'w':
197			l = strtol(optarg, &dummy, 10);
198			if (*dummy != '\0')
199				usage();
200			if (l < 1 || l > INT_MAX)
201				usage();
202			interval = l;
203			break;
204
205		case 'x':
206			xflag++;
207			break;
208
209		case '?':
210		default:
211			usage();
212		}
213
214	}
215	argc -= optind;
216	argv += optind;
217
218	/* We require that either 0 or 1 mode flags be set. */
219	tmp = bflag + cflag + eflag + fflag + iflag + jflag + (kflag ? 1 : 0) +
220	    lflag + sflag + tflag + vflag + xflag;
221	if (!(tmp == 0 || tmp == 1))
222		usage();
223
224	/* We allow -k to be specified up to twice, but not more. */
225	if (kflag > 2)
226		usage();
227
228	/* Must specify either the -a flag or a list of pids. */
229	if (!(aflag == 1 && argc == 0) && !(aflag == 0 && argc > 0))
230		usage();
231
232	/* Only allow -C with -f. */
233	if (Cflag && !fflag)
234		usage();
235
236	if (memf != NULL)
237		prstat = procstat_open_kvm(nlistf, memf);
238	else
239		prstat = procstat_open_sysctl();
240	if (prstat == NULL)
241		errx(1, "procstat_open()");
242	do {
243		if (aflag) {
244			p = procstat_getprocs(prstat, KERN_PROC_PROC, 0, &cnt);
245			if (p == NULL)
246				errx(1, "procstat_getprocs()");
247			kinfo_proc_sort(p, cnt);
248			for (i = 0; i < cnt; i++) {
249				procstat(prstat, &p[i]);
250
251				/* Suppress header after first process. */
252				hflag = 1;
253			}
254			procstat_freeprocs(prstat, p);
255		}
256		for (i = 0; i < argc; i++) {
257			l = strtol(argv[i], &dummy, 10);
258			if (*dummy == '\0') {
259				if (l < 0)
260					usage();
261				pid = l;
262
263				p = procstat_getprocs(prstat, KERN_PROC_PID, pid, &cnt);
264				if (p == NULL)
265					errx(1, "procstat_getprocs()");
266				if (cnt != 0)
267					procstat(prstat, p);
268				procstat_freeprocs(prstat, p);
269			} else {
270				cprstat = procstat_open_core(argv[i]);
271				if (cprstat == NULL) {
272					warnx("procstat_open()");
273					continue;
274				}
275				p = procstat_getprocs(cprstat, KERN_PROC_PID,
276				    -1, &cnt);
277				if (p == NULL)
278					errx(1, "procstat_getprocs()");
279				if (cnt != 0)
280					procstat(cprstat, p);
281				procstat_freeprocs(cprstat, p);
282				procstat_close(cprstat);
283			}
284			/* Suppress header after first process. */
285			hflag = 1;
286		}
287		if (interval)
288			sleep(interval);
289	} while (interval);
290	procstat_close(prstat);
291	exit(0);
292}
293