/* * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include #include "top.h" /* Default signal and signal name. */ #define DISP_SIG SIGTERM #define DISP_SIGNAME "TERM" /* Value to ungetch() when a signal should cause resize. */ #define DISP_KEY_RESIZE (KEY_MAX + 1) /* Value to ungetch() when a signal should cause shutdown. */ #define DISP_KEY_EXIT (KEY_MAX + 2) /* * Buffer large enough to hold a full line of text, plus a '\0'. This is * dynamically resized as necessary. */ static char *disp_lbuf; /* * Buffer for interactive command line large enough to hold a full line of text, * plus a '\0'. This is dynamically resized as necessary (same as disp_lbuf). */ static char *disp_sbuf; /* Size of disp_lbuf and disp_sbuf. */ static int disp_bufsize; /* * Buffer large enough to hold any of the following, plus a '\0': * * + mode (1) * + number of processes (13) * + sort key (6) * + signal name (6) * + signal number (13) * + pid (9) * + update interval (13) * + username (8) * + uid (13) */ static char disp_ibuf[17]; /* Default signal number and name. */ static int disp_sig; static const char *disp_signame; /* Main window and panel. */ static WINDOW *disp_dwin; static PANEL *disp_dpan; /* * Line on which to print next. If the line is outside the range displayable, * output is ignored. This is done in order to keep the sampling code from * having to understand the constraints of the terminal size. * * Trickery is necessary when printing to the interactive command line and when * printing the help screen, since these operations involve non-linear printing. */ static unsigned disp_curline; /* * Minimum number of seconds to leave the contents of the interaction line * displayed before clearing it. This is only a lower bound; if the sample * update delay is longer, then the text will not be cleared until the next * sample update. */ #define DISP_ILINE_CLEAR_DELAY 1 /* Line on which interactive prompts and messages are displayed. */ static int disp_iline; static struct timeval disp_ilinetime; static boolean_t disp_ilineclear; static boolean_t disp_p_skipl(void); static boolean_t disp_p_printl(const char *a_format, ...); static boolean_t disp_p_println(const char *a_format, ...); static boolean_t disp_p_vprintln(boolean_t a_newline, const char *a_format, va_list a_p); static boolean_t disp_p_wprintl(WINDOW *a_window, const char *a_format, ...); static boolean_t disp_p_vwprintln(WINDOW *a_window, boolean_t a_newline, const char *a_format, va_list a_p); static boolean_t disp_p_mvwprintl(WINDOW *a_window, int a_y, int a_x, const char *a_format, ...); static boolean_t disp_p_mvwprintln(WINDOW *a_window, int a_y, int a_x, const char *a_format, ...); static boolean_t disp_p_vmvwprintln(WINDOW *a_window, boolean_t a_newline, int a_y, int a_x, const char *a_format, va_list a_p); static const char * disp_p_iline_prompt(const char *a_format, ...); static boolean_t disp_p_iline_set(const char *a_format, ...); static boolean_t disp_p_iline_eset(boolean_t a_newline, const char *a_format, va_list a_p); static boolean_t disp_p_iline_vset(const char *a_format, va_list a_p); static boolean_t disp_p_init(void); static boolean_t disp_p_fini(void); static void disp_p_shutdown(int a_signal); static boolean_t disp_p_help(void); static void disp_p_sigwinch(int a_signal); static boolean_t disp_p_resize(void); static boolean_t disp_p_interp_c(void); static boolean_t disp_p_interp_ns(const char *a_name, int *r_int); static boolean_t disp_p_interp_Oo(const char *a_name, top_sort_key_t *r_key, boolean_t *r_ascend); static boolean_t disp_p_interp_S(void); static boolean_t disp_p_interp_U(void); /* Main entry point for the interactive display. */ boolean_t disp_run(void) { boolean_t retval; int c, i, y, x, ysize, xsize; struct timeval curtime; if (disp_p_init() || samp_init(disp_p_skipl, disp_p_printl, disp_p_println, disp_p_vprintln, disp_p_iline_eset)) { retval = TRUE; goto RETURN; } /* * Get the current time for the first redisplay, since gettimeofday() is * called immediatly after wgetch(), so that it is up to date for the * input processing code. */ gettimeofday(&curtime, NULL); for (;;) { #ifdef TOP_DEPRECATED /* * Short circuit commands that were not supported in the old top * if deprecated mode is enabled. */ AGAIN: #endif /* If detached, quit */ if (!isatty(0)) exit(1); /* Take a sample and print it. */ disp_curline = 0; if (wmove(disp_dwin, 0, 0) == ERR || samp_run()) { retval = TRUE; goto RETURN; } /* * Clear lines between the current position and the end of the * screen, excluding the interactive command line. */ getmaxyx(disp_dwin, ysize, xsize); getyx(disp_dwin, y, x); if (y < disp_iline) { /* Before interactive command line. */ for (i = y + 1; i <= disp_iline && i < ysize; i++) { if (wclrtoeol(disp_dwin) == ERR || wmove(disp_dwin, i, 0) == ERR) { retval = TRUE; goto RETURN; } } getyx(disp_dwin, y, x); } if (y == disp_iline && y + 1 < ysize) { /* Skip interactive command line. */ if (wmove(disp_dwin, disp_iline + 1, 0) == ERR) { retval = TRUE; goto RETURN; } getyx(disp_dwin, y, x); } if (y > disp_iline) { if (wclrtobot(disp_dwin) == ERR) { retval = TRUE; goto RETURN; } } /* Redisplay. */ update_panels(); if (doupdate() == ERR) { retval = TRUE; goto RETURN; } /* Read a character. */ c = wgetch(disp_dwin); /* Get the current time. */ gettimeofday(&curtime, NULL); /* * Clear the interaction line if necessary. This must be done * after the wgetch() call above, since that call implicitly * does a wrefresh(). */ if (disp_ilineclear && disp_ilinetime.tv_sec + DISP_ILINE_CLEAR_DELAY < curtime.tv_sec) { if (disp_p_iline_set("")) { retval = TRUE; goto RETURN; } disp_ilineclear = FALSE; } /* Interpret the typed character. */ switch (c) { case 'c': #ifdef TOP_DEPRECATED if (top_opt_x) { goto AGAIN; } #endif /* Set the output mode. */ if (disp_p_interp_c()) { retval = TRUE; goto RETURN; } gettimeofday(&disp_ilinetime, NULL); disp_ilineclear = TRUE; break; case 'f': #ifdef TOP_DEPRECATED if (top_opt_x) { goto AGAIN; } #endif /* Toggle shared library reporting. */ top_opt_f = !top_opt_f; if (disp_p_iline_set(top_opt_f ? "Report shared library statistics" : "Do not report shared library statistics" )) { retval = TRUE; goto RETURN; } gettimeofday(&disp_ilinetime, NULL); disp_ilineclear = TRUE; break; case 'n': #ifdef TOP_DEPRECATED if (top_opt_x) { goto AGAIN; } #endif /* Set the update interval. */ if (disp_p_interp_ns("number of processes", &top_opt_n)) { retval = TRUE; goto RETURN; } gettimeofday(&disp_ilinetime, NULL); disp_ilineclear = TRUE; break; case 'O': #ifdef TOP_DEPRECATED if (top_opt_x) { goto AGAIN; } #endif /* Set the secondary sort key. */ if (disp_p_interp_Oo("secondary", &top_opt_O, &top_opt_O_ascend)) { retval = TRUE; goto RETURN; } gettimeofday(&disp_ilinetime, NULL); disp_ilineclear = TRUE; break; case 'o': #ifdef TOP_DEPRECATED if (top_opt_x) { goto AGAIN; } #endif /* Set the primary sort key. */ if (disp_p_interp_Oo("primary", &top_opt_o, &top_opt_o_ascend)) { retval = TRUE; goto RETURN; } gettimeofday(&disp_ilinetime, NULL); disp_ilineclear = TRUE; break; case DISP_KEY_EXIT: /* SIGINT or SIGQUIT. */ case 'q': /* Quit. */ samp_fini(); retval = FALSE; goto RETURN; case 'r': #ifdef TOP_DEPRECATED if (top_opt_x) { goto AGAIN; } #endif /* Toggle memory object map reporting. */ top_opt_r = !top_opt_r; if (disp_p_iline_set(top_opt_r ? "Report process memory object maps" : "Do not report process memory object maps" )) { retval = TRUE; goto RETURN; } gettimeofday(&disp_ilinetime, NULL); disp_ilineclear = TRUE; break; case 'S': #ifdef TOP_DEPRECATED if (top_opt_x) { goto AGAIN; } #endif /* Send a signal. */ if (disp_p_interp_S()) { retval = TRUE; goto RETURN; } gettimeofday(&disp_ilinetime, NULL); disp_ilineclear = TRUE; break; case 's': #ifdef TOP_DEPRECATED if (top_opt_x) { goto AGAIN; } #endif /* Set the update interval. */ if (disp_p_interp_ns("update interval", &top_opt_s)) { retval = TRUE; goto RETURN; } wtimeout(disp_dwin, top_opt_s * 1000); gettimeofday(&disp_ilinetime, NULL); disp_ilineclear = TRUE; break; case 't': #ifdef TOP_DEPRECATED if (top_opt_x) { goto AGAIN; } #endif /* Toggle memory object map reporting for pid 0. */ top_opt_t = !top_opt_t; if (disp_p_iline_set(top_opt_t ? "Translate uid numbers to usernames" : "Do not translate uid numbers to usernames")) { retval = TRUE; goto RETURN; } gettimeofday(&disp_ilinetime, NULL); disp_ilineclear = TRUE; break; case 'U': #ifdef TOP_DEPRECATED if (top_opt_x) { goto AGAIN; } #endif /* Only display processes owned by a particular user. */ if (disp_p_interp_U()) { retval = TRUE; goto RETURN; } gettimeofday(&disp_ilinetime, NULL); disp_ilineclear = TRUE; break; case 'w': #ifdef TOP_DEPRECATED if (top_opt_x) { goto AGAIN; } #endif /* Toggle wide/narrow delta mode. */ top_opt_w = !top_opt_w; if (disp_p_iline_set(top_opt_w ? "Display wide deltas" : "Display narrow deltas")) { retval = TRUE; goto RETURN; } gettimeofday(&disp_ilinetime, NULL); disp_ilineclear = TRUE; break; #ifdef TOP_DEPRECATED case 'x': /* Toggle compatibility mode. */ top_opt_x = !top_opt_x; if (disp_p_iline_set(top_opt_x ? "Normal (non-compatibility) mode" : "Compatibility mode")) { retval = TRUE; goto RETURN; } if (top_opt_x) { top_opt_f = TRUE; top_opt_r = TRUE; } gettimeofday(&disp_ilinetime, NULL); disp_ilineclear = TRUE; break; #endif case '\x0c': /* C-l */ #ifdef TOP_DEPRECATED if (top_opt_x) { goto AGAIN; } #endif /* Redraw. */ if (disp_ilineclear) { if (disp_p_iline_set("")) { retval = TRUE; goto RETURN; } disp_ilineclear = FALSE; } if (disp_p_resize() || redrawwin(disp_dwin) == ERR) { retval = TRUE; goto RETURN; } break; case DISP_KEY_RESIZE: /* SIGWINCH. */ if (disp_p_resize()) { retval = TRUE; goto RETURN; } break; case '?': #ifdef TOP_DEPRECATED if (top_opt_x) { goto AGAIN; } #endif /* Display the help panel. */ if (disp_p_help()) { retval = TRUE; goto RETURN; } /* Fall through. */ case ERR: /* Timeout. */ default: /* Just redisplay. */ break; } } assert(0); /* Not reached. */ RETURN: if (disp_p_fini()) { retval = TRUE; goto RETURN; } return retval; } /* * The following print functions take pains to assure that any text that falls * outside the dimensions of the window are silently cropped. Additionally, the * bottom right cell of the window is never printed to. */ /* Skip the interactive prompt line, if not on the last line. */ static boolean_t disp_p_skipl(void) { boolean_t retval; int y, x, ysize, xsize; getmaxyx(disp_dwin, ysize, xsize); getyx(disp_dwin, y, x); if (y + 1 < ysize) { if (disp_iline != y) { /* The interactive command line is moving. */ disp_iline = y; if (disp_p_iline_set(disp_sbuf)) { retval = TRUE; goto RETURN; } } if (wmove(disp_dwin, y + 1, 0) == ERR) { retval = TRUE; goto RETURN; } } retval = FALSE; RETURN: return retval; } /* Print a formatted string to a_window at the current position. */ static boolean_t disp_p_printl(const char *a_format, ...) { boolean_t retval; va_list ap; va_start(ap, a_format); retval = disp_p_vwprintln(disp_dwin, FALSE, a_format, ap); va_end(ap); return retval; } /* * Print a formatted string to a_window at the current position, and move to * the beginning of the next line, if not already on the last line. */ static boolean_t disp_p_println(const char *a_format, ...) { boolean_t retval; va_list ap; va_start(ap, a_format); retval = disp_p_vwprintln(disp_dwin, TRUE, a_format, ap); va_end(ap); return retval; } /* * Print a formatted string to a_window at the current position, and move to * the beginning of the next line, if a_newline is TRUE and not already on the * last line. */ static boolean_t disp_p_vprintln(boolean_t a_newline, const char *a_format, va_list a_p) { return disp_p_vwprintln(disp_dwin, a_newline, a_format, a_p); } /* Print a formatted string to a_window at the current position. */ static boolean_t disp_p_wprintl(WINDOW *a_window, const char *a_format, ...) { boolean_t retval; va_list ap; va_start(ap, a_format); retval = disp_p_vwprintln(a_window, FALSE, a_format, ap); va_end(ap); return retval; } /* * Print a formatted string to a_window at the current position, and move to * the beginning of the next line, if a_newline is TRUE and not already on the * last line. */ static boolean_t disp_p_vwprintln(WINDOW *a_window, boolean_t a_newline, const char *a_format, va_list a_p) { boolean_t retval; int y, x, ysize, xsize, maxlen; getmaxyx(a_window, ysize, xsize); getyx(a_window, y, x); if (disp_curline + 1 >= ysize) { /* Past the bottom of the screen. */ maxlen = 0; assert(maxlen + 1 <= disp_bufsize); } else if (y + 1 == ysize) { /* Bottom line. Avoid bottom right corner. */ maxlen = xsize - x - 1; assert(maxlen + 1 <= disp_bufsize); } else #ifdef TOP_DBG if (y < ysize) #endif { /* Above bottom line. */ maxlen = xsize - x; assert(maxlen + 1 <= disp_bufsize); } #ifdef TOP_DBG else { assert(0); } #endif if (maxlen > 0) { /* Clear to end of line. */ if (wclrtoeol(a_window) == ERR) { retval = TRUE; goto RETURN; } /* Print. */ if (vsnprintf(disp_lbuf, maxlen + 1, a_format, a_p) == -1 || waddnstr(a_window, disp_lbuf, maxlen) == ERR) { retval = TRUE; goto RETURN; } } /* * Move to the beginning of the next line if requested, and not on the * last line. */ if (a_newline) { disp_curline++; if (y + 1 < ysize) { if (wmove(a_window, y + 1, 0) == ERR) { retval = TRUE; goto RETURN; } } } retval = FALSE; RETURN: return retval; } /* Print a formatted string to a_window at (a_y, a_x). */ static boolean_t disp_p_mvwprintl(WINDOW *a_window, int a_y, int a_x, const char *a_format, ...) { boolean_t retval; va_list ap; va_start(ap, a_format); retval = disp_p_vmvwprintln(a_window, FALSE, a_y, a_x, a_format, ap); va_end(ap); return retval; } /* * Print a formatted string to a_window at (a_y, a_x), and move to the beginning * of the next line, if not already on the last line. */ static boolean_t disp_p_mvwprintln(WINDOW *a_window, int a_y, int a_x, const char *a_format, ...) { boolean_t retval; va_list ap; va_start(ap, a_format); retval = disp_p_vmvwprintln(a_window, TRUE, a_y, a_x, a_format, ap); va_end(ap); return retval; } /* * Print a formatted string to a_window at (a_y, a_x), and move to the beginning * of the next line, if a_newline is TRUE and not already on the last line. */ static boolean_t disp_p_vmvwprintln(WINDOW *a_window, boolean_t a_newline, int a_y, int a_x, const char *a_format, va_list a_p) { boolean_t retval; int oldy, oldx, ysize, xsize; getmaxyx(a_window, ysize, xsize); if (a_y < ysize && a_x < xsize) { getyx(a_window, oldy, oldx); if (wmove(a_window, a_y, a_x) == ERR) { retval = TRUE; goto RETURN; } if (disp_p_vwprintln(a_window, a_newline, a_format, a_p)) { wmove(a_window, oldy, oldx); retval = TRUE; goto RETURN; } } retval = FALSE; RETURN: return retval; } /* * Print a formatted string on the interactive command line and get the user * input. Return a pointer the result string, which is '\0'-terminated, or * return NULL if there is an error. * * The maximum size of the input is constrained by disp_ibuf. * * The allowable input characters are limited to those that make sense for the * various inputs top needs to understand. */ static const char * disp_p_iline_prompt(const char *a_format, ...) { const char * retval; va_list ap; boolean_t done, resized = FALSE, exited = FALSE; int c, y, x, ysize, xsize, ilen, tcurline; getmaxyx(disp_dwin, ysize, xsize); tcurline = disp_curline; /* Display the prompt. */ va_start(ap, a_format); if (disp_p_iline_vset(a_format, ap)) { retval = NULL; goto RETURN; } va_end(ap); /* Get the starting position for displaying user input. */ getyx(disp_dwin, y, x); /* Initialize the input string. */ ilen = 0; disp_ibuf[ilen] = '\0'; for (done = FALSE; done == FALSE;) { /* Render the input string if it is visible. */ if (y == disp_iline && x < xsize - 1) { disp_curline = 0; if (wmove(disp_dwin, y, x) == ERR || wclrtoeol(disp_dwin) == ERR || disp_p_wprintl(disp_dwin, "%s", disp_ibuf)) { wmove(disp_dwin, y, x); retval = NULL; goto RETURN; } } /* Redisplay. */ update_panels(); if (doupdate() == ERR) { retval = NULL; goto RETURN; } /* Read a character. */ c = wgetch(disp_dwin); /* Interpret the typed character. */ switch (c) { case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '_': case '+': case '-': if (ilen < sizeof(disp_ibuf) - 1) { disp_ibuf[ilen] = c; ilen++; disp_ibuf[ilen] = '\0'; } break; case '\x08': /* Backspace. */ case '\x7f': /* Delete. */ case KEY_BACKSPACE: case KEY_DC: if (ilen > 0) { ilen--; disp_ibuf[ilen] = '\0'; } break; case '\x0a': /* Line feed. */ case '\x0d': /* Carriage return. */ case KEY_ENTER: done = TRUE; break; case '\x07': /* C-g. */ ilen = 0; disp_ibuf[ilen] = '\0'; if (disp_p_iline_set("")) { retval = NULL; goto RETURN; } done = TRUE; break; case DISP_KEY_RESIZE: /* SIGWINCH. */ if (disp_p_resize()) { retval = NULL; goto RETURN; } /* Display the prompt. */ va_start(ap, a_format); if (disp_p_iline_vset(a_format, ap)) { retval = NULL; goto RETURN; } va_end(ap); /* * Get the starting position for displaying user input. */ getyx(disp_dwin, y, x); resized = TRUE; break; case DISP_KEY_EXIT: /* SIGINT or SIGQUIT. */ done = TRUE; exited = TRUE; break; case '?': /* Display the help panel. */ if (disp_p_help()) { retval = NULL; goto RETURN; } /* Fall through. */ case ERR: /* Timeout. */ default: break; } } /* * Append the input string to the interactive command line buffer, in * case the interactive command line has to be redrawn on a different * line. */ strncat(disp_sbuf, disp_ibuf, disp_bufsize - strlen(disp_sbuf) - 1); retval = disp_ibuf; RETURN: disp_curline = tcurline; /* * Unget DISP_KEY_EXIT if we processed an exit event, so that the main * event loop knows to exit. */ if (exited) { if (ungetch(DISP_KEY_EXIT) == ERR) { retval = NULL; } } /* * Unget DISP_KEY_RESIZE if we processed a resize event, so that the * main event loop knows to resize. */ if (resized) { if (ungetch(DISP_KEY_RESIZE) == ERR) { retval = NULL; } } return retval; } /* Set the contents of the interactive command line. */ static boolean_t disp_p_iline_set(const char *a_format, ...) { boolean_t retval; va_list ap; va_start(ap, a_format); retval = disp_p_iline_vset(a_format, ap); va_end(ap); return retval; } /* * Set the contents of the interactive command line, but preserve the current * position. */ static boolean_t disp_p_iline_eset(boolean_t a_newline, const char *a_format, va_list a_p) { boolean_t retval; int y, x; getyx(disp_dwin, y, x); if (disp_p_iline_vset(a_format, a_p)) { retval = TRUE; goto RETURN; } if (wmove(disp_dwin, y, x) == ERR) { retval = TRUE; goto RETURN; } gettimeofday(&disp_ilinetime, NULL); disp_ilineclear = TRUE; retval = FALSE; RETURN: return retval; } /* Set the contents of the interactive command line. */ static boolean_t disp_p_iline_vset(const char *a_format, va_list a_p) { boolean_t retval; int ysize, xsize, tcurline; getmaxyx(disp_dwin, ysize, xsize); tcurline = disp_curline; /* Render to the buffer that stores the interactive command line. */ if (vsnprintf(disp_sbuf, disp_bufsize, a_format, a_p) == -1) { retval = TRUE; goto RETURN; } /* If the interactive command line is visible, update it. */ if (disp_iline < ysize) { disp_curline = 0; if (disp_p_mvwprintl(disp_dwin, disp_iline, 0, "%s", disp_sbuf)) { retval = TRUE; goto RETURN; } update_panels(); if (doupdate() == ERR) { retval = TRUE; goto RETURN; } } retval = FALSE; RETURN: disp_curline = tcurline; return retval; } /* Initializer. */ static boolean_t disp_p_init(void) { boolean_t retval; /* Do generic curses initialization. */ if (initscr() == NULL || cbreak() == ERR || noecho() == ERR || nonl() == ERR || intrflush(stdscr, FALSE) == ERR || meta(stdscr, TRUE) == ERR || keypad(stdscr, TRUE) == ERR) { retval = TRUE; goto RETURN; } /* * Initialize signal handlers. SIGWINCH is taken care of by ncurses, * via DISP_KEY_RESIZE. */ signal(SIGINT, disp_p_shutdown); signal(SIGQUIT, disp_p_shutdown); signal(SIGWINCH, disp_p_sigwinch); /* Initialize windows and panels. */ if ((disp_dwin = newwin(0, 0, 0, 0)) == NULL) { retval = TRUE; goto RETURN; } wtimeout(disp_dwin, top_opt_s * 1000); if ((disp_dpan = new_panel(disp_dwin)) == NULL) { retval = TRUE; goto RETURN; } /* Allocate lbuf. */ disp_lbuf = (char *)malloc(COLS + 1); if (disp_lbuf == NULL) { retval = TRUE; goto RETURN; } /* Allocate sbuf. */ disp_sbuf = (char *)malloc(COLS + 1); if (disp_sbuf == NULL) { retval = TRUE; goto RETURN; } disp_bufsize = COLS + 1; /* Initialize the default signal. */ disp_sig = DISP_SIG; disp_signame = DISP_SIGNAME; disp_iline = 0; disp_ilineclear = FALSE; retval = FALSE; RETURN: return retval; } /* Clean up routine. */ static boolean_t disp_p_fini(void) { boolean_t retval; /* If no longer attached to tty, just exit */ if (!isatty(0)) exit(1); if (del_panel(disp_dpan) == ERR || delwin(disp_dwin) == ERR || endwin() == ERR) { retval = TRUE; goto RETURN; } /* Free sbuf. */ free(disp_sbuf); /* Free lbuf. */ free(disp_lbuf); retval = FALSE; RETURN: return retval; } /* Set a flag to shut down. */ static void disp_p_shutdown(int a_signal) { ungetch(DISP_KEY_EXIT); } /* Display the help screen. */ static boolean_t disp_p_help(void) { boolean_t retval, again, resized = FALSE, exited = FALSE; int c, y, tcurline; WINDOW *hwin; PANEL *hpan; char opt_U_str[11]; tcurline = disp_curline; disp_curline = 0; if ((hwin = newwin(0, 0, 0, 0)) == NULL) { retval = TRUE; goto RETURN; } /* Set a timeout so that signals cause a "timeout". */ wtimeout(hwin, 1000); if ((hpan = new_panel(hwin)) == NULL) { retval = TRUE; goto RETURN; } /* Loop in the case of a resize or a timeout. */ for (again = TRUE; again;) { again = FALSE; /* Initialize the contents of the help window. */ y = 0; if (wattron(hwin, A_UNDERLINE) == ERR || disp_p_mvwprintl(hwin, y, 5, "State") || disp_p_mvwprintl(hwin, y, 12, "Command") || disp_p_mvwprintln(hwin, y, 27, "Description") || wattroff(hwin, A_UNDERLINE) == ERR) { retval = TRUE; goto RETURN; } y++; if (top_opt_U == FALSE) { opt_U_str[0] = '\0'; } else if (top_opt_t) { struct passwd *pwd; pwd = getpwuid((uid_t)top_opt_U_uid); if (pwd == NULL) { retval = TRUE; goto RETURN; } snprintf(opt_U_str, sizeof(opt_U_str), "%s", pwd->pw_name); endpwent(); } else { snprintf(opt_U_str, sizeof(opt_U_str), "%u", top_opt_U_uid); } if (disp_p_mvwprintln(hwin, y++, 0, "\ ? Display this help screen, regardless of context.") || disp_p_mvwprintln(hwin, y++, 0, "\ ^L Redraw the screen.") || disp_p_mvwprintln(hwin, y++, 0, "\ %10c c Set event counting mode to {a|d|e|n}.", top_opt_c) || disp_p_mvwprintln(hwin, y++, 0, "\ %10s f Toggle shared library reporting.", top_opt_f ? "on" : "off") || disp_p_mvwprintln(hwin, y++, 0, "\ %10u n Only display processes.", top_opt_n) || disp_p_mvwprintln(hwin, y++, 0, "\ %10s O Set secondary sort key to (see o).", top_sort_key_str(top_opt_O, top_opt_O_ascend)) || disp_p_mvwprintln(hwin, y++, 0, "\ %10s o Set primary sort key to : [+-]{command|cpu|pid", top_sort_key_str(top_opt_o, top_opt_o_ascend)) || disp_p_mvwprintln(hwin, y++, 0, "\ |prt|reg|rprvt|rshrd|rsize|th|time|uid|username|vprvt") || disp_p_mvwprintln(hwin, y++, 0, "\ |vsize}.") || disp_p_mvwprintln(hwin, y++, 0, "\ q Quit.") || disp_p_mvwprintln(hwin, y++, 0, "\ %10s r Toggle process memory object map reporting.", top_opt_r ? "on" : "off") || disp_p_mvwprintln(hwin, y++, 0, "\ S\\n Send signal to pid .") || disp_p_mvwprintln(hwin, y++, 0, "\ %10u s Set the delay between updates to seconds.", top_opt_s) || disp_p_mvwprintln(hwin, y++, 0, "\ %10s t Toggle uid to username translation.", top_opt_t ? "on" : "off") || disp_p_mvwprintln(hwin, y++, 0, "\ %10s U Only display processes owned by , or all.", opt_U_str) || disp_p_mvwprintln(hwin, y++, 0, "\ %10s w Toggle wide/narrow delta mode.", top_opt_w ? "wide" : "narrow") || disp_p_mvwprintln(hwin, y++, 0, "\ ") || disp_p_mvwprintl(hwin, y++, 0, "\ Press any key to continue...")) { retval = TRUE; goto RETURN; } /* Refresh the display. */ update_panels(); if (doupdate() == ERR) { retval = TRUE; goto RETURN; } /* Get a keypress. */ c = wgetch(hwin); switch (c) { case DISP_KEY_RESIZE: /* * Resize, but preserve the resize event for the main * event loop so that the main window can be redone. */ again = TRUE; resized = TRUE; if (disp_p_resize() || wresize(hwin, LINES, COLS) == ERR || replace_panel(hpan, hwin) == ERR) { retval = TRUE; goto RETURN; } break; case DISP_KEY_EXIT: /* SIGINT or SIGQUIT. */ exited = TRUE; break; case ERR: /* Timeout. */ again = TRUE; break; default: /* Do nothing. */ break; } } /* Clean up. */ if (del_panel(hpan) == ERR || delwin(hwin) == ERR) { retval = TRUE; goto RETURN; } /* Refresh the display. */ update_panels(); if (doupdate() == ERR) { retval = TRUE; goto RETURN; } retval = FALSE; RETURN: disp_curline = tcurline; /* * Unget DISP_KEY_EXIT if we processed an exit event, so that the main * event loop knows to exit. */ if (exited) { if (ungetch(DISP_KEY_EXIT) == ERR) { retval = NULL; } } /* * Unget DISP_KEY_RESIZE if we processed a resize event, so that the * main event loop knows to resize. */ if (resized) { if (ungetch(DISP_KEY_RESIZE) == ERR) { retval = TRUE; } } return retval; } /* Make a note to resize the terminal. */ static void disp_p_sigwinch(int a_signal) { if (ungetch(DISP_KEY_RESIZE) == ERR) { _exit(1); } } /* * Resize the terminal. This is done in response to DISP_KEY_RESIZE, which * happens when a SIGWINCH signal was received by the process. */ static boolean_t disp_p_resize(void) { boolean_t retval; struct winsize size; char *p, *q; if (ioctl(1, TIOCGWINSZ, &size) == -1 || resizeterm(size.ws_row, size.ws_col) == ERR || (p = (char *)realloc(disp_lbuf, size.ws_col + 1)) == NULL || (q = (char *)realloc(disp_sbuf, size.ws_col + 1)) == NULL) { retval = TRUE; goto RETURN; } disp_lbuf = p; disp_sbuf = q; disp_bufsize = size.ws_col + 1; if (wresize(disp_dwin, size.ws_row, size.ws_col) == ERR || replace_panel(disp_dpan, disp_dwin) == ERR) { retval = TRUE; goto RETURN; } retval = FALSE; RETURN: return retval; } /* Interpret the 'm' (mode) command. */ static boolean_t disp_p_interp_c(void) { boolean_t retval; const char *s; if ((s = disp_p_iline_prompt("mode [%c]: ", top_opt_c)) == NULL) { retval = TRUE; goto RETURN; } if (strlen(s) == 1 && (*s == 'a' || *s == 'd' || *s == 'e' || *s == 'n')) { top_opt_c = s[0]; } else if (strlen(s) != 0) { if (disp_p_iline_set( "Invalid mode: %s", s)) { retval = TRUE; goto RETURN; } } retval = FALSE; RETURN: return retval; } /* * Interpret the 'n' (max number of processes) or 's' (update interval) command. */ static boolean_t disp_p_interp_ns(const char *a_name, int *r_int) { boolean_t retval; const char *s; char *p; unsigned n; if ((s = disp_p_iline_prompt("%s [%u]: ", a_name, *r_int)) == NULL) { retval = TRUE; goto RETURN; } if (strlen(s) > 0) { errno = 0; n = strtoul(s, &p, 0); if ((errno == EINVAL && n == 0) || (errno == ERANGE && n == ULONG_MAX) || *p != '\0' || n > TOP_MAX_NPROCS) { if (disp_p_iline_set("Invalid %s: %s", a_name, s)) { retval = TRUE; goto RETURN; } } else { *r_int = n; } } retval = FALSE; RETURN: return retval; } /* Interpret the 'O' (secondary sort key) or 'o' (primary sort key) command. */ static boolean_t disp_p_interp_Oo(const char *a_name, top_sort_key_t *r_key, boolean_t *r_ascend) { boolean_t retval; const char *s, *p; boolean_t ascend; if ((s = p = disp_p_iline_prompt("%s key [%s]: ", a_name, top_sort_key_str(*r_key, *r_ascend))) == NULL) { retval = TRUE; goto RETURN; } if (strlen(p) > 0) { if (*p == '+') { ascend = TRUE; p++; } else if (*p == '-') { ascend = FALSE; p++; } else { ascend = FALSE; } *r_ascend = ascend; if (strcmp(p, "command") == 0) { *r_key = TOP_SORT_command; } else if (strcmp(p, "cpu") == 0) { *r_key = TOP_SORT_cpu; } else if (strcmp(p, "pid") == 0) { *r_key = TOP_SORT_pid; } else if (strcmp(p, "prt") == 0) { *r_key = TOP_SORT_prt; } else if (strcmp(p, "reg") == 0) { *r_key = TOP_SORT_reg; } else if (strcmp(p, "rprvt") == 0) { *r_key = TOP_SORT_rprvt; } else if (strcmp(p, "rshrd") == 0) { *r_key = TOP_SORT_rshrd; } else if (strcmp(p, "rsize") == 0) { *r_key = TOP_SORT_rsize; } else if (strcmp(p, "th") == 0) { *r_key = TOP_SORT_th; } else if (strcmp(p, "time") == 0) { *r_key = TOP_SORT_time; } else if (strcmp(p, "uid") == 0) { *r_key = TOP_SORT_uid; } else if (strcmp(p, "username") == 0) { *r_key = TOP_SORT_username; } else if (strcmp(p, "vprvt") == 0) { *r_key = TOP_SORT_vprvt; } else if (strcmp(p, "vsize") == 0) { *r_key = TOP_SORT_vsize; } else { if (disp_p_iline_set("Invalid key: %s", s)) { retval = TRUE; goto RETURN; } } } retval = FALSE; RETURN: return retval; } /* Interpret the 'S' (signal) command. */ static boolean_t disp_p_interp_S(void) { boolean_t retval; const char *s, *signame = NULL; char *p; int pid, sig, error; uid_t euid; gid_t egid; /* Get the signal name or number. */ if (disp_signame != NULL) { if ((s = disp_p_iline_prompt("signal [%s]: ", disp_signame)) == NULL) { retval = TRUE; goto RETURN; } } else { if ((s = disp_p_iline_prompt("signal [%d]: ", disp_sig)) == NULL) { retval = TRUE; goto RETURN; } } if (strlen(s) > 0) { /* Try to interpret s as a number. */ errno = 0; sig = strtol(s, &p, 0); if ((errno == EINVAL && sig == 0) || (errno == ERANGE && sig == LONG_MIN) || (errno == ERANGE && sig == LONG_MAX) || *p != '\0') { /* * s is not a number. Try to interpret it as a signal * name. */ if (strcmp(s, "HUP") == 0) { sig = SIGHUP; signame = "HUP"; } else if (strcmp(s, "INT") == 0) { sig = SIGINT; signame = "INT"; } else if (strcmp(s, "QUIT") == 0) { sig = SIGQUIT; signame = "QUIT"; } else if (strcmp(s, "ILL") == 0) { sig = SIGILL; signame = "ILL"; } else if (strcmp(s, "TRAP") == 0) { sig = SIGTRAP; signame = "TRAP"; } else if (strcmp(s, "ABRT") == 0) { sig = SIGABRT; signame = "ABRT"; } else if (strcmp(s, "IOT") == 0) { sig = SIGIOT; signame = "IOT"; } else if (strcmp(s, "EMT") == 0) { sig = SIGEMT; signame = "EMT"; } else if (strcmp(s, "FPE") == 0) { sig = SIGFPE; signame = "FPE"; } else if (strcmp(s, "KILL") == 0) { sig = SIGKILL; signame = "KILL"; } else if (strcmp(s, "BUS") == 0) { sig = SIGBUS; signame = "BUS"; } else if (strcmp(s, "SEGV") == 0) { sig = SIGSEGV; signame = "SEGV"; } else if (strcmp(s, "SYS") == 0) { sig = SIGSYS; signame = "SYS"; } else if (strcmp(s, "PIPE") == 0) { sig = SIGPIPE; signame = "PIPE"; } else if (strcmp(s, "ALRM") == 0) { sig = SIGALRM; signame = "ALRM"; } else if (strcmp(s, "TERM") == 0) { sig = SIGTERM; signame = "TERM"; } else if (strcmp(s, "URG") == 0) { sig = SIGURG; signame = "URG"; } else if (strcmp(s, "STOP") == 0) { sig = SIGSTOP; signame = "STOP"; } else if (strcmp(s, "TSTP") == 0) { sig = SIGTSTP; signame = "TSTP"; } else if (strcmp(s, "CONT") == 0) { sig = SIGCONT; signame = "CONT"; } else if (strcmp(s, "CHLD") == 0) { sig = SIGCHLD; signame = "CHLD"; } else if (strcmp(s, "TTIN") == 0) { sig = SIGTTIN; signame = "TTIN"; } else if (strcmp(s, "TTOU") == 0) { sig = SIGTTOU; signame = "TTOU"; } else if (strcmp(s, "IO") == 0) { sig = SIGIO; signame = "IO"; } else if (strcmp(s, "XCPU") == 0) { sig = SIGXCPU; signame = "XCPU"; } else if (strcmp(s, "XFSZ") == 0) { sig = SIGXFSZ; signame = "XFSZ"; } else if (strcmp(s, "VTALRM") == 0) { sig = SIGVTALRM; signame = "VTALRM"; } else if (strcmp(s, "PROF") == 0) { sig = SIGPROF; signame = "PROF"; } else if (strcmp(s, "WINCH") == 0) { sig = SIGWINCH; signame = "WINCH"; } else if (strcmp(s, "INFO") == 0) { sig = SIGINFO; signame = "INFO"; } else if (strcmp(s, "USR1") == 0) { sig = SIGUSR1; signame = "USR1"; } else if (strcmp(s, "USR2") == 0) { sig = SIGUSR2; signame = "USR2"; } else { if (disp_p_iline_set("Invalid signal: %s", s)) { retval = TRUE; goto RETURN; } retval = FALSE; goto RETURN; } } } else { sig = disp_sig; signame = disp_signame; } /* Get the pid. */ if (signame != NULL) { if ((s = disp_p_iline_prompt("Send signal %s to pid: ", signame)) == NULL) { retval = TRUE; goto RETURN; } } else { if ((s = disp_p_iline_prompt("Send signal %u to pid: ", sig)) == NULL) { retval = TRUE; goto RETURN; } } if (strlen(s) > 0) { errno = 0; pid = strtol(s, &p, 0); if ((errno == EINVAL && pid == 0) || (errno == ERANGE && pid == LONG_MIN) || (errno == ERANGE && pid == LONG_MAX) || *p != '\0') { if (disp_p_iline_set("Invalid pid: %s", s)) { retval = TRUE; goto RETURN; } retval = FALSE; goto RETURN; } /* Temporarily drop permissions. */ euid = geteuid(); egid = getegid(); if (seteuid(getuid()) == -1 || setegid(getgid()) == -1) { if (disp_p_iline_set("Missing setuid bit", s)) { retval = TRUE; goto RETURN; } retval = FALSE; goto RETURN; } /* Actually send the signal. */ error = kill((pid_t)pid, sig); /* Regain permissions. */ if (seteuid(euid) == -1 || setegid(egid) == -1) { if (disp_p_iline_set( "Error restoring setuid bit", s)) { retval = TRUE; goto RETURN; } retval = FALSE; goto RETURN; } /* Process errors from kill(). */ if (error == -1) { switch (errno) { case EINVAL: if (disp_p_iline_set("Invalid signal: %d", sig)) { retval = TRUE; goto RETURN; } break; case ESRCH: if (disp_p_iline_set("Invalid pid: %d", pid)) { retval = TRUE; goto RETURN; } break; case EPERM: if (disp_p_iline_set( "Permission error signaling pid: %d", pid)) { retval = TRUE; goto RETURN; } break; default: assert(0); break; } retval = FALSE; goto RETURN; } else { if (signame != NULL) { if (disp_p_iline_set( "Send signal %s to pid %d", signame, pid)) { retval = TRUE; goto RETURN; } } else { if (disp_p_iline_set( "Send signal %d to pid %d", sig, pid)) { retval = TRUE; goto RETURN; } } /* * Update the default signal, now that the signal was * successfully sent. */ disp_sig = sig; disp_signame = signame; } } else { if (disp_p_iline_set("Signal command canceled")) { retval = TRUE; goto RETURN; } } retval = FALSE; RETURN: return retval; } /* Interpret the 'U' (user) command. */ static boolean_t disp_p_interp_U(void) { boolean_t retval; const char *s; char *p; unsigned uid; struct passwd *pwd; /* Get the username or uid. */ if ((s = disp_p_iline_prompt("user: ")) == NULL) { retval = TRUE; goto RETURN; } if (strlen(s) > 0) { errno = 0; uid = strtoul(s, &p, 0); if ((errno == EINVAL && uid == 0) || (errno == ERANGE && uid == ULONG_MAX) || *p != '\0') { /* Not a uid. Try it as a username. */ pwd = getpwnam(s); if (pwd != NULL) { top_opt_U = TRUE; top_opt_U_uid = pwd->pw_uid; } else { /* Not a known username. */ pwd = NULL; } } else { /* A number was specified. Make sure is a valid uid. */ pwd = getpwuid((uid_t)uid); if (pwd != NULL) { top_opt_U = TRUE; top_opt_U_uid = (uid_t)uid; } } endpwent(); if (pwd == NULL) { if (disp_p_iline_set("Invalid user: %s", s)) { retval = TRUE; goto RETURN; } } } else { top_opt_U = FALSE; } retval = FALSE; RETURN: return retval; }