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