2 * Copyright (c) 1984 through 2008, William LeFebvre
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
16 * * Neither the name of William LeFebvre nor the names of other
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 "Copyright (c) 1984 through 2008, William LeFebvre";
37 * Changes to other files that we can do at the same time:
38 * screen.c:init_termcap: get rid of the "interactive" argument and have it
39 * pass back something meaningful (such as success/failure/error).
46 #include <sys/types.h>
50 #ifdef HAVE_SYS_UTSNAME_H
51 #include <sys/utsname.h>
60 #define STDIN_FILENO 0
63 /* determine which type of signal functions to use */
64 /* cant have sigaction without sigprocmask */
65 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGPROCMASK)
68 /* always use sigaction when it is available */
72 /* use sighold/sigrelse, otherwise use old fashioned BSD signals */
73 #if !defined(HAVE_SIGHOLD) || !defined(HAVE_SIGRELSE)
78 /* if FD_SET and friends aren't present, then fake something up */
81 #define FD_ZERO(x) (*(x) = 0)
82 #define FD_SET(f, x) (*(x) = 1<<f)
85 /* includes specific to top */
89 #include "globalstate.h"
102 #define BUFFERSIZE 4096
106 /* externs for getopt: */
111 static char stdoutbuf
[BUFFERSIZE
];
112 static jmp_buf jmp_int
;
115 char *myname
= "top";
132 set_signal(int sig
, RETSIGTYPE (*handler
)(int))
135 #ifdef HAVE_SIGACTION
136 struct sigaction action
;
138 action
.sa_handler
= handler
;
140 (void) sigaction(sig
, &action
, NULL
);
142 (void) signal(sig
, handler
);
147 release_signal(int sig
)
150 #ifdef HAVE_SIGACTION
153 sigaddset(&set
, sig
);
154 sigprocmask(SIG_UNBLOCK
, &set
, NULL
);
162 (void) sigsetmask(sigblock(0) & ~(sigmask(sig
)));
167 sig_leave(int i
) /* exit under normal conditions -- INT handler */
175 sig_tstop(int i
) /* SIGTSTP handler */
178 /* move to the lower left */
182 /* default the signal handler action */
183 set_signal(SIGTSTP
, SIG_DFL
);
185 /* unblock the TSTP signal */
186 release_signal(SIGTSTP
);
188 /* send ourselves a TSTP to stop the process */
189 (void) kill(0, SIGTSTP
);
191 /* reset the signal handler */
192 set_signal(SIGTSTP
, sig_tstop
);
197 /* jump back to a known place in the main loop */
198 longjmp(jmp_int
, JMP_RESUME
);
205 sig_winch(int i
) /* SIGWINCH handler */
208 /* reascertain the screen dimensions */
211 /* jump back to a known place in the main loop */
212 longjmp(jmp_int
, JMP_RESIZE
);
216 #ifdef HAVE_SIGACTION
217 static sigset_t signalset
;
224 #ifdef HAVE_SIGACTION
225 sigemptyset(&signalset
);
226 sigaddset(&signalset
, SIGINT
);
227 sigaddset(&signalset
, SIGQUIT
);
228 sigaddset(&signalset
, SIGTSTP
);
230 sigaddset(&signalset
, SIGWINCH
);
232 sigprocmask(SIG_BLOCK
, &signalset
, NULL
);
233 return (void *)(&signalset
);
249 mask
= sigblock(sigmask(SIGINT
) | sigmask(SIGQUIT
) |
250 sigmask(SIGTSTP
) | sigmask(SIGWINCH
));
252 mask
= sigblock(sigmask(SIGINT
) | sigmask(SIGQUIT
) | sigmask(SIGTSTP
));
263 (void) set_signal(SIGINT
, sig_leave
);
264 (void) set_signal(SIGQUIT
, sig_leave
);
265 (void) set_signal(SIGTSTP
, sig_tstop
);
267 (void) set_signal(SIGWINCH
, sig_winch
);
272 release_signals(void *parm
)
275 #ifdef HAVE_SIGACTION
276 sigprocmask(SIG_UNBLOCK
, (sigset_t
*)parm
, NULL
);
289 (void) sigsetmask((int)parm
);
294 * void do_arguments(globalstate *gstate, int ac, char **av)
296 * Arguments processing. gstate points to the global state,
297 * ac and av are the arguments to process. This can be called
298 * multiple times with different sets of arguments.
301 #ifdef HAVE_GETOPT_LONG
302 static struct option longopts
[] = {
303 { "color", no_argument
, NULL
, 'C' },
304 { "debug", no_argument
, NULL
, 'D' },
305 { "system-procs", no_argument
, NULL
, 'S' },
306 { "idle-procs", no_argument
, NULL
, 'I' },
307 { "tag-names", no_argument
, NULL
, 'T' },
308 { "all", no_argument
, NULL
, 'a' },
309 { "batch", no_argument
, NULL
, 'b' },
310 { "full-commands", no_argument
, NULL
, 'c' },
311 { "interactive", no_argument
, NULL
, 'i' },
312 { "quick", no_argument
, NULL
, 'q' },
313 { "threads", no_argument
, NULL
, 't' },
314 { "uids", no_argument
, NULL
, 'u' },
315 { "version", no_argument
, NULL
, 'v' },
316 { "delay", required_argument
, NULL
, 's' },
317 { "displays", required_argument
, NULL
, 'd' },
318 { "user", required_argument
, NULL
, 'U' },
319 { "sort-order", required_argument
, NULL
, 'o' },
320 { "display-mode", required_argument
, NULL
, 'm' },
321 { NULL
, 0, NULL
, 0 },
327 do_arguments(globalstate
*gstate
, int ac
, char **av
)
332 /* this appears to keep getopt happy */
335 #ifdef HAVE_GETOPT_LONG
336 while ((i
= getopt_long(ac
, av
, "CDSIMTabcinqtuvs:d:U:o:m:", longopts
, NULL
)) != -1)
338 while ((i
= getopt(ac
, av
, "CDSIMTabcinqtuvs:d:U:o:m:")) != EOF
)
345 gstate
->use_color
= !gstate
->use_color
;
354 fprintf(stderr
, "%s: version %s\n", myname
, version_string());
360 gstate
->interactive
= No
;
364 gstate
->displays
= Infinity
;
365 gstate
->topn
= Infinity
;
369 gstate
->interactive
= Yes
;
373 gstate
->order_name
= optarg
;
378 if (i
== Invalid
|| i
== 0)
380 message_error(" Bad display count");
384 gstate
->displays
= i
;
390 if (i
< 0 || (i
== 0 && getuid() != 0))
392 message_error(" Bad seconds delay");
401 gstate
->show_usernames
= !gstate
->show_usernames
;
408 message_error(" Unknown user '%s'", optarg
);
412 gstate
->pselect
.uid
= i
;
418 gstate
->pselect
.mode
= i
;
422 gstate
->pselect
.system
= !gstate
->pselect
.system
;
426 gstate
->pselect
.idle
= !gstate
->pselect
.idle
;
436 gstate
->show_tags
= 1;
441 gstate
->pselect
.fullcmd
= !gstate
->pselect
.fullcmd
;
445 gstate
->pselect
.threads
= !gstate
->pselect
.threads
;
448 case 'q': /* be quick about it */
449 /* only allow this if user is really root */
452 /* be very un-nice! */
457 message_error(" Option -q can only be used by root");
464 Usage: %s [-ISTabcinqu] [-d x] [-s x] [-o field] [-U username] [number]\n",
465 version_string(), myname
);
470 /* get count of top processes to display */
471 if (optind
< ac
&& *av
[optind
])
473 if ((i
= atoiwi(av
[optind
])) == Invalid
)
475 message_error(" Process count not a number");
485 do_display(globalstate
*gstate
)
492 struct system_info system_info
;
496 time_mark(&(gstate
->now
));
497 curr_time
= (time_t)(gstate
->now
.tv_sec
);
499 /* get the current stats */
500 get_system_info(&system_info
);
502 /* get the current processes */
503 processes
= get_process_info(&system_info
, &(gstate
->pselect
), gstate
->order_index
);
505 /* determine number of processes to actually display */
506 if (gstate
->topn
> 0)
508 /* this number will be the smallest of: active processes,
509 number user requested, number current screen accomodates */
510 active_procs
= system_info
.P_ACTIVE
;
511 if (active_procs
> gstate
->topn
)
513 active_procs
= gstate
->topn
;
515 if (active_procs
> gstate
->max_topn
)
517 active_procs
= gstate
->max_topn
;
526 hdr
= gstate
->header_text
;
528 /* full screen or update? */
529 if (gstate
->fulldraw
)
532 i_loadave(system_info
.last_pid
, system_info
.load_avg
);
533 i_uptime(&(gstate
->statics
->boottime
), &curr_time
);
534 i_timeofday(&curr_time
);
535 i_procstates(system_info
.p_total
, system_info
.procstates
, gstate
->pselect
.threads
);
536 if (gstate
->show_cpustates
)
538 i_cpustates(system_info
.cpustates
);
546 gstate
->show_cpustates
= Yes
;
548 i_kernel(system_info
.kernel
);
549 i_memory(system_info
.memory
);
550 i_swap(system_info
.swap
);
551 i_message(&(gstate
->now
));
553 for (i
= 0; i
< active_procs
; i
++)
555 i_process(i
, format_next_process(processes
, gstate
->get_userid
));
558 if (gstate
->smart_terminal
)
560 gstate
->fulldraw
= No
;
565 u_loadave(system_info
.last_pid
, system_info
.load_avg
);
566 u_uptime(&(gstate
->statics
->boottime
), &curr_time
);
567 i_timeofday(&curr_time
);
568 u_procstates(system_info
.p_total
, system_info
.procstates
, gstate
->pselect
.threads
);
569 u_cpustates(system_info
.cpustates
);
570 u_kernel(system_info
.kernel
);
571 u_memory(system_info
.memory
);
572 u_swap(system_info
.swap
);
573 u_message(&(gstate
->now
));
575 for (i
= 0; i
< active_procs
; i
++)
577 u_process(i
, format_next_process(processes
, gstate
->get_userid
));
585 timeval_xdprint(char *s
, struct timeval tv
)
588 xdprintf("%s %d.%06d\n", s
, tv
.tv_sec
, tv
.tv_usec
);
593 do_wait(globalstate
*gstate
)
598 wait
.tv_sec
= gstate
->delay
;
600 select(0, NULL
, NULL
, NULL
, &wait
);
604 do_command(globalstate
*gstate
)
608 struct timeval wait
= {0, 0};
613 /* calculate new refresh time */
614 gstate
->refresh
= gstate
->now
;
615 gstate
->refresh
.tv_sec
+= gstate
->delay
;
618 /* loop waiting for time to expire */
620 /* calculate time to wait */
621 if (gstate
->delay
> 0)
623 wait
= gstate
->refresh
;
624 wait
.tv_usec
-= now
.tv_usec
;
625 if (wait
.tv_usec
< 0)
627 wait
.tv_usec
+= 1000000;
630 wait
.tv_sec
-= now
.tv_sec
;
633 /* set up arguments for select on stdin (0) */
635 FD_SET(STDIN_FILENO
, &readfds
);
637 /* wait for something to read or time out */
638 if (select(32, &readfds
, NULL
, NULL
, &wait
) > 0)
641 if (read(STDIN_FILENO
, &ch
, 1) != 1)
644 message_error(" Read error on stdin");
649 /* mark pending messages as old */
653 status
= command_process(gstate
, (int)ch
);
664 message_error(" Unknown command");
668 message_error(" Command not available");
674 } while (timercmp(&now
, &(gstate
->refresh
), < ));
678 do_minidisplay(globalstate
*gstate
)
682 struct system_info si
;
684 /* save the real delay and substitute 1 second */
685 real_delay
= gstate
->delay
;
688 /* wait 1 second for a command */
689 time_mark(&(gstate
->now
));
692 /* do a mini update that only updates the cpustates */
693 get_system_info(&si
);
694 u_cpustates(si
.cpustates
);
696 /* restore the delay time */
697 gstate
->delay
= real_delay
;
704 main(int argc
, char *argv
[])
713 struct statics statics
;
719 if ((myname
= strrchr(argv
[0], '/')) == 0)
729 /* binary compatibility check */
734 if (uname(&uts
) == 0)
736 if (strcmp(uts
.machine
, UNAME_HARDWARE
) != 0)
738 fprintf(stderr
, "%s: incompatible hardware platform\n",
740 exit(EX_UNAVAILABLE
);
747 gstate
= (globalstate
*)calloc(1, sizeof(globalstate
));
748 gstate
->statics
= &statics
;
751 /* preset defaults for various options */
752 gstate
->show_usernames
= Yes
;
753 gstate
->topn
= DEFAULT_TOPN
;
754 gstate
->delay
= DEFAULT_DELAY
;
755 gstate
->fulldraw
= Yes
;
756 gstate
->use_color
= Yes
;
757 gstate
->interactive
= Maybe
;
759 /* preset defaults for process selection */
760 gstate
->pselect
.idle
= Yes
;
761 gstate
->pselect
.system
= No
;
762 gstate
->pselect
.fullcmd
= No
;
763 gstate
->pselect
.command
= NULL
;
764 gstate
->pselect
.uid
= -1;
765 gstate
->pselect
.mode
= 0;
767 /* use a large buffer for stdout */
769 setvbuf(stdout
, stdoutbuf
, _IOFBF
, BUFFERSIZE
);
771 #ifdef HAVE_SETBUFFER
772 setbuffer(stdout
, stdoutbuf
, BUFFERSIZE
);
776 /* get preset options from the environment */
777 if ((env_top
= getenv("TOP")) != NULL
)
779 preset_argv
= argparse(env_top
, &preset_argc
);
780 preset_argv
[0] = myname
;
781 do_arguments(gstate
, preset_argc
, preset_argv
);
784 /* process arguments */
785 do_arguments(gstate
, argc
, argv
);
788 /* If colour has been turned on read in the settings. */
789 env_top
= getenv("TOPCOLOURS");
792 env_top
= getenv("TOPCOLORS");
794 /* must do something about error messages */
795 color_env_parse(env_top
);
796 color_activate(gstate
->use_color
);
799 /* in order to support forward compatability, we have to ensure that
800 the entire statics structure is set to a known value before we call
801 machine_init. This way fields that a module does not know about
802 will retain their default values */
803 memzero((void *)&statics
, sizeof(statics
));
804 statics
.boottime
= -1;
806 /* call the platform-specific init */
807 if (machine_init(&statics
) == -1)
812 /* create a helper list of sort order names */
813 gstate
->order_namelist
= string_list(statics
.order_names
);
815 /* look up chosen sorting order */
816 if (gstate
->order_name
!= NULL
)
820 if (statics
.order_names
== NULL
)
822 message_error(" This platform does not support arbitrary ordering");
824 else if ((i
= string_index(gstate
->order_name
,
825 statics
.order_names
)) == -1)
827 message_error(" Sort order `%s' not recognized", gstate
->order_name
);
828 message_error(" Recognized sort orders: %s", gstate
->order_namelist
);
832 gstate
->order_index
= i
;
836 /* initialize extensions */
839 /* initialize termcap */
840 gstate
->smart_terminal
= screen_readtermcap(gstate
->interactive
);
842 /* determine interactive state */
843 if (gstate
->interactive
== Maybe
)
845 gstate
->interactive
= smart_terminal
;
848 /* if displays were not specified, choose an appropriate default */
849 if (gstate
->displays
== 0)
851 gstate
->displays
= gstate
->smart_terminal
? Infinity
: 1;
854 /* we don't need a mini display when delay is less than 2
855 seconds or when we are not on a smart terminal */
856 if (gstate
->delay
<= 1 || !smart_terminal
)
861 /* set constants for username/uid display */
862 if (gstate
->show_usernames
)
864 gstate
->header_text
= format_header("USERNAME");
865 gstate
->get_userid
= username
;
869 gstate
->header_text
= format_header(" UID ");
870 gstate
->get_userid
= itoa7
;
872 gstate
->pselect
.usernames
= gstate
->show_usernames
;
874 /* initialize display */
875 if ((gstate
->max_topn
= display_init(&statics
)) == -1)
877 fprintf(stderr
, "%s: can't allocate sufficient memory\n", myname
);
881 /* check for infinity and for overflowed screen */
882 if (gstate
->topn
== Infinity
)
884 gstate
->topn
= INT_MAX
;
886 else if (gstate
->topn
> gstate
->max_topn
)
889 message_error(" This terminal can only display %d processes",
895 /* producing a list of color tags is easy */
896 if (gstate
->show_tags
)
903 /* hold all signals while we initialize the screen */
904 mask
= hold_signals();
907 /* set the signal handlers */
910 /* longjmp re-entry point */
911 /* set the jump buffer for long jumps out of signal handlers */
912 if (setjmp(jmp_int
) != 0)
914 /* this is where we end up after processing sigwinch or sigtstp */
916 /* tell display to resize its buffers, and get the new length */
917 if ((gstate
->max_topn
= display_resize()) == -1)
924 /* set up for a full redraw, and get the current line count */
925 gstate
->fulldraw
= Yes
;
927 /* safe to release the signals now */
928 release_signals(mask
);
932 /* release the signals */
933 release_signals(mask
);
935 /* some systems require a warmup */
936 /* always do a warmup for batch mode */
937 if (gstate
->interactive
== 0 || statics
.flags
.warmup
)
939 struct system_info system_info
;
940 struct timeval timeout
;
942 time_mark(&(gstate
->now
));
943 get_system_info(&system_info
);
944 (void)get_process_info(&system_info
, &gstate
->pselect
, 0);
947 select(0, NULL
, NULL
, NULL
, &timeout
);
949 /* if we've warmed up, then we can show good states too */
950 gstate
->show_cpustates
= Yes
;
956 while ((gstate
->displays
== -1) || (--gstate
->displays
> 0))
959 if (gstate
->interactive
)
963 do_minidisplay(gstate
);
974 /* do one last display */
979 return 1; /* Keep compiler quiet. */