1 /* $OpenBSD: ps.c,v 1.40 2004/11/24 19:17:10 deraadt Exp $ */
2 /* $NetBSD: ps.c,v 1.15 1995/05/18 20:33:25 mycroft Exp $ */
3
4 /*-
5 * Copyright (c) 1990, 1993, 1994
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #include <sys/param.h>
34 #include <sys/user.h>
35 #include <sys/time.h>
36 #include <sys/resource.h>
37 #include <sys/proc.h>
38 #include <sys/stat.h>
39 #include <sys/ioctl.h>
40 #include <sys/sysctl.h>
41
42 #include <ctype.h>
43 #include <err.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <kvm.h>
47 #include <nlist.h>
48 #include <paths.h>
49 #include <pwd.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54 #include <limits.h>
55
56 #include "ps.h"
57
58 __IDSTRING(copyright, "@(#) Copyright (c) 1990, 1993, 1994\n\
59 The Regents of the University of California. All rights reserved.\n");
60 __SCCSID("@(#)ps.c 8.4 (Berkeley) 4/2/94");
61 __RCSID("$MirOS: src/bin/ps/ps.c,v 1.2 2006/09/21 01:56:10 tg Exp $");
62
63 extern char *__progname;
64
65 struct varent *vhead;
66
67 int eval; /* exit value */
68 int rawcpu; /* -C */
69 int sumrusage; /* -S */
70 int termwidth; /* width of screen (0 == infinity) */
71 int totwidth; /* calculated width of requested variables */
72
73 int ncpu = 1;
74
75 int needcomm, needenv, commandonly;
76
77 enum sort { DEFAULT, SORTMEM, SORTCPU } sortby = DEFAULT;
78
79 static char *kludge_oldps_options(char *);
80 static int pscomp(const void *, const void *);
81 static void scanvars(void);
82 static void usage(void);
83
84 char dfmt[] = "pid tt state time command";
85 char jfmt[] = "user pid ppid pgid sess jobc state tt time command";
86 char lfmt[] = "uid pid ppid cpu pri nice vsz rss wchan state tt time command";
87 char o1[] = "pid";
88 char o2[] = "tt state time command";
89 char ufmt[] = "user pid %cpu %mem vsz rss tt state start time command";
90 char vfmt[] = "pid state time sl re pagein vsz rss lim tsiz %cpu %mem command";
91
92 kvm_t *kd;
93 int kvm_sysctl_only;
94
95 int
main(int argc,char * argv[])96 main(int argc, char *argv[])
97 {
98 struct kinfo_proc2 *kp, **kinfo;
99 struct varent *vent;
100 struct winsize ws;
101 struct passwd *pwd;
102 dev_t ttydev;
103 pid_t pid;
104 uid_t uid;
105 int all, ch, flag, i, fmt, lineno, nentries, mib[6];
106 int prtheader, wflag, kflag, what, Uflag, xflg;
107 char *nlistf, *memf, *swapf, errbuf[_POSIX2_LINE_MAX];
108 size_t size;
109
110 if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
111 ioctl(STDERR_FILENO, TIOCGWINSZ, (char *)&ws) == -1 &&
112 ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&ws) == -1) ||
113 ws.ws_col == 0)
114 termwidth = 79;
115 else
116 termwidth = ws.ws_col - 1;
117
118 if (argc > 1)
119 argv[1] = kludge_oldps_options(argv[1]);
120
121 all = fmt = prtheader = wflag = kflag = Uflag = xflg = 0;
122 pid = -1;
123 uid = 0;
124 ttydev = NODEV;
125 memf = nlistf = swapf = NULL;
126 while ((ch = getopt(argc, argv,
127 "acCeghjkLlM:mN:O:o:p:rSTt:U:uvW:wx")) != -1)
128 switch((char)ch) {
129 case 'a':
130 all = 1;
131 break;
132 case 'c':
133 commandonly = 1;
134 break;
135 case 'e': /* XXX set ufmt */
136 needenv = 1;
137 break;
138 case 'C':
139 rawcpu = 1;
140 break;
141 case 'g':
142 break; /* no-op */
143 case 'h':
144 prtheader = ws.ws_row > 5 ? ws.ws_row : 22;
145 break;
146 case 'j':
147 parsefmt(jfmt);
148 fmt = 1;
149 jfmt[0] = '\0';
150 break;
151 case 'k':
152 kflag++;
153 break;
154 case 'L':
155 showkey();
156 exit(0);
157 case 'l':
158 parsefmt(lfmt);
159 fmt = 1;
160 lfmt[0] = '\0';
161 break;
162 case 'M':
163 memf = optarg;
164 break;
165 case 'm':
166 sortby = SORTMEM;
167 break;
168 case 'N':
169 nlistf = optarg;
170 break;
171 case 'O':
172 parsefmt(o1);
173 parsefmt(optarg);
174 parsefmt(o2);
175 o1[0] = o2[0] = '\0';
176 fmt = 1;
177 break;
178 case 'o':
179 parsefmt(optarg);
180 fmt = 1;
181 break;
182 case 'p':
183 pid = atol(optarg);
184 xflg = 1;
185 break;
186 case 'r':
187 sortby = SORTCPU;
188 break;
189 case 'S':
190 sumrusage = 1;
191 break;
192 case 'T':
193 if ((optarg = ttyname(STDIN_FILENO)) == NULL)
194 errx(1, "stdin: not a terminal");
195 /* FALLTHROUGH */
196 case 't': {
197 struct stat sb;
198 char *ttypath, pathbuf[MAXPATHLEN];
199
200 if (strcmp(optarg, "co") == 0)
201 ttypath = _PATH_CONSOLE;
202 else if (*optarg != '/')
203 (void)snprintf(ttypath = pathbuf,
204 sizeof(pathbuf), "%s%s", _PATH_TTY, optarg);
205 else
206 ttypath = optarg;
207 if (stat(ttypath, &sb) == -1)
208 err(1, "%s", ttypath);
209 if (!S_ISCHR(sb.st_mode))
210 errx(1, "%s: not a terminal", ttypath);
211 ttydev = sb.st_rdev;
212 break;
213 }
214 case 'U':
215 pwd = getpwnam(optarg);
216 if (pwd == NULL)
217 errx(1, "%s: no such user", optarg);
218 uid = pwd->pw_uid;
219 endpwent();
220 Uflag = xflg = 1;
221 break;
222 case 'u':
223 parsefmt(ufmt);
224 sortby = SORTCPU;
225 fmt = 1;
226 ufmt[0] = '\0';
227 break;
228 case 'v':
229 parsefmt(vfmt);
230 sortby = SORTMEM;
231 fmt = 1;
232 vfmt[0] = '\0';
233 break;
234 case 'W':
235 swapf = optarg;
236 break;
237 case 'w':
238 if (wflag)
239 termwidth = UNLIMITED;
240 else if (termwidth < 131)
241 termwidth = 131;
242 wflag++;
243 break;
244 case 'x':
245 xflg = 1;
246 break;
247 default:
248 usage();
249 }
250 argc -= optind;
251 argv += optind;
252
253 #define BACKWARD_COMPATIBILITY
254 #ifdef BACKWARD_COMPATIBILITY
255 if (*argv) {
256 nlistf = *argv;
257 if (*++argv) {
258 memf = *argv;
259 if (*++argv)
260 swapf = *argv;
261 }
262 }
263 #endif
264
265 if (nlistf == NULL && memf == NULL && swapf == NULL) {
266 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
267 kvm_sysctl_only = 1;
268 } else {
269 kd = kvm_openfiles(nlistf, memf, swapf, O_RDONLY, errbuf);
270 }
271 if (kd == NULL && (nlistf != NULL || memf != NULL || swapf != NULL))
272 errx(1, "%s", errbuf);
273
274 if (!fmt)
275 parsefmt(dfmt);
276
277 /* XXX - should be cleaner */
278 if (!all && ttydev == NODEV && pid == -1 && !Uflag) {
279 uid = getuid();
280 Uflag = 1;
281 }
282
283 /*
284 * scan requested variables, noting what structures are needed,
285 * and adjusting header widths as appropriate.
286 */
287 scanvars();
288 /*
289 * get proc list
290 */
291 if (Uflag) {
292 what = KERN_PROC_UID;
293 flag = uid;
294 } else if (ttydev != NODEV) {
295 what = KERN_PROC_TTY;
296 flag = ttydev;
297 } else if (pid != -1) {
298 what = KERN_PROC_PID;
299 flag = pid;
300 } else if (kflag) {
301 what = KERN_PROC_KTHREAD;
302 flag = 0;
303 } else {
304 what = KERN_PROC_ALL;
305 flag = 0;
306 }
307
308 mib[0] = CTL_HW;
309 mib[1] = HW_NCPU;
310 size = sizeof(ncpu);
311 (void) sysctl(mib, 2, &ncpu, &size, NULL, 0);
312
313 /*
314 * select procs
315 */
316 if (kd != NULL) {
317 kp = kvm_getproc2(kd, what, flag, sizeof(*kp), &nentries);
318 if (kp == NULL)
319 errx(1, "%s", kvm_geterr(kd));
320 } else {
321 mib[0] = CTL_KERN;
322 mib[1] = KERN_PROC2;
323 mib[2] = what;
324 mib[3] = flag;
325 mib[4] = sizeof(struct kinfo_proc2);
326 mib[5] = 0;
327 retry:
328 if (sysctl(mib, 6, NULL, &size, NULL, 0) < 0)
329 err(1, "could not get kern.proc2 size");
330 size = 5 * size / 4; /* extra slop */
331 if ((kp = malloc(size)) == NULL)
332 err(1,
333 "failed to allocate memory for proc structures");
334 mib[5] = (int)(size / sizeof(struct kinfo_proc2));
335 if (sysctl(mib, 6, kp, &size, NULL, 0) < 0) {
336 if (errno == ENOMEM) {
337 free(kp);
338 goto retry;
339 }
340 err(1, "could not read kern.proc2");
341 }
342 nentries = (int)(size / sizeof(struct kinfo_proc2));
343 }
344 /*
345 * print header
346 */
347 printheader();
348 if (nentries == 0)
349 exit(1);
350 /*
351 * sort proc list, we convert from an array of structs to an array
352 * of pointers to make the sort cheaper.
353 */
354 if ((kinfo = malloc(sizeof(*kinfo) * nentries)) == NULL)
355 err(1, "failed to allocate memory for proc pointers");
356 for (i = 0; i < nentries; i++)
357 kinfo[i] = &kp[i];
358 qsort(kinfo, nentries, sizeof(*kinfo), pscomp);
359 /*
360 * for each proc, call each variable output function.
361 */
362 for (i = lineno = 0; i < nentries; i++) {
363 if (xflg == 0 && ((int)kinfo[i]->p_tdev == NODEV ||
364 (kinfo[i]->p_flag & P_CONTROLT ) == 0))
365 continue;
366 for (vent = vhead; vent; vent = vent->next) {
367 (vent->var->oproc)(kinfo[i], vent);
368 if (vent->next != NULL)
369 (void)putchar(' ');
370 }
371 (void)putchar('\n');
372 if (prtheader && lineno++ == prtheader - 4) {
373 (void)putchar('\n');
374 printheader();
375 lineno = 0;
376 }
377 }
378 exit(eval);
379 }
380
381 static void
scanvars(void)382 scanvars(void)
383 {
384 struct varent *vent;
385 VAR *v;
386 int i;
387
388 for (vent = vhead; vent; vent = vent->next) {
389 v = vent->var;
390 i = strlen(v->header);
391 if (v->width < i)
392 v->width = i;
393 totwidth += v->width + 1; /* +1 for space */
394 if (v->flag & COMM)
395 needcomm = 1;
396 }
397 totwidth--;
398 }
399
400 static int
pscomp(const void * v1,const void * v2)401 pscomp(const void *v1, const void *v2)
402 {
403 const struct kinfo_proc2 *kp1 = *(const struct kinfo_proc2 **)v1;
404 const struct kinfo_proc2 *kp2 = *(const struct kinfo_proc2 **)v2;
405 int i;
406 #define VSIZE(k) ((k)->p_vm_dsize + (k)->p_vm_ssize + (k)->p_vm_tsize)
407
408 if (sortby == SORTCPU && (i = getpcpu(kp2) - getpcpu(kp1)) != 0)
409 return (i);
410 if (sortby == SORTMEM && (i = VSIZE(kp2) - VSIZE(kp1)) != 0)
411 return (i);
412 if ((i = kp1->p_tdev - kp2->p_tdev) == 0 &&
413 (i = kp1->p_ustart_sec - kp2->p_ustart_sec) == 0)
414 i = kp1->p_ustart_usec - kp2->p_ustart_usec;
415 return (i);
416 }
417
418 /*
419 * ICK (all for getopt), would rather hide the ugliness
420 * here than taint the main code.
421 *
422 * ps foo -> ps -foo
423 * ps 34 -> ps -p34
424 *
425 * The old convention that 't' with no trailing tty arg means the users
426 * tty, is only supported if argv[1] doesn't begin with a '-'. This same
427 * feature is available with the option 'T', which takes no argument.
428 */
429 static char *
kludge_oldps_options(char * s)430 kludge_oldps_options(char *s)
431 {
432 size_t len;
433 char *newopts, *ns, *cp;
434
435 len = strlen(s);
436 if ((newopts = ns = malloc(2 + len + 1)) == NULL)
437 err(1, NULL);
438 /*
439 * options begin with '-'
440 */
441 if (*s != '-')
442 *ns++ = '-'; /* add option flag */
443
444 /*
445 * gaze to end of argv[1]
446 */
447 cp = s + len - 1;
448 /*
449 * if last letter is a 't' flag with no argument (in the context
450 * of the oldps options -- option string NOT starting with a '-' --
451 * then convert to 'T' (meaning *this* terminal, i.e. ttyname(0)).
452 */
453 if (*cp == 't' && *s != '-')
454 *cp = 'T';
455 else {
456 /*
457 * otherwise check for trailing number, which *may* be a
458 * pid.
459 */
460 while (cp >= s && isdigit(*cp))
461 --cp;
462 }
463 cp++;
464 memmove(ns, s, (size_t)(cp - s)); /* copy up to trailing number */
465 ns += cp - s;
466 /*
467 * if there's a trailing number, and not a preceding 'p' (pid) or
468 * 't' (tty) flag, then assume it's a pid and insert a 'p' flag.
469 */
470 if (isdigit(*cp) && (cp == s || (cp[-1] != 't' && cp[-1] != 'p' &&
471 (cp - 1 == s || cp[-2] != 't'))))
472 *ns++ = 'p';
473 /* and append the number */
474 (void)strlcpy(ns, cp, newopts + len + 3 - ns);
475
476 return (newopts);
477 }
478
479 static void
usage(void)480 usage(void)
481 {
482 (void)fprintf(stderr,
483 "usage: %s [-][aCcehjklmrSTuvwx] [-M core] [-N system] [-O fmt] [-o fmt] [-p pid]\n",
484 __progname);
485 fprintf(stderr, "%-*s[-t tty] [-U username] [-W swap]\n",
486 (int)strlen(__progname) + 8, "");
487 (void)fprintf(stderr, " %s [-L]\n", __progname);
488 exit(1);
489 }
490