/* * Copyright (c) 1984 through 2008, William LeFebvre * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * * Neither the name of William LeFebvre nor the names of other * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ char *copyright = "Copyright (c) 1984 through 2008, William LeFebvre"; /* * Changes to other files that we can do at the same time: * screen.c:init_termcap: get rid of the "interactive" argument and have it * pass back something meaningful (such as success/failure/error). */ #include "os.h" #include #include #include #include #include #include #ifdef HAVE_SYS_UTSNAME_H #include #endif #ifdef HAVE_GETOPT_H #include #endif /* definitions */ #ifndef STDIN_FILENO #define STDIN_FILENO 0 #endif /* determine which type of signal functions to use */ /* cant have sigaction without sigprocmask */ #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGPROCMASK) #undef HAVE_SIGACTION #endif /* always use sigaction when it is available */ #ifdef HAVE_SIGACTION #undef HAVE_SIGHOLD #else /* use sighold/sigrelse, otherwise use old fashioned BSD signals */ #if !defined(HAVE_SIGHOLD) || !defined(HAVE_SIGRELSE) #define BSD_SIGNALS #endif #endif /* if FD_SET and friends aren't present, then fake something up */ #ifndef FD_SET typedef int fd_set; #define FD_ZERO(x) (*(x) = 0) #define FD_SET(f, x) (*(x) = 1<use_color = !gstate->use_color; break; #endif case 'D': debug_set(1); break; case 'v': fprintf(stderr, "%s: version %s\n", myname, version_string()); exit(EX_OK); break; case 'b': case 'n': gstate->interactive = No; break; case 'a': gstate->displays = Infinity; gstate->topn = Infinity; break; case 'i': gstate->interactive = Yes; break; case 'o': gstate->order_name = optarg; break; case 'd': i = atoiwi(optarg); if (i == Invalid || i == 0) { message_error(" Bad display count"); } else { gstate->displays = i; } break; case 's': i = atoi(optarg); if (i < 0 || (i == 0 && getuid() != 0)) { message_error(" Bad seconds delay"); } else { gstate->delay = i; } break; case 'u': gstate->show_usernames = !gstate->show_usernames; break; case 'U': i = userid(optarg); if (i == -1) { message_error(" Unknown user '%s'", optarg); } else { gstate->pselect.uid = i; } break; case 'm': i = atoi(optarg); gstate->pselect.mode = i; break; case 'S': gstate->pselect.system = !gstate->pselect.system; break; case 'I': gstate->pselect.idle = !gstate->pselect.idle; break; case 'M': enable_ncpus = 1; break; #ifdef ENABLE_COLOR case 'T': gstate->show_tags = 1; break; #endif case 'c': gstate->pselect.fullcmd = !gstate->pselect.fullcmd; break; case 't': gstate->pselect.threads = !gstate->pselect.threads; break; case 'q': /* be quick about it */ /* only allow this if user is really root */ if (getuid() == 0) { /* be very un-nice! */ (void) nice(-20); } else { message_error(" Option -q can only be used by root"); } break; default: fprintf(stderr, "\ Top version %s\n\ Usage: %s [-ISTabcinqu] [-d x] [-s x] [-o field] [-U username] [number]\n", version_string(), myname); exit(EX_USAGE); } } /* get count of top processes to display */ if (optind < ac && *av[optind]) { if ((i = atoiwi(av[optind])) == Invalid) { message_error(" Process count not a number"); } else { gstate->topn = i; } } } void do_display(globalstate *gstate) { int active_procs; int i; time_t curr_time; caddr_t processes; struct system_info system_info; char *hdr; /* get the time */ time_mark(&(gstate->now)); curr_time = (time_t)(gstate->now.tv_sec); /* get the current stats */ get_system_info(&system_info); /* get the current processes */ processes = get_process_info(&system_info, &(gstate->pselect), gstate->order_index); /* determine number of processes to actually display */ if (gstate->topn > 0) { /* this number will be the smallest of: active processes, number user requested, number current screen accomodates */ active_procs = system_info.P_ACTIVE; if (active_procs > gstate->topn) { active_procs = gstate->topn; } if (active_procs > gstate->max_topn) { active_procs = gstate->max_topn; } } else { /* dont show any */ active_procs = 0; } #ifdef HAVE_FORMAT_PROCESS_HEADER /* get the process header to use */ hdr = format_process_header(&(gstate->pselect), processes, active_procs); #else hdr = gstate->header_text; #endif /* full screen or update? */ if (gstate->fulldraw) { display_clear(); i_loadave(system_info.last_pid, system_info.load_avg); i_uptime(&(gstate->statics->boottime), &curr_time); i_timeofday(&curr_time); i_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads); if (gstate->show_cpustates) { i_cpustates(system_info.cpustates); } else { if (smart_terminal) { z_cpustates(); } gstate->show_cpustates = Yes; } i_kernel(system_info.kernel); i_memory(system_info.memory); i_swap(system_info.swap); i_message(&(gstate->now)); i_header(hdr); for (i = 0; i < active_procs; i++) { i_process(i, format_next_process(processes, gstate->get_userid)); } i_endscreen(); if (gstate->smart_terminal) { gstate->fulldraw = No; } } else { u_loadave(system_info.last_pid, system_info.load_avg); u_uptime(&(gstate->statics->boottime), &curr_time); i_timeofday(&curr_time); u_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads); u_cpustates(system_info.cpustates); u_kernel(system_info.kernel); u_memory(system_info.memory); u_swap(system_info.swap); u_message(&(gstate->now)); u_header(hdr); for (i = 0; i < active_procs; i++) { u_process(i, format_next_process(processes, gstate->get_userid)); } u_endscreen(); } } #ifdef DEBUG void timeval_xdprint(char *s, struct timeval tv) { xdprintf("%s %d.%06d\n", s, tv.tv_sec, tv.tv_usec); } #endif void do_wait(globalstate *gstate) { struct timeval wait; wait.tv_sec = gstate->delay; wait.tv_usec = 0; select(0, NULL, NULL, NULL, &wait); } void do_command(globalstate *gstate) { int status; struct timeval wait = {0, 0}; struct timeval now; fd_set readfds; unsigned char ch; /* calculate new refresh time */ gstate->refresh = gstate->now; gstate->refresh.tv_sec += gstate->delay; time_get(&now); /* loop waiting for time to expire */ do { /* calculate time to wait */ if (gstate->delay > 0) { wait = gstate->refresh; wait.tv_usec -= now.tv_usec; if (wait.tv_usec < 0) { wait.tv_usec += 1000000; wait.tv_sec--; } wait.tv_sec -= now.tv_sec; } /* set up arguments for select on stdin (0) */ FD_ZERO(&readfds); FD_SET(STDIN_FILENO, &readfds); /* wait for something to read or time out */ if (select(32, &readfds, NULL, NULL, &wait) > 0) { /* read it */ if (read(STDIN_FILENO, &ch, 1) != 1) { /* read error */ message_error(" Read error on stdin"); quit(EX_DATAERR); /*NOTREACHED*/ } /* mark pending messages as old */ message_mark(); /* dispatch */ status = command_process(gstate, (int)ch); switch(status) { case CMD_ERROR: quit(EX_SOFTWARE); /*NOTREACHED*/ case CMD_REFRESH: return; case CMD_UNKNOWN: message_error(" Unknown command"); break; case CMD_NA: message_error(" Command not available"); } } /* get new time */ time_get(&now); } while (timercmp(&now, &(gstate->refresh), < )); } void do_minidisplay(globalstate *gstate) { int real_delay; struct system_info si; /* save the real delay and substitute 1 second */ real_delay = gstate->delay; gstate->delay = 1; /* wait 1 second for a command */ time_mark(&(gstate->now)); do_command(gstate); /* do a mini update that only updates the cpustates */ get_system_info(&si); u_cpustates(si.cpustates); /* restore the delay time */ gstate->delay = real_delay; /* done */ i_endscreen(); } int main(int argc, char *argv[]) { char *env_top; char **preset_argv; int preset_argc = 0; void *mask; int need_mini = 1; struct statics statics; globalstate *gstate; /* get our name */ if (argc > 0) { if ((myname = strrchr(argv[0], '/')) == 0) { myname = argv[0]; } else { myname++; } } /* binary compatibility check */ #ifdef HAVE_UNAME { struct utsname uts; if (uname(&uts) == 0) { if (strcmp(uts.machine, UNAME_HARDWARE) != 0) { fprintf(stderr, "%s: incompatible hardware platform\n", myname); exit(EX_UNAVAILABLE); } } } #endif /* initialization */ gstate = (globalstate *)calloc(1, sizeof(globalstate)); gstate->statics = &statics; time_mark(NULL); /* preset defaults for various options */ gstate->show_usernames = Yes; gstate->topn = DEFAULT_TOPN; gstate->delay = DEFAULT_DELAY; gstate->fulldraw = Yes; gstate->use_color = Yes; gstate->interactive = Maybe; /* preset defaults for process selection */ gstate->pselect.idle = Yes; gstate->pselect.system = No; gstate->pselect.fullcmd = No; gstate->pselect.command = NULL; gstate->pselect.uid = -1; gstate->pselect.mode = 0; /* use a large buffer for stdout */ #ifdef HAVE_SETVBUF setvbuf(stdout, stdoutbuf, _IOFBF, BUFFERSIZE); #else #ifdef HAVE_SETBUFFER setbuffer(stdout, stdoutbuf, BUFFERSIZE); #endif #endif /* get preset options from the environment */ if ((env_top = getenv("TOP")) != NULL) { preset_argv = argparse(env_top, &preset_argc); preset_argv[0] = myname; do_arguments(gstate, preset_argc, preset_argv); } /* process arguments */ do_arguments(gstate, argc, argv); #ifdef ENABLE_COLOR /* If colour has been turned on read in the settings. */ env_top = getenv("TOPCOLOURS"); if (!env_top) { env_top = getenv("TOPCOLORS"); } /* must do something about error messages */ color_env_parse(env_top); color_activate(gstate->use_color); #endif /* in order to support forward compatability, we have to ensure that the entire statics structure is set to a known value before we call machine_init. This way fields that a module does not know about will retain their default values */ memzero((void *)&statics, sizeof(statics)); statics.boottime = -1; /* call the platform-specific init */ if (machine_init(&statics) == -1) { exit(EX_SOFTWARE); } /* create a helper list of sort order names */ gstate->order_namelist = string_list(statics.order_names); /* look up chosen sorting order */ if (gstate->order_name != NULL) { int i; if (statics.order_names == NULL) { message_error(" This platform does not support arbitrary ordering"); } else if ((i = string_index(gstate->order_name, statics.order_names)) == -1) { message_error(" Sort order `%s' not recognized", gstate->order_name); message_error(" Recognized sort orders: %s", gstate->order_namelist); } else { gstate->order_index = i; } } /* initialize extensions */ init_username(); /* initialize termcap */ gstate->smart_terminal = screen_readtermcap(gstate->interactive); /* determine interactive state */ if (gstate->interactive == Maybe) { gstate->interactive = smart_terminal; } /* if displays were not specified, choose an appropriate default */ if (gstate->displays == 0) { gstate->displays = gstate->smart_terminal ? Infinity: 1; } /* we don't need a mini display when delay is less than 2 seconds or when we are not on a smart terminal */ if (gstate->delay <= 1 || !smart_terminal) { need_mini = 0; } #ifndef HAVE_FORMAT_PROCESS_HEADER /* set constants for username/uid display */ if (gstate->show_usernames) { gstate->header_text = format_header("USERNAME"); gstate->get_userid = username; } else { gstate->header_text = format_header(" UID "); gstate->get_userid = itoa7; } #endif gstate->pselect.usernames = gstate->show_usernames; /* initialize display */ if ((gstate->max_topn = display_init(&statics)) == -1) { fprintf(stderr, "%s: can't allocate sufficient memory\n", myname); exit(EX_OSERR); } /* check for infinity and for overflowed screen */ if (gstate->topn == Infinity) { gstate->topn = INT_MAX; } else if (gstate->topn > gstate->max_topn) { #if 0 message_error(" This terminal can only display %d processes", gstate->max_topn); #endif } #ifdef ENABLE_COLOR /* producing a list of color tags is easy */ if (gstate->show_tags) { color_dump(stdout); exit(EX_OK); } #endif /* hold all signals while we initialize the screen */ mask = hold_signals(); screen_init(); /* set the signal handlers */ set_signals(); /* longjmp re-entry point */ /* set the jump buffer for long jumps out of signal handlers */ if (setjmp(jmp_int) != 0) { /* this is where we end up after processing sigwinch or sigtstp */ /* tell display to resize its buffers, and get the new length */ if ((gstate->max_topn = display_resize()) == -1) { /* thats bad */ quit(EX_OSERR); /*NOTREACHED*/ } /* set up for a full redraw, and get the current line count */ gstate->fulldraw = Yes; /* safe to release the signals now */ release_signals(mask); } else { /* release the signals */ release_signals(mask); /* some systems require a warmup */ /* always do a warmup for batch mode */ if (gstate->interactive == 0 || statics.flags.warmup) { struct system_info system_info; struct timeval timeout; time_mark(&(gstate->now)); get_system_info(&system_info); (void)get_process_info(&system_info, &gstate->pselect, 0); timeout.tv_sec = 1; timeout.tv_usec = 0; select(0, NULL, NULL, NULL, &timeout); /* if we've warmed up, then we can show good states too */ gstate->show_cpustates = Yes; need_mini = 0; } } /* main loop */ while ((gstate->displays == -1) || (--gstate->displays > 0)) { do_display(gstate); if (gstate->interactive) { if (need_mini) { do_minidisplay(gstate); need_mini = 0; } do_command(gstate); } else { do_wait(gstate); } } /* do one last display */ do_display(gstate); quit(EX_OK); /* NOTREACHED */ return 1; /* Keep compiler quiet. */ }