/* Process viewer/killer utility Copyright (c) Tudor Hulubei & Andrei Pitis, May 1994 This file is part of UIT (UNIX Interactive Tools) UIT is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. UIT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details . You should have received a copy of the GNU General Public License along with UIT; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include "tty.h" #include "termcap.h" #include "window.h" #include "config.h" #define max(a, b) ((a) >= (b) ? (a) : (b)) #define min(a, b) ((a) <= (b) ? (a) : (b)) int SCREEN_X, SCREEN_Y; #define MAX_KEYS 2048 #define MAX_PROCESS 1024 #define KILLER_FIELDS 12 static char PSFields[KILLER_FIELDS][40] = { "TitleForeground", "TitleBackground", "TitleBrightness", "HeaderForeground", "HeaderBackground", "HeaderBrightness", "ScreenForeground", "ScreenBackground", "ScreenBrightness", "StatusForeground", "StatusBackground", "StatusBrightness" }; #ifdef HAVE_LINUX static int PSColors[KILLER_FIELDS] = { CYAN, BLUE, ON, CYAN, RED, ON, BLACK, CYAN, OFF, CYAN, BLUE, ON }; #else static int PSColors[KILLER_FIELDS] = { BLACK, WHITE, OFF, WHITE, BLACK, ON, WHITE, BLACK, OFF, BLACK, WHITE, OFF }; #endif #define TitleForeground PSColors[0] #define TitleBackground PSColors[1] #define TitleBrightness PSColors[2] #define HeaderForeground PSColors[3] #define HeaderBackground PSColors[4] #define HeaderBrightness PSColors[5] #define ScreenForeground PSColors[6] #define ScreenBackground PSColors[7] #define ScreenBrightness PSColors[8] #define StatusForeground PSColors[9] #define StatusBackground PSColors[10] #define StatusBrightness PSColors[11] #ifdef HAVE_LINUX int LinuxConsole = 1; int ColorMonitor = 1; #else int LinuxConsole = 0; int ColorMonitor = 0; #endif extern char tty_type[]; char cSection[] = "[UITPS-Color]"; char bwSection[] = "[UITPS-Monochrome]"; int processes; int PID_index; int signal_type = 11; /* index of SIGTERM in signals table */ char homedirectory[PATH_MAX]; char configfile[PATH_MAX] = ""; char TempDirectory[PATH_MAX]; char *header_text; configuration *config; char *ps_vect[MAX_PROCESS]; int first_on_screen, current_process; window *title_win, *header_win, *screen_win, *status_win; static char title_text[] = " UNIX Interactive Tools 4.2b - Process Viewer/Killer"; static char UitPsModeHelp[256]; static char no_perm[] = "not owner !"; static char no_proc[] = "no such process ! (REFRESH recommended)"; struct SIGNAL { char signame[16]; int signal; }; static struct SIGNAL signals[] = { { "SIGHUP ", SIGHUP }, /* 0 */ { "SIGINT ", SIGINT }, /* 1 */ { "SIGQUIT", SIGQUIT }, /* 2 */ { "SIGILL ", SIGILL }, /* 3 */ { "SIGFPE ", SIGFPE }, /* 4 */ { "SIGKILL", SIGKILL }, /* 5 */ { "SIGUSR1", SIGUSR1 }, /* 6 */ { "SIGSEGV", SIGSEGV }, /* 7 */ { "SIGUSR2", SIGUSR2 }, /* 8 */ { "SIGPIPE", SIGPIPE }, /* 9 */ { "SIGALRM", SIGALRM }, /* 10 */ { "SIGTERM", SIGTERM }, /* 11 */ { "SIGCHLD", SIGCHLD }, /* 12 */ { "SIGCONT", SIGCONT }, /* 13 */ { "SIGSTOP", SIGSTOP }, /* 14 */ { "SIGTSTP", SIGTSTP }, /* 15 */ { "SIGABRT", SIGABRT }, /* 16 */ { "SIGTRAP", SIGTRAP } /* 17 */ }; #define BUILTIN_OPERATIONS 24 #define BUILTIN_CursorUp 0 #define BUILTIN_CursorDown 1 #define BUILTIN_PageUp 2 #define BUILTIN_PageDown 3 #define BUILTIN_Home 4 #define BUILTIN_End 5 #define BUILTIN_NextSignal 6 #define BUILTIN_SIGHUP 7 #define BUILTIN_SIGINT 8 #define BUILTIN_SIGQUIT 9 #define BUILTIN_SIGILL 10 #define BUILTIN_SIGFPE 11 #define BUILTIN_SIGKILL 12 #define BUILTIN_SIGUSR1 13 #define BUILTIN_SIGSEGV 14 #define BUILTIN_SIGUSR2 15 #define BUILTIN_SIGPIPE 16 #define BUILTIN_SIGALRM 17 #define BUILTIN_SIGTERM 18 #define BUILTIN_SIGCHLD 19 #define BUILTIN_SIGCONT 20 #define BUILTIN_KillProcess 21 #define BUILTIN_Refresh 22 #define BUILTIN_Exit 23 #define MAX_BUILTIN_NAME 15 char built_in[BUILTIN_OPERATIONS][MAX_BUILTIN_NAME] = { "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" }; void removelog(void) { char name[PATH_MAX]; sprintf(name, "%s/uitps.1.%d", TempDirectory, getpid()); remove(name); sprintf(name, "%s/uitps.2.%d", TempDirectory, getpid()); remove(name); } void settitle(void) { char temp[256]; memset(temp, ' ', SCREEN_X); memcpy(temp, title_text, strlen(title_text)); tty_bright(TitleBrightness); tty_foreground(TitleForeground); tty_background(TitleBackground); window_cursormove(title_win, 0, 0); window_write(temp, SCREEN_X); } void setheader(void) { char temp[256]; memset(temp, ' ', SCREEN_X); memcpy(temp, header_text, strlen(header_text)); tty_bright(HeaderBrightness); tty_foreground(HeaderForeground); tty_background(HeaderBackground); window_cursormove(header_win, 0, 0); window_write(temp, SCREEN_X); } void setstatus(char *what) { char temp[256]; memset(temp, ' ', SCREEN_X); if (what) memcpy(temp, what, strlen(what)); else memcpy(temp, UitPsModeHelp, strlen(UitPsModeHelp)); tty_bright(StatusBrightness); tty_foreground(StatusForeground); tty_background(StatusBackground); window_cursormove(status_win, 0, 0); window_write(temp, SCREEN_X); } void setsignal(void) { int len = strlen(signals[signal_type].signame); tty_bright(StatusBrightness); tty_foreground(StatusForeground); tty_background(StatusBackground); window_cursormove(status_win, 0, SCREEN_X - len - 1); window_write(signals[signal_type].signame, len); } void free_ps_list(void) { int i; for (i = 0; i < MAX_PROCESS; i++) if (ps_vect[i]) { free(ps_vect[i]); ps_vect[i] = NULL; } } char *read_ps_line(FILE *ps_output, char *line) { char *ok, c; int lastchar; ok = fgets(line, SCREEN_X + 1, ps_output); if (line[lastchar = strlen(line) - 1] == '\n') line[lastchar] = 0; else while ((c = fgetc(ps_output)) != '\n' && c != EOF); return ok; } int get_PID_index(FILE *ps_output) { int i; char *h = header_text; if (read_ps_line(ps_output, header_text) == NULL) return -1; if (strstr(header_text, "PID") == NULL) return -1; for(i = 0; ; i++) { while (isspace(*h)) h++; if (memcmp(h, "PID", 3) == 0) return i; while (!isspace(*h)) h++; } } int kill_process(int process_index) { int i; char pidstr[32]; char *p = ps_vect[process_index]; if (p == NULL) return 0; for (i = 0; i < PID_index; i++) { while (isspace(*p)) p++; if (memcmp(p, "PID", 3) == 0) return i; while (!isspace(*p)) p++; } i = 0; while (isspace(*p)) p++; while (!isspace(*p)) pidstr[i++] = *p++; pidstr[i] = 0; return !kill(atoi(pidstr), signals[signal_type].signal); } void build_ps_list(FILE *ps_output) { int i = 0; do ps_vect[i] = (char *)malloc(SCREEN_X + 1); while (read_ps_line(ps_output, ps_vect[i++])); free(ps_vect[--i]); ps_vect[i] = NULL; processes = i; } void update_process(int process, int update_color) { char temp[256]; memset(temp, ' ', SCREEN_X); memcpy(temp, ps_vect[process], strlen(ps_vect[process])); if (update_color) { if (process == current_process) { tty_foreground(ScreenBackground); tty_background(ScreenForeground); } else { tty_foreground(ScreenForeground); tty_background(ScreenBackground); } tty_bright(ScreenBrightness); } window_cursormove(screen_win, process - first_on_screen, 0); window_write(temp, SCREEN_X); } void update_all(void) { int i; tty_foreground(ScreenForeground); tty_background(ScreenBackground); tty_bright(ScreenBrightness); window_cursormove(screen_win, 0, 0); for (i = first_on_screen; i < processes && (i - first_on_screen < SCREEN_Y - 3); i++) if (i != current_process) update_process(i, OFF); else window_cursormove(screen_win, i - first_on_screen + 1, 0); update_process(current_process, ON); } void clean_up(void) { free_ps_list(); if (header_text) { free(header_text); header_text = NULL; } tty_end(); tty_clrscr(); removelog(); } void fatal(char *postmsg) { clean_up(); fprintf(stderr, "Fatal error : %s !\n", postmsg); removelog(); exit(1); } int ps(char *args) { FILE *stdout_log, *stderr_log; char ps_cmd[PATH_MAX], fname[PATH_MAX], *tty_name = ttyname(1); close(1); close(2); sprintf(fname, "%s/uitps.1.%d", TempDirectory, getpid()); stdout_log = fopen(fname, "w"); sprintf(fname, "%s/uitps.2.%d", TempDirectory, getpid()); stderr_log = fopen(fname, "w"); if (args) sprintf(ps_cmd, "ps %s", args); else sprintf(ps_cmd, "ps"); if (system(ps_cmd)) { fclose(stderr_log); fclose(stdout_log); open(tty_name, O_RDWR); open(tty_name, O_RDWR); fprintf(stderr, "Invalid command line !\n"); return 0; } fclose(stderr_log); fclose(stdout_log); open(tty_name, O_RDWR); open(tty_name, O_RDWR); return 1; } void panic(int signum) { tcflush(0, TCIOFLUSH); switch (signum) { case SIGHUP: clean_up(); /* :-) */ fprintf(stderr, "Got SIGHUP !\n"); break; case SIGINT: clean_up(); break; case SIGTERM: case SIGQUIT: clean_up(); fprintf(stderr, "Got %s !\nbye\n", (signum == SIGTERM) ? "SIGTERM" : "SIGQUIT"); break; case SIGSEGV: tty_end(); tty_clrscr(); fprintf(stderr, "Got SIGSEGV !\n"); fprintf(stderr, "Please report to tudor@ulise.cs.pub.ro\n"); break; } removelog(); exit(1); } int main(int argc, char *argv[]) { FILE *stdout_log; struct key_struct *ks; int key, repeat_count; char tmp[256], buf[256]; int sectionptr = 0, i, j, index; char *home, *data = NULL, fname[PATH_MAX], *section; int need_update, need_update_all, old_current_process; termcap_init(); tty_kbdinit(1); signal(SIGTERM, panic); signal(SIGINT , panic); signal(SIGQUIT, panic); signal(SIGSEGV, panic); signal(SIGHUP, panic); signal(SIGILL, SIG_IGN); signal(SIGTRAP, SIG_IGN); signal(SIGABRT, SIG_IGN); signal(SIGUSR1, SIG_IGN); signal(SIGUSR2, SIG_IGN); signal(SIGTSTP, SIG_IGN); signal(SIGCONT, SIG_IGN); signal(SIGTTIN, SIG_IGN); signal(SIGTTOU, SIG_IGN); signal(SIGALRM, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGFPE, SIG_IGN); if (home = getenv("HOME")) { strcpy(homedirectory, home); strcpy(configfile, home); if (configfile[strlen(configfile) - 1] != '/') strcat(configfile, "/"); } strcat(configfile, ".uitrc."); strcat(configfile, tty_type); config = configuration_init(configfile); if (configuration_getstatus(config) == STATUS_OK) { sectionptr = configuration_getsectionptr(config,"[Setup]"); if (sectionptr == -1) puts("[Setup] section missing."); else { configuration_getfielddata(config, sectionptr, "TempDirectory", &data, 1, DO_SEEK); strncpy(TempDirectory, data ? data : homedirectory, PATH_MAX); #ifdef HAVE_LINUX configuration_getfielddata(config, sectionptr, "LinuxConsole", &data, 1, DO_SEEK); LinuxConsole = data && strcmp(data, "ON") == 0; configuration_getfielddata(config, sectionptr, "ColorMonitor", &data, 1, DO_SEEK); ColorMonitor = data && strcmp(data, "ON") == 0; #endif } sectionptr = configuration_getsectionptr(config, "[UITPS-Setup]"); if (sectionptr == -1) fprintf(stderr, "[UITPS-Setup] section missing."); else { configuration_getfielddata(config, sectionptr, "UitPsModeHelp", &data, 1, DO_SEEK); if (data) strncpy(UitPsModeHelp, data, 256); } section = (LinuxConsole && ColorMonitor) ? cSection : bwSection; sectionptr = configuration_getsectionptr(config, section); if (sectionptr == -1) fprintf(stderr, "%s section missing.", section); else for (i = 0; i < KILLER_FIELDS; i++) { configuration_getfielddata(config, sectionptr, PSFields[i], &data, 1, DO_SEEK); if (!data || (index = tty_getcolorindex(data))== -1) fprintf(stderr, "Invalid %s (%s).\n", PSFields[i], data); else PSColors[i] = index; } sectionptr = configuration_getsectionptr(config, "[UITPS-Keys]"); if (sectionptr == -1) fprintf(stderr, "[UITPS-Keys] section missing."); else { char key_seq[16]; char *contents; for (i = 0; i < MAX_KEYS; i++) { configuration_getfielddata(config, sectionptr, key_seq, &contents, 1, NO_SEEK); if (*key_seq == 0) break; if (contents == NULL) continue; for (j = 0; j < BUILTIN_OPERATIONS; j++) if (strcmp(contents, built_in[j]) == 0) break; if (j < BUILTIN_OPERATIONS) { if (tty_key_convert(key_seq)) tty_key_list_insert(key_seq, (void *)j); } else fprintf(stderr, "Invalid builtin operation: %s\n", contents); } if (i == MAX_KEYS) fprintf(stderr, "Too many key sequences. Only %d are allowed.\n", MAX_KEYS); } configuration_end(config); } else { fprintf(stderr, "\nCannot open configuration file %s\n", configfile); fprintf(stderr, "See the manual page for details.\n"); fprintf(stderr, "If your TERM environment variable is, let's say,\n"); fprintf(stderr, "vt102, your configuration file name should be\n"); fprintf(stderr, ".uitrc.vt102 . Try renaming .uitrc.console ...\n"); exit(1); } tty_getsize(&SCREEN_X, &SCREEN_Y); header_text = (char *)malloc(SCREEN_X + 1); title_win = window_init(0, 0, 1, SCREEN_X); header_win = window_init(0, 1, 1, SCREEN_X); screen_win = window_init(0, 2, SCREEN_Y - 2, SCREEN_X); status_win = window_init(0, SCREEN_Y - 1, 1, SCREEN_X); tty_init(); first_on_screen = current_process = 0; restart: if (ps(argc > 1 ? argv[1] : NULL) == 0) { removelog(); goto end; } sprintf(fname, "%s/uitps.1.%d", TempDirectory, getpid()); stdout_log = fopen(fname, "r"); removelog(); if ((PID_index = get_PID_index(stdout_log)) == -1) { fclose(stdout_log); goto end; } free_ps_list(); build_ps_list(stdout_log); fclose(stdout_log); tty_foreground(ScreenForeground); tty_background(ScreenBackground); tty_bright(ScreenBrightness); tty_clrscr(); settitle(); setstatus(NULL); setsignal(); setheader(); current_process = min(current_process, processes - 1); update_all(); while (1) { ks = tty_getkey(&repeat_count); key = (int)ks->aux_data; switch (key) { case BUILTIN_CursorUp: need_update_all = need_update = 0; while (repeat_count--) { if (current_process == 0) break; if (current_process == first_on_screen) { current_process--; first_on_screen--; need_update_all = 1; } else { current_process--; if (!need_update) update_process(current_process + 1, ON); need_update = 1; } } if (need_update_all) update_all(); else if (need_update) update_process(current_process, ON); break; case BUILTIN_CursorDown: need_update_all = need_update = 0; while (repeat_count--) { if (current_process < processes - 1) current_process++; else break; if (current_process - first_on_screen >= SCREEN_Y - 3) { first_on_screen++; need_update_all = 1; continue; } if (!need_update) update_process(current_process - 1, ON); need_update = 1; } if (need_update_all) update_all(); else if (need_update) update_process(current_process, ON); break; case BUILTIN_PageUp: if (current_process == 0) break; old_current_process = current_process; if (current_process < SCREEN_Y - 3) current_process = first_on_screen = 0; else { current_process -= SCREEN_Y - 3; first_on_screen = max(0, first_on_screen - (SCREEN_Y - 3)); } if (processes > SCREEN_Y - 3) update_all(); else { update_process(old_current_process, ON); update_process(current_process, ON); } break; case BUILTIN_PageDown: if (current_process == processes - 1) break; old_current_process = current_process; if (processes - 1 - first_on_screen < SCREEN_Y - 3) current_process = processes - 1; else if (processes - 1 - current_process < SCREEN_Y - 3) { current_process = processes - 1; first_on_screen = processes - 1 - (SCREEN_Y - 3) + 1; } else { current_process += SCREEN_Y - 3; first_on_screen = min(first_on_screen + SCREEN_Y - 3, (processes - 1) - (SCREEN_Y - 3) + 1); } if (processes > SCREEN_Y - 3) update_all(); else { update_process(old_current_process, ON); update_process(current_process, ON); } break; case BUILTIN_Home: if (current_process == 0) break; current_process = first_on_screen = 0; update_all(); break; case BUILTIN_End: if (current_process == processes - 1) break; current_process = processes - 1; first_on_screen = max(0, (processes - 1) - (SCREEN_Y - 3) + 1); update_all(); break; case BUILTIN_NextSignal: signal_type++; signal_type %= sizeof(signals) / sizeof(struct SIGNAL); setsignal(); break; case BUILTIN_SIGHUP : signal_type = 0; setsignal(); break; case BUILTIN_SIGINT : signal_type = 1; setsignal(); break; case BUILTIN_SIGQUIT: signal_type = 2; setsignal(); break; case BUILTIN_SIGILL : signal_type = 3; setsignal(); break; case BUILTIN_SIGFPE : signal_type = 4; setsignal(); break; case BUILTIN_SIGKILL: signal_type = 5; setsignal(); break; case BUILTIN_SIGUSR1: signal_type = 6; setsignal(); break; case BUILTIN_SIGSEGV: signal_type = 7; setsignal(); break; case BUILTIN_SIGUSR2: signal_type = 8; setsignal(); break; case BUILTIN_SIGPIPE: signal_type = 9; setsignal(); break; case BUILTIN_SIGALRM: signal_type = 10; setsignal(); break; case BUILTIN_SIGTERM: signal_type = 11; setsignal(); break; case BUILTIN_SIGCHLD: signal_type = 12; setsignal(); break; case BUILTIN_SIGCONT: signal_type = 13; setsignal(); break; case BUILTIN_Refresh: goto restart; case BUILTIN_Exit: goto end; case BUILTIN_KillProcess: if (!kill_process(current_process)) { tty_beep(); memset(buf, ' ', SCREEN_X); sprintf(tmp, "Error: %s", (errno==EPERM)?no_perm:no_proc); memcpy(buf, tmp, strlen(tmp)); setstatus(buf); errno = 0; tty_getkey(NULL); setstatus(NULL); setsignal(); } break; } } end: clean_up(); return 0; }