| File: | src/usr.bin/top/display.c |
| Warning: | line 348, column 9 Array access (from variable 'tag') results in a null pointer dereference |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: display.c,v 1.67 2022/09/10 16:58:51 cheloha Exp $ */ | |||
| 2 | ||||
| 3 | /* | |||
| 4 | * Top users/processes display for Unix | |||
| 5 | * Version 3 | |||
| 6 | * | |||
| 7 | * Copyright (c) 1984, 1989, William LeFebvre, Rice University | |||
| 8 | * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University | |||
| 9 | * | |||
| 10 | * Redistribution and use in source and binary forms, with or without | |||
| 11 | * modification, are permitted provided that the following conditions | |||
| 12 | * are met: | |||
| 13 | * 1. Redistributions of source code must retain the above copyright | |||
| 14 | * notice, this list of conditions and the following disclaimer. | |||
| 15 | * 2. Redistributions in binary form must reproduce the above copyright | |||
| 16 | * notice, this list of conditions and the following disclaimer in the | |||
| 17 | * documentation and/or other materials provided with the distribution. | |||
| 18 | * | |||
| 19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |||
| 20 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
| 21 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |||
| 22 | * IN NO EVENT SHALL THE AUTHOR OR HIS EMPLOYER BE LIABLE FOR ANY DIRECT, INDIRECT, | |||
| 23 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |||
| 24 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
| 25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
| 26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
| 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |||
| 28 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| 29 | */ | |||
| 30 | ||||
| 31 | /* | |||
| 32 | * This file contains the routines that display information on the screen. | |||
| 33 | * Each section of the screen has two routines: one for initially writing | |||
| 34 | * all constant and dynamic text, and one for only updating the text that | |||
| 35 | * changes. The prefix "i_" is used on all the "initial" routines and the | |||
| 36 | * prefix "u_" is used for all the "updating" routines. | |||
| 37 | * | |||
| 38 | * ASSUMPTIONS: | |||
| 39 | * None of the "i_" routines use any of the termcap capabilities. | |||
| 40 | * In this way, those routines can be safely used on terminals that | |||
| 41 | * have minimal (or nonexistent) terminal capabilities. | |||
| 42 | * | |||
| 43 | * The routines are called in this order: *_loadave, i_timeofday, | |||
| 44 | * *_procstates, *_cpustates, *_memory, *_message, *_header, | |||
| 45 | * *_process, u_endscreen. | |||
| 46 | */ | |||
| 47 | ||||
| 48 | #include <sys/types.h> | |||
| 49 | #include <sys/time.h> | |||
| 50 | #include <sys/sched.h> | |||
| 51 | #include <curses.h> | |||
| 52 | #include <errno(*__errno()).h> | |||
| 53 | #include <stdio.h> | |||
| 54 | #include <ctype.h> | |||
| 55 | #include <err.h> | |||
| 56 | #include <signal.h> | |||
| 57 | #include <stdlib.h> | |||
| 58 | #include <string.h> | |||
| 59 | #include <unistd.h> | |||
| 60 | #include <stdbool.h> | |||
| 61 | ||||
| 62 | #include "screen.h" /* interface to screen package */ | |||
| 63 | #include "layout.h" /* defines for screen position layout */ | |||
| 64 | #include "display.h" | |||
| 65 | #include "top.h" | |||
| 66 | #include "machine.h" /* we should eliminate this!!! */ | |||
| 67 | #include "utils.h" | |||
| 68 | ||||
| 69 | #ifdef DEBUG | |||
| 70 | FILE *debug; | |||
| 71 | #endif | |||
| 72 | ||||
| 73 | static int display_width = MAX_COLS768; | |||
| 74 | ||||
| 75 | static char *cpustates_tag(int); | |||
| 76 | static int string_count(char **); | |||
| 77 | static void summary_format(char *, size_t, int *, char **); | |||
| 78 | static int readlinedumb(char *, int); | |||
| 79 | ||||
| 80 | #define lineindex(l)((l)*display_width) ((l)*display_width) | |||
| 81 | ||||
| 82 | /* things initialized by display_init and used throughout */ | |||
| 83 | ||||
| 84 | /* buffer of proc information lines for display updating */ | |||
| 85 | char *screenbuf = NULL((void *)0); | |||
| 86 | ||||
| 87 | static char **procstate_names; | |||
| 88 | static char **cpustate_names; | |||
| 89 | static char **memory_names; | |||
| 90 | ||||
| 91 | static int num_cpustates; | |||
| 92 | ||||
| 93 | static int *cpustate_columns; | |||
| 94 | static int cpustate_total_length; | |||
| 95 | ||||
| 96 | /* display ips */ | |||
| 97 | int y_mem; | |||
| 98 | int y_message; | |||
| 99 | int y_header; | |||
| 100 | int y_idlecursor; | |||
| 101 | int y_procs; | |||
| 102 | extern int ncpu; | |||
| 103 | extern int ncpuonline; | |||
| 104 | extern int combine_cpus; | |||
| 105 | extern struct process_select ps; | |||
| 106 | ||||
| 107 | int header_status = true1; | |||
| 108 | ||||
| 109 | static int | |||
| 110 | empty(void) | |||
| 111 | { | |||
| 112 | return OK(0); | |||
| 113 | } | |||
| 114 | ||||
| 115 | static int | |||
| 116 | myfputs(const char *s) | |||
| 117 | { | |||
| 118 | return fputs(s, stdout(&__sF[1])); | |||
| 119 | } | |||
| 120 | ||||
| 121 | static int (*addstrp)(const char *); | |||
| 122 | static int (*printwp)(const char *, ...); | |||
| 123 | static int (*standoutp)(void); | |||
| 124 | static int (*standendp)(void); | |||
| 125 | ||||
| 126 | int | |||
| 127 | display_resize(void) | |||
| 128 | { | |||
| 129 | int cpu_lines, display_lines; | |||
| 130 | ||||
| 131 | ncpuonline = getncpuonline(); | |||
| 132 | cpu_lines = (combine_cpus ? 1 : ncpuonline); | |||
| 133 | y_mem = 2 + cpu_lines; | |||
| 134 | y_header = 4 + cpu_lines; | |||
| 135 | y_procs = 5 + cpu_lines; | |||
| 136 | ||||
| 137 | /* calculate the current dimensions */ | |||
| 138 | /* if operating in "dumb" mode, we only need one line */ | |||
| 139 | display_lines = smart_terminal ? screen_length - y_procs : 1; | |||
| 140 | ||||
| 141 | y_idlecursor = y_message = 3 + (combine_cpus ? 1 : ncpuonline); | |||
| 142 | if (screen_length <= y_message) | |||
| 143 | y_idlecursor = y_message = screen_length - 1; | |||
| 144 | ||||
| 145 | /* | |||
| 146 | * we don't want more than MAX_COLS columns, since the | |||
| 147 | * machine-dependent modules make static allocations based on | |||
| 148 | * MAX_COLS and we don't want to run off the end of their buffers | |||
| 149 | */ | |||
| 150 | display_width = screen_width; | |||
| 151 | if (display_width >= MAX_COLS768) | |||
| 152 | display_width = MAX_COLS768 - 1; | |||
| 153 | ||||
| 154 | if (display_lines < 0) | |||
| 155 | display_lines = 0; | |||
| 156 | ||||
| 157 | /* return number of lines available */ | |||
| 158 | /* for dumb terminals, pretend like we can show any amount */ | |||
| 159 | return (smart_terminal ? display_lines : Largest0x7fffffff); | |||
| 160 | } | |||
| 161 | ||||
| 162 | int | |||
| 163 | display_init(struct statics * statics) | |||
| 164 | { | |||
| 165 | int display_lines, *ip, i; | |||
| 166 | char **pp; | |||
| 167 | ||||
| 168 | if (smart_terminal) { | |||
| 169 | addstrp = addstr; | |||
| 170 | printwp = printw; | |||
| 171 | standoutp = standout; | |||
| 172 | standendp = standend; | |||
| 173 | } else { | |||
| 174 | addstrp = myfputs; | |||
| 175 | printwp = printf; | |||
| 176 | standoutp = empty; | |||
| 177 | standendp = empty; | |||
| 178 | } | |||
| 179 | ||||
| 180 | /* call resize to do the dirty work */ | |||
| 181 | display_lines = display_resize(); | |||
| 182 | ||||
| 183 | /* only do the rest if we need to */ | |||
| 184 | /* save pointers and allocate space for names */ | |||
| 185 | procstate_names = statics->procstate_names; | |||
| 186 | ||||
| 187 | cpustate_names = statics->cpustate_names; | |||
| 188 | num_cpustates = string_count(cpustate_names); | |||
| 189 | ||||
| 190 | cpustate_columns = calloc(num_cpustates, sizeof(int)); | |||
| 191 | if (cpustate_columns == NULL((void *)0)) | |||
| 192 | err(1, NULL((void *)0)); | |||
| 193 | ||||
| 194 | memory_names = statics->memory_names; | |||
| 195 | ||||
| 196 | /* calculate starting columns where needed */ | |||
| 197 | cpustate_total_length = 0; | |||
| 198 | pp = cpustate_names; | |||
| 199 | ip = cpustate_columns; | |||
| 200 | while (*pp != NULL((void *)0)) { | |||
| 201 | if ((i = strlen(*pp++)) > 0) { | |||
| 202 | *ip++ = cpustate_total_length; | |||
| 203 | cpustate_total_length += i + 8; | |||
| 204 | } | |||
| 205 | } | |||
| 206 | ||||
| 207 | /* return number of lines available */ | |||
| 208 | return (display_lines); | |||
| 209 | } | |||
| 210 | ||||
| 211 | /* | |||
| 212 | * Print the time elapsed since the system booted. | |||
| 213 | */ | |||
| 214 | static void | |||
| 215 | format_uptime(char *buf, size_t buflen) | |||
| 216 | { | |||
| 217 | struct timespec boottime; | |||
| 218 | time_t uptime; | |||
| 219 | unsigned int days, hrs, mins, secs; | |||
| 220 | ||||
| 221 | if (clock_gettime(CLOCK_BOOTTIME6, &boottime) == -1) | |||
| 222 | err(1, "clock_gettime"); | |||
| 223 | ||||
| 224 | uptime = boottime.tv_sec; | |||
| 225 | days = uptime / (3600 * 24); | |||
| 226 | uptime %= (3600 * 24); | |||
| 227 | hrs = uptime / 3600; | |||
| 228 | uptime %= 3600; | |||
| 229 | mins = uptime / 60; | |||
| 230 | secs = uptime % 60; | |||
| 231 | snprintf(buf, buflen, "up %u days %02u:%02u:%02u", | |||
| 232 | days, hrs, mins, secs); | |||
| 233 | } | |||
| 234 | ||||
| 235 | ||||
| 236 | void | |||
| 237 | i_loadave(double *avenrun) | |||
| 238 | { | |||
| 239 | if (screen_length > 1 || !smart_terminal) { | |||
| 240 | int i; | |||
| 241 | ||||
| 242 | move(0, 0)wmove(stdscr,(0),(0)); | |||
| 243 | clrtoeol()wclrtoeol(stdscr); | |||
| 244 | ||||
| 245 | addstrp("load averages"); | |||
| 246 | for (i = 0; i < 3; i++) | |||
| 247 | printwp("%c %5.2f", i == 0 ? ':' : ',', avenrun[i]); | |||
| 248 | } | |||
| 249 | ||||
| 250 | } | |||
| 251 | ||||
| 252 | /* | |||
| 253 | * Display the current time. | |||
| 254 | * "ctime" always returns a string that looks like this: | |||
| 255 | * | |||
| 256 | * Sun Sep 16 01:03:52 1973 | |||
| 257 | * 012345678901234567890123 | |||
| 258 | * 1 2 | |||
| 259 | * | |||
| 260 | * We want indices 11 thru 18 (length 8). | |||
| 261 | */ | |||
| 262 | ||||
| 263 | void | |||
| 264 | i_timeofday(time_t * tod) | |||
| 265 | { | |||
| 266 | static char buf[30]; | |||
| 267 | ||||
| 268 | if (buf[0] == '\0') | |||
| 269 | gethostname(buf, sizeof(buf)); | |||
| 270 | ||||
| 271 | if (screen_length > 1 || !smart_terminal) { | |||
| 272 | if (smart_terminal) { | |||
| 273 | move(0, screen_width - 8 - strlen(buf) - 1)wmove(stdscr,(0),(screen_width - 8 - strlen(buf) - 1)); | |||
| 274 | } else { | |||
| 275 | if (fputs(" ", stdout(&__sF[1])) == EOF(-1)) | |||
| 276 | exit(1); | |||
| 277 | } | |||
| 278 | #ifdef DEBUG | |||
| 279 | { | |||
| 280 | char *foo; | |||
| 281 | foo = ctime(tod); | |||
| 282 | addstrp(foo); | |||
| 283 | } | |||
| 284 | #endif | |||
| 285 | printwp("%s %-8.8s", buf, &(ctime(tod)[11])); | |||
| 286 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
| 287 | } | |||
| 288 | } | |||
| 289 | ||||
| 290 | /* | |||
| 291 | * *_procstates(total, states, threads) - print the process/thread summary line | |||
| 292 | * | |||
| 293 | * Assumptions: cursor is at the beginning of the line on entry | |||
| 294 | */ | |||
| 295 | void | |||
| 296 | i_procstates(int total, int *states, int threads) | |||
| 297 | { | |||
| 298 | if (screen_length > 2 || !smart_terminal) { | |||
| 299 | char procstates_buffer[MAX_COLS768]; | |||
| 300 | char uptime[40]; | |||
| 301 | ||||
| 302 | move(1, 0)wmove(stdscr,(1),(0)); | |||
| 303 | clrtoeol()wclrtoeol(stdscr); | |||
| 304 | /* write current number of procs and remember the value */ | |||
| 305 | printwp("%d %s: ", total, threads ? "threads" : "processes"); | |||
| 306 | ||||
| 307 | /* format and print the process state summary */ | |||
| 308 | summary_format(procstates_buffer, sizeof(procstates_buffer), | |||
| 309 | states, procstate_names); | |||
| 310 | ||||
| 311 | addstrp(procstates_buffer); | |||
| 312 | ||||
| 313 | format_uptime(uptime, sizeof(uptime)); | |||
| 314 | if (smart_terminal) | |||
| 315 | move(1, screen_width - strlen(uptime))wmove(stdscr,(1),(screen_width - strlen(uptime))); | |||
| 316 | else | |||
| 317 | printwp(" "); | |||
| 318 | printwp("%s", uptime); | |||
| 319 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
| 320 | } | |||
| 321 | } | |||
| 322 | ||||
| 323 | /* | |||
| 324 | * *_cpustates(states) - print the cpu state percentages | |||
| 325 | * | |||
| 326 | * Assumptions: cursor is on the PREVIOUS line | |||
| 327 | */ | |||
| 328 | ||||
| 329 | /* cpustates_tag() calculates the correct tag to use to label the line */ | |||
| 330 | ||||
| 331 | static char * | |||
| 332 | cpustates_tag(int cpu) | |||
| 333 | { | |||
| 334 | if (screen_length > 3 || !smart_terminal) { | |||
| 335 | static char *tag; | |||
| 336 | static int cpulen, old_width; | |||
| 337 | int i; | |||
| 338 | ||||
| 339 | if (cpulen
| |||
| 340 | /* compute length of the cpu string */ | |||
| 341 | for (i = ncpu; i
| |||
| 342 | continue; | |||
| 343 | } | |||
| 344 | ||||
| 345 | if (old_width == screen_width) { | |||
| 346 | if (ncpu
| |||
| 347 | /* just store the cpu number in the tag */ | |||
| 348 | i = tag[3 + cpulen]; | |||
| ||||
| 349 | snprintf(tag + 3, cpulen + 1, "%.*d", cpulen, cpu); | |||
| 350 | tag[3 + cpulen] = i; | |||
| 351 | } | |||
| 352 | } else { | |||
| 353 | /* | |||
| 354 | * use a long tag if it will fit, otherwise use short one. | |||
| 355 | */ | |||
| 356 | free(tag); | |||
| 357 | if (cpustate_total_length + 10 + cpulen >= screen_width) | |||
| 358 | i = asprintf(&tag, "CPU%.*d: ", cpulen, cpu); | |||
| 359 | else | |||
| 360 | i = asprintf(&tag, "CPU%.*d states: ", cpulen, cpu); | |||
| 361 | if (i == -1) | |||
| 362 | tag = NULL((void *)0); | |||
| 363 | else | |||
| 364 | old_width = screen_width; | |||
| 365 | } | |||
| 366 | return (tag); | |||
| 367 | } else | |||
| 368 | return ("\0"); | |||
| 369 | } | |||
| 370 | ||||
| 371 | void | |||
| 372 | i_cpustates(int64_t *ostates, int *online) | |||
| 373 | { | |||
| 374 | int i, first, cpu, cpu_line; | |||
| 375 | double value; | |||
| 376 | int64_t *states; | |||
| 377 | char **names, *thisname; | |||
| 378 | ||||
| 379 | if (combine_cpus) { | |||
| ||||
| 380 | static double *values; | |||
| 381 | if (!values) { | |||
| 382 | values = calloc(num_cpustates, sizeof(*values)); | |||
| 383 | if (!values) | |||
| 384 | err(1, NULL((void *)0)); | |||
| 385 | } | |||
| 386 | memset(values, 0, num_cpustates * sizeof(*values)); | |||
| 387 | for (cpu = 0; cpu < ncpu; cpu++) { | |||
| 388 | if (!online[cpu]) | |||
| 389 | continue; | |||
| 390 | names = cpustate_names; | |||
| 391 | states = ostates + (CPUSTATES6 * cpu); | |||
| 392 | i = 0; | |||
| 393 | while ((thisname = *names++) != NULL((void *)0)) { | |||
| 394 | if (*thisname != '\0') { | |||
| 395 | /* retrieve the value and remember it */ | |||
| 396 | values[i++] += *states++; | |||
| 397 | } | |||
| 398 | } | |||
| 399 | } | |||
| 400 | if (screen_length > 2 || !smart_terminal) { | |||
| 401 | names = cpustate_names; | |||
| 402 | i = 0; | |||
| 403 | first = 0; | |||
| 404 | move(2, 0)wmove(stdscr,(2),(0)); | |||
| 405 | clrtoeol()wclrtoeol(stdscr); | |||
| 406 | printwp("%-3d CPUs: ", ncpuonline); | |||
| 407 | ||||
| 408 | while ((thisname = *names++) != NULL((void *)0)) { | |||
| 409 | if (*thisname != '\0') { | |||
| 410 | value = values[i++] / ncpuonline; | |||
| 411 | /* if percentage is >= 1000, print it as 100% */ | |||
| 412 | printwp((value >= 1000 ? "%s%4.0f%% %s" : | |||
| 413 | "%s%4.1f%% %s"), first++ == 0 ? "" : ", ", | |||
| 414 | value / 10., thisname); | |||
| 415 | } | |||
| 416 | } | |||
| 417 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
| 418 | } | |||
| 419 | return; | |||
| 420 | } | |||
| 421 | for (cpu = cpu_line = 0; cpu < ncpu; cpu++) { | |||
| 422 | /* skip if offline */ | |||
| 423 | if (!online[cpu]) | |||
| 424 | continue; | |||
| 425 | ||||
| 426 | /* now walk thru the names and print the line */ | |||
| 427 | names = cpustate_names; | |||
| 428 | first = 0; | |||
| 429 | states = ostates + (CPUSTATES6 * cpu); | |||
| 430 | ||||
| 431 | if (screen_length > 2 + cpu_line || !smart_terminal) { | |||
| 432 | move(2 + cpu_line, 0)wmove(stdscr,(2 + cpu_line),(0)); | |||
| 433 | clrtoeol()wclrtoeol(stdscr); | |||
| 434 | addstrp(cpustates_tag(cpu)); | |||
| 435 | ||||
| 436 | while ((thisname = *names++) != NULL((void *)0)) { | |||
| 437 | if (*thisname != '\0') { | |||
| 438 | /* retrieve the value and remember it */ | |||
| 439 | value = *states++; | |||
| 440 | ||||
| 441 | /* if percentage is >= 1000, print it as 100% */ | |||
| 442 | printwp((value >= 1000 ? "%s%4.0f%% %s" : | |||
| 443 | "%s%4.1f%% %s"), first++ == 0 ? "" : ", ", | |||
| 444 | value / 10., thisname); | |||
| 445 | } | |||
| 446 | } | |||
| 447 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
| 448 | cpu_line++; | |||
| 449 | } | |||
| 450 | } | |||
| 451 | } | |||
| 452 | ||||
| 453 | /* | |||
| 454 | * *_memory(stats) - print "Memory: " followed by the memory summary string | |||
| 455 | */ | |||
| 456 | void | |||
| 457 | i_memory(int *stats) | |||
| 458 | { | |||
| 459 | if (screen_length > y_mem || !smart_terminal) { | |||
| 460 | char memory_buffer[MAX_COLS768]; | |||
| 461 | ||||
| 462 | move(y_mem, 0)wmove(stdscr,(y_mem),(0)); | |||
| 463 | clrtoeol()wclrtoeol(stdscr); | |||
| 464 | addstrp("Memory: "); | |||
| 465 | ||||
| 466 | /* format and print the memory summary */ | |||
| 467 | summary_format(memory_buffer, sizeof(memory_buffer), stats, | |||
| 468 | memory_names); | |||
| 469 | addstrp(memory_buffer); | |||
| 470 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
| 471 | } | |||
| 472 | } | |||
| 473 | ||||
| 474 | /* | |||
| 475 | * *_message() - print the next pending message line, or erase the one | |||
| 476 | * that is there. | |||
| 477 | */ | |||
| 478 | ||||
| 479 | /* | |||
| 480 | * i_message is funny because it gets its message asynchronously (with | |||
| 481 | * respect to screen updates). | |||
| 482 | */ | |||
| 483 | ||||
| 484 | static char next_msg[MAX_COLS768 + 5]; | |||
| 485 | static int msgon = 0; | |||
| 486 | ||||
| 487 | void | |||
| 488 | i_message(void) | |||
| 489 | { | |||
| 490 | move(y_message, 0)wmove(stdscr,(y_message),(0)); | |||
| 491 | if (next_msg[0] != '\0') { | |||
| 492 | standoutp(); | |||
| 493 | addstrp(next_msg); | |||
| 494 | standendp(); | |||
| 495 | clrtoeol()wclrtoeol(stdscr); | |||
| 496 | msgon = TRUE1; | |||
| 497 | next_msg[0] = '\0'; | |||
| 498 | } else if (msgon) { | |||
| 499 | clrtoeol()wclrtoeol(stdscr); | |||
| 500 | msgon = FALSE0; | |||
| 501 | } | |||
| 502 | } | |||
| 503 | ||||
| 504 | /* | |||
| 505 | * *_header(text) - print the header for the process area | |||
| 506 | */ | |||
| 507 | ||||
| 508 | void | |||
| 509 | i_header(char *text) | |||
| 510 | { | |||
| 511 | if (header_status && (screen_length > y_header | |||
| 512 | || !smart_terminal)) { | |||
| 513 | if (!smart_terminal) { | |||
| 514 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
| 515 | if (fputs(text, stdout(&__sF[1])) == EOF(-1)) | |||
| 516 | exit(1); | |||
| 517 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
| 518 | } else { | |||
| 519 | move(y_header, 0)wmove(stdscr,(y_header),(0)); | |||
| 520 | clrtoeol()wclrtoeol(stdscr); | |||
| 521 | addstrp(text); | |||
| 522 | } | |||
| 523 | } | |||
| 524 | } | |||
| 525 | ||||
| 526 | /* | |||
| 527 | * *_process(line, thisline) - print one process line | |||
| 528 | */ | |||
| 529 | ||||
| 530 | void | |||
| 531 | i_process(int line, char *thisline, int hl) | |||
| 532 | { | |||
| 533 | /* make sure we are on the correct line */ | |||
| 534 | move(y_procs + line, 0)wmove(stdscr,(y_procs + line),(0)); | |||
| 535 | ||||
| 536 | /* truncate the line to conform to our current screen width */ | |||
| 537 | thisline[display_width] = '\0'; | |||
| 538 | ||||
| 539 | /* write the line out */ | |||
| 540 | if (hl && smart_terminal) | |||
| 541 | standoutp(); | |||
| 542 | addstrp(thisline); | |||
| 543 | if (hl && smart_terminal) | |||
| 544 | standendp(); | |||
| 545 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
| 546 | clrtoeol()wclrtoeol(stdscr); | |||
| 547 | } | |||
| 548 | ||||
| 549 | void | |||
| 550 | u_endscreen(void) | |||
| 551 | { | |||
| 552 | if (smart_terminal) { | |||
| 553 | clrtobot()wclrtobot(stdscr); | |||
| 554 | /* move the cursor to a pleasant place */ | |||
| 555 | move(y_idlecursor, x_idlecursor)wmove(stdscr,(y_idlecursor),(0)); | |||
| 556 | } else { | |||
| 557 | /* | |||
| 558 | * separate this display from the next with some vertical | |||
| 559 | * room | |||
| 560 | */ | |||
| 561 | if (fputs("\n\n", stdout(&__sF[1])) == EOF(-1)) | |||
| 562 | exit(1); | |||
| 563 | } | |||
| 564 | } | |||
| 565 | ||||
| 566 | void | |||
| 567 | display_header(int status) | |||
| 568 | { | |||
| 569 | header_status = status; | |||
| 570 | } | |||
| 571 | ||||
| 572 | void | |||
| 573 | new_message(int type, const char *msgfmt,...) | |||
| 574 | { | |||
| 575 | va_list ap; | |||
| 576 | ||||
| 577 | va_start(ap, msgfmt)__builtin_va_start((ap), msgfmt); | |||
| 578 | /* first, format the message */ | |||
| 579 | vsnprintf(next_msg, sizeof(next_msg), msgfmt, ap); | |||
| 580 | va_end(ap)__builtin_va_end((ap)); | |||
| 581 | ||||
| 582 | if (next_msg[0] != '\0') { | |||
| 583 | /* message there already -- can we clear it? */ | |||
| 584 | /* yes -- write it and clear to end */ | |||
| 585 | if ((type & MT_delayed2) == 0) { | |||
| 586 | move(y_message, 0)wmove(stdscr,(y_message),(0)); | |||
| 587 | if (type & MT_standout1) | |||
| 588 | standoutp(); | |||
| 589 | addstrp(next_msg); | |||
| 590 | if (type & MT_standout1) | |||
| 591 | standendp(); | |||
| 592 | clrtoeol()wclrtoeol(stdscr); | |||
| 593 | msgon = TRUE1; | |||
| 594 | next_msg[0] = '\0'; | |||
| 595 | if (smart_terminal) | |||
| 596 | refresh()wrefresh(stdscr); | |||
| 597 | } | |||
| 598 | } | |||
| 599 | } | |||
| 600 | ||||
| 601 | void | |||
| 602 | clear_message(void) | |||
| 603 | { | |||
| 604 | move(y_message, 0)wmove(stdscr,(y_message),(0)); | |||
| 605 | clrtoeol()wclrtoeol(stdscr); | |||
| 606 | } | |||
| 607 | ||||
| 608 | ||||
| 609 | static int | |||
| 610 | readlinedumb(char *buffer, int size) | |||
| 611 | { | |||
| 612 | char *ptr = buffer, ch, cnt = 0, maxcnt = 0; | |||
| 613 | extern volatile sig_atomic_t leaveflag; | |||
| 614 | ssize_t len; | |||
| 615 | ||||
| 616 | /* allow room for null terminator */ | |||
| 617 | size -= 1; | |||
| 618 | ||||
| 619 | /* read loop */ | |||
| 620 | while ((fflush(stdout(&__sF[1])), (len = read(STDIN_FILENO0, ptr, 1)) > 0)) { | |||
| 621 | ||||
| 622 | if (len == 0 || leaveflag) { | |||
| 623 | end_screen(); | |||
| 624 | exit(0); | |||
| 625 | } | |||
| 626 | ||||
| 627 | /* newline means we are done */ | |||
| 628 | if ((ch = *ptr) == '\n') | |||
| 629 | break; | |||
| 630 | ||||
| 631 | /* handle special editing characters */ | |||
| 632 | if (ch == ch_kill) { | |||
| 633 | /* return null string */ | |||
| 634 | *buffer = '\0'; | |||
| 635 | putr()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\r', ( &__sF[1])) : (putc)('\r', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
| 636 | return (-1); | |||
| 637 | } else if (ch == ch_erase) { | |||
| 638 | /* erase previous character */ | |||
| 639 | if (cnt <= 0) { | |||
| 640 | /* none to erase! */ | |||
| 641 | if (putchar('\7')(!__isthreaded ? __sputc('\7', (&__sF[1])) : (putc)('\7', (&__sF[1]))) == EOF(-1)) | |||
| 642 | exit(1); | |||
| 643 | } else { | |||
| 644 | if (fputs("\b \b", stdout(&__sF[1])) == EOF(-1)) | |||
| 645 | exit(1); | |||
| 646 | ptr--; | |||
| 647 | cnt--; | |||
| 648 | } | |||
| 649 | } | |||
| 650 | /* check for character validity and buffer overflow */ | |||
| 651 | else if (cnt == size || !isprint((unsigned char)ch)) { | |||
| 652 | /* not legal */ | |||
| 653 | if (putchar('\7')(!__isthreaded ? __sputc('\7', (&__sF[1])) : (putc)('\7', (&__sF[1]))) == EOF(-1)) | |||
| 654 | exit(1); | |||
| 655 | } else { | |||
| 656 | /* echo it and store it in the buffer */ | |||
| 657 | if (putchar(ch)(!__isthreaded ? __sputc(ch, (&__sF[1])) : (putc)(ch, (& __sF[1]))) == EOF(-1)) | |||
| 658 | exit(1); | |||
| 659 | ptr++; | |||
| 660 | cnt++; | |||
| 661 | if (cnt > maxcnt) | |||
| 662 | maxcnt = cnt; | |||
| 663 | } | |||
| 664 | } | |||
| 665 | ||||
| 666 | /* all done -- null terminate the string */ | |||
| 667 | *ptr = '\0'; | |||
| 668 | ||||
| 669 | /* return either inputted number or string length */ | |||
| 670 | putr()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\r', ( &__sF[1])) : (putc)('\r', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
| 671 | return (cnt == 0 ? -1 : cnt); | |||
| 672 | } | |||
| 673 | ||||
| 674 | int | |||
| 675 | readline(char *buffer, int size) | |||
| 676 | { | |||
| 677 | size_t cnt; | |||
| 678 | ||||
| 679 | /* allow room for null terminator */ | |||
| 680 | size -= 1; | |||
| 681 | ||||
| 682 | if (smart_terminal) { | |||
| 683 | int y, x; | |||
| 684 | getyx(stdscr, y, x)(y = ((0 != (const void *)((stdscr))) ? (stdscr)->_cury : ( -1)), x = ((0 != (const void *)((stdscr))) ? (stdscr)->_curx : (-1))); | |||
| 685 | while (getnstr(buffer, size)wgetnstr(stdscr, buffer, (size)) == KEY_RESIZE0632) | |||
| 686 | move(y, x)wmove(stdscr,(y),(x)); | |||
| 687 | } else | |||
| 688 | return readlinedumb(buffer, size); | |||
| 689 | ||||
| 690 | cnt = strlen(buffer); | |||
| 691 | if (cnt > 0 && buffer[cnt - 1] == '\n') | |||
| 692 | buffer[cnt - 1] = '\0'; | |||
| 693 | return (cnt == 0 ? -1 : cnt); | |||
| 694 | } | |||
| 695 | ||||
| 696 | /* internal support routines */ | |||
| 697 | static int | |||
| 698 | string_count(char **pp) | |||
| 699 | { | |||
| 700 | int cnt; | |||
| 701 | ||||
| 702 | cnt = 0; | |||
| 703 | while (*pp++ != NULL((void *)0)) | |||
| 704 | cnt++; | |||
| 705 | return (cnt); | |||
| 706 | } | |||
| 707 | ||||
| 708 | #define COPYLEFT(to, from)do { len = strlcpy((to), (from), left); if (len >= left) return ; p += len; left -= len; } while (0) \ | |||
| 709 | do { \ | |||
| 710 | len = strlcpy((to), (from), left); \ | |||
| 711 | if (len >= left) \ | |||
| 712 | return; \ | |||
| 713 | p += len; \ | |||
| 714 | left -= len; \ | |||
| 715 | } while (0) | |||
| 716 | ||||
| 717 | static void | |||
| 718 | summary_format(char *buf, size_t left, int *numbers, char **names) | |||
| 719 | { | |||
| 720 | char *p, *thisname; | |||
| 721 | int len; | |||
| 722 | int num; | |||
| 723 | ||||
| 724 | /* format each number followed by its string */ | |||
| 725 | p = buf; | |||
| 726 | while ((thisname = *names++) != NULL((void *)0)) { | |||
| 727 | /* get the number to format */ | |||
| 728 | num = *numbers++; | |||
| 729 | ||||
| 730 | if (num >= 0) { | |||
| 731 | /* is this number in kilobytes? */ | |||
| 732 | if (thisname[0] == 'K') { | |||
| 733 | /* yes: format it as a memory value */ | |||
| 734 | COPYLEFT(p, format_k(num))do { len = strlcpy((p), (format_k(num)), left); if (len >= left) return; p += len; left -= len; } while (0); | |||
| 735 | ||||
| 736 | /* | |||
| 737 | * skip over the K, since it was included by | |||
| 738 | * format_k | |||
| 739 | */ | |||
| 740 | COPYLEFT(p, thisname + 1)do { len = strlcpy((p), (thisname + 1), left); if (len >= left ) return; p += len; left -= len; } while (0); | |||
| 741 | } else if (num > 0) { | |||
| 742 | len = snprintf(p, left, "%d%s", num, thisname); | |||
| 743 | if (len < 0 || len >= left) | |||
| 744 | return; | |||
| 745 | p += len; | |||
| 746 | left -= len; | |||
| 747 | } | |||
| 748 | } else { | |||
| 749 | /* | |||
| 750 | * Ignore negative numbers, but display corresponding | |||
| 751 | * string. | |||
| 752 | */ | |||
| 753 | COPYLEFT(p, thisname)do { len = strlcpy((p), (thisname), left); if (len >= left ) return; p += len; left -= len; } while (0); | |||
| 754 | } | |||
| 755 | } | |||
| 756 | ||||
| 757 | /* if the last two characters in the string are ", ", delete them */ | |||
| 758 | p -= 2; | |||
| 759 | if (p >= buf && p[0] == ',' && p[1] == ' ') | |||
| 760 | *p = '\0'; | |||
| 761 | } | |||
| 762 | ||||
| 763 | /* | |||
| 764 | * printable(str) - make the string pointed to by "str" into one that is | |||
| 765 | * printable (i.e.: all ascii), by converting all non-printable | |||
| 766 | * characters into '?'. Replacements are done in place and a pointer | |||
| 767 | * to the original buffer is returned. | |||
| 768 | */ | |||
| 769 | char * | |||
| 770 | printable(char *str) | |||
| 771 | { | |||
| 772 | char *ptr, ch; | |||
| 773 | ||||
| 774 | ptr = str; | |||
| 775 | while ((ch = *ptr) != '\0') { | |||
| 776 | if (!isprint((unsigned char)ch)) | |||
| 777 | *ptr = '?'; | |||
| 778 | ptr++; | |||
| 779 | } | |||
| 780 | return (str); | |||
| 781 | } | |||
| 782 | ||||
| 783 | ||||
| 784 | /* | |||
| 785 | * show_help() - display the help screen; invoked in response to | |||
| 786 | * either 'h' or '?'. | |||
| 787 | */ | |||
| 788 | void | |||
| 789 | show_help(void) | |||
| 790 | { | |||
| 791 | if (smart_terminal) { | |||
| 792 | clear()wclear(stdscr); | |||
| 793 | nl(); | |||
| 794 | } | |||
| 795 | printwp("These single-character commands are available:\n" | |||
| 796 | "\n" | |||
| 797 | "^L - redraw screen\n" | |||
| 798 | "<space> - update screen\n" | |||
| 799 | "+ - reset any P highlight, g, p, or u filters\n" | |||
| 800 | "1 - display CPU statistics on a single line\n" | |||
| 801 | "9 | 0 - scroll up/down the process list by one line\n" | |||
| 802 | "( | ) - scroll up/down the process list by screen half\n" | |||
| 803 | "C - toggle the display of command line arguments\n" | |||
| 804 | "d count - show `count' displays, then exit\n" | |||
| 805 | "e - list errors generated by last \"kill\" or \"renice\" command\n" | |||
| 806 | "g|/ string - filter on command name (g+ or /+ selects all commands)\n" | |||
| 807 | "h | ? - help; show this text\n" | |||
| 808 | "H - toggle the display of threads\n" | |||
| 809 | "I | i - toggle the display of idle processes\n" | |||
| 810 | "k [-sig] pid - send signal `-sig' (TERM by default) to process `pid'\n" | |||
| 811 | "n|# count - show `count' processes\n" | |||
| 812 | "o [-]field - specify sort order (size, res, cpu, time, pri, pid, command)\n" | |||
| 813 | " (o -field sorts in reverse)\n" | |||
| 814 | "P pid - highlight process `pid' (P+ switches highlighting off)\n" | |||
| 815 | "p pid - display process by `pid' (p+ selects all processes)\n" | |||
| 816 | "q - quit\n" | |||
| 817 | "r count pid - renice process `pid' to nice value `count'\n" | |||
| 818 | "S - toggle the display of system processes\n" | |||
| 819 | "s time - change delay between displays to `time' seconds\n" | |||
| 820 | "T [-]rtable - show processes associated with routing table `rtable'\n" | |||
| 821 | " (T+ shows all, T -rtable hides rtable)\n" | |||
| 822 | "t - toggle the display of routing tables\n" | |||
| 823 | "u [-]user - show processes for `user' (u+ shows all, u -user hides user)\n" | |||
| 824 | "\n"); | |||
| 825 | ||||
| 826 | if (smart_terminal) { | |||
| 827 | nonl(); | |||
| 828 | refresh()wrefresh(stdscr); | |||
| 829 | } | |||
| 830 | } | |||
| 831 | ||||
| 832 | /* | |||
| 833 | * show_errors() - display on stdout the current log of errors. | |||
| 834 | */ | |||
| 835 | void | |||
| 836 | show_errors(void) | |||
| 837 | { | |||
| 838 | struct errs *errp = errs; | |||
| 839 | int cnt = 0; | |||
| 840 | ||||
| 841 | if (smart_terminal) { | |||
| 842 | clear()wclear(stdscr); | |||
| 843 | nl(); | |||
| 844 | } | |||
| 845 | printwp("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s"); | |||
| 846 | while (cnt++ < errcnt) { | |||
| 847 | printwp("%5s: %s\n", errp->arg, | |||
| 848 | errp->err == 0 ? "Not a number" : strerror(errp->err)); | |||
| 849 | errp++; | |||
| 850 | } | |||
| 851 | printwp("\n"); | |||
| 852 | if (smart_terminal) { | |||
| 853 | nonl(); | |||
| 854 | refresh()wrefresh(stdscr); | |||
| 855 | } | |||
| 856 | } | |||
| 857 | ||||
| 858 | void | |||
| 859 | anykey(void) | |||
| 860 | { | |||
| 861 | int ch; | |||
| 862 | ssize_t len; | |||
| 863 | ||||
| 864 | standoutp(); | |||
| 865 | addstrp("Hit any key to continue: "); | |||
| 866 | standendp(); | |||
| 867 | if (smart_terminal) | |||
| 868 | refresh()wrefresh(stdscr); | |||
| 869 | else | |||
| 870 | fflush(stdout(&__sF[1])); | |||
| 871 | while (1) { | |||
| 872 | len = read(STDIN_FILENO0, &ch, 1); | |||
| 873 | if (len == -1 && errno(*__errno()) == EINTR4) | |||
| 874 | continue; | |||
| 875 | if (len == 0) | |||
| 876 | exit(1); | |||
| 877 | break; | |||
| 878 | } | |||
| 879 | } |