top.c revision 322509
1139823Simpchar *copyright = 221830Sjoerg "Copyright (c) 1984 through 1996, William LeFebvre"; 321830Sjoerg 47055Sdg/* 57055Sdg * Top users/processes display for Unix 67055Sdg * Version 3 77055Sdg * 87055Sdg * This program may be freely redistributed, 97055Sdg * but this entire comment MUST remain intact. 107055Sdg * 117055Sdg * Copyright (c) 1984, 1989, William LeFebvre, Rice University 127055Sdg * Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University 137055Sdg * Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory 147055Sdg * Copyright (c) 1996, William LeFebvre, Group sys Consulting 157055Sdg * 167055Sdg * $FreeBSD: stable/11/contrib/top/top.c 322509 2017-08-14 15:08:49Z gahr $ 177055Sdg */ 187055Sdg 197055Sdg/* 207055Sdg * See the file "Changes" for information on version-to-version changes. 217055Sdg */ 227055Sdg 237055Sdg/* 247055Sdg * This file contains "main" and other high-level routines. 257055Sdg */ 267055Sdg 277055Sdg/* 287055Sdg * The following preprocessor variables, when defined, are used to 297055Sdg * distinguish between different Unix implementations: 307055Sdg * 317055Sdg * SIGHOLD - use SVR4 sighold function when defined 327055Sdg * SIGRELSE - use SVR4 sigrelse function when defined 337055Sdg * FD_SET - macros FD_SET and FD_ZERO are used when defined 347055Sdg */ 357061Sdg 3650477Speter#include "os.h" 377055Sdg 387055Sdg#include <sys/jail.h> 3932356Seivind#include <sys/time.h> 4032350Seivind 4154263Sshin#include <ctype.h> 4231742Seivind#include <errno.h> 4331742Seivind#include <jail.h> 447055Sdg#include <setjmp.h> 457055Sdg#include <signal.h> 4693375Smdodd#include <unistd.h> 4793375Smdodd 487055Sdg/* includes specific to top */ 4993375Smdodd#include "commands.h" 507055Sdg#include "display.h" /* interface to display package */ 5193375Smdodd#include "screen.h" /* interface to screen package */ 527055Sdg#include "top.h" 537055Sdg#include "top.local.h" 54112271Smdodd#include "boolean.h" 557055Sdg#include "machine.h" 567055Sdg#include "utils.h" 57186119Sqingli#include "username.h" 58112271Smdodd 59184709Sbz/* Size of the stdio buffer given to stdout */ 6093375Smdodd#define Buffersize 2048 6193375Smdodd 6293375Smdodd/* The buffer that stdio will use */ 6393373Smdoddchar stdoutbuf[Buffersize]; 647055Sdg 6554263Sshin/* build Signal masks */ 667055Sdg#define Smask(s) (1 << ((s) - 1)) 677055Sdg 6832350Seivind/* for getopt: */ 697055Sdgextern int optind; 7054263Sshinextern char *optarg; 7154263Sshin 7254263Sshin/* imported from screen.c */ 737055Sdgextern int overstrike; 7411819Sjulian 7521830Sjoergstatic int fmt_flags = 0; 7611819Sjulianint pcpu_stats = No; 7711819Sjulian 7811819Sjulian/* signal handling routines */ 797055Sdgsigret_t leave(); 807055Sdgsigret_t tstop(); 817055Sdg#ifdef SIGWINCH 827055Sdgsigret_t winch(); 8321830Sjoerg#endif 8421830Sjoerg 8521830Sjoergvolatile sig_atomic_t leaveflag; 8621830Sjoergvolatile sig_atomic_t tstopflag; 8721830Sjoergvolatile sig_atomic_t winchflag; 8821830Sjoerg 8921830Sjoerg/* internal routines */ 9021830Sjoergvoid quit(); 9121830Sjoerg 92163606Srwatson/* values which need to be accessed by signal handlers */ 93163606Srwatsonstatic int max_topn; /* maximum displayable processes */ 94126788Srwatson 9593382Smdodd/* miscellaneous things */ 9693382Smdoddstruct process_select ps; 9793383Smdoddchar *myname = "top"; 9893084Sbdejmp_buf jmp_int; 99249925Sglebius 100191148Skmacy/* routines that don't return int */ 101106939Ssam 10268180Sumechar *username(); 103112276Smdoddchar *ctime(); 10493369Smdoddchar *kill_procs(); 1057055Sdgchar *renice_procs(); 1067055Sdg 1077055Sdg#ifdef ORDER 1087055Sdgextern int (*compares[])(); 1097055Sdg#else 1107055Sdgextern int proc_compare(); 1117055Sdgextern int io_compare(); 11293383Smdodd#endif 113249925Sglebiustime_t time(); 114249925Sglebius 1157055Sdgcaddr_t get_process_info(); 11621830Sjoerg 11769152Sjlemon/* different routines for displaying the user's identification */ 11893373Smdodd/* (values assigned to get_userid) */ 11993367Smdoddchar *username(); 120193891Sbzchar *itoa7(); 121186119Sqingli 122193891Sbz/* pointers to display routines */ 1237055Sdgvoid (*d_loadave)() = i_loadave; 124105577Srwatsonvoid (*d_procstates)() = i_procstates; 125172930Srwatsonvoid (*d_cpustates)() = i_cpustates; 126105577Srwatsonvoid (*d_memory)() = i_memory; 127105577Srwatsonvoid (*d_arc)() = i_arc; 128105577Srwatsonvoid (*d_carc)() = i_carc; 129105577Srwatsonvoid (*d_swap)() = i_swap; 130112308Smdoddvoid (*d_message)() = i_message; 131112308Smdoddvoid (*d_header)() = i_header; 132148887Srwatsonvoid (*d_process)() = i_process; 133148887Srwatson 1347055Sdgvoid reset_display(void); 13534961Sphk 136111767Smdoddstatic void 1377055Sdgreset_uids() 1387055Sdg{ 13921830Sjoerg for (size_t i = 0; i < TOP_MAX_UIDS; ++i) 140193891Sbz ps.uid[i] = -1; 141193891Sbz} 142193891Sbz 143193891Sbzstatic int 144186119Sqingliadd_uid(int uid) 145128636Sluigi{ 146128636Sluigi size_t i = 0; 14721830Sjoerg 1487055Sdg /* Add the uid if there's room */ 14921830Sjoerg for (; i < TOP_MAX_UIDS; ++i) 150126951Smdodd { 151126951Smdodd if (ps.uid[i] == -1 || ps.uid[i] == uid) 152126951Smdodd { 153126951Smdodd ps.uid[i] = uid; 154126951Smdodd break; 155126951Smdodd } 156126951Smdodd } 157126951Smdodd 158126951Smdodd return (i == TOP_MAX_UIDS); 159126951Smdodd} 160126951Smdodd 161126951Smdoddstatic void 162126951Smdoddrem_uid(int uid) 163126951Smdodd{ 164126951Smdodd size_t i = 0; 165126951Smdodd size_t where = TOP_MAX_UIDS; 166126951Smdodd 167126951Smdodd /* Look for the user to remove - no problem if it's not there */ 168126951Smdodd for (; i < TOP_MAX_UIDS; ++i) 169126951Smdodd { 170126951Smdodd if (ps.uid[i] == -1) 171126951Smdodd break; 172126951Smdodd if (ps.uid[i] == uid) 173126951Smdodd where = i; 174126951Smdodd } 175126951Smdodd 176126951Smdodd /* Make sure we don't leave a hole in the middle */ 177112266Smdodd if (where != TOP_MAX_UIDS) 17854263Sshin { 17954263Sshin ps.uid[where] = ps.uid[i-1]; 180186217Sqingli ps.uid[i-1] = -1; 181128636Sluigi } 182128636Sluigi} 18354263Sshin 18454263Sshinstatic int 185112266Smdoddhandle_user(char *buf, size_t buflen) 18611819Sjulian{ 18711819Sjulian int rc = 0; 18821830Sjoerg int uid = -1; 189249925Sglebius char *buf2 = buf; 190249925Sglebius 19111819Sjulian new_message(MT_standout, "Username to show (+ for all): "); 192112266Smdodd if (readline(buf, buflen, No) <= 0) 19321830Sjoerg { 19421830Sjoerg clear_message(); 19521830Sjoerg return rc; 196249925Sglebius } 19721830Sjoerg 19821830Sjoerg if (buf[0] == '+' || buf[0] == '-') 19921830Sjoerg { 20021830Sjoerg if (buf[1] == '\0') 201249925Sglebius { 20221830Sjoerg reset_uids(); 20321830Sjoerg goto end; 20421830Sjoerg } 20521830Sjoerg else 20621830Sjoerg ++buf2; 20721830Sjoerg } 20821830Sjoerg 20921830Sjoerg if ((uid = userid(buf2)) == -1) 21021830Sjoerg { 21121830Sjoerg new_message(MT_standout, " %s: unknown user", buf2); 212243882Sglebius rc = 1; 21321830Sjoerg goto end; 21421830Sjoerg } 21593371Smdodd 21693371Smdodd if (buf2 == buf) 21793373Smdodd { 21821830Sjoerg reset_uids(); 21921830Sjoerg ps.uid[0] = uid; 22021830Sjoerg goto end; 22121830Sjoerg } 222194819Srwatson 22321830Sjoerg if (buf[0] == '+') 22421830Sjoerg { 22521830Sjoerg if (add_uid(uid)) 2267055Sdg { 22752248Smsmith new_message(MT_standout, " too many users, reset with '+'"); 22852248Smsmith rc = 1; 229249925Sglebius goto end; 230249925Sglebius } 23152248Smsmith } 232249925Sglebius else 233249925Sglebius rem_uid(uid); 23452248Smsmith 23552248Smsmithend: 23652248Smsmith putchar('\r'); 2377055Sdg return rc; 2387055Sdg} 239249925Sglebius 240249925Sglebiusint 24136992Sjulianmain(argc, argv) 242249925Sglebius 243249925Sglebiusint argc; 2447055Sdgchar *argv[]; 2457055Sdg 2467055Sdg{ 2477055Sdg register int i; 2487055Sdg register int active_procs; 2497055Sdg register int change; 2507055Sdg 2517055Sdg struct system_info system_info; 2527055Sdg struct statics statics; 2537055Sdg caddr_t processes; 2547055Sdg 2557055Sdg static char tempbuf1[50]; 2567055Sdg static char tempbuf2[50]; 2577055Sdg int old_sigmask; /* only used for BSD-style signals */ 2587055Sdg int topn = Default_TOPN; 2597055Sdg int delay = Default_DELAY; 2607055Sdg int displays = 0; /* indicates unspecified */ 2617055Sdg int sel_ret = 0; 2627055Sdg time_t curr_time; 2637055Sdg char *(*get_userid)() = username; 2647055Sdg char *uname_field = "USERNAME"; 2657055Sdg char *header_text; 2667055Sdg char *env_top; 2677055Sdg char **preset_argv; 2687055Sdg int preset_argc = 0; 2697055Sdg char **av; 2707055Sdg int ac; 2717055Sdg char dostates = No; 2727055Sdg char do_unames = Yes; 2737055Sdg char interactive = Maybe; 2747055Sdg char warnings = 0; 2757055Sdg#if Default_TOPN == Infinity 2767055Sdg char topn_specified = No; 2777055Sdg#endif 2787055Sdg char ch; 2797055Sdg char *iptr; 2807055Sdg char no_command = 1; 2817055Sdg struct timeval timeout; 2827055Sdg#ifdef ORDER 2837055Sdg char *order_name = NULL; 284105598Sbrooks int order_index = 0; 2857055Sdg#endif 2867055Sdg#ifndef FD_SET 2877055Sdg /* FD_SET and friends are not present: fake it */ 28893380Smdodd typedef int fd_set; 28993380Smdodd#define FD_ZERO(x) (*(x) = 0) 29093380Smdodd#define FD_SET(f, x) (*(x) = 1<<f) 2917055Sdg#endif 29293367Smdodd fd_set readfds; 293243882Sglebius 2947055Sdg#ifdef ORDER 2957055Sdg static char command_chars[] = "\f qh?en#sdkriIutHmSCajzPJwo"; 2967055Sdg#else 2977055Sdg static char command_chars[] = "\f qh?en#sdkriIutHmSCajzPJw"; 2987055Sdg#endif 299112266Smdodd/* these defines enumerate the "strchr"s of the commands in command_chars */ 300112266Smdodd#define CMD_redraw 0 301112266Smdodd#define CMD_update 1 302112266Smdodd#define CMD_quit 2 3037055Sdg#define CMD_help1 3 30436908Sjulian#define CMD_help2 4 3057055Sdg#define CMD_OSLIMIT 4 /* terminals with OS can only handle commands */ 3067055Sdg#define CMD_errors 5 /* less than or equal to CMD_OSLIMIT */ 3077055Sdg#define CMD_number1 6 3087055Sdg#define CMD_number2 7 309243882Sglebius#define CMD_delay 8 3107055Sdg#define CMD_displays 9 3117055Sdg#define CMD_kill 10 3127055Sdg#define CMD_renice 11 3137055Sdg#define CMD_idletog 12 31493375Smdodd#define CMD_idletog2 13 3157055Sdg#define CMD_user 14 31652248Smsmith#define CMD_selftog 15 31793375Smdodd#define CMD_thrtog 16 31852248Smsmith#define CMD_viewtog 17 319152315Sru#define CMD_viewsys 18 32093373Smdodd#define CMD_wcputog 19 32193377Smdodd#define CMD_showargs 20 32236908Sjulian#define CMD_jidtog 21 32336908Sjulian#define CMD_kidletog 22 32436908Sjulian#define CMD_pcputog 23 32536908Sjulian#define CMD_jail 24 32636908Sjulian#define CMD_swaptog 25 32736908Sjulian#ifdef ORDER 32836908Sjulian#define CMD_order 26 32936908Sjulian#endif 33036908Sjulian 33193377Smdodd /* set the buffer for stdout */ 332112279Smdodd#ifdef DEBUG 333112279Smdodd extern FILE *debug; 334112279Smdodd debug = fopen("debug.run", "w"); 335112279Smdodd setbuffer(stdout, NULL, 0); 336112279Smdodd#else 337112279Smdodd setbuffer(stdout, stdoutbuf, Buffersize); 338112279Smdodd#endif 339112279Smdodd 340112279Smdodd /* get our name */ 34193369Smdodd if (argc > 0) 34236908Sjulian { 34336908Sjulian if ((myname = strrchr(argv[0], '/')) == 0) 34436908Sjulian { 345185164Skmacy myname = argv[0]; 346130549Smlaier } 347130549Smlaier else 348130549Smlaier { 3497055Sdg myname++; 3507055Sdg } 3517055Sdg } 35293379Smdodd 3537055Sdg /* initialize some selection options */ 3547055Sdg ps.idle = Yes; 3557055Sdg ps.self = -1; 3567055Sdg ps.system = No; 3577055Sdg reset_uids(); 3587055Sdg ps.thread = No; 359112308Smdodd ps.wcpu = 1; 3607055Sdg ps.jid = -1; 361106939Ssam ps.jail = No; 362106939Ssam ps.swap = No; 3637055Sdg ps.kidle = Yes; 3647055Sdg ps.command = NULL; 3657055Sdg 366111888Sjlemon /* get preset options from the environment */ 36793367Smdodd if ((env_top = getenv("TOP")) != NULL) 368106939Ssam { 3697055Sdg av = preset_argv = argparse(env_top, &preset_argc); 370112308Smdodd ac = preset_argc; 371112308Smdodd 372112308Smdodd /* set the dummy argument to an explanatory message, in case 373112308Smdodd getopt encounters a bad argument */ 374112308Smdodd preset_argv[0] = "while processing environment"; 375112308Smdodd } 376112308Smdodd 377112308Smdodd /* process options */ 378112308Smdodd do { 379112308Smdodd /* if we're done doing the presets, then process the real arguments */ 380112308Smdodd if (preset_argc == 0) 381112308Smdodd { 382112308Smdodd ac = argc; 383112308Smdodd av = argv; 384112308Smdodd 385112308Smdodd /* this should keep getopt happy... */ 386112308Smdodd optind = 1; 387112308Smdodd } 388112308Smdodd 389112308Smdodd while ((i = getopt(ac, av, "CSIHPabijJ:nquvzs:d:U:m:o:tw")) != EOF) 390112308Smdodd { 391112308Smdodd switch(i) 392106939Ssam { 393106939Ssam case 'v': /* show version number */ 39493379Smdodd fprintf(stderr, "%s: version %s\n", 39593379Smdodd myname, version_string()); 39693379Smdodd exit(1); 397148887Srwatson break; 398148887Srwatson 39993379Smdodd case 'u': /* toggle uid/username display */ 40093379Smdodd do_unames = !do_unames; 401112308Smdodd break; 402112308Smdodd 403112308Smdodd case 'U': /* display only username's processes */ 404112308Smdodd if ((ps.uid[0] = userid(optarg)) == -1) 405112308Smdodd { 406112308Smdodd fprintf(stderr, "%s: unknown user\n", optarg); 407112308Smdodd exit(1); 408112308Smdodd } 409112308Smdodd break; 410112308Smdodd 411112308Smdodd case 'S': /* show system processes */ 412112308Smdodd ps.system = !ps.system; 413112308Smdodd break; 414105577Srwatson 415172930Srwatson case 'I': /* show idle processes */ 416105577Srwatson ps.idle = !ps.idle; 417105577Srwatson break; 41893379Smdodd 419112287Smdodd case 'i': /* go interactive regardless */ 420112287Smdodd interactive = Yes; 421112287Smdodd break; 422112287Smdodd 423112287Smdodd case 'n': /* batch, or non-interactive */ 424112287Smdodd case 'b': 42593379Smdodd interactive = No; 42693379Smdodd break; 42793379Smdodd 42893379Smdodd case 'a': 429152315Sru fmt_flags ^= FMT_SHOWARGS; 43093379Smdodd break; 43193379Smdodd 43293379Smdodd case 'd': /* number of displays to show */ 43393379Smdodd if ((i = atoiwi(optarg)) == Invalid || i == 0) 43493379Smdodd { 43593379Smdodd fprintf(stderr, 43621830Sjoerg "%s: warning: display count should be positive -- option ignored\n", 437121436Simp myname); 438121436Simp warnings++; 43921830Sjoerg } 44021830Sjoerg else 44121830Sjoerg { 4427055Sdg displays = i; 44321830Sjoerg } 4447055Sdg break; 44521830Sjoerg 44621830Sjoerg case 's': 44721830Sjoerg if ((delay = atoi(optarg)) < 0 || (delay == 0 && getuid() != 0)) 44821830Sjoerg { 44921830Sjoerg fprintf(stderr, 45021830Sjoerg "%s: warning: seconds delay should be positive -- using default\n", 45121830Sjoerg myname); 45221830Sjoerg delay = Default_DELAY; 45321830Sjoerg warnings++; 45421830Sjoerg } 455106939Ssam break; 456111790Smdodd 457106939Ssam case 'q': /* be quick about it */ 458111790Smdodd /* only allow this if user is really root */ 45993379Smdodd if (getuid() == 0) 46093379Smdodd { 46193379Smdodd /* be very un-nice! */ 46293379Smdodd (void) nice(-20); 4637055Sdg } 46493377Smdodd else 4657055Sdg { 4667055Sdg fprintf(stderr, 4677055Sdg "%s: warning: `-q' option can only be used by root\n", 46821830Sjoerg myname); 469112266Smdodd warnings++; 470112266Smdodd } 47193379Smdodd break; 4727055Sdg 47393379Smdodd case 'm': /* select display mode */ 47421830Sjoerg if (strcmp(optarg, "io") == 0) { 475128396Sluigi displaymode = DISP_IO; 476111888Sjlemon } else if (strcmp(optarg, "cpu") == 0) { 477111888Sjlemon displaymode = DISP_CPU; 478111888Sjlemon } else { 479111888Sjlemon fprintf(stderr, 480111888Sjlemon "%s: warning: `-m' option can only take args " 48121830Sjoerg "'io' or 'cpu'\n", 48221830Sjoerg myname); 483128396Sluigi exit(1); 484111888Sjlemon } 485111888Sjlemon break; 486111888Sjlemon 487111888Sjlemon case 'o': /* select sort order */ 488111888Sjlemon#ifdef ORDER 48921830Sjoerg order_name = optarg; 49021830Sjoerg#else 49193377Smdodd fprintf(stderr, 49293377Smdodd "%s: this platform does not support arbitrary ordering. Sorry.\n", 49393379Smdodd myname); 49493379Smdodd warnings++; 4957055Sdg#endif 49693379Smdodd break; 49793377Smdodd 49821830Sjoerg case 't': 49993377Smdodd ps.self = (ps.self == -1) ? getpid() : -1; 50093377Smdodd break; 50121830Sjoerg 5027055Sdg case 'C': 5037055Sdg ps.wcpu = !ps.wcpu; 504295896Sgnn break; 505295896Sgnn 506111888Sjlemon case 'H': 5077055Sdg ps.thread = !ps.thread; 5087055Sdg break; 5097055Sdg 51078295Sjlemon case 'j': 51178295Sjlemon ps.jail = !ps.jail; 512111888Sjlemon break; 5137055Sdg 5147055Sdg case 'J': /* display only jail's processes */ 51554263Sshin if ((ps.jid = jail_getid(optarg)) == -1) 51654263Sshin { 517111888Sjlemon fprintf(stderr, "%s: unknown jail\n", optarg); 51854263Sshin exit(1); 51954263Sshin } 52021830Sjoerg ps.jail = 1; 52121830Sjoerg break; 522111888Sjlemon 52321830Sjoerg case 'P': 52421830Sjoerg pcpu_stats = !pcpu_stats; 5257055Sdg break; 52621830Sjoerg 527111888Sjlemon case 'w': 5287055Sdg ps.swap = 1; 5297055Sdg break; 53021830Sjoerg 53121830Sjoerg case 'z': 532111888Sjlemon ps.kidle = !ps.kidle; 53321830Sjoerg break; 53421830Sjoerg 535111888Sjlemon default: 536111888Sjlemon fprintf(stderr, 53721830Sjoerg"Top version %s\n" 5387055Sdg"Usage: %s [-abCHIijnPqStuvz] [-d count] [-m io | cpu] [-o field] [-s time]\n" 53921830Sjoerg" [-J jail] [-U username] [number]\n", 5407055Sdg version_string(), myname); 5417055Sdg exit(1); 5427055Sdg } 5437055Sdg } 5447055Sdg 54521830Sjoerg /* get count of top processes to display (if any) */ 5467055Sdg if (optind < ac) 54721830Sjoerg { 5487055Sdg if ((topn = atoiwi(av[optind])) == Invalid) 54993377Smdodd { 5507055Sdg fprintf(stderr, 551223741Sbz "%s: warning: process display count should be non-negative -- using default\n", 552111888Sjlemon myname); 55393377Smdodd warnings++; 55493377Smdodd } 55593377Smdodd#if Default_TOPN == Infinity 55693377Smdodd else 55793377Smdodd { 55893377Smdodd topn_specified = Yes; 55993377Smdodd } 5607055Sdg#endif 56193380Smdodd } 5627055Sdg 5637055Sdg /* tricky: remember old value of preset_argc & set preset_argc = 0 */ 5647055Sdg i = preset_argc; 5657055Sdg preset_argc = 0; 566152296Sru 56793367Smdodd /* repeat only if we really did the preset arguments */ 568152296Sru } while (i != 0); 56993383Smdodd 5707055Sdg /* set constants for username/uid display correctly */ 57193367Smdodd if (!do_unames) 57293367Smdodd { 5737055Sdg uname_field = " UID "; 5747055Sdg get_userid = itoa7; 57593373Smdodd } 5767055Sdg 57793379Smdodd /* initialize the kernel memory interface */ 57893379Smdodd if (machine_init(&statics, do_unames) == -1) 57993379Smdodd { 5807055Sdg exit(1); 58193379Smdodd } 582106939Ssam 58368180Sume#ifdef ORDER 58493379Smdodd /* determine sorting order index, if necessary */ 58516063Sgpalmer if (order_name != NULL) 58621830Sjoerg { 58721830Sjoerg if ((order_index = string_index(order_name, statics.order_names)) == -1) 58821830Sjoerg { 589152315Sru char **pp; 590152315Sru 59193379Smdodd fprintf(stderr, "%s: '%s' is not a recognized sorting order.\n", 59221831Sjoerg myname, order_name); 59321831Sjoerg fprintf(stderr, "\tTry one of these:"); 59421831Sjoerg pp = statics.order_names; 595152296Sru while (*pp != NULL) 59693379Smdodd { 59793383Smdodd fprintf(stderr, " %s", *pp++); 59893383Smdodd } 59993383Smdodd fputc('\n', stderr); 60093379Smdodd exit(1); 6017055Sdg } 60268180Sume } 60393382Smdodd#endif 60493382Smdodd 60593382Smdodd#ifdef no_initialization_needed 60693382Smdodd /* initialize the hashing stuff */ 60793382Smdodd if (do_unames) 60893382Smdodd { 60993382Smdodd init_hash(); 61093382Smdodd } 61193382Smdodd#endif 61293382Smdodd 61393382Smdodd /* initialize termcap */ 61493382Smdodd init_termcap(interactive); 61593382Smdodd 61693382Smdodd /* get the string to use for the process area header */ 61793382Smdodd header_text = format_header(uname_field); 61893382Smdodd 61993382Smdodd /* initialize display interface */ 620194581Srdivacky if ((max_topn = display_init(&statics)) == -1) 62193382Smdodd { 62293382Smdodd fprintf(stderr, "%s: can't allocate sufficient memory\n", myname); 62393382Smdodd exit(4); 62493382Smdodd } 62593382Smdodd 62693382Smdodd /* print warning if user requested more processes than we can display */ 62793382Smdodd if (topn > max_topn) 62893382Smdodd { 62993382Smdodd fprintf(stderr, 63093382Smdodd "%s: warning: this terminal can only display %d processes.\n", 63193382Smdodd myname, max_topn); 63293382Smdodd warnings++; 63393382Smdodd } 63493382Smdodd 63593382Smdodd /* adjust for topn == Infinity */ 63693382Smdodd if (topn == Infinity) 63793382Smdodd { 63893382Smdodd /* 63993382Smdodd * For smart terminals, infinity really means everything that can 64093382Smdodd * be displayed, or Largest. 64193382Smdodd * On dumb terminals, infinity means every process in the system! 64293382Smdodd * We only really want to do that if it was explicitly specified. 64393382Smdodd * This is always the case when "Default_TOPN != Infinity". But if 64493382Smdodd * topn wasn't explicitly specified and we are on a dumb terminal 64593382Smdodd * and the default is Infinity, then (and only then) we use 64693382Smdodd * "Nominal_TOPN" instead. 64793382Smdodd */ 64893382Smdodd#if Default_TOPN == Infinity 64993382Smdodd topn = smart_terminal ? Largest : 65093382Smdodd (topn_specified ? Largest : Nominal_TOPN); 65193382Smdodd#else 65293382Smdodd topn = Largest; 653152315Sru#endif 65493382Smdodd } 65593382Smdodd 656152315Sru /* set header display accordingly */ 657147256Sbrooks display_header(topn > 0); 65893382Smdodd 65993382Smdodd /* determine interactive state */ 66093382Smdodd if (interactive == Maybe) 66193382Smdodd { 66293382Smdodd interactive = smart_terminal; 66393382Smdodd } 66493382Smdodd 66593382Smdodd /* if # of displays not specified, fill it in */ 66693382Smdodd if (displays == 0) 66793382Smdodd { 66893382Smdodd displays = smart_terminal ? Infinity : 1; 66993382Smdodd } 670104302Sphk 671144045Smdodd /* hold interrupt signals while setting up the screen and the handlers */ 67293382Smdodd#ifdef SIGHOLD 67393382Smdodd sighold(SIGINT); 67493382Smdodd sighold(SIGQUIT); 67593382Smdodd sighold(SIGTSTP); 676152315Sru#else 67793382Smdodd old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP)); 67893382Smdodd#endif 67993382Smdodd init_screen(); 68093382Smdodd (void) signal(SIGINT, leave); 68193382Smdodd (void) signal(SIGQUIT, leave); 68293382Smdodd (void) signal(SIGTSTP, tstop); 68393382Smdodd#ifdef SIGWINCH 68493382Smdodd (void) signal(SIGWINCH, winch); 68593382Smdodd#endif 68693382Smdodd#ifdef SIGRELSE 68793382Smdodd sigrelse(SIGINT); 68893382Smdodd sigrelse(SIGQUIT); 68993382Smdodd sigrelse(SIGTSTP); 69093382Smdodd#else 69193382Smdodd (void) sigsetmask(old_sigmask); 692144045Smdodd#endif 69393382Smdodd if (warnings) 69493382Smdodd { 69593382Smdodd fputs("....", stderr); 69693382Smdodd fflush(stderr); /* why must I do this? */ 69793382Smdodd sleep((unsigned)(3 * warnings)); 69893382Smdodd fputc('\n', stderr); 69968180Sume } 70068180Sume 70168180Sumerestart: 70268180Sume 70368180Sume /* 70468180Sume * main loop -- repeat while display count is positive or while it 70568180Sume * indicates infinity (by being -1) 706184709Sbz */ 70768180Sume 708184709Sbz while ((displays == -1) || (displays-- > 0)) 70968180Sume { 71068180Sume int (*compare)(); 71168180Sume 71268180Sume 71368180Sume /* get the current stats */ 71468180Sume get_system_info(&system_info); 71568180Sume 71668180Sume#ifdef ORDER 71768180Sume compare = compares[order_index]; 71868180Sume#else 71968180Sume if (displaymode == DISP_CPU) 72068180Sume compare = proc_compare; 72168180Sume else 72293369Smdodd compare = io_compare; 72368180Sume#endif 72493369Smdodd 72568180Sume /* get the current set of processes */ 72668180Sume processes = 72768180Sume get_process_info(&system_info, &ps, compare); 72868180Sume 72968180Sume /* display the load averages */ 73093369Smdodd (*d_loadave)(system_info.last_pid, 731184205Sdes system_info.load_avg); 732148641Srwatson 733148641Srwatson /* display the current time */ 734148641Srwatson /* this method of getting the time SHOULD be fairly portable */ 73568180Sume time(&curr_time); 73668180Sume i_uptime(&system_info.boottime, &curr_time); 73768180Sume i_timeofday(&curr_time); 73868180Sume 73968180Sume /* display process state breakdown */ 74093375Smdodd (*d_procstates)(system_info.p_total, 74168180Sume system_info.procstates); 74268180Sume 74368180Sume /* display the cpu state percentage breakdown */ 74468180Sume if (dostates) /* but not the first time */ 74593369Smdodd { 74668180Sume (*d_cpustates)(system_info.cpustates); 74768180Sume } 74868180Sume else 74968180Sume { 75068180Sume /* we'll do it next time */ 75168180Sume if (smart_terminal) 75268180Sume { 75368180Sume z_cpustates(); 75468180Sume } 75568180Sume else 75668180Sume { 75768180Sume putchar('\n'); 75893369Smdodd } 75968180Sume dostates = Yes; 76068180Sume } 76193369Smdodd 762184205Sdes /* display memory stats */ 763148641Srwatson (*d_memory)(system_info.memory); 764148641Srwatson (*d_arc)(system_info.arc); 765148641Srwatson (*d_carc)(system_info.carc); 76668180Sume 76768180Sume /* display swap stats */ 76868180Sume (*d_swap)(system_info.swap); 76968180Sume 77068180Sume /* handle message area */ 77193375Smdodd (*d_message)(); 77268180Sume 77368180Sume /* update the header area */ 77468180Sume (*d_header)(header_text); 77568180Sume 77693369Smdodd if (topn > 0) 77768180Sume { 77868180Sume /* determine number of processes to actually display */ 77968180Sume /* this number will be the smallest of: active processes, 78068180Sume number user requested, number current screen accomodates */ 78168180Sume active_procs = system_info.P_ACTIVE; 78268180Sume if (active_procs > topn) 78368180Sume { 78493369Smdodd active_procs = topn; 78568180Sume } 78693375Smdodd if (active_procs > max_topn) 78793375Smdodd { 78868180Sume active_procs = max_topn; 78993375Smdodd } 79093375Smdodd 79193375Smdodd /* now show the top "n" processes. */ 79293375Smdodd for (i = 0; i < active_procs; i++) 793241394Skevlo { 79493375Smdodd (*d_process)(i, format_next_process(processes, get_userid, 79593375Smdodd fmt_flags)); 79693375Smdodd } 79793375Smdodd } 798 else 799 { 800 i = 0; 801 } 802 803 /* do end-screen processing */ 804 u_endscreen(i); 805 806 /* now, flush the output buffer */ 807 if (fflush(stdout) != 0) 808 { 809 new_message(MT_standout, " Write error on stdout"); 810 putchar('\r'); 811 quit(1); 812 /*NOTREACHED*/ 813 } 814 815 /* only do the rest if we have more displays to show */ 816 if (displays) 817 { 818 /* switch out for new display on smart terminals */ 819 if (smart_terminal) 820 { 821 if (overstrike) 822 { 823 reset_display(); 824 } 825 else 826 { 827 d_loadave = u_loadave; 828 d_procstates = u_procstates; 829 d_cpustates = u_cpustates; 830 d_memory = u_memory; 831 d_arc = u_arc; 832 d_carc = u_carc; 833 d_swap = u_swap; 834 d_message = u_message; 835 d_header = u_header; 836 d_process = u_process; 837 } 838 } 839 840 no_command = Yes; 841 if (!interactive) 842 { 843 sleep(delay); 844 if (leaveflag) { 845 end_screen(); 846 exit(0); 847 } 848 } 849 else while (no_command) 850 { 851 /* assume valid command unless told otherwise */ 852 no_command = No; 853 854 /* set up arguments for select with timeout */ 855 FD_ZERO(&readfds); 856 FD_SET(0, &readfds); /* for standard input */ 857 timeout.tv_sec = delay; 858 timeout.tv_usec = 0; 859 860 if (leaveflag) { 861 end_screen(); 862 exit(0); 863 } 864 865 if (tstopflag) { 866 /* move to the lower left */ 867 end_screen(); 868 fflush(stdout); 869 870 /* default the signal handler action */ 871 (void) signal(SIGTSTP, SIG_DFL); 872 873 /* unblock the signal and send ourselves one */ 874#ifdef SIGRELSE 875 sigrelse(SIGTSTP); 876#else 877 (void) sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1))); 878#endif 879 (void) kill(0, SIGTSTP); 880 881 /* reset the signal handler */ 882 (void) signal(SIGTSTP, tstop); 883 884 /* reinit screen */ 885 reinit_screen(); 886 reset_display(); 887 tstopflag = 0; 888 goto restart; 889 } 890 891 if (winchflag) { 892 /* reascertain the screen dimensions */ 893 get_screensize(); 894 895 /* tell display to resize */ 896 max_topn = display_resize(); 897 898 /* reset the signal handler */ 899 (void) signal(SIGWINCH, winch); 900 901 reset_display(); 902 winchflag = 0; 903 goto restart; 904 } 905 906 /* wait for either input or the end of the delay period */ 907 sel_ret = select(2, &readfds, NULL, NULL, &timeout); 908 if (sel_ret < 0 && errno != EINTR) 909 quit(0); 910 if (sel_ret > 0) 911 { 912 int newval; 913 char *errmsg; 914 915 /* something to read -- clear the message area first */ 916 clear_message(); 917 918 /* now read it and convert to command strchr */ 919 /* (use "change" as a temporary to hold strchr) */ 920 if (read(0, &ch, 1) != 1) 921 { 922 /* read error: either 0 or -1 */ 923 new_message(MT_standout, " Read error on stdin"); 924 putchar('\r'); 925 quit(1); 926 /*NOTREACHED*/ 927 } 928 if ((iptr = strchr(command_chars, ch)) == NULL) 929 { 930 if (ch != '\r' && ch != '\n') 931 { 932 /* illegal command */ 933 new_message(MT_standout, " Command not understood"); 934 } 935 putchar('\r'); 936 no_command = Yes; 937 } 938 else 939 { 940 change = iptr - command_chars; 941 if (overstrike && change > CMD_OSLIMIT) 942 { 943 /* error */ 944 new_message(MT_standout, 945 " Command cannot be handled by this terminal"); 946 putchar('\r'); 947 no_command = Yes; 948 } 949 else switch(change) 950 { 951 case CMD_redraw: /* redraw screen */ 952 reset_display(); 953 break; 954 955 case CMD_update: /* merely update display */ 956 /* is the load average high? */ 957 if (system_info.load_avg[0] > LoadMax) 958 { 959 /* yes, go home for visual feedback */ 960 go_home(); 961 fflush(stdout); 962 } 963 break; 964 965 case CMD_quit: /* quit */ 966 quit(0); 967 /*NOTREACHED*/ 968 break; 969 970 case CMD_help1: /* help */ 971 case CMD_help2: 972 reset_display(); 973 clear(); 974 show_help(); 975 standout("Hit any key to continue: "); 976 fflush(stdout); 977 (void) read(0, &ch, 1); 978 break; 979 980 case CMD_errors: /* show errors */ 981 if (error_count() == 0) 982 { 983 new_message(MT_standout, 984 " Currently no errors to report."); 985 putchar('\r'); 986 no_command = Yes; 987 } 988 else 989 { 990 reset_display(); 991 clear(); 992 show_errors(); 993 standout("Hit any key to continue: "); 994 fflush(stdout); 995 (void) read(0, &ch, 1); 996 } 997 break; 998 999 case CMD_number1: /* new number */ 1000 case CMD_number2: 1001 new_message(MT_standout, 1002 "Number of processes to show: "); 1003 newval = readline(tempbuf1, 8, Yes); 1004 if (newval > -1) 1005 { 1006 if (newval > max_topn) 1007 { 1008 new_message(MT_standout | MT_delayed, 1009 " This terminal can only display %d processes.", 1010 max_topn); 1011 putchar('\r'); 1012 } 1013 1014 if (newval == 0) 1015 { 1016 /* inhibit the header */ 1017 display_header(No); 1018 } 1019 else if (newval > topn && topn == 0) 1020 { 1021 /* redraw the header */ 1022 display_header(Yes); 1023 d_header = i_header; 1024 } 1025 topn = newval; 1026 } 1027 break; 1028 1029 case CMD_delay: /* new seconds delay */ 1030 new_message(MT_standout, "Seconds to delay: "); 1031 if ((i = readline(tempbuf1, 8, Yes)) > -1) 1032 { 1033 if ((delay = i) == 0 && getuid() != 0) 1034 { 1035 delay = 1; 1036 } 1037 } 1038 clear_message(); 1039 break; 1040 1041 case CMD_displays: /* change display count */ 1042 new_message(MT_standout, 1043 "Displays to show (currently %s): ", 1044 displays == -1 ? "infinite" : 1045 itoa(displays)); 1046 if ((i = readline(tempbuf1, 10, Yes)) > 0) 1047 { 1048 displays = i; 1049 } 1050 else if (i == 0) 1051 { 1052 quit(0); 1053 } 1054 clear_message(); 1055 break; 1056 1057 case CMD_kill: /* kill program */ 1058 new_message(0, "kill "); 1059 if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) 1060 { 1061 if ((errmsg = kill_procs(tempbuf2)) != NULL) 1062 { 1063 new_message(MT_standout, "%s", errmsg); 1064 putchar('\r'); 1065 no_command = Yes; 1066 } 1067 } 1068 else 1069 { 1070 clear_message(); 1071 } 1072 break; 1073 1074 case CMD_renice: /* renice program */ 1075 new_message(0, "renice "); 1076 if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) 1077 { 1078 if ((errmsg = renice_procs(tempbuf2)) != NULL) 1079 { 1080 new_message(MT_standout, "%s", errmsg); 1081 putchar('\r'); 1082 no_command = Yes; 1083 } 1084 } 1085 else 1086 { 1087 clear_message(); 1088 } 1089 break; 1090 1091 case CMD_idletog: 1092 case CMD_idletog2: 1093 ps.idle = !ps.idle; 1094 new_message(MT_standout | MT_delayed, 1095 " %sisplaying idle processes.", 1096 ps.idle ? "D" : "Not d"); 1097 putchar('\r'); 1098 break; 1099 1100 case CMD_selftog: 1101 ps.self = (ps.self == -1) ? getpid() : -1; 1102 new_message(MT_standout | MT_delayed, 1103 " %sisplaying self.", 1104 (ps.self == -1) ? "D" : "Not d"); 1105 putchar('\r'); 1106 break; 1107 1108 case CMD_user: 1109 if (handle_user(tempbuf2, sizeof(tempbuf2))) 1110 no_command = Yes; 1111 break; 1112 1113 case CMD_thrtog: 1114 ps.thread = !ps.thread; 1115 new_message(MT_standout | MT_delayed, 1116 " Displaying threads %s", 1117 ps.thread ? "separately" : "as a count"); 1118 header_text = format_header(uname_field); 1119 reset_display(); 1120 putchar('\r'); 1121 break; 1122 case CMD_wcputog: 1123 ps.wcpu = !ps.wcpu; 1124 new_message(MT_standout | MT_delayed, 1125 " Displaying %s CPU", 1126 ps.wcpu ? "weighted" : "raw"); 1127 header_text = format_header(uname_field); 1128 reset_display(); 1129 putchar('\r'); 1130 break; 1131 case CMD_viewtog: 1132 if (++displaymode == DISP_MAX) 1133 displaymode = 0; 1134 header_text = format_header(uname_field); 1135 display_header(Yes); 1136 d_header = i_header; 1137 reset_display(); 1138 break; 1139 case CMD_viewsys: 1140 ps.system = !ps.system; 1141 break; 1142 case CMD_showargs: 1143 fmt_flags ^= FMT_SHOWARGS; 1144 break; 1145#ifdef ORDER 1146 case CMD_order: 1147 new_message(MT_standout, 1148 "Order to sort: "); 1149 if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) 1150 { 1151 if ((i = string_index(tempbuf2, statics.order_names)) == -1) 1152 { 1153 new_message(MT_standout, 1154 " %s: unrecognized sorting order", tempbuf2); 1155 no_command = Yes; 1156 } 1157 else 1158 { 1159 order_index = i; 1160 } 1161 putchar('\r'); 1162 } 1163 else 1164 { 1165 clear_message(); 1166 } 1167 break; 1168#endif 1169 case CMD_jidtog: 1170 ps.jail = !ps.jail; 1171 new_message(MT_standout | MT_delayed, 1172 " %sisplaying jail ID.", 1173 ps.jail ? "D" : "Not d"); 1174 header_text = format_header(uname_field); 1175 reset_display(); 1176 putchar('\r'); 1177 break; 1178 1179 case CMD_jail: 1180 new_message(MT_standout, 1181 "Jail to show (+ for all): "); 1182 if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) 1183 { 1184 if (tempbuf2[0] == '+' && 1185 tempbuf2[1] == '\0') 1186 { 1187 ps.jid = -1; 1188 } 1189 else if ((i = jail_getid(tempbuf2)) == -1) 1190 { 1191 new_message(MT_standout, 1192 " %s: unknown jail", tempbuf2); 1193 no_command = Yes; 1194 } 1195 else 1196 { 1197 ps.jid = i; 1198 } 1199 if (ps.jail == 0) { 1200 ps.jail = 1; 1201 new_message(MT_standout | 1202 MT_delayed, " Displaying jail " 1203 "ID."); 1204 header_text = 1205 format_header(uname_field); 1206 reset_display(); 1207 } 1208 putchar('\r'); 1209 } 1210 else 1211 { 1212 clear_message(); 1213 } 1214 break; 1215 1216 case CMD_kidletog: 1217 ps.kidle = !ps.kidle; 1218 new_message(MT_standout | MT_delayed, 1219 " %sisplaying system idle process.", 1220 ps.kidle ? "D" : "Not d"); 1221 putchar('\r'); 1222 break; 1223 case CMD_pcputog: 1224 pcpu_stats = !pcpu_stats; 1225 new_message(MT_standout | MT_delayed, 1226 " Displaying %sCPU statistics.", 1227 pcpu_stats ? "per-" : "global "); 1228 toggle_pcpustats(); 1229 max_topn = display_updatecpus(&statics); 1230 reset_display(); 1231 putchar('\r'); 1232 break; 1233 case CMD_swaptog: 1234 ps.swap = !ps.swap; 1235 new_message(MT_standout | MT_delayed, 1236 " %sisplaying per-process swap usage.", 1237 ps.swap ? "D" : "Not d"); 1238 header_text = format_header(uname_field); 1239 reset_display(); 1240 putchar('\r'); 1241 break; 1242 default: 1243 new_message(MT_standout, " BAD CASE IN SWITCH!"); 1244 putchar('\r'); 1245 } 1246 } 1247 1248 /* flush out stuff that may have been written */ 1249 fflush(stdout); 1250 } 1251 } 1252 } 1253 } 1254 1255#ifdef DEBUG 1256 fclose(debug); 1257#endif 1258 quit(0); 1259 /*NOTREACHED*/ 1260} 1261 1262/* 1263 * reset_display() - reset all the display routine pointers so that entire 1264 * screen will get redrawn. 1265 */ 1266 1267void 1268reset_display() 1269 1270{ 1271 d_loadave = i_loadave; 1272 d_procstates = i_procstates; 1273 d_cpustates = i_cpustates; 1274 d_memory = i_memory; 1275 d_arc = i_arc; 1276 d_carc = i_carc; 1277 d_swap = i_swap; 1278 d_message = i_message; 1279 d_header = i_header; 1280 d_process = i_process; 1281} 1282 1283/* 1284 * signal handlers 1285 */ 1286 1287sigret_t leave() /* exit under normal conditions -- INT handler */ 1288 1289{ 1290 leaveflag = 1; 1291} 1292 1293sigret_t tstop(i) /* SIGTSTP handler */ 1294 1295int i; 1296 1297{ 1298 tstopflag = 1; 1299} 1300 1301#ifdef SIGWINCH 1302sigret_t winch(i) /* SIGWINCH handler */ 1303 1304int i; 1305 1306{ 1307 winchflag = 1; 1308} 1309#endif 1310 1311void quit(status) /* exit under duress */ 1312 1313int status; 1314 1315{ 1316 end_screen(); 1317 exit(status); 1318 /*NOTREACHED*/ 1319} 1320