1 /*-
2 * Top users/processes display for Unix
3 *
4 * This program may be freely redistributed,
5 * but this entire comment MUST remain intact.
6 *
7 * Copyright (c) 1984, 1989, William LeFebvre, Rice University
8 * Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University
9 * Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory
10 * Copyright (c) 1996, William LeFebvre, Group sys Consulting
11 */
12
13 #include <sys/types.h>
14 #include <sys/time.h>
15 #include <sys/cdefs.h>
16 #include <sys/limits.h>
17 #include <sys/resource.h>
18 #include <sys/select.h>
19 #include <sys/signal.h>
20
21 #include <assert.h>
22 #include <err.h>
23 #include <errno.h>
24 #include <getopt.h>
25 #include <jail.h>
26 #include <locale.h>
27 #include <stdbool.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <signal.h>
31 #include <string.h>
32 #include <unistd.h>
33
34 #include "commands.h"
35 #include "display.h" /* interface to display package */
36 #include "screen.h" /* interface to screen package */
37 #include "top.h"
38 #include "machine.h"
39 #include "utils.h"
40 #include "username.h"
41
42 /* Size of the stdio buffer given to stdout */
43 #define Buffersize 2048
44
45 char copyright[] =
46 "Copyright (c) 1984 through 1996, William LeFebvre";
47
48 typedef void sigret_t;
49
50 /* The buffer that stdio will use */
51 static char stdoutbuf[Buffersize];
52
53 static int fmt_flags = 0;
54 int show_args = false;
55 int pcpu_stats = false;
56
57 /* signal handling routines */
58 static sigret_t leave(int);
59 static sigret_t tstop(int);
60 static sigret_t top_winch(int);
61
62 static volatile sig_atomic_t leaveflag;
63 static volatile sig_atomic_t tstopflag;
64 static volatile sig_atomic_t winchflag;
65
66 /* values which need to be accessed by signal handlers */
67 static int max_topn; /* maximum displayable processes */
68
69 /* miscellaneous things */
70 struct process_select ps;
71 pid_t mypid;
72
73 /* pointers to display routines */
74 static void (*d_loadave)(int mpid, double *avenrun) = i_loadave;
75 static void (*d_procstates)(int total, int *brkdn) = i_procstates;
76 static void (*d_cpustates)(int *states) = i_cpustates;
77 static void (*d_memory)(int *stats) = i_memory;
78 static void (*d_arc)(int *stats) = i_arc;
79 static void (*d_carc)(int *stats) = i_carc;
80 static void (*d_swap)(int *stats) = i_swap;
81 static void (*d_message)(void) = i_message;
82 static void (*d_header)(const char *text) = i_header;
83 static void (*d_process)(int line, char *thisline) = i_process;
84
85 static void reset_display(void);
86
87 static const struct option longopts[] = {
88 { "cpu-display-mode", no_argument, NULL, 'C' }, /* differs from orignal */
89 /* D reserved */
90 { "thread", no_argument, NULL, 'H' },
91 { "idle-procs", no_argument, NULL, 'I' },
92 { "jail", required_argument, NULL, 'J' },
93 { "per-cpu", no_argument, NULL, 'P' },
94 { "system-procs", no_argument, NULL, 'S' },
95 { "thread-id", no_argument, NULL, 'T' }, /* differs from orignal */
96 { "user", required_argument, NULL, 'U' },
97 { "all", no_argument, NULL, 'a' },
98 { "batch", no_argument, NULL, 'b' },
99 /* c reserved */
100 { "displays", required_argument, NULL, 'd' },
101 { "interactive", no_argument, NULL, 'i' },
102 { "jail-id", no_argument, NULL, 'j' },
103 { "display-mode", required_argument, NULL, 'm' },
104 /* n is identical to batch */
105 { "sort-order", required_argument, NULL, 'o' },
106 { "pid", required_argument, NULL, 'p' },
107 { "quick", no_argument, NULL, 'q' },
108 { "delay", required_argument, NULL, 's' },
109 { "threads", no_argument, NULL, 't' },
110 { "uids", no_argument, NULL, 'u' },
111 { "version", no_argument, NULL, 'v' },
112 { "swap", no_argument, NULL, 'w' },
113 { "system-idle-procs", no_argument, NULL, 'z' },
114 { NULL, 0, NULL, 0 }
115 };
116
117 static void
reset_uids(void)118 reset_uids(void)
119 {
120 for (size_t i = 0; i < TOP_MAX_UIDS; ++i)
121 ps.uid[i] = -1;
122 }
123
124 static int
add_uid(int uid)125 add_uid(int uid)
126 {
127 size_t i = 0;
128
129 /* Add the uid if there's room */
130 for (; i < TOP_MAX_UIDS; ++i)
131 {
132 if (ps.uid[i] == -1 || ps.uid[i] == uid)
133 {
134 ps.uid[i] = uid;
135 break;
136 }
137 }
138
139 return (i == TOP_MAX_UIDS);
140 }
141
142 static void
rem_uid(int uid)143 rem_uid(int uid)
144 {
145 size_t i = 0;
146 size_t where = TOP_MAX_UIDS;
147
148 /* Look for the user to remove - no problem if it's not there */
149 for (; i < TOP_MAX_UIDS; ++i)
150 {
151 if (ps.uid[i] == -1)
152 break;
153 if (ps.uid[i] == uid)
154 where = i;
155 }
156
157 /* Make sure we don't leave a hole in the middle */
158 if (where != TOP_MAX_UIDS)
159 {
160 ps.uid[where] = ps.uid[i-1];
161 ps.uid[i-1] = -1;
162 }
163 }
164
165 static int
handle_user(char * buf,size_t buflen)166 handle_user(char *buf, size_t buflen)
167 {
168 int rc = 0;
169 int uid = -1;
170 char *buf2 = buf;
171
172 new_message(MT_standout, "Username to show (+ for all): ");
173 if (readline(buf, buflen, false) <= 0)
174 {
175 clear_message();
176 return (rc);
177 }
178
179 if (buf[0] == '+' || buf[0] == '-')
180 {
181 if (buf[1] == '\0')
182 {
183 reset_uids();
184 goto end;
185 }
186 else
187 ++buf2;
188 }
189
190 if ((uid = userid(buf2)) == -1)
191 {
192 new_message(MT_standout, " %s: unknown user", buf2);
193 rc = 1;
194 goto end;
195 }
196
197 if (buf2 == buf)
198 {
199 reset_uids();
200 ps.uid[0] = uid;
201 goto end;
202 }
203
204 if (buf[0] == '+')
205 {
206 if (add_uid(uid))
207 {
208 new_message(MT_standout, " too many users, reset with '+'");
209 rc = 1;
210 goto end;
211 }
212 }
213 else
214 rem_uid(uid);
215
216 end:
217 putchar('\r');
218 return (rc);
219 }
220
221 int
main(int argc,const char * argv[])222 main(int argc, const char *argv[])
223 {
224 int i;
225 int active_procs;
226
227 struct system_info system_info;
228 struct statics statics;
229 void * processes;
230
231 static char tempbuf1[50];
232 static char tempbuf2[50];
233 sigset_t old_sigmask, new_sigmask;
234 int topn = Infinity;
235 struct timeval delay = { 2, 0 };
236 int displays = 0; /* indicates unspecified */
237 int sel_ret = 0;
238 time_t curr_time;
239 char *(*get_userid)(int) = username;
240 const char *uname_field = "USERNAME";
241 const char *header_text;
242 char *env_top;
243 const char **preset_argv;
244 int preset_argc = 0;
245 const char **av = NULL;
246 int ac = -1;
247 bool do_unames = true;
248 char interactive = 2;
249 char warnings = 0;
250 char topn_specified = false;
251 char ch;
252 char no_command = 1;
253 struct timeval timeout;
254 char *order_name = NULL;
255 int order_index = 0;
256 fd_set readfds;
257 char *nptr;
258
259 /* set the buffer for stdout */
260 #ifdef DEBUG
261 extern FILE *debug;
262 debug = fopen("debug.run", "w");
263 setbuffer(stdout, NULL, 0);
264 #else
265 setbuffer(stdout, stdoutbuf, Buffersize);
266 #endif
267
268 if (setlocale(LC_ALL, "") == NULL) {
269 fprintf(stderr, "invalid locale.\n");
270 exit(1);
271 }
272
273 mypid = getpid();
274
275 /* get our name */
276 /* initialize some selection options */
277 ps.idle = true;
278 ps.self = true;
279 ps.system = false;
280 reset_uids();
281 ps.thread = false;
282 ps.wcpu = 1;
283 ps.jid = -1;
284 ps.jail = false;
285 ps.swap = false;
286 ps.kidle = true;
287 ps.pid = -1;
288 ps.command = NULL;
289 ps.thread_id = false;
290
291 /* get preset options from the environment */
292 if ((env_top = getenv("TOP")) != NULL)
293 {
294 av = preset_argv = argparse(env_top, &preset_argc);
295 ac = preset_argc;
296
297 /* set the dummy argument to an explanatory message, in case
298 getopt encounters a bad argument */
299 preset_argv[0] = "while processing environment";
300 }
301
302 /* process options */
303 do {
304 /* if we're done doing the presets, then process the real arguments */
305 if (preset_argc == 0)
306 {
307 ac = argc;
308 av = argv;
309
310 /* this should keep getopt happy... */
311 optind = 1;
312 }
313
314 while ((i = getopt_long(ac, __DECONST(char * const *, av), "CSIHPabijJ:nquvzs:d:U:m:o:p:Ttw", longopts, NULL)) != EOF)
315 {
316 switch(i)
317 {
318 case 'v': /* show version number */
319 errx(0, "version FreeBSD");
320 break;
321
322 case 'u': /* toggle uid/username display */
323 do_unames = !do_unames;
324 break;
325
326 case 'U': /* display only username's processes */
327 if ((ps.uid[0] = userid(optarg)) == -1)
328 {
329 errx(1, "%s: unknown user\n", optarg);
330 }
331 break;
332
333 case 'S': /* show system processes */
334 ps.system = true;
335 break;
336
337 case 'I': /* show idle processes */
338 ps.idle = !ps.idle;
339 break;
340
341 case 'i': /* go interactive regardless */
342 interactive = 1;
343 break;
344
345 case 'n': /* batch, or non-interactive */
346 case 'b':
347 interactive = 0;
348 break;
349
350 case 'a':
351 fmt_flags ^= FMT_SHOWARGS;
352 break;
353
354 case 'd': /* number of displays to show */
355 if ((i = atoiwi(optarg)) == Invalid || i == 0)
356 {
357 warnx("warning: display count should be positive -- option ignored");
358 warnings++;
359 }
360 else
361 {
362 displays = i;
363 }
364 break;
365 case 'p': {
366 unsigned long long num;
367 const char *errstr;
368
369 num = strtonum(optarg, 0, INT_MAX, &errstr);
370 if (errstr != NULL || !find_pid(num)) {
371 fprintf(stderr, "%s: unknown pid\n", optarg);
372 exit(1);
373 }
374 ps.pid = (pid_t)num;
375 ps.system = true;
376 break;
377 }
378
379 case 's':
380 {
381 double delay_d = strtod(optarg, &nptr);
382 if (nptr == optarg)
383 {
384 warnx("warning: invalid delay");
385 warnings++;
386 }
387 else if (delay_d <= 0)
388 {
389 warnx("warning: seconds delay should be positive -- using default");
390 warnings++;
391 }
392 else
393 {
394 delay.tv_sec = delay_d;
395 delay.tv_usec = (delay_d - delay.tv_sec) * 1e6;
396 }
397 break;
398 }
399
400 case 'q': /* be quick about it */
401 errno = 0;
402 i = setpriority(PRIO_PROCESS, 0, PRIO_MIN);
403 if (i == -1 && errno != 0) {
404 warnx("warning: `-q' option failed (%m)");
405 warnings++;
406 }
407 break;
408
409 case 'm': /* select display mode */
410 if (strcmp(optarg, "io") == 0) {
411 displaymode = DISP_IO;
412 } else if (strcmp(optarg, "cpu") == 0) {
413 displaymode = DISP_CPU;
414 } else {
415 errx(1, "warning: `-m' option can only take args 'io' or 'cpu'");
416 }
417 break;
418
419 case 'o': /* select sort order */
420 order_name = optarg;
421 break;
422
423 case 't':
424 ps.self = !ps.self;
425 break;
426
427 case 'C':
428 ps.wcpu = !ps.wcpu;
429 break;
430
431 case 'H':
432 ps.thread = !ps.thread;
433 break;
434
435 case 'T':
436 ps.thread_id = !ps.thread_id;
437 break;
438
439 case 'j':
440 ps.jail = !ps.jail;
441 break;
442
443 case 'J': /* display only jail's processes */
444 if ((ps.jid = jail_getid(optarg)) == -1)
445 {
446 fprintf(stderr, "%s: unknown jail\n", optarg);
447 exit(1);
448 }
449 ps.jail = 1;
450 break;
451
452 case 'P':
453 pcpu_stats = !pcpu_stats;
454 break;
455
456 case 'w':
457 ps.swap = 1;
458 break;
459
460 case 'z':
461 ps.kidle = !ps.kidle;
462 break;
463
464 default:
465 errx(1,
466 "[-abCHIijnPqStuvwz] [-d count] [-J jail] [-m cpu | io] [-o field]\n"
467 " [-p pid] [-s time] [-U username] [number]");
468 }
469 }
470
471 /* get count of top processes to display (if any) */
472 if (optind < ac)
473 {
474 if ((topn = atoiwi(av[optind])) == Invalid)
475 {
476 warnx("warning: process display count should be non-negative -- using default");
477 warnings++;
478 }
479 else
480 {
481 topn_specified = true;
482 }
483 }
484
485 /* tricky: remember old value of preset_argc & set preset_argc = 0 */
486 i = preset_argc;
487 preset_argc = 0;
488
489 /* repeat only if we really did the preset arguments */
490 } while (i != 0);
491
492 /* set constants for username/uid display correctly */
493 if (!do_unames)
494 {
495 uname_field = " UID ";
496 get_userid = itoa7;
497 }
498
499 /* initialize the kernel memory interface */
500 if (machine_init(&statics) == -1)
501 {
502 exit(1);
503 }
504
505 /* determine sorting order index, if necessary */
506 if (order_name != NULL)
507 {
508 if ((order_index = string_index(order_name, statics.order_names)) == -1)
509 {
510 const char * const *pp;
511
512 warnx("'%s' is not a recognized sorting order.", order_name);
513 fprintf(stderr, "\tTry one of these:");
514 pp = statics.order_names;
515 while (*pp != NULL)
516 {
517 fprintf(stderr, " %s", *pp++);
518 }
519 fputc('\n', stderr);
520 exit(1);
521 }
522 }
523
524 /* initialize termcap */
525 init_termcap(interactive);
526
527 /* get the string to use for the process area header */
528 header_text = format_header(uname_field);
529
530 /* initialize display interface */
531 if ((max_topn = display_init(&statics)) == -1)
532 {
533 errx(4, "can't allocate sufficient memory");
534 }
535
536 /* print warning if user requested more processes than we can display */
537 if (topn > max_topn)
538 {
539 warnx("warning: this terminal can only display %d processes.", max_topn);
540 warnings++;
541 }
542
543 /* adjust for topn == Infinity */
544 if (topn == Infinity)
545 {
546 /*
547 * For smart terminals, infinity really means everything that can
548 * be displayed, or Largest.
549 * On dumb terminals, infinity means every process in the system!
550 * We only really want to do that if it was explicitly specified.
551 * This is always the case when "Default_TOPN != Infinity". But if
552 * topn wasn't explicitly specified and we are on a dumb terminal
553 * and the default is Infinity, then (and only then) we use
554 * "Nominal_TOPN" instead.
555 */
556 topn = smart_terminal ? Largest :
557 (topn_specified ? Largest : Nominal_TOPN);
558 }
559
560 /* set header display accordingly */
561 display_header(topn > 0);
562
563 /* determine interactive state */
564 if (interactive == 2)
565 {
566 interactive = smart_terminal;
567 }
568
569 /* if # of displays not specified, fill it in */
570 if (displays == 0)
571 {
572 displays = smart_terminal ? Infinity : 1;
573 }
574
575 /* hold interrupt signals while setting up the screen and the handlers */
576
577 sigemptyset(&new_sigmask);
578 sigaddset(&new_sigmask, SIGINT);
579 sigaddset(&new_sigmask, SIGQUIT);
580 sigaddset(&new_sigmask, SIGTSTP);
581 sigprocmask(SIG_BLOCK, &new_sigmask, &old_sigmask);
582 init_screen();
583 signal(SIGINT, leave);
584 signal(SIGQUIT, leave);
585 signal(SIGTSTP, tstop);
586 signal(SIGWINCH, top_winch);
587 sigprocmask(SIG_SETMASK, &old_sigmask, NULL);
588 if (warnings)
589 {
590 fputs("....", stderr);
591 fflush(stderr);
592 sleep(3 * warnings);
593 fputc('\n', stderr);
594 }
595
596 restart:
597
598 /*
599 * main loop -- repeat while display count is positive or while it
600 * indicates infinity (by being -1)
601 */
602
603 while ((displays == -1) || (displays-- > 0))
604 {
605 int (*compare)(const void * const, const void * const);
606
607
608 /* get the current stats */
609 get_system_info(&system_info);
610
611 compare = compares[order_index];
612
613 /* get the current set of processes */
614 processes =
615 get_process_info(&system_info, &ps, compare);
616
617 /* display the load averages */
618 (*d_loadave)(system_info.last_pid,
619 system_info.load_avg);
620
621 /* display the battery info (if any) */
622 i_battery(statics.nbatteries, system_info.battery);
623
624 /* display the current time */
625 /* this method of getting the time SHOULD be fairly portable */
626 time(&curr_time);
627 i_uptime(&system_info.boottime, &curr_time);
628 i_timeofday(&curr_time);
629
630 /* display process state breakdown */
631 (*d_procstates)(system_info.p_total,
632 system_info.procstates);
633 (*d_cpustates)(system_info.cpustates);
634
635 /* display memory stats */
636 (*d_memory)(system_info.memory);
637 (*d_arc)(system_info.arc);
638 (*d_carc)(system_info.carc);
639
640 /* display swap stats */
641 (*d_swap)(system_info.swap);
642
643 /* handle message area */
644 (*d_message)();
645
646 /* update the header area */
647 (*d_header)(header_text);
648
649 if (topn > 0)
650 {
651 /* determine number of processes to actually display */
652 /* this number will be the smallest of: active processes,
653 number user requested, number current screen accommodates */
654 active_procs = system_info.p_pactive;
655 if (active_procs > topn)
656 {
657 active_procs = topn;
658 }
659 if (active_procs > max_topn)
660 {
661 active_procs = max_topn;
662 }
663
664 /* now show the top "n" processes. */
665 for (i = 0; i < active_procs; i++)
666 {
667 (*d_process)(i, format_next_process(processes, get_userid,
668 fmt_flags));
669 }
670 }
671 else
672 {
673 i = 0;
674 }
675
676 /* do end-screen processing */
677 u_endscreen(i);
678
679 /* now, flush the output buffer */
680 if (fflush(stdout) != 0)
681 {
682 new_message(MT_standout, " Write error on stdout");
683 putchar('\r');
684 quit(1);
685 }
686
687 /* only do the rest if we have more displays to show */
688 if (displays)
689 {
690 /* switch out for new display on smart terminals */
691 if (smart_terminal)
692 {
693 if (overstrike)
694 {
695 reset_display();
696 }
697 else
698 {
699 d_loadave = u_loadave;
700 d_procstates = u_procstates;
701 d_cpustates = u_cpustates;
702 d_memory = u_memory;
703 d_arc = u_arc;
704 d_carc = u_carc;
705 d_swap = u_swap;
706 d_message = u_message;
707 d_header = u_header;
708 d_process = u_process;
709 }
710 }
711
712 no_command = true;
713 if (!interactive)
714 {
715 timeout = delay;
716 select(0, NULL, NULL, NULL, &timeout);
717 if (leaveflag) {
718 end_screen();
719 exit(0);
720 }
721 }
722 else while (no_command)
723 {
724 /* assume valid command unless told otherwise */
725 no_command = false;
726
727 /* set up arguments for select with timeout */
728 FD_ZERO(&readfds);
729 FD_SET(0, &readfds); /* for standard input */
730 timeout = delay;
731
732 if (leaveflag) {
733 end_screen();
734 exit(0);
735 }
736
737 if (tstopflag) {
738 /* move to the lower left */
739 end_screen();
740 fflush(stdout);
741
742 /* default the signal handler action */
743 signal(SIGTSTP, SIG_DFL);
744
745 /* unblock the signal and send ourselves one */
746 sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
747 kill(0, SIGTSTP);
748
749 /* reset the signal handler */
750 signal(SIGTSTP, tstop);
751
752 /* reinit screen */
753 reinit_screen();
754 reset_display();
755 tstopflag = 0;
756 goto restart;
757 }
758
759 if (winchflag) {
760 /* reascertain the screen dimensions */
761 get_screensize();
762
763 /* tell display to resize */
764 max_topn = display_resize();
765
766 /* reset the signal handler */
767 signal(SIGWINCH, top_winch);
768
769 reset_display();
770 winchflag = 0;
771 goto restart;
772 }
773
774 /* wait for either input or the end of the delay period */
775 sel_ret = select(2, &readfds, NULL, NULL, &timeout);
776 if (sel_ret < 0 && errno != EINTR)
777 quit(0);
778 if (sel_ret > 0)
779 {
780 int newval;
781 const char *errmsg;
782 const struct command *cptr;
783
784 /* something to read -- clear the message area first */
785 clear_message();
786
787 /* now read it and convert to command strchr */
788 /* (use "change" as a temporary to hold strchr) */
789 if (read(0, &ch, 1) != 1)
790 {
791 /* read error: either 0 or -1 */
792 new_message(MT_standout, " Read error on stdin");
793 putchar('\r');
794 quit(1);
795 }
796 if (ch == '\r' || ch == '\n') {
797 continue;
798 }
799 cptr = all_commands;
800 while (cptr->c != '\0') {
801 if (cptr->c == ch) {
802 break;
803 }
804 cptr++;
805 }
806 if (cptr->c == '\0') {
807 new_message(MT_standout, " Command not understood");
808 putchar('\r');
809 no_command = true;
810 }
811 if (overstrike && !cptr->available_to_dumb)
812 {
813 new_message(MT_standout,
814 " Command cannot be handled by this terminal");
815 putchar('\r');
816 no_command = true;
817 }
818 if (!no_command) {
819 switch(cptr->id)
820 {
821 case CMD_redraw: /* redraw screen */
822 reset_display();
823 break;
824
825 case CMD_update: /* merely update display */
826 break;
827
828 case CMD_quit:
829 quit(0);
830 break;
831
832 case CMD_help:
833 reset_display();
834 top_clear();
835 show_help();
836 top_standout("Hit any key to continue: ");
837 fflush(stdout);
838 read(0, &ch, 1);
839 break;
840
841 case CMD_errors: /* show errors */
842 if (error_count() == 0)
843 {
844 new_message(MT_standout,
845 " Currently no errors to report.");
846 putchar('\r');
847 no_command = true;
848 }
849 else
850 {
851 reset_display();
852 top_clear();
853 show_errors();
854 top_standout("Hit any key to continue: ");
855 fflush(stdout);
856 read(0, &ch, 1);
857 }
858 break;
859
860 case CMD_number:
861 new_message(MT_standout,
862 "Number of processes to show: ");
863 newval = readline(tempbuf1, 8, true);
864 if (newval > -1)
865 {
866 if (newval > max_topn)
867 {
868 new_message(MT_standout | MT_delayed,
869 " This terminal can only display %d processes.",
870 max_topn);
871 putchar('\r');
872 }
873
874 if (newval == 0)
875 {
876 /* inhibit the header */
877 display_header(false);
878 }
879 else if (newval > topn && topn == 0)
880 {
881 /* redraw the header */
882 display_header(true);
883 d_header = i_header;
884 }
885 topn = newval;
886 }
887 break;
888
889 case CMD_delay: /* new seconds delay */
890 new_message(MT_standout, "Seconds to delay: ");
891 if ((i = readline(tempbuf1, 8, false)) > 0)
892 {
893 double delay_d = strtod(tempbuf1, &nptr);
894 if (nptr == tempbuf1 || delay_d <= 0)
895 {
896 new_message(MT_standout, " Invalid delay");
897 putchar('\r');
898 no_command = true;
899 }
900 else
901 {
902 delay.tv_sec = delay_d;
903 delay.tv_usec = (delay_d - delay.tv_sec) * 1e6;
904 clear_message();
905 }
906 }
907 break;
908
909 case CMD_grep: /* grep command name */
910 new_message(MT_standout,
911 "Grep command name (+ for all): ");
912 if (readline(tempbuf1, sizeof(tempbuf1), false) > 0) {
913 free(ps.command);
914 if (tempbuf1[0] == '+' && tempbuf1[1] == '\0') {
915 ps.command = NULL;
916 } else if ((ps.command = strdup(tempbuf1)) == NULL)
917 quit(1);
918 }
919 clear_message();
920 break;
921
922 case CMD_displays: /* change display count */
923 new_message(MT_standout,
924 "Displays to show (currently %s): ",
925 displays == -1 ? "infinite" :
926 itoa(displays));
927 if ((i = readline(tempbuf1, 10, true)) > 0)
928 {
929 displays = i;
930 }
931 else if (i == 0)
932 {
933 quit(0);
934 }
935 clear_message();
936 break;
937
938 case CMD_kill: /* kill program */
939 new_message(0, "kill ");
940 if (readline(tempbuf2, sizeof(tempbuf2), false) > 0)
941 {
942 if ((errmsg = kill_procs(tempbuf2)) != NULL)
943 {
944 new_message(MT_standout, "%s", errmsg);
945 putchar('\r');
946 no_command = true;
947 }
948 }
949 else
950 {
951 clear_message();
952 }
953 break;
954
955 case CMD_renice: /* renice program */
956 new_message(0, "renice ");
957 if (readline(tempbuf2, sizeof(tempbuf2), false) > 0)
958 {
959 if ((errmsg = renice_procs(tempbuf2)) != NULL)
960 {
961 new_message(MT_standout, "%s", errmsg);
962 putchar('\r');
963 no_command = true;
964 }
965 }
966 else
967 {
968 clear_message();
969 }
970 break;
971
972 case CMD_idletog:
973 ps.idle = !ps.idle;
974 new_message(MT_standout | MT_delayed,
975 " %sisplaying idle processes.",
976 ps.idle ? "D" : "Not d");
977 putchar('\r');
978 break;
979
980 case CMD_selftog:
981 ps.self = !ps.self;
982 new_message(MT_standout | MT_delayed,
983 " %sisplaying self.",
984 (ps.self) ? "D" : "Not d");
985 putchar('\r');
986 break;
987
988 case CMD_user:
989 if (handle_user(tempbuf2, sizeof(tempbuf2)))
990 no_command = true;
991 break;
992
993 case CMD_thrtog:
994 ps.thread = !ps.thread;
995 new_message(MT_standout | MT_delayed,
996 " Displaying threads %s",
997 ps.thread ? "separately" : "as a count");
998 header_text = format_header(uname_field);
999 reset_display();
1000 putchar('\r');
1001 break;
1002
1003 case CMD_toggletid:
1004 ps.thread_id = !ps.thread_id;
1005 new_message(MT_standout | MT_delayed,
1006 " Displaying %s",
1007 ps.thread_id ? "tid" : "pid");
1008 header_text = format_header(uname_field);
1009 reset_display();
1010 putchar('\r');
1011 break;
1012
1013 case CMD_wcputog:
1014 ps.wcpu = !ps.wcpu;
1015 new_message(MT_standout | MT_delayed,
1016 " Displaying %s CPU",
1017 ps.wcpu ? "weighted" : "raw");
1018 header_text = format_header(uname_field);
1019 reset_display();
1020 putchar('\r');
1021 break;
1022 case CMD_viewtog:
1023 displaymode = displaymode == DISP_IO ? DISP_CPU : DISP_IO;
1024 new_message(MT_standout | MT_delayed,
1025 " Displaying %s statistics.",
1026 displaymode == DISP_IO ? "IO" : "CPU");
1027 header_text = format_header(uname_field);
1028 display_header(true);
1029 d_header = i_header;
1030 reset_display();
1031 break;
1032 case CMD_viewsys:
1033 ps.system = !ps.system;
1034 new_message(MT_standout | MT_delayed,
1035 " %sisplaying system processes.",
1036 ps.system ? "D" : "Not d");
1037 break;
1038 case CMD_showargs:
1039 fmt_flags ^= FMT_SHOWARGS;
1040 show_args = fmt_flags & FMT_SHOWARGS;
1041 new_message(MT_standout | MT_delayed,
1042 " %sisplaying process arguments.",
1043 fmt_flags & FMT_SHOWARGS ? "D" : "Not d");
1044 break;
1045 case CMD_order:
1046 new_message(MT_standout,
1047 "Order to sort: ");
1048 if (readline(tempbuf2, sizeof(tempbuf2), false) > 0)
1049 {
1050 if ((i = string_index(tempbuf2, statics.order_names)) == -1)
1051 {
1052 new_message(MT_standout,
1053 " %s: unrecognized sorting order", tempbuf2);
1054 no_command = true;
1055 }
1056 else
1057 {
1058 order_index = i;
1059 }
1060 putchar('\r');
1061 }
1062 else
1063 {
1064 clear_message();
1065 }
1066 break;
1067 case CMD_jidtog:
1068 ps.jail = !ps.jail;
1069 new_message(MT_standout | MT_delayed,
1070 " %sisplaying jail ID.",
1071 ps.jail ? "D" : "Not d");
1072 header_text = format_header(uname_field);
1073 reset_display();
1074 putchar('\r');
1075 break;
1076
1077 case CMD_jail:
1078 new_message(MT_standout,
1079 "Jail to show (+ for all): ");
1080 if (readline(tempbuf2, sizeof(tempbuf2), false) > 0)
1081 {
1082 if (tempbuf2[0] == '+' &&
1083 tempbuf2[1] == '\0')
1084 {
1085 ps.jid = -1;
1086 }
1087 else if ((i = jail_getid(tempbuf2)) == -1)
1088 {
1089 new_message(MT_standout,
1090 " %s: unknown jail", tempbuf2);
1091 no_command = true;
1092 }
1093 else
1094 {
1095 ps.jid = i;
1096 }
1097 if (ps.jail == 0) {
1098 ps.jail = 1;
1099 new_message(MT_standout |
1100 MT_delayed, " Displaying jail "
1101 "ID.");
1102 header_text =
1103 format_header(uname_field);
1104 reset_display();
1105 }
1106 putchar('\r');
1107 }
1108 else
1109 {
1110 clear_message();
1111 }
1112 break;
1113
1114 case CMD_kidletog:
1115 ps.kidle = !ps.kidle;
1116 new_message(MT_standout | MT_delayed,
1117 " %sisplaying system idle process.",
1118 ps.kidle ? "D" : "Not d");
1119 putchar('\r');
1120 break;
1121 case CMD_pcputog:
1122 pcpu_stats = !pcpu_stats;
1123 new_message(MT_standout | MT_delayed,
1124 " Displaying %sCPU statistics.",
1125 pcpu_stats ? "per-" : "global ");
1126 toggle_pcpustats();
1127 max_topn = display_updatecpus(&statics);
1128 reset_display();
1129 putchar('\r');
1130 break;
1131 case CMD_swaptog:
1132 ps.swap = !ps.swap;
1133 new_message(MT_standout | MT_delayed,
1134 " %sisplaying per-process swap usage.",
1135 ps.swap ? "D" : "Not d");
1136 header_text = format_header(uname_field);
1137 reset_display();
1138 putchar('\r');
1139 break;
1140 case CMD_pid:
1141 new_message(MT_standout,
1142 "Process id to show (+ for all): ");
1143 if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) {
1144 if (tempbuf2[0] == '+' &&
1145 tempbuf2[1] == '\0') {
1146 ps.pid = (pid_t)-1;
1147 } else {
1148 unsigned long long num;
1149 const char *errstr;
1150
1151 num = strtonum(tempbuf2, 0, INT_MAX,
1152 &errstr);
1153 if (errstr != NULL || !find_pid(num)) {
1154 new_message(MT_standout,
1155 " %s: unknown pid",
1156 tempbuf2);
1157 no_command = true;
1158 } else {
1159 ps.pid = (pid_t)num;
1160 }
1161 }
1162 putchar('\r');
1163 } else
1164 clear_message();
1165 break;
1166 case CMD_NONE:
1167 assert(false && "reached switch without command");
1168 }
1169 }
1170 }
1171
1172 /* flush out stuff that may have been written */
1173 fflush(stdout);
1174 }
1175 }
1176 }
1177
1178 #ifdef DEBUG
1179 fclose(debug);
1180 #endif
1181 quit(0);
1182 }
1183
1184 /*
1185 * reset_display() - reset all the display routine pointers so that entire
1186 * screen will get redrawn.
1187 */
1188
1189 static void
reset_display(void)1190 reset_display(void)
1191 {
1192 d_loadave = i_loadave;
1193 d_procstates = i_procstates;
1194 d_cpustates = i_cpustates;
1195 d_memory = i_memory;
1196 d_arc = i_arc;
1197 d_carc = i_carc;
1198 d_swap = i_swap;
1199 d_message = i_message;
1200 d_header = i_header;
1201 d_process = i_process;
1202 }
1203
1204 /*
1205 * signal handlers
1206 */
1207
1208 static sigret_t
leave(int i __unused)1209 leave(int i __unused) /* exit under normal conditions -- INT handler */
1210 {
1211
1212 leaveflag = 1;
1213 }
1214
1215 static sigret_t
tstop(int i __unused)1216 tstop(int i __unused) /* SIGTSTP handler */
1217 {
1218
1219 tstopflag = 1;
1220 }
1221
1222 static sigret_t
top_winch(int i __unused)1223 top_winch(int i __unused) /* SIGWINCH handler */
1224 {
1225
1226 winchflag = 1;
1227 }
1228
1229 void __dead2
quit(int status)1230 quit(int status) /* exit under duress */
1231 {
1232 end_screen();
1233 exit(status);
1234 }
1235