sob@soma.UUCP (Stan Barber) (09/30/86)
#! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # top.c # commands.c # display.c # kernel.c # screen.c # This archive created: Mon Sep 29 16:14:10 1986 export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'top.c'" '(29035 characters)' if test -f 'top.c' then echo shar: "will not over-write existing file 'top.c'" else cat << \SHAR_EOF > 'top.c' char *copyright = "Top, version 2.0, copyright (c) 1984, 1986, William LeFebvre"; /* * Top users display for Berkeley Unix * Version 2.0 * * This program may be freely redistributed to other Unix sites, but this * entire comment MUST remain intact. * * Copyright (c) 1984, 1986, William LeFebvre, Rice University * * This program is designed to run on either Berkeley 4.1 or 4.2 Unix. * Compile with the preprocessor constant "FOUR_ONE" set to get an * executable that will run on Berkeley 4.1 Unix. * * The Sun kernel uses scaled integers instead of floating point. * Compilation with the preprocessor variable "sun" gets an executable * that will run on Sun Unix version 1.1 or later ("sun" is automatically * set by the Sun C compiler). * * The Pyramid splits the stack size (p_ssize) into control stack and user * stack sizes. Compilation with the preprocessor variable "pyr" gets an * executable that will run on Pyramids ("pyr" is automatically set by the * Pyramid C compiler). * * See the file "Changes" for more information on version-to-version changes. */ #include <stdio.h> #include <pwd.h> #include <nlist.h> #include <signal.h> #include <setjmp.h> #include <sys/param.h> #include <sys/dir.h> #include "masscomp.h" #include <sys/user.h> #include <sys/proc.h> #ifndef MASSCOMP #include <sys/dk.h> #endif #include <sys/vm.h> /* includes specific to top */ #include "layout.h" #include "screen.h" /* interface to screen package */ #include "top.h" #include "top.local.h" #include "boolean.h" /* Special atoi routine returns either a non-negative number or one of: */ #define Infinity -1 #define Invalid -2 /* Size of the stdio buffer given to stdout */ #define Buffersize 2048 /* The buffer the stdio will use */ char stdoutbuf[Buffersize]; /* wish list for kernel symbols */ #ifdef MASSCOMP struct nlist nlst[] = { { "_avenrun" }, /* load averages vector */ #define X_AVENRUN 0 { "_ccpu" }, #define X_CCPU 1 { "_mpid" }, /* maximum process id */ #define X_MPID 2 { "_physmem" }, /* total available physical memory */ #define X_PHYSMEM 3 { "_nswap" }, /* swap device size */ #define X_NSWAP 4 { "_freemem" }, /* current free memory size */ #define X_FREEMEM 5 { 0 }, }; #else struct nlist nlst[] = { { "_avenrun" }, #define X_AVENRUN 0 { "_ccpu" }, #define X_CCPU 1 { "_cp_time" }, #define X_CP_TIME 2 { "_hz" }, #define X_HZ 3 { "_mpid" }, #define X_MPID 4 { "_nproc" }, #define X_NPROC 5 { "_proc" }, #define X_PROC 6 { "_total" }, #define X_TOTAL 7 { 0 }, }; #endif /* build Signal masks */ #define Smask(s) (1 << ((s) - 1)) /* for system errors */ extern int errno; /* for getopt: */ extern int optind; extern char *optarg; /* signal handling routines */ int leave(); int onalrm(); int tstop(); int nproc; int mpid; /* kernel "hz" variable -- clock rate */ long hz; /* All this is to calculate the cpu state percentages */ long cp_time[CPUSTATES]; long cp_old[CPUSTATES]; long cp_change[CPUSTATES]; long total_change; long mpid_offset; long avenrun_offset; long cp_time_offset; long total_offset; #ifdef sun long ccpu; long avenrun[3]; #else double ccpu; double avenrun[3]; #endif double logcpu; struct vmtotal total; #ifdef MASSCOMP struct cu_des cu_des; #endif struct proc *proc; struct proc *pbase; int bytes; int initialized = No; char *myname = "top"; jmp_buf jmp_int; char input_waiting = No; /* routines that don't return int */ struct passwd *getpwent(); char *username(); char *itoa(); char *ctime(); char *rindex(); char *kill_procs(); char *renice_procs(); int proc_compar(); long time(); /* different routines for displaying the user's identification */ /* (values assigned to get_userid) */ char *username(); char *itoa7(); /* display routines that need to be predeclared */ int i_loadave(); int u_loadave(); int i_procstates(); int u_procstates(); int i_cpustates(); int u_cpustates(); int i_memory(); int u_memory(); int i_header(); int u_header(); int i_process(); int u_process(); /* pointers to display routines */ int (*d_loadave)() = i_loadave; int (*d_procstates)() = i_procstates; int (*d_cpustates)() = i_cpustates; int (*d_memory)() = i_memory; int (*d_header)() = i_header; int (*d_process)() = i_process; /* buffer of proc information lines for display updating */ /* unfortunate that this must be declared globally */ char (* screenbuf)[Display_width]; main(argc, argv) int argc; char *argv[]; { register struct proc *pp; register struct proc **prefp; register int i; register int active_procs; register int change; static struct proc **pref; static char tempbuf1[50]; static char tempbuf2[50]; int total_procs; int old_sigmask; #ifdef MASSCOMP int proc_brkdn[9]; #else int proc_brkdn[7]; #endif int topn = Default_TOPN; int delay = Default_DELAY; int displays = 0; /* indicates unspecified */ long curr_time; char *(*get_userid)() = username; char *uname_field = "USERNAME"; #ifndef FOUR_ONE char ch; char msg_showing = 0; int readfds; struct timeval timeout; #endif char dostates = No; char do_unames = Yes; char do_init = No; char interactive = Maybe; char show_sysprocs = No; char topn_specified = No; char warnings = 0; /* set the buffer for stdout */ #ifndef MASSCOMP /* this should be there, but is not for some reason */ setbuffer(stdout, stdoutbuf, Buffersize); #endif #ifdef MASSCOMP nproc = MAX_PROC; #endif /* get our name */ if (argc > 0) { if ((myname = rindex(argv[0], '/')) == 0) { myname = argv[0]; } else { myname++; } } /* process options */ while ((i = getopt(argc, argv, "Sbinus:d:")) != EOF) { switch(i) { case 'u': /* display uid instead of name */ do_unames = No; uname_field = " UID "; get_userid = itoa7; break; case 'S': /* show system processes */ show_sysprocs = Yes; break; case 'i': /* go interactive regardless */ interactive = Yes; break; case 'n': /* batch, or non-interactive */ case 'b': interactive = No; break; case 'd': /* number of displays to show */ if ((i = atoiwi(optarg)) == Invalid || i == 0) { fprintf(stderr, "%s: warning: display count should be positive -- option ignored\n", myname); warnings++; } else { displays = i; } break; case 's': if ((delay = atoi(optarg)) < 0) { fprintf(stderr, "%s: warning: seconds delay should be non-negative -- using default\n", myname); delay = Default_DELAY; warnings++; } break; default: fprintf(stderr, "Usage: %s [-Sbinu] [-d x] [-s x] [number]\n", myname); exit(1); } } /* get count of top processes to display (if any) */ if (optind < argc) { if ((topn = atoiwi(argv[optind])) == Invalid) { fprintf(stderr, "%s: warning: process display count should be non-negative -- using default\n", myname); topn = Default_TOPN; warnings++; } else { topn_specified = Yes; } } /* initialize the kernel memory interface */ init_kernel(); if (initialized != 1) { /* get the list of symbols we want to access in the kernel */ /* errno = 0; ??? */ nlist(VMUNIX, nlst); if (nlst[0].n_type == 0) { fprintf(stderr, "%s: can't nlist image\n", VMUNIX); exit(2); } /* get the symbol values out of kmem */ #ifndef MASSCOMP getkval(nlst[X_PROC].n_value, &proc, sizeof(int), nlst[X_PROC].n_name); getkval(nlst[X_NPROC].n_value, &nproc, sizeof(int), nlst[X_NPROC].n_name); getkval(nlst[X_HZ].n_value, &hz, sizeof(int), nlst[X_HZ].n_name); #endif getkval(nlst[X_CCPU].n_value, &ccpu, sizeof(int), nlst[X_CCPU].n_name); /* some calculations we use later */ mpid_offset = nlst[X_MPID].n_value; avenrun_offset = nlst[X_AVENRUN].n_value; #ifndef MASSCOMP cp_time_offset = nlst[X_CP_TIME].n_value; total_offset = nlst[X_TOTAL].n_value; #endif /* this is used in calculating WCPU -- calculate it ahead of time */ #ifdef sun logcpu = log((double)ccpu / FSCALE); #else logcpu = log(ccpu); #endif /* allocate space for proc structure array and array of pointers */ bytes = nproc * sizeof(struct proc); pbase = (struct proc *)sbrk(bytes); pref = (struct proc **)sbrk(nproc * sizeof(struct proc *)); /* Just in case ... */ if (pbase == (struct proc *)NULL || pref == (struct proc **)NULL) { fprintf(stderr, "%s: can't allocate sufficient memory\n", myname); exit(3); } /* initialize the hashing stuff */ if (do_unames) { init_hash(); } if (do_init) { initialized = 1; kill(0, SIGQUIT); exit(99); } } /* initialize termcap */ init_termcap(); /* * Smart terminals can only display so many processes, precisely * "screen_length - Header_lines". When run on dumb terminals, nothing * fancy is done anyway, so we can display as many processes as the * system can make. But since we never need to remember what is on the * screen, we only allocate a buffer for one screen line. */ if (smart_terminal) { /* can only display (screen_length - Header_lines) processes */ i = screen_length - Header_lines; if (topn > i) /* false even when topn == Infinity */ { fprintf(stderr, "%s: warning: this terminal can only display %d processes.\n", myname, screen_length - Header_lines); topn = i; warnings++; } } else { i = 1; screen_length = nproc + Header_lines; } /* allocate space for the screen buffer */ screenbuf = (char (*)[])sbrk(i * Display_width); if (screenbuf == (char (*)[])NULL) { fprintf(stderr, "%s: can't allocate sufficient memory\n", myname); exit(4); } /* adjust for topn == Infinity */ if (topn == Infinity) { /* * For smart terminals, infinity really means everything that can * be displayed (which just happens to be "i" at this point). * On dumb terminals, infinity means every process in the system! * We only really want to do that if it was explicitly specified. * This is always the case when "Default_TOPN != Infinity". But if * topn wasn't explicitly specified and we are on a dumb terminal * and the default is Infinity, then (and only then) we use * "Nominal_TOPN" instead. */ #if Default_TOPN == Infinity topn = smart_terminal ? i : (topn_specified ? nproc : Nominal_TOPN); #else topn = smart_terminal ? i : nproc; #endif } /* determine interactive state */ if (interactive == Maybe) { interactive = smart_terminal; } /* if # of displays not specified, fill it in */ if (displays == 0) { displays = smart_terminal ? Infinity : 1; } /* hold interrupt signals while setting up the screen and the handlers */ #ifndef FOUR_ONE old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP)); #endif init_screen(); signal(SIGINT, leave); signal(SIGQUIT, leave); signal(SIGTSTP, tstop); #ifndef FOUR_ONE sigsetmask(old_sigmask); #endif if (warnings) { fprintf(stderr, "...."); fflush(stderr); /* why must I do this? */ sleep(3 * warnings); } clear(); /* setup the jump buffer for stops */ if (setjmp(jmp_int) != 0) { /* control ends up here after an interrupt */ clear(); reset_display(); } /* * main loop -- repeat while display count is positive or while it * indicates infinity (by being -1) */ while ((displays == -1) || (displays-- > 0)) { /* read all the proc structures in one fell swoop */ #ifdef MASSCOMP cu_des.p_max = MAX_PROC; cu_des.t_max = 0; /* not interested in text */ cu_des.c_max = 0; /* not interested in cmap */ cinfo(&cu_des,pbase,NULL,NULL); if (cu_des.p_ret < 0 ) nproc = 0; else nproc = cu_des.p_ret; /* this is not really the correct way to do this * but it will do */ bzero((char *)cp_time,sizeof(cp_time)); bzero((char *)cp_old,sizeof(cp_time)); #else getkval(proc, pbase, bytes, "proc array"); /* get the cp_time array */ getkval(cp_time_offset, cp_time, sizeof(cp_time), "_cp_time"); #endif /* get load average array */ getkval(avenrun_offset, avenrun, sizeof(avenrun), "_avenrun"); /* get mpid -- process id of last process */ getkval(mpid_offset, &mpid, sizeof(mpid), "_mpid"); #ifdef MASSCOMP /* get total -- systemwide main memory usage structure */ getmem(&total); #else getkval(total_offset, &total, sizeof(total), "_total"); #endif /* count up process states and get pointers to interesting procs */ total_procs = 0; active_procs = 0; bzero(proc_brkdn, sizeof(proc_brkdn)); prefp = pref; for (pp = pbase, i = 0; i < nproc; pp++, i++) { /* * Place pointers to each valid proc structure in pref[]. * Process slots that are actually in use have a non-zero * status field. Processes with SSYS set are system * processes---these get ignored unless show_sysprocs is set. */ if (pp->p_stat != 0 && (show_sysprocs || ((pp->p_flag & SSYS) == 0))) { total_procs++; #ifdef MASSCOMP proc_brkdn[bitmask(pp->p_stat)]++; total.t_arm += pp->p_rssize; total.t_avm += (pp->p_tsize + pp->p_dsize + pp->p_ssize - pp->p_rssize); #else proc_brkdn[pp->p_stat]++; #endif if (pp->p_stat != SZOMB) { *prefp++ = pp; active_procs++; } } #ifdef MASSCOMP if (((pp->p_flag) & SSYS) != 0 || pp->p_pid == 0 || pp->p_uid == 0) cp_time[2] += pp->p_pctcpu; else cp_time[0] += pp->p_pctcpu; if (pp->p_nice - NZERO) cp_time[1] += pp->p_pctcpu; #endif } #ifdef MASSCOMP cp_time[3] = 100 - cp_time[2] - cp_time[0]; total.t_rm = total.t_free+total.t_arm; /* gag! */ #endif /* display the load averages */ (*d_loadave)(mpid, avenrun); /* * Display the current time. * "ctime" always returns a string that looks like this: * * Sun Sep 16 01:03:52 1973 * 012345678901234567890123 * 1 2 * * We want indices 11 thru 18 (length 8). */ curr_time = time(0); if (smart_terminal) { Move_to(screen_width - 8, 0); } else { fputs(" ", stdout); } printf("%-8.8s\n", &(ctime(&curr_time)[11])); /* display process state breakdown */ (*d_procstates)(total_procs, proc_brkdn); /* calculate percentage time in each cpu state */ if (dostates) /* but not the first time */ { total_change = 0; for (i = 0; i < CPUSTATES; i++) { /* calculate changes for each state and overall change */ if (cp_time[i] < cp_old[i]) { /* this only happens when the counter wraps */ change = (int) ((unsigned long)cp_time[i]-(unsigned long)cp_old[i]); } else { change = cp_time[i] - cp_old[i]; } total_change += (cp_change[i] = change); cp_old[i] = cp_time[i]; } (*d_cpustates)(cp_change, total_change); } else { /* we'll do it next time */ if (smart_terminal) { z_cpustates(); } else { putchar('\n'); } dostates = Yes; bzero(cp_old, sizeof(cp_old)); } /* display main memory statistics */ (*d_memory)( pagetok(total.t_rm), pagetok(total.t_arm), pagetok(total.t_vm), pagetok(total.t_avm), pagetok(total.t_free)); i = 0; if (topn > 0) { /* update the header area */ (*d_header)(uname_field); /* sort by cpu percentage (pctcpu) */ qsort(pref, active_procs, sizeof(struct proc *), proc_compar); /* adjust for a lack of processes */ if (active_procs > topn) { active_procs = topn; } /* * Now, show the top "n" processes. The method is slightly * different for dumb terminals, so we will just use two very * similar loops; this increases speed but also code size. */ if (smart_terminal) { for (prefp = pref, i = 0; i < active_procs; prefp++, i++) { pp = *prefp; (*d_process)(i, pp, get_userid); } } else for (prefp = pref, i = 0; i < active_procs; prefp++, i++) { pp = *prefp; /* (only one buffer lien with dumb terminals) */ (*d_process)(0, pp, get_userid); } } /* do end-screen processing */ u_endscreen(i); /* now, flush the output buffer */ fflush(stdout); /* only do the rest if we have more displays to show */ if (displays) { /* switch out for new display on smart terminals */ if (smart_terminal) { d_loadave = u_loadave; d_procstates = u_procstates; d_cpustates = u_cpustates; d_memory = u_memory; d_header = u_header; d_process = u_process; } #ifndef FOUR_ONE if (!interactive) #endif { /* set up alarm */ signal(SIGALRM, onalrm); alarm(delay); /* wait for the rest of it .... */ pause(); } #ifndef FOUR_ONE else { /* wait for either input or the end of the delay period */ readfds = 1; /* for standard input */ timeout.tv_sec = delay; timeout.tv_usec = 0; if (select(32, &readfds, 0, 0, &timeout) > 0) { int newval; char *errmsg; /* something to read -- clear the message area first */ if (msg_showing) { if (smart_terminal) { putcap(clear_line); } msg_showing = No; } /* now read it and act on it */ read(0, &ch, 1); switch(ch) { case '\f': /* redraw screen */ reset_display(); clear(); break; case ' ': /* merely update display */ break; case 'q': /* quit */ quit(0); break; case 'h': /* help */ case '?': reset_display(); clear(); show_help(); standout("Hit any key to continue: "); fflush(stdout); read(0, &ch, 1); clear(); break; case 'e': /* show errors */ if (error_count() == 0) { standout(" Currently no errors to report."); msg_showing = Yes; } else { reset_display(); clear(); show_errors(); standout("Hit any key to continue: "); fflush(stdout); read(0, &ch, 1); clear(); } break; case 'n': /* new number */ case '#': standout("Number of processes to show: "); newval = readline(tempbuf1, 8, Yes); putchar('\r'); if (newval > -1) { if (newval > (i = screen_length - Header_lines)) { standout( " This terminal can only display %d processes.", i); newval = i; msg_showing = Yes; break; } if (newval > topn) { /* zero fill appropriate part of screenbuf */ bzero(screenbuf[topn], (newval - topn) * Display_width); /* redraw header if need be */ if (topn == 0) { d_header = i_header; } } topn = newval; } putcap(clear_line); break; case 's': /* new seconds delay */ standout("Seconds to delay: "); if ((i = readline(tempbuf1, 8, Yes)) > -1) { delay = i; } putchar('\r'); putcap(clear_line); break; case 'd': /* change display count */ standout("Displays to show (currently %s): ", displays == -1 ? "infinite" : itoa(displays)); if ((i = readline(tempbuf1, 10, Yes)) > 0) { displays = i; } else if (i == 0) { quit(0); } putchar('\r'); putcap(clear_line); break; case 'k': /* kill program */ fputs("kill ", stdout); if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) { if ((errmsg = kill_procs(tempbuf2)) != NULL) { putchar('\r'); standout(errmsg); } msg_showing = Yes; } else { putchar('\r'); } putcap(clear_line); break; case 'r': /* renice program */ fputs("renice ", stdout); if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) { if ((errmsg = renice_procs(tempbuf2)) != NULL) { putchar('\r'); standout(errmsg); msg_showing = Yes; } } else { putchar('\r'); } putcap(clear_line); break; default: standout(" Command not understood"); msg_showing = Yes; } } else if (msg_showing) { if (smart_terminal) { putcap(clear_line); } msg_showing = No; } } #endif } } quit(0); } /* * reset_display() - reset all the display routine pointers so that entire * screen will get redrawn. */ reset_display() { d_loadave = i_loadave; d_procstates = i_procstates; d_cpustates = i_cpustates; d_memory = i_memory; d_header = i_header; d_process = i_process; } readline(buffer, size, numeric) char *buffer; int size; int numeric; { register char *ptr = buffer; register char ch; register char cnt = 0; size -= 1; while ((fflush(stdout), read(0, ptr, 1) > 0)) { if ((ch = *ptr) == '\n') { break; } if (ch == ch_kill) { *buffer = '\0'; return(-1); } else if (ch == ch_erase) { if (cnt <= 0) { putchar('\7'); } else { fputs("\b \b", stdout); ptr--; cnt--; } } else if (cnt == size || (numeric && (ch < '0' || ch > '9'))) { putchar('\7'); } else { putchar(ch); ptr++; cnt++; } } *ptr = '\0'; return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt); } /* * signal handlers */ leave() /* exit under normal conditions -- INT handler */ { end_screen(); exit(0); } tstop() { /* move to the lower left */ end_screen(); fflush(stdout); #ifdef FOUR_ONE /* a 4.1 system */ /* send a STOP (uncatchable) to everyone in the process group */ kill(0, SIGSTOP); /* reset the signal handler */ signal(SIGTSTP, tstop); #else /* assume it is a 4.2 system */ /* default the signal handler action */ signal(SIGTSTP, SIG_DFL); /* unblock the signal and send ourselves one */ sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1))); kill(0, SIGTSTP); /* reset the signal handler */ signal(SIGTSTP, tstop); #endif /* reinit screen */ reinit_screen(); /* jump to appropriate place */ longjmp(jmp_int, 1); /*NOTREACHED*/ } quit(status) /* exit under duress */ int status; { end_screen(); exit(status); } onalrm() { return(0); } /* * proc_compar - comparison function for "qsort" * Compares the resource consumption of two processes using five * distinct keys. The keys (in descending order of importance) are: * percent cpu, cpu ticks, state, resident set size, total virtual * memory usage. The process states are ordered as follows (from least * to most important): WAIT, zombie, sleep, stop, start, run. The * array declaration below maps a process state index into a number * that reflects this ordering. */ #ifdef MASSCOMP unsigned char sorted_state[] = { 6, /* awaiting memory */ 0, /* sleep */ 4, /* tired */ 2, /* idle */ 1, /* run */ 3, /* start */ 8, /* zombie */ 5, /* alert */ 7 /* stop */ }; #else unsigned char sorted_state[] = { 0, /* not used */ 3, /* sleep */ 1, /* ABANDONED (WAIT) */ 6, /* run */ 5, /* start */ 2, /* zombie */ 4 /* stop */ }; #endif proc_compar(pp1, pp2) struct proc **pp1; struct proc **pp2; { register struct proc *p1; register struct proc *p2; register int result; #ifndef sun register double dresult; #endif /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; /* compare percent cpu (pctcpu) */ #ifdef sun if ((result = p2->p_pctcpu - p1->p_pctcpu) == 0) #else if ((dresult = p2->p_pctcpu - p1->p_pctcpu) == 0) #endif { /* use cpticks to break the tie */ if ((result = p2->p_cpticks - p1->p_cpticks) == 0) { /* use process state to break the tie */ #ifdef MASSCOMP if ((result = sorted_state[bitmask(p2->p_stat)] - sorted_state[bitmask(p1->p_stat)]) == 0) #else if ((result = sorted_state[p2->p_stat] - sorted_state[p1->p_stat]) == 0) #endif { /* use priority to break the tie */ if ((result = p2->p_pri - p1->p_pri) == 0) { /* use resident set size (rssize) to break the tie */ if ((result = p2->p_rssize - p1->p_rssize) == 0) { /* use total memory to break the tie */ #ifdef pyr result = (p2->p_tsize + p2->p_dsize + p2->p_ussize) - (p1->p_tsize + p1->p_dsize + p1->p_ussize); #else result = (p2->p_tsize + p2->p_dsize + p2->p_ssize) - (p1->p_tsize + p1->p_dsize + p1->p_ssize); #endif } } } } } #ifndef sun else { result = dresult < 0.0 ? -1 : 1; } #endif return(result); } /* routines to translate uids into a string */ char *user_name(euid, ruid) int euid, ruid; { return(username(euid)); } char *user_uid(euid, ruid) int euid, ruid; { return(itoa7(euid)); } /* * These routines handle uid to username mapping. * They use a hashing table scheme to reduce reading overhead. */ struct hash_el { int uid; char name[8]; }; #define H_empty -1 /* simple minded hashing function */ #define hashit(i) ((i) % Table_size) struct hash_el hash_table[Table_size]; init_hash() { register int i; register struct hash_el *h; for (h = hash_table, i = 0; i < Table_size; h++, i++) { h->uid = H_empty; } } char *username(uid) register int uid; { register int index; register int found; register char *name; /* This is incredibly naive, but it'll probably get changed anyway */ index = hashit(uid); while ((found = hash_table[index].uid) != uid) { if (found == H_empty) { /* not here -- get it out of passwd */ index = get_user(uid); break; /* out of while */ } index = (index + 1) % Table_size; } return(hash_table[index].name); } enter_user(uid, name) register int uid; register char *name; { register int length; register int index; register int try; static int uid_count = 0; /* avoid table overflow -- insure at least one empty slot */ if (++uid_count >= Table_size) { fprintf(stderr, "table overflow: too many users\n"); quit(10); } index = hashit(uid); while ((try = hash_table[index].uid) != H_empty) { if (try == uid) { return(index); } index = (index + 1) % Table_size; } hash_table[index].uid = uid; strncpy(hash_table[index].name, name, 8); return(index); } get_user(uid) register int uid; { struct passwd *pwd; register int last_index; while ((pwd = getpwent()) != NULL) { last_index = enter_user(pwd->pw_uid, pwd->pw_name); if (pwd->pw_uid == uid) { return(last_index); } } return(enter_user(uid, itoa7(uid))); } atoiwi(str) char *str; { register int len; len = strlen(str); if (len != 0) { if (strncmp(str, "infinity", len) == 0 || strncmp(str, "all", len) == 0 || strncmp(str, "maximum", len) == 0) { return(Infinity); } else if (str[0] == '-') { return(Invalid); } else { return(atoi(str)); } } return(0); } /* * itoa - convert integer (decimal) to ascii string for positive numbers * only (we don't bother with negative numbers since we know we * don't use them). */ static char buffer[16]; /* shared by the next two routines */ char *itoa(val) register int val; { register char *ptr; ptr = buffer + sizeof(buffer); *--ptr = '\0'; if (val == 0) { *--ptr = '0'; } else while (val != 0) { *--ptr = (val % 10) + '0'; val /= 10; } return(ptr); } /* * itoa7(val) - like itoa, except the number is right justified in a 7 * character field. This code is a duplication of itoa instead of * a front end to a more general routine for efficiency. */ char *itoa7(val) register int val; { register char *ptr; ptr = buffer + sizeof(buffer); *--ptr = '\0'; if (val == 0) { *--ptr = '0'; } else while (val != 0) { *--ptr = (val % 10) + '0'; val /= 10; } while (ptr > buffer + sizeof(buffer) - 7) { *--ptr = ' '; } return(ptr); } #ifdef MASSCOMP getmem(val) struct vmtotal * val; { int nswap; int freemem; int physmem; getkval(nlst[X_NSWAP].n_value, &nswap, sizeof(int), nlst[X_NSWAP].n_name); getkval(nlst[X_FREEMEM].n_value, &freemem, sizeof(int), nlst[X_FREEMEM].n_name); getkval(nlst[X_PHYSMEM].n_value, &physmem, sizeof(int), nlst[X_PHYSMEM].n_name); val->t_free = freemem; /* free memory pages */ /* this does not work for some reason */ val->t_rm = physmem; /* total real memory in use */ val->t_arm= 0; /* active real memory */ val->t_vm = dtoc(nswap); /* total virtual memory */ val->t_avm = 0; /* active virtual memory */ } #endif SHAR_EOF fi echo shar: "extracting 'commands.c'" '(8905 characters)' if test -f 'commands.c' then echo shar: "will not over-write existing file 'commands.c'" else cat << \SHAR_EOF > 'commands.c' /* * Top users display for Berkeley Unix * * This file contains the routines that implement some of the interactive * mode commands. Note that some of the commands are implemented in-line * in "main". This is necessary because they change the global state of * "top" (i.e.: changing the number of processes to display). */ #include <stdio.h> #include <ctype.h> #include <signal.h> #include <sys/time.h> #include <sys/resource.h> #include "sigdesc.h" /* generated automatically */ #include "boolean.h" extern int errno; extern int sys_nerr; extern char *sys_errlist[]; extern char *copyright; int err_compar(); char *err_string(); char *index(); /* * show_help() - display the help screen; invoked in response to * either 'h' or '?'. */ show_help() { fputs(copyright, stdout); fputs("\n\n\ A top users display for Unix\n\ \n\ These single-character commands are available:\n\ \n\ ^L - redraw screen\n\ q - quit\n\ h or ? - help; show this text\n\ d - change number of displays to show\n\ e - list errors generated by last \"kill\" or \"renice\" command\n\ k - kill processes; send a signal to a list of processes\n\ n or # - change number of processes to display\n\ r - renice a process\n\ s - change number of seconds to delay between updates\n\ \n\ \n", stdout); } /* * Utility routines that help with some of the commands. */ char *next_field(str) register char *str; { register char *temp; if ((str = index(str, ' ')) == NULL) { return(NULL); } *str = '\0'; while (*++str == ' ') /* loop */; return(str); } scanint(str, intp) char *str; int *intp; { register int val = 0; register char ch; while ((ch = *str++) != '\0') { if (isdigit(ch)) { val = val * 10 + (ch - '0'); } else if (isspace(ch)) { break; } else { return(-1); } } *intp = val; return(0); } /* * Some of the commands make system calls that could generate errors. * These errors are collected up in an array of structures for later * contemplation and display. Such routines return a string containing an * error message, or NULL if no errors occurred. The next few routines are * for manipulating and displaying these errors. We need an upper limit on * the number of errors, so we arbitrarily choose 20. */ #define ERRMAX 20 struct errs /* structure for a system-call error */ { int errno; /* value of errno (that is, the actual error) */ char *arg; /* argument that caused the error */ }; static struct errs errs[ERRMAX]; static int errcnt; static char *err_toomany = " too many errors occurred"; static char *err_listem = " Many errors occurred. Press `e' to display the list of errors."; /* These macros get used to reset and log the errors */ #define ERR_RESET errcnt = 0 #define ERROR(p, e) if (errcnt >= ERRMAX) \ { \ return(err_toomany); \ } \ else \ { \ errs[errcnt].arg = (p); \ errs[errcnt++].errno = (e); \ } /* * err_string() - return an appropriate error string. This is what the * command will return for displaying. If no errors were logged, then * return NULL. The maximum length of the error string is defined by * "STRMAX". */ #define STRMAX 80 char *err_string() { register char *ptr; register struct errs *errp; register int cnt = 0; register int first = Yes; register int currerr = -1; int stringlen; /* characters still available in "string" */ char string[STRMAX]; /* if there are no errors, return NULL */ if (errcnt == 0) { return(NULL); } /* sort the errors */ qsort(errs, errcnt, sizeof(struct errs), err_compar); /* need a space at the from of the error string */ string[0] = ' '; string[1] = '\0'; stringlen = STRMAX - 2; /* loop thru the sorted list, building an error string */ ptr = string; while (cnt < errcnt) { errp = &(errs[cnt++]); if (errp->errno != currerr) { if (currerr != -1) { if ((stringlen = str_adderr(string, stringlen, currerr)) < 2) { return(err_listem); } strcat(string, "; "); /* we know there's more */ } currerr = errp->errno; first = Yes; } if ((stringlen = str_addarg(string, stringlen, errp->arg, first)) ==0) { return(err_listem); } first = No; } /* add final message */ stringlen = str_adderr(string, stringlen, currerr); /* return the error string */ return(stringlen == 0 ? err_listem : string); } /* * str_adderr(str, len, err) - add an explanation of error "err" to * the string "str". */ str_adderr(str, len, err) char *str; int len; int err; { register char *msg; register int msglen; msg = err == 0 ? "Not a number" : sys_errlist[err]; msglen = strlen(msg) + 2; if (len <= msglen) { return(0); } strcat(str, ": "); strcat(str, msg); return(len - msglen); } /* * str_addarg(str, len, arg, first) - add the string argument "arg" to * the string "str". This is the first in the group when "first" * is set (indicating that a comma should NOT be added to the front). */ str_addarg(str, len, arg, first) char *str; int len; char *arg; int first; { register int arglen; arglen = strlen(arg); if (!first) { arglen += 2; } if (len <= arglen) { return(0); } if (!first) { strcat(str, ", "); } strcat(str, arg); return(len - arglen); } /* * err_compar(p1, p2) - comparison routine used by "qsort" * for sorting errors. */ err_compar(p1, p2) register struct errs *p1, *p2; { register int result; if ((result = p1->errno - p2->errno) == 0) { return(strcmp(p1->arg, p2->arg)); } return(result); } /* * error_count() - return the number of errors currently logged. */ error_count() { return(errcnt); } /* * show_errors() - display on stdout the current log of errors. */ show_errors() { register int cnt = 0; register struct errs *errp = errs; printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s"); while (cnt++ < errcnt) { printf("%5s: %s\n", errp->arg, errp->errno == 0 ? "Not a number" : sys_errlist[errp->errno]); errp++; } } /* * kill_procs(str) - send signals to processes, much like the "kill" * command does; invoked in response to 'k'. */ char *kill_procs(str) char *str; { register char *nptr; register char *optr; int signum = SIGTERM; /* default */ int procnum; char badnum = 0; struct sigdesc *sigp; ERR_RESET; if (str[0] == '-') { /* explicit signal specified */ if ((optr = nptr = next_field(str)) == NULL) { return(" kill: no processes specified"); } if (isdigit(str[1])) { scanint(str + 1, &signum); if (signum <= 0 || signum >= NSIG) { return(" invalid signal number"); } } else { /* terminate the end of the signal name */ while (*--optr == ' '); *++optr = '\0'; /* translate the name into a number */ for (sigp = sigdesc; sigp->name != NULL; sigp++) { if (strcmp(sigp->name, str + 1) == 0) { signum = sigp->number; break; } } /* was it ever found */ if (sigp->name == NULL) { return(" bad signal name"); } } /* put the new pointer in place */ str = nptr; } /* loop thru the string, killing processes */ do { if (scanint(str, &procnum) == -1) { ERROR(str, 0); } else if (kill(procnum, signum) == -1) { /* chalk up an error */ ERROR(str, errno); } } while ((str = next_field(str)) != NULL); /* return appropriate error string */ return(err_string()); } /* * renice_procs(str) - change the "nice" of processes, much like the * "renice" command does; invoked in response to 'r'. */ char *renice_procs(str) char *str; { register char negate; int prio; int procnum; ERR_RESET; /* allow for negative priority values */ if ((negate = *str == '-')) { /* move past the minus sign */ str++; } /* use procnum as a temporary holding place and get the number */ procnum = scanint(str, &prio); /* negate if necessary */ if (negate) { prio = -prio; } /* check for validity */ if (procnum == -1 || prio <= PRIO_MIN || prio >= PRIO_MAX) { return(" bad priority value"); } /* move to the first process number */ if ((str = next_field(str)) == NULL) { return(" no processes specified"); } /* loop thru the process numbers, renicing each one */ do { if (scanint(str, &procnum) == -1) { ERROR(str, 0); } else if (setpriority(PRIO_PROCESS, procnum, prio) == -1) { ERROR(str, errno); } } while ((str = next_field(str)) != NULL); /* return appropriate error string */ return(err_string()); } SHAR_EOF fi echo shar: "extracting 'display.c'" '(12497 characters)' if test -f 'display.c' then echo shar: "will not over-write existing file 'display.c'" else cat << \SHAR_EOF > 'display.c' /* * Top - a top users display for Berkeley Unix * * This file contains the routines that display information on the screen. * Each section of the screen has two routines: one for initially writing * all constant and dynamic text, and one for only updating the text that * changes. The prefix "i_" is used on all the "initial" routines and the * prefix "u_" is used for all the "updating" routines. NOTE: it is * assumed that none of the "i_" routines use any of the termcap * capabilities. In this way, those routines can be safely used on * terminals that have minimal (or nonexistant) terminal capabilities. */ #include <stdio.h> #include <ctype.h> #include <sys/param.h> #include <sys/dir.h> #include "masscomp.h" #include <sys/user.h> #include <sys/proc.h> #ifndef MASSCOMP #include <sys/dk.h> #endif #include "screen.h" /* interface to screen package */ #include "layout.h" /* defines for screen position layout */ #include "top.h" #include "boolean.h" static int lmpid = 0; static struct user u; char *printable(); /* Verbose process state names */ #ifdef MASSCOMP char *state_name[] = { "sleeping", "running", "idle", "runnable","tired", "alert", "awaiting memory", "stopped", "zombie" }; #else char *state_name[] = { "", "sleeping", "ABANDONED", "running", "starting", "zombie", "stopped" }; #endif /* process state names for the "STATE" column of the display */ #ifdef MASSCOMP char *state_abbrev[] = { "sleep", "run", "idle", "avail","tired", "alert", "mem", "stop", "zomb" }; #else char *state_abbrev[] = { "", "sleep", "WAIT", "run", "start", "zomb", "stop" }; #endif /* cpu state names for percentages */ char *cpu_state[] = { "user", "nice", "system", "idle" }; /* screen positions for cpustate figures */ char x_cpustates[] = { 12, 24, 36, 50 }; i_loadave(mpid, avenrun) int mpid; #ifdef sun long *avenrun; #else double *avenrun; #endif sun { register int i; printf("last pid: %5d; load averages", mpid); for (i = 0; i < 3; i++) { printf("%c %4.2f", i == 0 ? ':' : ',', #ifdef sun (double)avenrun[i] / FSCALE); #else avenrun[i]); #endif } lmpid = mpid; } u_loadave(mpid, avenrun) int mpid; #ifdef sun long *avenrun; #else double *avenrun; #endif sun { register int i; if (mpid != lmpid); { Move_to(x_lastpid, y_lastpid); printf("%5d", mpid); lmpid = mpid; } Move_to(x_loadave, y_loadave); for (i = 0; i < 3; i++) { printf("%s%4.2f", i == 0 ? "" : ", ", #ifdef sun (double)avenrun[i] / FSCALE); #else avenrun[i]); #endif } } static int ltotal = 0; #ifdef MASSCOMP static int lbrkdn[9]; #else static int lbrkdn[7]; #endif i_procstates(total, brkdn) int total; int *brkdn; { register int i; printf("%2d processes", total); /* ??? */ ltotal = total; #ifdef MASSCOMP for (i = 0; i < 9; i++) #else for (i = 1; i < 7; i++) #endif { if (brkdn[i] != 0) { printf("%c %d %s%s", i == 1 ? ':' : ',', brkdn[i], state_name[i], (i == SZOMB) && (brkdn[i] > 1) ? "s" : ""); } } bcopy(brkdn, lbrkdn, sizeof(lbrkdn)); } u_procstates(total, brkdn) int total; int *brkdn; { register int i; if (ltotal != total) { Move_to(x_procstate, y_procstate); printf("%d ", total); ltotal = total; } else if (bcmp(brkdn, lbrkdn, sizeof(lbrkdn)) == 0) { return; } Move_to(x_brkdn, y_brkdn); #ifdef MASSCOMP for (i = 0; i < 9; i++) #else for (i = 1; i < 7; i++) #endif { if (brkdn[i] != 0) { printf("%s%d %s%s", i == 1 ? ':' : ',', brkdn[i], state_name[i], (i == SZOMB) && (brkdn[i] > 1) ? "s" : ""); } } putcap(clear_line); bcopy(brkdn, lbrkdn, sizeof(lbrkdn)); } i_cpustates(changes, total) int *changes; int total; { register int i; printf("\nCpu states: "); if (total){ for (i = 0; i < CPUSTATES; i++) { printf("%s%4.1f%% %s", i == 0 ? "" : ", ", ((float)changes[i] / (float)total) * 100.0, cpu_state[i]); } } printf("\n"); } u_cpustates(changes, total) int *changes; int total; { register int i; if (total){ for (i = 0; i < CPUSTATES; i++) { Move_to(x_cpustates[i], y_cpustates); printf("%4.1f", ((float)changes[i] / (float)total) * 100.0); } } } z_cpustates() { register int i; printf("\nCpu states: "); for (i = 0; i < CPUSTATES; i++) { printf("%s %% %s", i == 0 ? "" : ", ", cpu_state[i]); } printf("\n"); } i_memory(i1, i2, i3, i4, i5) int i1, i2, i3, i4, i5; { printf("Memory: %4dK (%4dK) real, %5dK (%5dK) virtual, %4dK free", i1, i2, i3, i4, i5); } u_memory(i1, i2, i3, i4, i5) int i1, i2, i3, i4, i5; { Move_to(x_realmem, y_mem); printf("%4dK (%4d", i1, i2); Move_to(x_virtmem, y_mem); printf("%5dK (%5d", i3, i4); Move_to(x_free, y_mem); printf("%4d", i5); } i_header(f2) char *f2; { printf( "\n\n PID %s PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND", f2); } u_header() { Move_to(0, y_header); } #ifdef sun #define percent_cpu(pp) ((double)(pp)->p_pctcpu / FSCALE) #else #define percent_cpu(pp) ((pp)->p_pctcpu) #endif #define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \ ((pct) / (1.0 - exp((pp)->p_time * logcpu)))) #define Proc_format "%5d %-8.8s %3d %4d%6dK %4dK %-5s%4d:%02d %5.2f%% %5.2f%% %.14s" #ifdef DEBUG FILE *debug; #endif i_process(line, pp, get_userid) int line; struct proc *pp; char *(*get_userid)(); { register long cputime; register double pctcpu; register char *thisline; int len; #ifdef DEBUG debug = fopen("debug", "w"); #endif /* calculate a pointer to the buffer for this line */ thisline = screenbuf[line]; /* get the cpu usage and calculate the cpu percentages */ cputime = get_ucpu(pp); #ifdef MASSCOMP if (cputime == 0) cputime = (pp->p_ru.ru_utime.tv_sec + pp->p_ru.ru_stime.tv_sec); #endif pctcpu = percent_cpu(pp); /* format the line */ sprintf(thisline, Proc_format, pp->p_pid, (*get_userid)(pp->p_uid), pp->p_pri - PZERO, pp->p_nice - NZERO, #ifdef pyr pagetok(pp->p_tsize + pp->p_dsize + pp->p_cssize + pp->p_ussize), #else pagetok(pp->p_tsize + pp->p_dsize + pp->p_ssize), #endif pagetok(pp->p_rssize), #ifdef MASSCOMP state_abbrev[bitmask(pp->p_stat)], #else state_abbrev[pp->p_stat], #endif cputime / 60l, cputime % 60l, #ifdef MASSCOMP weighted_cpu(pctcpu,pp), pctcpu, #else 100.0 * weighted_cpu(pctcpu, pp), 100.0 * pctcpu, #endif printable(u.u_comm)); /* write the line out */ putchar('\n'); fputs(thisline, stdout); /* zero fill the rest of it */ len = strlen(thisline); bzero(thisline + len, Display_width - len); } static int lastline = 0; u_process(line, pp, get_userid) int line; struct proc *pp; char *(*get_userid)(); { register char *optr; register char *nptr; register int ch; register int diff; register int newcol = 1; register int lastcol = 0; register long cputime; register double pctcpu; char cursor_on_line = No; char *thisline; int screen_line = line + Header_lines; static char newline[Display_width]; /* get a pointer to the old text for this line */ optr = thisline = screenbuf[line]; /* get the cpu usage and calculate the cpu percentages */ cputime = get_ucpu(pp); pctcpu = percent_cpu(pp); /* format the line */ sprintf(newline, Proc_format, pp->p_pid, (*get_userid)(pp->p_uid), pp->p_pri - PZERO, pp->p_nice - NZERO, #ifdef pyr pagetok(pp->p_tsize + pp->p_dsize + pp->p_cssize + pp->p_ussize), #else pagetok(pp->p_tsize + pp->p_dsize + pp->p_ssize), #endif pagetok(pp->p_rssize), #ifdef MASSCOMP state_abbrev[bitmask(pp->p_stat)], #else state_abbrev[pp->p_stat], #endif cputime / 60l, cputime % 60l, #ifdef MASSCOMP weighted_cpu(pctcpu,pp), pctcpu, #else 100.0 * weighted_cpu(pctcpu, pp), 100.0 * pctcpu, #endif printable(u.u_comm)); /* compare the two strings and only rewrite what has changed */ nptr = newline; #ifdef DEBUG fputs(optr, debug); fputc('\n', debug); fputs(nptr, debug); fputs("\n-\n", debug); #endif /* start things off on the right foot */ /* this is to make sure the invariants get set up right */ if ((ch = *nptr++) != *optr) { if (screen_line - lastline == 1) { putchar('\n'); } else { Move_to(0, screen_line); } cursor_on_line = Yes; putchar(ch); *optr = ch; lastcol = 1; } optr++; /* * main loop -- check each character. If the old and new aren't the * same, then update the display. When the distance from the current * cursor position to the new change is small enough, the characters * that belong there are written to move the cursor over. * * Invariants: * lastcol is the column where the cursor currently is sitting * (always one beyond the end of the last mismatch). */ do /* yes, a do...while */ { if ((ch = *nptr++) != *optr) { /* new character is different from old */ /* put the new character in the screen buffer */ *optr = ch; /* make sure the cursor is on top of this character */ diff = newcol - lastcol; if (diff > 0) { /* some motion is required--figure out which is shorter */ if (diff < 6 && cursor_on_line) { /* overwrite old stuff--get it out of the screen buffer */ printf("%.*s", diff, &thisline[lastcol]); } else { /* use cursor addressing */ Move_to(newcol, screen_line); cursor_on_line = Yes; } /* remember where the cursor is */ lastcol = newcol + 1; } else { /* already there, update position */ lastcol++; } /* write what we need to */ if (ch == '\0') { /* at the end--terminate with a clear-to-end-of-line */ putcap(clear_line); } else { /* write the new character */ putchar(ch); } } /* update working column and screen buffer pointer */ newcol++; optr++; } while (ch != '\0'); /* zero out the rest of the line buffer -- MUST BE DONE! */ bzero(optr, Display_width - newcol); /* remember where the current line is */ if (cursor_on_line) { lastline = screen_line; } } static int last_hi = 0; u_endscreen(hi) register int hi; { register int screen_line = hi + Header_lines; if (smart_terminal) { if (hi < last_hi) { if (hi == 0) { putchar('\n'); putchar('\n'); putcap(clear_line); putchar('\n'); } else if (screen_line - lastline == 1) { putchar('\n'); } else { Move_to(0, screen_line); } while (--last_hi > hi) { putcap(clear_line); putchar('\n'); } putcap(clear_line); } else { last_hi = hi; } /* move the cursor to a pleasant place */ Move_to(x_idlecursor, y_idlecursor); } else { /* separate this display from the next with some vertical room */ fputs("\n\n", stdout); } } /* * get_ucpu(pp) - retrieve the user structure associated with the proc * structure pointed to by pp and return the cpu usage. The user * structure is stored in the global structure "u" for later use. */ get_ucpu(pp) struct proc *pp; { if (getu(pp, &u) == -1) { strcpy(u.u_comm, "<swapped>"); return(0); } else { /* set u_comm for system processes */ if (u.u_comm[0] == '\0') { if (pp->p_pid == 0) { strcpy(u.u_comm, "Swapper"); } else if (pp->p_pid == 2) { strcpy(u.u_comm, "Pager"); } } #ifdef FOUR_ONE return((int)((float)(u.u_vm.vm_utime + u.u_vm.vm_stime)/hz)); #else return(u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec); #endif } } /* * printable(str) - make the string pointed to by "str" into one that is * printable (i.e.: all ascii), by converting all non-printable * characters into '?'. Replacements are done in place and a pointer * to the original buffer is returned. */ char *printable(str) char *str; { register char *ptr; register char ch; ptr = str; while ((ch = *ptr) != '\0') { if (!isprint(ch)) { *ptr = '?'; } ptr++; } return(str); } #ifdef MASSCOMP /* * bitmask returns an integer representing the first bit (from the right) * detected on in the passed arguement... */ bitmask(x) int x; { int i,j; i = 1; for (j=0;j<8;j++) { if (x & i<<j) return(j); } } SHAR_EOF fi echo shar: "extracting 'kernel.c'" '(3138 characters)' if test -f 'kernel.c' then echo shar: "will not over-write existing file 'kernel.c'" else cat << \SHAR_EOF > 'kernel.c' /* * Top - a top users display for Berkeley Unix * * This file contains all the routines that retrieve values from * kernel and user memory. */ #include <stdio.h> #include <sys/param.h> #include <sys/dir.h> #include "masscomp.h" #ifndef MASSCOMP #if defined(FOUR_ONE) || defined(pyr) #include <sys/pte.h> #else #include <machine/pte.h> #endif #endif #include <sys/user.h> #include <sys/proc.h> #include "top.local.h" /* useful externals */ extern int errno; extern char *sys_errlist[]; static int kmem = -1; static int mem = -1; init_kernel() { /* open kmem and mem */ if ((kmem = open(KMEM, 0)) < 0) { perror(KMEM); exit(20); } if ((mem = open(MEM, 0)) < 0) { perror(MEM); exit(21); } } /* * getu(p, u) - get the user structure for the process whose proc structure * is pointed to by p. The user structure is put in the buffer pointed * to by u. Return 0 if successful, -1 on failure (such as the process * being swapped out). */ getu(p, u) register struct proc *p; struct user *u; { struct pte uptes[UPAGES]; register caddr_t upage; register struct pte *pte; register nbytes, n; /* * Check if the process is currently loaded or swapped out. The way we * get the u area is totally different for the two cases. For this * application, we just don't bother if the process is swapped out. */ if ((p->p_flag & SLOAD) == 0) { return(-1); } /* * Process is currently in memory, we hope! */ if (!getkval(p->p_addr, uptes, sizeof(uptes), "!p->p_addr")) { /* we can't seem to get to it, so pretend it's swapped out */ return(-1); } upage = (caddr_t)u; pte = uptes; for (nbytes = sizeof(struct user); nbytes > 0; nbytes -= NBPG) { lseek(mem, pte++->pg_pfnum * NBPG, 0); n = MIN(nbytes, NBPG); if (read(mem, upage, n) != n) { /* we can't seem to get to it, so pretend it's swapped out */ return(-1); } upage += n; } return(0); } /* * getkval(offset, ptr, size, refstr) - get a value out of the kernel. * "offset" is the byte offset into the kernel for the desired value, * "ptr" points to a buffer into which the value is retrieved, * "size" is the size of the buffer (and the object to retrieve), * "refstr" is a reference string used when printing error meessages, * if "refstr" starts with a '!', then a failure on read will not * be fatal (this may seem like a silly way to do things, but I * really didn't want the overhead of another argument). * */ getkval(offset, ptr, size, refstr) long offset; int *ptr; int size; char *refstr; { if (lseek(kmem, offset, 0) == -1) { if (*refstr == '!') { refstr++; } fprintf(stderr, "%s: lseek to %s: %s\n", KMEM, refstr, sys_errlist[errno]); quit(22); } if (read(kmem, ptr, size) == -1) { if (*refstr == '!') { /* we lost the race with the kernel, process isn't in memory */ return(0); } else { fprintf(stderr, "%s: reading %s: %s\n", KMEM, refstr, sys_errlist[errno]); quit(23); } } return(1); } SHAR_EOF fi echo shar: "extracting 'screen.c'" '(4485 characters)' if test -f 'screen.c' then echo shar: "will not over-write existing file 'screen.c'" else cat << \SHAR_EOF > 'screen.c' /* * Top - a top users display for Berkeley Unix * * This file contains the routines that interface to termcap and stty/gtty. */ #include <stdio.h> #include <sgtty.h> #include "screen.h" #include "boolean.h" extern char *myname; int putstdout(); int scrolls; int hardcopy; int screen_length; int screen_width; char ch_erase; char ch_kill; char smart_terminal; char PC; char *tgetstr(); char *tgoto(); char termcap_buf[1024]; char init_buf[1024]; char string_buffer[1024]; char home[15]; char lower_left[15]; char *clear_line; char *clear_screen; char *cursor_motion; char *start_standout; char *end_standout; char *terminal_init; char *terminal_end; short ospeed; static struct sgttyb old_settings; static struct sgttyb new_settings; static char is_a_terminal = No; init_termcap() { char *bufptr; char *PCptr; char *term_name; char *temp_ptr; char *getenv(); int status; /* assume we have a smart terminal until proven otherwise */ smart_terminal = Yes; /* now get terminal name and termcap entry */ term_name = getenv("TERM"); if ((status = tgetent(termcap_buf, term_name)) != 1) { if (status == -1) { fprintf(stderr, "%s: can't open termcap file\n", myname); } else { fprintf(stderr, "%s: no termcap entry for a `%s' terminal\n", myname, getenv("TERM")); } /* pretend it's dumb and proceed */ smart_terminal = No; return; } /* these immediately indicate a very stupid terminal */ if (tgetflag("hc") || tgetflag("os")) { smart_terminal = No; return; } /* set up common terminal capabilities */ if ((screen_length = tgetnum("li")) <= 0) { screen_length = smart_terminal = 0; return; } /* screen_width is a little different */ if ((screen_width = tgetnum("co")) == -1) { screen_width = 79; } else { screen_width -= 1; } /* initialize the pointer into the termcap string buffer */ bufptr = string_buffer; /* get necessary capabilities */ if ((clear_line = tgetstr("ce", &bufptr)) == NULL || (clear_screen = tgetstr("cl", &bufptr)) == NULL || (cursor_motion = tgetstr("cm", &bufptr)) == NULL) { smart_terminal = No; return; } /* get some more sophisticated stuff -- these are optional */ terminal_init = tgetstr("ti", &bufptr); terminal_end = tgetstr("te", &bufptr); start_standout = tgetstr("so", &bufptr); end_standout = tgetstr("se", &bufptr); /* pad character */ PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0; /* set convenience strings */ strcpy(home, tgoto(cursor_motion, 0, 0)); strcpy(lower_left, tgoto(cursor_motion, 0, screen_length - 1)); /* if stdout is not a terminal, pretend we are a dumb terminal */ if (gtty(1, &old_settings) == -1) { smart_terminal = No; } } init_screen() { /* get the old settings for safe keeping */ if (gtty(1, &old_settings) == 0) { /* copy the settings so we can modify them */ new_settings = old_settings; /* turn on CBREAK and turn off character echo and tab expansion */ new_settings.sg_flags |= CBREAK; new_settings.sg_flags &= ~(ECHO|XTABS); stty(1, &new_settings); /* remember the erase and kill characters */ ch_erase = old_settings.sg_erase; ch_kill = old_settings.sg_kill; /* remember that it really is a terminal */ is_a_terminal = Yes; /* send the termcap initialization string */ putcap(terminal_init); } else { /* not a terminal at all---consider it dumb */ smart_terminal = No; } } end_screen() { /* move to the lower left, clear the line and send "te" */ if (smart_terminal) { putcap(lower_left); putcap(clear_line); putcap(terminal_end); } /* if we have settings to reset, then do so */ if (is_a_terminal) { stty(1, &old_settings); } } reinit_screen() { /* install our settings if it is a terminal */ if (is_a_terminal) { stty(1, &new_settings); } /* send init string */ if (smart_terminal) { putcap(terminal_init); } } standout(fmt, a1, a2, a3) char *fmt; int a1, a2, a3; { if (smart_terminal) { putcap(start_standout); printf(fmt, a1, a2, a3); putcap(end_standout); } else { printf(fmt, a1, a2, a3); } } clear() { if (smart_terminal) { putcap(clear_screen); } } /* This has to be defined as a subroutine for tputs (instead of a macro) */ putstdout(ch) char ch; { putchar(ch); } SHAR_EOF fi exit 0 # End of shell archive