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