Annotation of src/usr.bin/top/commands.c, Revision 1.1
1.1 ! downsj 1: /* $OpenBSD$ */
! 2:
! 3: /*
! 4: * Top users/processes display for Unix
! 5: * Version 3
! 6: *
! 7: * This program may be freely redistributed,
! 8: * but this entire comment MUST remain intact.
! 9: *
! 10: * Copyright (c) 1984, 1989, William LeFebvre, Rice University
! 11: * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
! 12: */
! 13:
! 14: /*
! 15: * This file contains the routines that implement some of the interactive
! 16: * mode commands. Note that some of the commands are implemented in-line
! 17: * in "main". This is necessary because they change the global state of
! 18: * "top" (i.e.: changing the number of processes to display).
! 19: */
! 20:
! 21: #include "os.h"
! 22: #include <ctype.h>
! 23: #include <signal.h>
! 24: #include <errno.h>
! 25: #include <sys/time.h>
! 26: #include <sys/resource.h>
! 27:
! 28: #include "sigdesc.h" /* generated automatically */
! 29: #include "boolean.h"
! 30: #include "utils.h"
! 31:
! 32: extern int errno;
! 33:
! 34: extern char *copyright;
! 35:
! 36: /* imported from screen.c */
! 37: extern int overstrike;
! 38:
! 39: int err_compar();
! 40: char *err_string();
! 41:
! 42: /*
! 43: * show_help() - display the help screen; invoked in response to
! 44: * either 'h' or '?'.
! 45: */
! 46:
! 47: show_help()
! 48:
! 49: {
! 50: printf("Top version %s, %s\n", version_string(), copyright);
! 51: fputs("\n\n\
! 52: A top users display for Unix\n\
! 53: \n\
! 54: These single-character commands are available:\n\
! 55: \n\
! 56: ^L - redraw screen\n\
! 57: q - quit\n\
! 58: h or ? - help; show this text\n", stdout);
! 59:
! 60: /* not all commands are availalbe with overstrike terminals */
! 61: if (overstrike)
! 62: {
! 63: fputs("\n\
! 64: Other commands are also available, but this terminal is not\n\
! 65: sophisticated enough to handle those commands gracefully.\n\n", stdout);
! 66: }
! 67: else
! 68: {
! 69: fputs("\
! 70: d - change number of displays to show\n\
! 71: e - list errors generated by last \"kill\" or \"renice\" command\n\
! 72: i - toggle the displaying of idle processes\n\
! 73: I - same as 'i'\n\
! 74: k - kill processes; send a signal to a list of processes\n\
! 75: n or # - change number of processes to display\n", stdout);
! 76: #ifdef ORDER
! 77: fputs("\
! 78: o - specify sort order (size, res, cpu, time)\n", stdout);
! 79: #endif
! 80: fputs("\
! 81: r - renice a process\n\
! 82: s - change number of seconds to delay between updates\n\
! 83: u - display processes for only one user (+ selects all users)\n\
! 84: \n\
! 85: \n", stdout);
! 86: }
! 87: }
! 88:
! 89: /*
! 90: * Utility routines that help with some of the commands.
! 91: */
! 92:
! 93: char *next_field(str)
! 94:
! 95: register char *str;
! 96:
! 97: {
! 98: if ((str = strchr(str, ' ')) == NULL)
! 99: {
! 100: return(NULL);
! 101: }
! 102: *str = '\0';
! 103: while (*++str == ' ') /* loop */;
! 104:
! 105: /* if there is nothing left of the string, return NULL */
! 106: /* This fix is dedicated to Greg Earle */
! 107: return(*str == '\0' ? NULL : str);
! 108: }
! 109:
! 110: scanint(str, intp)
! 111:
! 112: char *str;
! 113: int *intp;
! 114:
! 115: {
! 116: register int val = 0;
! 117: register char ch;
! 118:
! 119: /* if there is nothing left of the string, flag it as an error */
! 120: /* This fix is dedicated to Greg Earle */
! 121: if (*str == '\0')
! 122: {
! 123: return(-1);
! 124: }
! 125:
! 126: while ((ch = *str++) != '\0')
! 127: {
! 128: if (isdigit(ch))
! 129: {
! 130: val = val * 10 + (ch - '0');
! 131: }
! 132: else if (isspace(ch))
! 133: {
! 134: break;
! 135: }
! 136: else
! 137: {
! 138: return(-1);
! 139: }
! 140: }
! 141: *intp = val;
! 142: return(0);
! 143: }
! 144:
! 145: /*
! 146: * Some of the commands make system calls that could generate errors.
! 147: * These errors are collected up in an array of structures for later
! 148: * contemplation and display. Such routines return a string containing an
! 149: * error message, or NULL if no errors occurred. The next few routines are
! 150: * for manipulating and displaying these errors. We need an upper limit on
! 151: * the number of errors, so we arbitrarily choose 20.
! 152: */
! 153:
! 154: #define ERRMAX 20
! 155:
! 156: struct errs /* structure for a system-call error */
! 157: {
! 158: int errno; /* value of errno (that is, the actual error) */
! 159: char *arg; /* argument that caused the error */
! 160: };
! 161:
! 162: static struct errs errs[ERRMAX];
! 163: static int errcnt;
! 164: static char *err_toomany = " too many errors occurred";
! 165: static char *err_listem =
! 166: " Many errors occurred. Press `e' to display the list of errors.";
! 167:
! 168: /* These macros get used to reset and log the errors */
! 169: #define ERR_RESET errcnt = 0
! 170: #define ERROR(p, e) if (errcnt >= ERRMAX) \
! 171: { \
! 172: return(err_toomany); \
! 173: } \
! 174: else \
! 175: { \
! 176: errs[errcnt].arg = (p); \
! 177: errs[errcnt++].errno = (e); \
! 178: }
! 179:
! 180: /*
! 181: * err_string() - return an appropriate error string. This is what the
! 182: * command will return for displaying. If no errors were logged, then
! 183: * return NULL. The maximum length of the error string is defined by
! 184: * "STRMAX".
! 185: */
! 186:
! 187: #define STRMAX 80
! 188:
! 189: char *err_string()
! 190:
! 191: {
! 192: register struct errs *errp;
! 193: register int cnt = 0;
! 194: register int first = Yes;
! 195: register int currerr = -1;
! 196: int stringlen; /* characters still available in "string" */
! 197: static char string[STRMAX];
! 198:
! 199: /* if there are no errors, return NULL */
! 200: if (errcnt == 0)
! 201: {
! 202: return(NULL);
! 203: }
! 204:
! 205: /* sort the errors */
! 206: qsort((char *)errs, errcnt, sizeof(struct errs), err_compar);
! 207:
! 208: /* need a space at the front of the error string */
! 209: string[0] = ' ';
! 210: string[1] = '\0';
! 211: stringlen = STRMAX - 2;
! 212:
! 213: /* loop thru the sorted list, building an error string */
! 214: while (cnt < errcnt)
! 215: {
! 216: errp = &(errs[cnt++]);
! 217: if (errp->errno != currerr)
! 218: {
! 219: if (currerr != -1)
! 220: {
! 221: if ((stringlen = str_adderr(string, stringlen, currerr)) < 2)
! 222: {
! 223: return(err_listem);
! 224: }
! 225: (void) strcat(string, "; "); /* we know there's more */
! 226: }
! 227: currerr = errp->errno;
! 228: first = Yes;
! 229: }
! 230: if ((stringlen = str_addarg(string, stringlen, errp->arg, first)) ==0)
! 231: {
! 232: return(err_listem);
! 233: }
! 234: first = No;
! 235: }
! 236:
! 237: /* add final message */
! 238: stringlen = str_adderr(string, stringlen, currerr);
! 239:
! 240: /* return the error string */
! 241: return(stringlen == 0 ? err_listem : string);
! 242: }
! 243:
! 244: /*
! 245: * str_adderr(str, len, err) - add an explanation of error "err" to
! 246: * the string "str".
! 247: */
! 248:
! 249: str_adderr(str, len, err)
! 250:
! 251: char *str;
! 252: int len;
! 253: int err;
! 254:
! 255: {
! 256: register char *msg;
! 257: register int msglen;
! 258:
! 259: msg = err == 0 ? "Not a number" : errmsg(err);
! 260: msglen = strlen(msg) + 2;
! 261: if (len <= msglen)
! 262: {
! 263: return(0);
! 264: }
! 265: (void) strcat(str, ": ");
! 266: (void) strcat(str, msg);
! 267: return(len - msglen);
! 268: }
! 269:
! 270: /*
! 271: * str_addarg(str, len, arg, first) - add the string argument "arg" to
! 272: * the string "str". This is the first in the group when "first"
! 273: * is set (indicating that a comma should NOT be added to the front).
! 274: */
! 275:
! 276: str_addarg(str, len, arg, first)
! 277:
! 278: char *str;
! 279: int len;
! 280: char *arg;
! 281: int first;
! 282:
! 283: {
! 284: register int arglen;
! 285:
! 286: arglen = strlen(arg);
! 287: if (!first)
! 288: {
! 289: arglen += 2;
! 290: }
! 291: if (len <= arglen)
! 292: {
! 293: return(0);
! 294: }
! 295: if (!first)
! 296: {
! 297: (void) strcat(str, ", ");
! 298: }
! 299: (void) strcat(str, arg);
! 300: return(len - arglen);
! 301: }
! 302:
! 303: /*
! 304: * err_compar(p1, p2) - comparison routine used by "qsort"
! 305: * for sorting errors.
! 306: */
! 307:
! 308: err_compar(p1, p2)
! 309:
! 310: register struct errs *p1, *p2;
! 311:
! 312: {
! 313: register int result;
! 314:
! 315: if ((result = p1->errno - p2->errno) == 0)
! 316: {
! 317: return(strcmp(p1->arg, p2->arg));
! 318: }
! 319: return(result);
! 320: }
! 321:
! 322: /*
! 323: * error_count() - return the number of errors currently logged.
! 324: */
! 325:
! 326: error_count()
! 327:
! 328: {
! 329: return(errcnt);
! 330: }
! 331:
! 332: /*
! 333: * show_errors() - display on stdout the current log of errors.
! 334: */
! 335:
! 336: show_errors()
! 337:
! 338: {
! 339: register int cnt = 0;
! 340: register struct errs *errp = errs;
! 341:
! 342: printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s");
! 343: while (cnt++ < errcnt)
! 344: {
! 345: printf("%5s: %s\n", errp->arg,
! 346: errp->errno == 0 ? "Not a number" : errmsg(errp->errno));
! 347: errp++;
! 348: }
! 349: }
! 350:
! 351: /*
! 352: * kill_procs(str) - send signals to processes, much like the "kill"
! 353: * command does; invoked in response to 'k'.
! 354: */
! 355:
! 356: char *kill_procs(str)
! 357:
! 358: char *str;
! 359:
! 360: {
! 361: register char *nptr;
! 362: int signum = SIGTERM; /* default */
! 363: int procnum;
! 364: struct sigdesc *sigp;
! 365: int uid;
! 366:
! 367: /* reset error array */
! 368: ERR_RESET;
! 369:
! 370: /* remember our uid */
! 371: uid = getuid();
! 372:
! 373: /* skip over leading white space */
! 374: while (isspace(*str)) str++;
! 375:
! 376: if (str[0] == '-')
! 377: {
! 378: /* explicit signal specified */
! 379: if ((nptr = next_field(str)) == NULL)
! 380: {
! 381: return(" kill: no processes specified");
! 382: }
! 383:
! 384: if (isdigit(str[1]))
! 385: {
! 386: (void) scanint(str + 1, &signum);
! 387: if (signum <= 0 || signum >= NSIG)
! 388: {
! 389: return(" invalid signal number");
! 390: }
! 391: }
! 392: else
! 393: {
! 394: /* translate the name into a number */
! 395: for (sigp = sigdesc; sigp->name != NULL; sigp++)
! 396: {
! 397: if (strcmp(sigp->name, str + 1) == 0)
! 398: {
! 399: signum = sigp->number;
! 400: break;
! 401: }
! 402: }
! 403:
! 404: /* was it ever found */
! 405: if (sigp->name == NULL)
! 406: {
! 407: return(" bad signal name");
! 408: }
! 409: }
! 410: /* put the new pointer in place */
! 411: str = nptr;
! 412: }
! 413:
! 414: /* loop thru the string, killing processes */
! 415: do
! 416: {
! 417: if (scanint(str, &procnum) == -1)
! 418: {
! 419: ERROR(str, 0);
! 420: }
! 421: else
! 422: {
! 423: /* check process owner if we're not root */
! 424: if (uid && (uid != proc_owner(procnum)))
! 425: {
! 426: ERROR(str, EACCES);
! 427: }
! 428: /* go in for the kill */
! 429: else if (kill(procnum, signum) == -1)
! 430: {
! 431: /* chalk up an error */
! 432: ERROR(str, errno);
! 433: }
! 434: }
! 435: } while ((str = next_field(str)) != NULL);
! 436:
! 437: /* return appropriate error string */
! 438: return(err_string());
! 439: }
! 440:
! 441: /*
! 442: * renice_procs(str) - change the "nice" of processes, much like the
! 443: * "renice" command does; invoked in response to 'r'.
! 444: */
! 445:
! 446: char *renice_procs(str)
! 447:
! 448: char *str;
! 449:
! 450: {
! 451: register char negate;
! 452: int prio;
! 453: int procnum;
! 454: int uid;
! 455:
! 456: ERR_RESET;
! 457: uid = getuid();
! 458:
! 459: /* allow for negative priority values */
! 460: if ((negate = (*str == '-')) != 0)
! 461: {
! 462: /* move past the minus sign */
! 463: str++;
! 464: }
! 465:
! 466: /* use procnum as a temporary holding place and get the number */
! 467: procnum = scanint(str, &prio);
! 468:
! 469: /* negate if necessary */
! 470: if (negate)
! 471: {
! 472: prio = -prio;
! 473: }
! 474:
! 475: #if defined(PRIO_MIN) && defined(PRIO_MAX)
! 476: /* check for validity */
! 477: if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX)
! 478: {
! 479: return(" bad priority value");
! 480: }
! 481: #endif
! 482:
! 483: /* move to the first process number */
! 484: if ((str = next_field(str)) == NULL)
! 485: {
! 486: return(" no processes specified");
! 487: }
! 488:
! 489: /* loop thru the process numbers, renicing each one */
! 490: do
! 491: {
! 492: if (scanint(str, &procnum) == -1)
! 493: {
! 494: ERROR(str, 0);
! 495: }
! 496:
! 497: /* check process owner if we're not root */
! 498: else if (uid && (uid != proc_owner(procnum)))
! 499: {
! 500: ERROR(str, EACCES);
! 501: }
! 502: else if (setpriority(PRIO_PROCESS, procnum, prio) == -1)
! 503: {
! 504: ERROR(str, errno);
! 505: }
! 506: } while ((str = next_field(str)) != NULL);
! 507:
! 508: /* return appropriate error string */
! 509: return(err_string());
! 510: }
! 511: