| File: | src/usr.bin/top/display.c |
| Warning: | line 355, 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.65 2020/08/26 16:21:28 kn 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 | static void | |||
| 212 | format_uptime(char *buf, size_t buflen) | |||
| 213 | { | |||
| 214 | time_t uptime; | |||
| 215 | int days, hrs, mins; | |||
| 216 | struct timespec boottime; | |||
| 217 | ||||
| 218 | /* | |||
| 219 | * Print how long system has been up. | |||
| 220 | */ | |||
| 221 | if (clock_gettime(CLOCK_BOOTTIME6, &boottime) != -1) { | |||
| 222 | uptime = boottime.tv_sec; | |||
| 223 | uptime += 30; | |||
| 224 | days = uptime / (3600 * 24); | |||
| 225 | uptime %= (3600 * 24); | |||
| 226 | hrs = uptime / 3600; | |||
| 227 | uptime %= 3600; | |||
| 228 | mins = uptime / 60; | |||
| 229 | if (days > 0) | |||
| 230 | snprintf(buf, buflen, "up %d day%s, %2d:%02d", | |||
| 231 | days, days > 1 ? "s" : "", hrs, mins); | |||
| 232 | else | |||
| 233 | snprintf(buf, buflen, "up %2d:%02d", | |||
| 234 | hrs, mins); | |||
| 235 | } | |||
| 236 | } | |||
| 237 | ||||
| 238 | ||||
| 239 | void | |||
| 240 | i_loadave(pid_t mpid, double *avenrun) | |||
| 241 | { | |||
| 242 | if (screen_length > 1 || !smart_terminal) { | |||
| 243 | int i; | |||
| 244 | ||||
| 245 | move(0, 0)wmove(stdscr,0,0); | |||
| 246 | clrtoeol()wclrtoeol(stdscr); | |||
| 247 | ||||
| 248 | addstrp("load averages"); | |||
| 249 | /* mpid == -1 implies this system doesn't have an _mpid */ | |||
| 250 | if (mpid != -1) | |||
| 251 | printwp("last pid: %5ld; ", (long) mpid); | |||
| 252 | ||||
| 253 | for (i = 0; i < 3; i++) | |||
| 254 | printwp("%c %5.2f", i == 0 ? ':' : ',', avenrun[i]); | |||
| 255 | } | |||
| 256 | ||||
| 257 | } | |||
| 258 | ||||
| 259 | /* | |||
| 260 | * Display the current time. | |||
| 261 | * "ctime" always returns a string that looks like this: | |||
| 262 | * | |||
| 263 | * Sun Sep 16 01:03:52 1973 | |||
| 264 | * 012345678901234567890123 | |||
| 265 | * 1 2 | |||
| 266 | * | |||
| 267 | * We want indices 11 thru 18 (length 8). | |||
| 268 | */ | |||
| 269 | ||||
| 270 | void | |||
| 271 | i_timeofday(time_t * tod) | |||
| 272 | { | |||
| 273 | static char buf[30]; | |||
| 274 | ||||
| 275 | if (buf[0] == '\0') | |||
| 276 | gethostname(buf, sizeof(buf)); | |||
| 277 | ||||
| 278 | if (screen_length > 1 || !smart_terminal) { | |||
| 279 | if (smart_terminal) { | |||
| 280 | move(0, screen_width - 8 - strlen(buf) - 1)wmove(stdscr,0,screen_width - 8 - strlen(buf) - 1); | |||
| 281 | } else { | |||
| 282 | if (fputs(" ", stdout(&__sF[1])) == EOF(-1)) | |||
| 283 | exit(1); | |||
| 284 | } | |||
| 285 | #ifdef DEBUG | |||
| 286 | { | |||
| 287 | char *foo; | |||
| 288 | foo = ctime(tod); | |||
| 289 | addstrp(foo); | |||
| 290 | } | |||
| 291 | #endif | |||
| 292 | printwp("%s %-8.8s", buf, &(ctime(tod)[11])); | |||
| 293 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
| 294 | } | |||
| 295 | } | |||
| 296 | ||||
| 297 | /* | |||
| 298 | * *_procstates(total, states, threads) - print the process/thread summary line | |||
| 299 | * | |||
| 300 | * Assumptions: cursor is at the beginning of the line on entry | |||
| 301 | */ | |||
| 302 | void | |||
| 303 | i_procstates(int total, int *states, int threads) | |||
| 304 | { | |||
| 305 | if (screen_length > 2 || !smart_terminal) { | |||
| 306 | char procstates_buffer[MAX_COLS768]; | |||
| 307 | char uptime[40]; | |||
| 308 | ||||
| 309 | move(1, 0)wmove(stdscr,1,0); | |||
| 310 | clrtoeol()wclrtoeol(stdscr); | |||
| 311 | /* write current number of procs and remember the value */ | |||
| 312 | printwp("%d %s: ", total, threads ? "threads" : "processes"); | |||
| 313 | ||||
| 314 | /* format and print the process state summary */ | |||
| 315 | summary_format(procstates_buffer, sizeof(procstates_buffer), | |||
| 316 | states, procstate_names); | |||
| 317 | ||||
| 318 | addstrp(procstates_buffer); | |||
| 319 | ||||
| 320 | format_uptime(uptime, sizeof(uptime)); | |||
| 321 | if (smart_terminal) | |||
| 322 | move(1, screen_width - strlen(uptime))wmove(stdscr,1,screen_width - strlen(uptime)); | |||
| 323 | else | |||
| 324 | printwp(" "); | |||
| 325 | printwp("%s", uptime); | |||
| 326 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
| 327 | } | |||
| 328 | } | |||
| 329 | ||||
| 330 | /* | |||
| 331 | * *_cpustates(states) - print the cpu state percentages | |||
| 332 | * | |||
| 333 | * Assumptions: cursor is on the PREVIOUS line | |||
| 334 | */ | |||
| 335 | ||||
| 336 | /* cpustates_tag() calculates the correct tag to use to label the line */ | |||
| 337 | ||||
| 338 | static char * | |||
| 339 | cpustates_tag(int cpu) | |||
| 340 | { | |||
| 341 | if (screen_length > 3 || !smart_terminal) { | |||
| 342 | static char *tag; | |||
| 343 | static int cpulen, old_width; | |||
| 344 | int i; | |||
| 345 | ||||
| 346 | if (cpulen
| |||
| 347 | /* compute length of the cpu string */ | |||
| 348 | for (i = ncpu; i
| |||
| 349 | continue; | |||
| 350 | } | |||
| 351 | ||||
| 352 | if (old_width == screen_width) { | |||
| 353 | if (ncpu
| |||
| 354 | /* just store the cpu number in the tag */ | |||
| 355 | i = tag[3 + cpulen]; | |||
| ||||
| 356 | snprintf(tag + 3, cpulen + 1, "%.*d", cpulen, cpu); | |||
| 357 | tag[3 + cpulen] = i; | |||
| 358 | } | |||
| 359 | } else { | |||
| 360 | /* | |||
| 361 | * use a long tag if it will fit, otherwise use short one. | |||
| 362 | */ | |||
| 363 | free(tag); | |||
| 364 | if (cpustate_total_length + 10 + cpulen >= screen_width) | |||
| 365 | i = asprintf(&tag, "CPU%.*d: ", cpulen, cpu); | |||
| 366 | else | |||
| 367 | i = asprintf(&tag, "CPU%.*d states: ", cpulen, cpu); | |||
| 368 | if (i == -1) | |||
| 369 | tag = NULL((void *)0); | |||
| 370 | else | |||
| 371 | old_width = screen_width; | |||
| 372 | } | |||
| 373 | return (tag); | |||
| 374 | } else | |||
| 375 | return ("\0"); | |||
| 376 | } | |||
| 377 | ||||
| 378 | void | |||
| 379 | i_cpustates(int64_t *ostates, int *online) | |||
| 380 | { | |||
| 381 | int i, first, cpu, cpu_line; | |||
| 382 | double value; | |||
| 383 | int64_t *states; | |||
| 384 | char **names, *thisname; | |||
| 385 | ||||
| 386 | if (combine_cpus) { | |||
| ||||
| 387 | static double *values; | |||
| 388 | if (!values) { | |||
| 389 | values = calloc(num_cpustates, sizeof(*values)); | |||
| 390 | if (!values) | |||
| 391 | err(1, NULL((void *)0)); | |||
| 392 | } | |||
| 393 | memset(values, 0, num_cpustates * sizeof(*values)); | |||
| 394 | for (cpu = 0; cpu < ncpu; cpu++) { | |||
| 395 | if (!online[cpu]) | |||
| 396 | continue; | |||
| 397 | names = cpustate_names; | |||
| 398 | states = ostates + (CPUSTATES6 * cpu); | |||
| 399 | i = 0; | |||
| 400 | while ((thisname = *names++) != NULL((void *)0)) { | |||
| 401 | if (*thisname != '\0') { | |||
| 402 | /* retrieve the value and remember it */ | |||
| 403 | values[i++] += *states++; | |||
| 404 | } | |||
| 405 | } | |||
| 406 | } | |||
| 407 | if (screen_length > 2 || !smart_terminal) { | |||
| 408 | names = cpustate_names; | |||
| 409 | i = 0; | |||
| 410 | first = 0; | |||
| 411 | move(2, 0)wmove(stdscr,2,0); | |||
| 412 | clrtoeol()wclrtoeol(stdscr); | |||
| 413 | printwp("%-3d CPUs: ", ncpuonline); | |||
| 414 | ||||
| 415 | while ((thisname = *names++) != NULL((void *)0)) { | |||
| 416 | if (*thisname != '\0') { | |||
| 417 | value = values[i++] / ncpuonline; | |||
| 418 | /* if percentage is >= 1000, print it as 100% */ | |||
| 419 | printwp((value >= 1000 ? "%s%4.0f%% %s" : | |||
| 420 | "%s%4.1f%% %s"), first++ == 0 ? "" : ", ", | |||
| 421 | value / 10., thisname); | |||
| 422 | } | |||
| 423 | } | |||
| 424 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
| 425 | } | |||
| 426 | return; | |||
| 427 | } | |||
| 428 | for (cpu = cpu_line = 0; cpu < ncpu; cpu++) { | |||
| 429 | /* skip if offline */ | |||
| 430 | if (!online[cpu]) | |||
| 431 | continue; | |||
| 432 | ||||
| 433 | /* now walk thru the names and print the line */ | |||
| 434 | names = cpustate_names; | |||
| 435 | first = 0; | |||
| 436 | states = ostates + (CPUSTATES6 * cpu); | |||
| 437 | ||||
| 438 | if (screen_length > 2 + cpu_line || !smart_terminal) { | |||
| 439 | move(2 + cpu_line, 0)wmove(stdscr,2 + cpu_line,0); | |||
| 440 | clrtoeol()wclrtoeol(stdscr); | |||
| 441 | addstrp(cpustates_tag(cpu)); | |||
| 442 | ||||
| 443 | while ((thisname = *names++) != NULL((void *)0)) { | |||
| 444 | if (*thisname != '\0') { | |||
| 445 | /* retrieve the value and remember it */ | |||
| 446 | value = *states++; | |||
| 447 | ||||
| 448 | /* if percentage is >= 1000, print it as 100% */ | |||
| 449 | printwp((value >= 1000 ? "%s%4.0f%% %s" : | |||
| 450 | "%s%4.1f%% %s"), first++ == 0 ? "" : ", ", | |||
| 451 | value / 10., thisname); | |||
| 452 | } | |||
| 453 | } | |||
| 454 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
| 455 | cpu_line++; | |||
| 456 | } | |||
| 457 | } | |||
| 458 | } | |||
| 459 | ||||
| 460 | /* | |||
| 461 | * *_memory(stats) - print "Memory: " followed by the memory summary string | |||
| 462 | */ | |||
| 463 | void | |||
| 464 | i_memory(int *stats) | |||
| 465 | { | |||
| 466 | if (screen_length > y_mem || !smart_terminal) { | |||
| 467 | char memory_buffer[MAX_COLS768]; | |||
| 468 | ||||
| 469 | move(y_mem, 0)wmove(stdscr,y_mem,0); | |||
| 470 | clrtoeol()wclrtoeol(stdscr); | |||
| 471 | addstrp("Memory: "); | |||
| 472 | ||||
| 473 | /* format and print the memory summary */ | |||
| 474 | summary_format(memory_buffer, sizeof(memory_buffer), stats, | |||
| 475 | memory_names); | |||
| 476 | addstrp(memory_buffer); | |||
| 477 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
| 478 | } | |||
| 479 | } | |||
| 480 | ||||
| 481 | /* | |||
| 482 | * *_message() - print the next pending message line, or erase the one | |||
| 483 | * that is there. | |||
| 484 | */ | |||
| 485 | ||||
| 486 | /* | |||
| 487 | * i_message is funny because it gets its message asynchronously (with | |||
| 488 | * respect to screen updates). | |||
| 489 | */ | |||
| 490 | ||||
| 491 | static char next_msg[MAX_COLS768 + 5]; | |||
| 492 | static int msgon = 0; | |||
| 493 | ||||
| 494 | void | |||
| 495 | i_message(void) | |||
| 496 | { | |||
| 497 | move(y_message, 0)wmove(stdscr,y_message,0); | |||
| 498 | if (next_msg[0] != '\0') { | |||
| 499 | standoutp(); | |||
| 500 | addstrp(next_msg); | |||
| 501 | standendp(); | |||
| 502 | clrtoeol()wclrtoeol(stdscr); | |||
| 503 | msgon = TRUE1; | |||
| 504 | next_msg[0] = '\0'; | |||
| 505 | } else if (msgon) { | |||
| 506 | clrtoeol()wclrtoeol(stdscr); | |||
| 507 | msgon = FALSE0; | |||
| 508 | } | |||
| 509 | } | |||
| 510 | ||||
| 511 | /* | |||
| 512 | * *_header(text) - print the header for the process area | |||
| 513 | */ | |||
| 514 | ||||
| 515 | void | |||
| 516 | i_header(char *text) | |||
| 517 | { | |||
| 518 | if (header_status && (screen_length > y_header | |||
| 519 | || !smart_terminal)) { | |||
| 520 | if (!smart_terminal) { | |||
| 521 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
| 522 | if (fputs(text, stdout(&__sF[1])) == EOF(-1)) | |||
| 523 | exit(1); | |||
| 524 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
| 525 | } else { | |||
| 526 | move(y_header, 0)wmove(stdscr,y_header,0); | |||
| 527 | clrtoeol()wclrtoeol(stdscr); | |||
| 528 | addstrp(text); | |||
| 529 | } | |||
| 530 | } | |||
| 531 | } | |||
| 532 | ||||
| 533 | /* | |||
| 534 | * *_process(line, thisline) - print one process line | |||
| 535 | */ | |||
| 536 | ||||
| 537 | void | |||
| 538 | i_process(int line, char *thisline, int hl) | |||
| 539 | { | |||
| 540 | /* make sure we are on the correct line */ | |||
| 541 | move(y_procs + line, 0)wmove(stdscr,y_procs + line,0); | |||
| 542 | ||||
| 543 | /* truncate the line to conform to our current screen width */ | |||
| 544 | thisline[display_width] = '\0'; | |||
| 545 | ||||
| 546 | /* write the line out */ | |||
| 547 | if (hl && smart_terminal) | |||
| 548 | standoutp(); | |||
| 549 | addstrp(thisline); | |||
| 550 | if (hl && smart_terminal) | |||
| 551 | standendp(); | |||
| 552 | putn()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\n', ( &__sF[1])) : (putc)('\n', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
| 553 | clrtoeol()wclrtoeol(stdscr); | |||
| 554 | } | |||
| 555 | ||||
| 556 | void | |||
| 557 | u_endscreen(void) | |||
| 558 | { | |||
| 559 | if (smart_terminal) { | |||
| 560 | clrtobot()wclrtobot(stdscr); | |||
| 561 | /* move the cursor to a pleasant place */ | |||
| 562 | move(y_idlecursor, x_idlecursor)wmove(stdscr,y_idlecursor,0); | |||
| 563 | } else { | |||
| 564 | /* | |||
| 565 | * separate this display from the next with some vertical | |||
| 566 | * room | |||
| 567 | */ | |||
| 568 | if (fputs("\n\n", stdout(&__sF[1])) == EOF(-1)) | |||
| 569 | exit(1); | |||
| 570 | } | |||
| 571 | } | |||
| 572 | ||||
| 573 | void | |||
| 574 | display_header(int status) | |||
| 575 | { | |||
| 576 | header_status = status; | |||
| 577 | } | |||
| 578 | ||||
| 579 | void | |||
| 580 | new_message(int type, const char *msgfmt,...) | |||
| 581 | { | |||
| 582 | va_list ap; | |||
| 583 | ||||
| 584 | va_start(ap, msgfmt)__builtin_va_start(ap, msgfmt); | |||
| 585 | /* first, format the message */ | |||
| 586 | vsnprintf(next_msg, sizeof(next_msg), msgfmt, ap); | |||
| 587 | va_end(ap)__builtin_va_end(ap); | |||
| 588 | ||||
| 589 | if (next_msg[0] != '\0') { | |||
| 590 | /* message there already -- can we clear it? */ | |||
| 591 | /* yes -- write it and clear to end */ | |||
| 592 | if ((type & MT_delayed2) == 0) { | |||
| 593 | move(y_message, 0)wmove(stdscr,y_message,0); | |||
| 594 | if (type & MT_standout1) | |||
| 595 | standoutp(); | |||
| 596 | addstrp(next_msg); | |||
| 597 | if (type & MT_standout1) | |||
| 598 | standendp(); | |||
| 599 | clrtoeol()wclrtoeol(stdscr); | |||
| 600 | msgon = TRUE1; | |||
| 601 | next_msg[0] = '\0'; | |||
| 602 | if (smart_terminal) | |||
| 603 | refresh()wrefresh(stdscr); | |||
| 604 | } | |||
| 605 | } | |||
| 606 | } | |||
| 607 | ||||
| 608 | void | |||
| 609 | clear_message(void) | |||
| 610 | { | |||
| 611 | move(y_message, 0)wmove(stdscr,y_message,0); | |||
| 612 | clrtoeol()wclrtoeol(stdscr); | |||
| 613 | } | |||
| 614 | ||||
| 615 | ||||
| 616 | static int | |||
| 617 | readlinedumb(char *buffer, int size) | |||
| 618 | { | |||
| 619 | char *ptr = buffer, ch, cnt = 0, maxcnt = 0; | |||
| 620 | extern volatile sig_atomic_t leaveflag; | |||
| 621 | ssize_t len; | |||
| 622 | ||||
| 623 | /* allow room for null terminator */ | |||
| 624 | size -= 1; | |||
| 625 | ||||
| 626 | /* read loop */ | |||
| 627 | while ((fflush(stdout(&__sF[1])), (len = read(STDIN_FILENO0, ptr, 1)) > 0)) { | |||
| 628 | ||||
| 629 | if (len == 0 || leaveflag) { | |||
| 630 | end_screen(); | |||
| 631 | exit(0); | |||
| 632 | } | |||
| 633 | ||||
| 634 | /* newline means we are done */ | |||
| 635 | if ((ch = *ptr) == '\n') | |||
| 636 | break; | |||
| 637 | ||||
| 638 | /* handle special editing characters */ | |||
| 639 | if (ch == ch_kill) { | |||
| 640 | /* return null string */ | |||
| 641 | *buffer = '\0'; | |||
| 642 | putr()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\r', ( &__sF[1])) : (putc)('\r', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
| 643 | return (-1); | |||
| 644 | } else if (ch == ch_erase) { | |||
| 645 | /* erase previous character */ | |||
| 646 | if (cnt <= 0) { | |||
| 647 | /* none to erase! */ | |||
| 648 | if (putchar('\7')(!__isthreaded ? __sputc('\7', (&__sF[1])) : (putc)('\7', (&__sF[1]))) == EOF(-1)) | |||
| 649 | exit(1); | |||
| 650 | } else { | |||
| 651 | if (fputs("\b \b", stdout(&__sF[1])) == EOF(-1)) | |||
| 652 | exit(1); | |||
| 653 | ptr--; | |||
| 654 | cnt--; | |||
| 655 | } | |||
| 656 | } | |||
| 657 | /* check for character validity and buffer overflow */ | |||
| 658 | else if (cnt == size || !isprint((unsigned char)ch)) { | |||
| 659 | /* not legal */ | |||
| 660 | if (putchar('\7')(!__isthreaded ? __sputc('\7', (&__sF[1])) : (putc)('\7', (&__sF[1]))) == EOF(-1)) | |||
| 661 | exit(1); | |||
| 662 | } else { | |||
| 663 | /* echo it and store it in the buffer */ | |||
| 664 | if (putchar(ch)(!__isthreaded ? __sputc(ch, (&__sF[1])) : (putc)(ch, (& __sF[1]))) == EOF(-1)) | |||
| 665 | exit(1); | |||
| 666 | ptr++; | |||
| 667 | cnt++; | |||
| 668 | if (cnt > maxcnt) | |||
| 669 | maxcnt = cnt; | |||
| 670 | } | |||
| 671 | } | |||
| 672 | ||||
| 673 | /* all done -- null terminate the string */ | |||
| 674 | *ptr = '\0'; | |||
| 675 | ||||
| 676 | /* return either inputted number or string length */ | |||
| 677 | putr()do { if (!smart_terminal) if ((!__isthreaded ? __sputc('\r', ( &__sF[1])) : (putc)('\r', (&__sF[1]))) == (-1)) exit( 1); } while (0); | |||
| 678 | return (cnt == 0 ? -1 : cnt); | |||
| 679 | } | |||
| 680 | ||||
| 681 | int | |||
| 682 | readline(char *buffer, int size) | |||
| 683 | { | |||
| 684 | size_t cnt; | |||
| 685 | ||||
| 686 | /* allow room for null terminator */ | |||
| 687 | size -= 1; | |||
| 688 | ||||
| 689 | if (smart_terminal) { | |||
| 690 | int y, x; | |||
| 691 | getyx(stdscr, y, x)(y = ((stdscr) ? (stdscr)->_cury : (-1)), x = ((stdscr) ? ( stdscr)->_curx : (-1))); | |||
| 692 | while (getnstr(buffer, size)wgetnstr(stdscr, buffer, size) == KEY_RESIZE0632) | |||
| 693 | move(y, x)wmove(stdscr,y,x); | |||
| 694 | } else | |||
| 695 | return readlinedumb(buffer, size); | |||
| 696 | ||||
| 697 | cnt = strlen(buffer); | |||
| 698 | if (cnt > 0 && buffer[cnt - 1] == '\n') | |||
| 699 | buffer[cnt - 1] = '\0'; | |||
| 700 | return (cnt == 0 ? -1 : cnt); | |||
| 701 | } | |||
| 702 | ||||
| 703 | /* internal support routines */ | |||
| 704 | static int | |||
| 705 | string_count(char **pp) | |||
| 706 | { | |||
| 707 | int cnt; | |||
| 708 | ||||
| 709 | cnt = 0; | |||
| 710 | while (*pp++ != NULL((void *)0)) | |||
| 711 | cnt++; | |||
| 712 | return (cnt); | |||
| 713 | } | |||
| 714 | ||||
| 715 | #define COPYLEFT(to, from)do { len = strlcpy((to), (from), left); if (len >= left) return ; p += len; left -= len; } while (0) \ | |||
| 716 | do { \ | |||
| 717 | len = strlcpy((to), (from), left); \ | |||
| 718 | if (len >= left) \ | |||
| 719 | return; \ | |||
| 720 | p += len; \ | |||
| 721 | left -= len; \ | |||
| 722 | } while (0) | |||
| 723 | ||||
| 724 | static void | |||
| 725 | summary_format(char *buf, size_t left, int *numbers, char **names) | |||
| 726 | { | |||
| 727 | char *p, *thisname; | |||
| 728 | int len; | |||
| 729 | int num; | |||
| 730 | ||||
| 731 | /* format each number followed by its string */ | |||
| 732 | p = buf; | |||
| 733 | while ((thisname = *names++) != NULL((void *)0)) { | |||
| 734 | /* get the number to format */ | |||
| 735 | num = *numbers++; | |||
| 736 | ||||
| 737 | if (num >= 0) { | |||
| 738 | /* is this number in kilobytes? */ | |||
| 739 | if (thisname[0] == 'K') { | |||
| 740 | /* yes: format it as a memory value */ | |||
| 741 | COPYLEFT(p, format_k(num))do { len = strlcpy((p), (format_k(num)), left); if (len >= left) return; p += len; left -= len; } while (0); | |||
| 742 | ||||
| 743 | /* | |||
| 744 | * skip over the K, since it was included by | |||
| 745 | * format_k | |||
| 746 | */ | |||
| 747 | COPYLEFT(p, thisname + 1)do { len = strlcpy((p), (thisname + 1), left); if (len >= left ) return; p += len; left -= len; } while (0); | |||
| 748 | } else if (num > 0) { | |||
| 749 | len = snprintf(p, left, "%d%s", num, thisname); | |||
| 750 | if (len < 0 || len >= left) | |||
| 751 | return; | |||
| 752 | p += len; | |||
| 753 | left -= len; | |||
| 754 | } | |||
| 755 | } else { | |||
| 756 | /* | |||
| 757 | * Ignore negative numbers, but display corresponding | |||
| 758 | * string. | |||
| 759 | */ | |||
| 760 | COPYLEFT(p, thisname)do { len = strlcpy((p), (thisname), left); if (len >= left ) return; p += len; left -= len; } while (0); | |||
| 761 | } | |||
| 762 | } | |||
| 763 | ||||
| 764 | /* if the last two characters in the string are ", ", delete them */ | |||
| 765 | p -= 2; | |||
| 766 | if (p >= buf && p[0] == ',' && p[1] == ' ') | |||
| 767 | *p = '\0'; | |||
| 768 | } | |||
| 769 | ||||
| 770 | /* | |||
| 771 | * printable(str) - make the string pointed to by "str" into one that is | |||
| 772 | * printable (i.e.: all ascii), by converting all non-printable | |||
| 773 | * characters into '?'. Replacements are done in place and a pointer | |||
| 774 | * to the original buffer is returned. | |||
| 775 | */ | |||
| 776 | char * | |||
| 777 | printable(char *str) | |||
| 778 | { | |||
| 779 | char *ptr, ch; | |||
| 780 | ||||
| 781 | ptr = str; | |||
| 782 | while ((ch = *ptr) != '\0') { | |||
| 783 | if (!isprint((unsigned char)ch)) | |||
| 784 | *ptr = '?'; | |||
| 785 | ptr++; | |||
| 786 | } | |||
| 787 | return (str); | |||
| 788 | } | |||
| 789 | ||||
| 790 | ||||
| 791 | /* | |||
| 792 | * show_help() - display the help screen; invoked in response to | |||
| 793 | * either 'h' or '?'. | |||
| 794 | */ | |||
| 795 | void | |||
| 796 | show_help(void) | |||
| 797 | { | |||
| 798 | if (smart_terminal) { | |||
| 799 | clear()wclear(stdscr); | |||
| 800 | nl(); | |||
| 801 | } | |||
| 802 | printwp("These single-character commands are available:\n" | |||
| 803 | "\n" | |||
| 804 | "^L - redraw screen\n" | |||
| 805 | "<space> - update screen\n" | |||
| 806 | "+ - reset any P highlight, g, p, or u filters\n" | |||
| 807 | "1 - display CPU statistics on a single line\n" | |||
| 808 | "9 | 0 - scroll up/down the process list by one line\n" | |||
| 809 | "( | ) - scroll up/down the process list by screen half\n" | |||
| 810 | "C - toggle the display of command line arguments\n" | |||
| 811 | "d count - show `count' displays, then exit\n" | |||
| 812 | "e - list errors generated by last \"kill\" or \"renice\" command\n" | |||
| 813 | "g|/ string - filter on command name (g+ or /+ selects all commands)\n" | |||
| 814 | "h | ? - help; show this text\n" | |||
| 815 | "H - toggle the display of threads\n" | |||
| 816 | "I | i - toggle the display of idle processes\n" | |||
| 817 | "k [-sig] pid - send signal `-sig' (TERM by default) to process `pid'\n" | |||
| 818 | "n|# count - show `count' processes\n" | |||
| 819 | "o [-]field - specify sort order (size, res, cpu, time, pri, pid, command)\n" | |||
| 820 | " (o -field sorts in reverse)\n" | |||
| 821 | "P pid - highlight process `pid' (P+ switches highlighting off)\n" | |||
| 822 | "p pid - display process by `pid' (p+ selects all processes)\n" | |||
| 823 | "q - quit\n" | |||
| 824 | "r count pid - renice process `pid' to nice value `count'\n" | |||
| 825 | "S - toggle the display of system processes\n" | |||
| 826 | "s time - change delay between displays to `time' seconds\n" | |||
| 827 | "T [-]rtable - show processes associated with routing table `rtable'\n" | |||
| 828 | " (T+ shows all, T -rtable hides rtable)\n" | |||
| 829 | "t - toggle the display of routing tables\n" | |||
| 830 | "u [-]user - show processes for `user' (u+ shows all, u -user hides user)\n" | |||
| 831 | "\n"); | |||
| 832 | ||||
| 833 | if (smart_terminal) { | |||
| 834 | nonl(); | |||
| 835 | refresh()wrefresh(stdscr); | |||
| 836 | } | |||
| 837 | } | |||
| 838 | ||||
| 839 | /* | |||
| 840 | * show_errors() - display on stdout the current log of errors. | |||
| 841 | */ | |||
| 842 | void | |||
| 843 | show_errors(void) | |||
| 844 | { | |||
| 845 | struct errs *errp = errs; | |||
| 846 | int cnt = 0; | |||
| 847 | ||||
| 848 | if (smart_terminal) { | |||
| 849 | clear()wclear(stdscr); | |||
| 850 | nl(); | |||
| 851 | } | |||
| 852 | printwp("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s"); | |||
| 853 | while (cnt++ < errcnt) { | |||
| 854 | printwp("%5s: %s\n", errp->arg, | |||
| 855 | errp->err == 0 ? "Not a number" : strerror(errp->err)); | |||
| 856 | errp++; | |||
| 857 | } | |||
| 858 | printwp("\n"); | |||
| 859 | if (smart_terminal) { | |||
| 860 | nonl(); | |||
| 861 | refresh()wrefresh(stdscr); | |||
| 862 | } | |||
| 863 | } | |||
| 864 | ||||
| 865 | void | |||
| 866 | anykey(void) | |||
| 867 | { | |||
| 868 | int ch; | |||
| 869 | ssize_t len; | |||
| 870 | ||||
| 871 | standoutp(); | |||
| 872 | addstrp("Hit any key to continue: "); | |||
| 873 | standendp(); | |||
| 874 | if (smart_terminal) | |||
| 875 | refresh()wrefresh(stdscr); | |||
| 876 | else | |||
| 877 | fflush(stdout(&__sF[1])); | |||
| 878 | while (1) { | |||
| 879 | len = read(STDIN_FILENO0, &ch, 1); | |||
| 880 | if (len == -1 && errno(*__errno()) == EINTR4) | |||
| 881 | continue; | |||
| 882 | if (len == 0) | |||
| 883 | exit(1); | |||
| 884 | break; | |||
| 885 | } | |||
| 886 | } |