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