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.
33 const char *copyright
=
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
;
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 { "percpustates", no_argument
, NULL
, '1' },
304 { "color", no_argument
, NULL
, 'C' },
305 { "debug", no_argument
, NULL
, 'D' },
306 { "system-procs", no_argument
, NULL
, 'S' },
307 { "idle-procs", no_argument
, NULL
, 'I' },
308 { "tag-names", no_argument
, NULL
, 'T' },
309 { "all", no_argument
, NULL
, 'a' },
310 { "batch", no_argument
, NULL
, 'b' },
311 { "full-commands", no_argument
, NULL
, 'c' },
312 { "interactive", no_argument
, NULL
, 'i' },
313 { "quick", no_argument
, NULL
, 'q' },
314 { "threads", no_argument
, NULL
, 't' },
315 { "uids", no_argument
, NULL
, 'u' },
316 { "version", no_argument
, NULL
, 'v' },
317 { "delay", required_argument
, NULL
, 's' },
318 { "displays", required_argument
, NULL
, 'd' },
319 { "user", required_argument
, NULL
, 'U' },
320 { "sort-order", required_argument
, NULL
, 'o' },
321 { "pid", required_argument
, NULL
, 'p' },
322 { "display-mode", required_argument
, NULL
, 'm' },
323 { NULL
, 0, NULL
, 0 },
329 do_arguments(globalstate
*gstate
, int ac
, char **av
)
335 /* this appears to keep getopt happy */
338 #ifdef HAVE_GETOPT_LONG
339 while ((i
= getopt_long(ac
, av
, "1CDSITabcinp:qtuvs:d:U:o:m:", longopts
, NULL
)) != -1)
341 while ((i
= getopt(ac
, av
, "1CDSITabcinp:qtuvs:d:U:o:m:")) != EOF
)
347 gstate
->percpustates
= !gstate
->percpustates
;
348 gstate
->fulldraw
= Yes
;
349 gstate
->max_topn
+= display_setmulti(gstate
->percpustates
);
353 gstate
->use_color
= !gstate
->use_color
;
362 fprintf(stderr
, "%s: version %s\n", myname
, version_string());
368 gstate
->interactive
= No
;
372 gstate
->displays
= Infinity
;
373 gstate
->topn
= Infinity
;
377 gstate
->interactive
= Yes
;
381 gstate
->order_name
= optarg
;
386 if (i
== Invalid
|| i
== 0)
388 message_error(" Bad display count");
392 gstate
->displays
= i
;
398 if (f
< 0 || (f
== 0 && getuid() != 0))
400 message_error(" Bad seconds delay");
409 gstate
->show_usernames
= !gstate
->show_usernames
;
416 message_error(" Unknown user '%s'", optarg
);
420 gstate
->pselect
.uid
= i
;
426 gstate
->pselect
.mode
= i
;
430 gstate
->pselect
.system
= !gstate
->pselect
.system
;
434 gstate
->pselect
.idle
= !gstate
->pselect
.idle
;
439 gstate
->show_tags
= 1;
444 gstate
->pselect
.fullcmd
= !gstate
->pselect
.fullcmd
;
448 gstate
->pselect
.threads
= !gstate
->pselect
.threads
;
452 gstate
->pselect
.pid
= atoi(optarg
);
455 case 'q': /* be quick about it */
456 /* only allow this if user is really root */
459 /* be very un-nice! */
464 message_error(" Option -q can only be used by root");
471 Usage: %s [-1CISTabcinqtuv] [-d count] [-m mode] [-o field] [-p pid]\n\
472 [-s time] [-U username] [number]\n",
473 version_string(), myname
);
478 /* get count of top processes to display */
479 if (optind
< ac
&& *av
[optind
])
481 if ((i
= atoiwi(av
[optind
])) == Invalid
)
483 message_error(" Process count not a number");
493 do_display(globalstate
*gstate
)
500 struct system_info system_info
;
504 time_mark(&(gstate
->now
));
505 curr_time
= (time_t)(gstate
->now
.tv_sec
);
507 /* get the current stats */
508 get_system_info(&system_info
);
510 /* get the current processes */
511 processes
= get_process_info(&system_info
, &(gstate
->pselect
), gstate
->order_index
);
513 /* determine number of processes to actually display */
514 if (gstate
->topn
> 0)
516 /* this number will be the smallest of: active processes,
517 number user requested, number current screen accomodates */
518 active_procs
= system_info
.P_ACTIVE
;
519 if (active_procs
> gstate
->topn
)
521 active_procs
= gstate
->topn
;
523 if (active_procs
> gstate
->max_topn
)
525 active_procs
= gstate
->max_topn
;
534 #ifdef HAVE_FORMAT_PROCESS_HEADER
535 /* get the process header to use */
536 hdr
= format_process_header(&(gstate
->pselect
), processes
, active_procs
);
538 hdr
= gstate
->header_text
;
541 /* full screen or update? */
542 if (gstate
->fulldraw
)
545 i_loadave(system_info
.last_pid
, system_info
.load_avg
);
546 i_uptime(&(gstate
->statics
->boottime
), &curr_time
);
547 i_timeofday(&curr_time
);
548 i_procstates(system_info
.p_total
, system_info
.procstates
, gstate
->pselect
.threads
);
549 if (gstate
->show_cpustates
)
551 i_cpustates(system_info
.cpustates
);
559 gstate
->show_cpustates
= Yes
;
561 i_kernel(system_info
.kernel
);
562 i_memory(system_info
.memory
);
563 i_swap(system_info
.swap
);
564 i_message(&(gstate
->now
));
566 for (i
= 0; i
< active_procs
; i
++)
568 i_process(i
, format_next_process(processes
, gstate
->get_userid
));
571 if (gstate
->smart_terminal
)
573 gstate
->fulldraw
= No
;
578 u_loadave(system_info
.last_pid
, system_info
.load_avg
);
579 u_uptime(&(gstate
->statics
->boottime
), &curr_time
);
580 i_timeofday(&curr_time
);
581 u_procstates(system_info
.p_total
, system_info
.procstates
, gstate
->pselect
.threads
);
582 u_cpustates(system_info
.cpustates
);
583 u_kernel(system_info
.kernel
);
584 u_memory(system_info
.memory
);
585 u_swap(system_info
.swap
);
586 u_message(&(gstate
->now
));
588 for (i
= 0; i
< active_procs
; i
++)
590 u_process(i
, format_next_process(processes
, gstate
->get_userid
));
598 timeval_xdprint(char *s
, struct timeval tv
)
601 xdprintf("%s %d.%06d\n", s
, tv
.tv_sec
, tv
.tv_usec
);
606 do_wait(globalstate
*gstate
)
611 double2tv(&wait
, gstate
->delay
);
612 select(0, NULL
, NULL
, NULL
, &wait
);
616 do_command(globalstate
*gstate
)
620 struct timeval wait
= {0, 0};
625 /* calculate new refresh time */
626 gstate
->refresh
= gstate
->now
;
627 double2tv(&now
, gstate
->delay
);
628 timeradd(&now
, &gstate
->refresh
, &gstate
->refresh
);
631 /* loop waiting for time to expire */
633 /* calculate time to wait */
634 if (gstate
->delay
> 0)
636 wait
= gstate
->refresh
;
637 wait
.tv_usec
-= now
.tv_usec
;
638 if (wait
.tv_usec
< 0)
640 wait
.tv_usec
+= 1000000;
643 wait
.tv_sec
-= now
.tv_sec
;
646 /* set up arguments for select on stdin (0) */
648 FD_SET(STDIN_FILENO
, &readfds
);
650 /* wait for something to read or time out */
651 if (select(32, &readfds
, NULL
, NULL
, &wait
) > 0)
654 if (read(STDIN_FILENO
, &ch
, 1) != 1)
657 message_error(" Read error on stdin");
662 /* mark pending messages as old */
666 status
= command_process(gstate
, (int)ch
);
677 message_error(" Unknown command");
681 message_error(" Command not available");
687 } while (timercmp(&now
, &(gstate
->refresh
), < ));
691 do_minidisplay(globalstate
*gstate
)
695 struct system_info si
;
697 /* save the real delay and substitute 1 second */
698 real_delay
= gstate
->delay
;
701 /* wait 1 second for a command */
702 time_mark(&(gstate
->now
));
705 /* do a mini update that only updates the cpustates */
706 get_system_info(&si
);
707 u_cpustates(si
.cpustates
);
709 /* restore the delay time */
710 gstate
->delay
= real_delay
;
717 main(int argc
, char *argv
[])
724 volatile int need_mini
= 1;
725 static char top
[] = "top";
727 struct statics statics
;
733 if ((myname
= strrchr(argv
[0], '/')) == 0)
745 /* binary compatibility check */
750 if (uname(&uts
) == 0)
752 if (strcmp(uts
.machine
, UNAME_HARDWARE
) != 0)
754 fprintf(stderr
, "%s: incompatible hardware platform\n",
756 exit(EX_UNAVAILABLE
);
763 gstate
= ecalloc(1, sizeof(globalstate
));
764 gstate
->statics
= &statics
;
767 /* preset defaults for various options */
768 gstate
->show_usernames
= Yes
;
769 gstate
->topn
= DEFAULT_TOPN
;
770 gstate
->delay
= DEFAULT_DELAY
;
771 gstate
->fulldraw
= Yes
;
772 gstate
->use_color
= Yes
;
773 gstate
->interactive
= Maybe
;
774 gstate
->percpustates
= Yes
;
776 /* preset defaults for process selection */
777 gstate
->pselect
.idle
= Yes
;
778 gstate
->pselect
.system
= Yes
;
779 gstate
->pselect
.fullcmd
= No
;
780 gstate
->pselect
.command
= NULL
;
781 gstate
->pselect
.uid
= -1;
782 gstate
->pselect
.pid
= -1;
783 gstate
->pselect
.mode
= 0;
785 /* use a large buffer for stdout */
787 setvbuf(stdout
, stdoutbuf
, _IOFBF
, BUFFERSIZE
);
789 #ifdef HAVE_SETBUFFER
790 setbuffer(stdout
, stdoutbuf
, BUFFERSIZE
);
794 /* get preset options from the environment */
795 if ((env_top
= getenv("TOP")) != NULL
)
797 preset_argv
= argparse(env_top
, &preset_argc
);
798 preset_argv
[0] = myname
;
799 do_arguments(gstate
, preset_argc
, preset_argv
);
802 /* process arguments */
803 do_arguments(gstate
, argc
, argv
);
806 /* If colour has been turned on read in the settings. */
807 env_top
= getenv("TOPCOLOURS");
810 env_top
= getenv("TOPCOLORS");
812 /* must do something about error messages */
813 color_env_parse(env_top
);
814 color_activate(gstate
->use_color
);
817 /* in order to support forward compatability, we have to ensure that
818 the entire statics structure is set to a known value before we call
819 machine_init. This way fields that a module does not know about
820 will retain their default values */
821 memzero((void *)&statics
, sizeof(statics
));
822 statics
.boottime
= -1;
824 /* call the platform-specific init */
825 if (machine_init(&statics
) == -1)
830 /* create a helper list of sort order names */
831 gstate
->order_namelist
= string_list(statics
.order_names
);
833 /* look up chosen sorting order */
834 if (gstate
->order_name
!= NULL
)
838 if (statics
.order_names
== NULL
)
840 message_error(" This platform does not support arbitrary ordering");
842 else if ((i
= string_index(gstate
->order_name
,
843 statics
.order_names
)) == -1)
845 message_error(" Sort order `%s' not recognized", gstate
->order_name
);
846 message_error(" Recognized sort orders: %s", gstate
->order_namelist
);
850 gstate
->order_index
= i
;
854 /* initialize extensions */
857 /* initialize termcap */
858 gstate
->smart_terminal
= screen_readtermcap(gstate
->interactive
);
860 /* determine interactive state */
861 if (gstate
->interactive
== Maybe
)
863 gstate
->interactive
= smart_terminal
;
866 /* if displays were not specified, choose an appropriate default */
867 if (gstate
->displays
== 0)
869 gstate
->displays
= gstate
->smart_terminal
? Infinity
: 1;
872 /* we don't need a mini display when delay is less than 2
873 seconds or when we are not on a smart terminal */
874 if (gstate
->delay
<= 1 || !smart_terminal
)
879 #ifndef HAVE_FORMAT_PROCESS_HEADER
880 /* set constants for username/uid display */
881 if (gstate
->show_usernames
)
883 gstate
->header_text
= format_header("USERNAME");
884 gstate
->get_userid
= username
;
888 gstate
->header_text
= format_header(" UID ");
889 gstate
->get_userid
= itoa7
;
892 gstate
->pselect
.usernames
= gstate
->show_usernames
;
894 /* initialize display */
895 if ((gstate
->max_topn
= display_init(&statics
, gstate
->percpustates
)) == -1)
897 fprintf(stderr
, "%s: display too small\n", myname
);
901 /* check for infinity and for overflowed screen */
902 if (gstate
->topn
== Infinity
)
904 gstate
->topn
= INT_MAX
;
906 else if (gstate
->topn
> gstate
->max_topn
)
908 message_error(" This terminal can only display %d processes",
913 /* producing a list of color tags is easy */
914 if (gstate
->show_tags
)
921 /* hold all signals while we initialize the screen */
922 mask
= hold_signals();
925 /* set the signal handlers */
928 /* longjmp re-entry point */
929 /* set the jump buffer for long jumps out of signal handlers */
930 if (setjmp(jmp_int
) != 0)
932 /* this is where we end up after processing sigwinch or sigtstp */
934 /* tell display to resize its buffers, and get the new length */
935 if ((gstate
->max_topn
= display_resize()) == -1)
942 /* set up for a full redraw, and get the current line count */
943 gstate
->fulldraw
= Yes
;
945 /* safe to release the signals now */
946 release_signals(mask
);
950 /* release the signals */
951 release_signals(mask
);
953 /* some systems require a warmup */
954 /* always do a warmup for batch mode */
955 if (gstate
->interactive
== 0 || statics
.flags
.warmup
)
957 struct system_info system_info
;
958 struct timeval timeout
;
960 time_mark(&(gstate
->now
));
961 get_system_info(&system_info
);
962 (void)get_process_info(&system_info
, &gstate
->pselect
, 0);
965 select(0, NULL
, NULL
, NULL
, &timeout
);
967 /* if we've warmed up, then we can show good states too */
968 gstate
->show_cpustates
= Yes
;
974 while ((gstate
->displays
== -1) || (--gstate
->displays
> 0))
977 if (gstate
->interactive
)
981 do_minidisplay(gstate
);
992 /* do one last display */
997 return 1; /* Keep compiler quiet. */