1 |
|
|
/* $OpenBSD: top.c,v 1.89 2017/03/15 04:24:14 deraadt 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 |
|
|
#include <sys/types.h> |
32 |
|
|
#include <curses.h> |
33 |
|
|
#include <err.h> |
34 |
|
|
#include <errno.h> |
35 |
|
|
#include <stdio.h> |
36 |
|
|
#include <signal.h> |
37 |
|
|
#include <string.h> |
38 |
|
|
#include <poll.h> |
39 |
|
|
#include <stdlib.h> |
40 |
|
|
#include <limits.h> |
41 |
|
|
#include <unistd.h> |
42 |
|
|
|
43 |
|
|
/* includes specific to top */ |
44 |
|
|
#include "display.h" /* interface to display package */ |
45 |
|
|
#include "screen.h" /* interface to screen package */ |
46 |
|
|
#include "top.h" |
47 |
|
|
#include "top.local.h" |
48 |
|
|
#include "boolean.h" |
49 |
|
|
#include "machine.h" |
50 |
|
|
#include "utils.h" |
51 |
|
|
|
52 |
|
|
/* Size of the stdio buffer given to stdout */ |
53 |
|
|
#define BUFFERSIZE 2048 |
54 |
|
|
|
55 |
|
|
/* The buffer that stdio will use */ |
56 |
|
|
char stdoutbuf[BUFFERSIZE]; |
57 |
|
|
|
58 |
|
|
/* signal handling routines */ |
59 |
|
|
static void leave(int); |
60 |
|
|
static void onalrm(int); |
61 |
|
|
static void tstop(int); |
62 |
|
|
static void sigwinch(int); |
63 |
|
|
|
64 |
|
|
volatile sig_atomic_t leaveflag, tstopflag, winchflag; |
65 |
|
|
|
66 |
|
|
static void reset_display(void); |
67 |
|
|
int rundisplay(void); |
68 |
|
|
|
69 |
|
|
static int max_topn; /* maximum displayable processes */ |
70 |
|
|
|
71 |
|
|
extern int (*proc_compares[])(const void *, const void *); |
72 |
|
|
int order_index; |
73 |
|
|
|
74 |
|
|
int displays = 0; /* indicates unspecified */ |
75 |
|
|
char do_unames = Yes; |
76 |
|
|
struct process_select ps; |
77 |
|
|
char interactive = Maybe; |
78 |
|
|
double delay = Default_DELAY; |
79 |
|
|
char *order_name = NULL; |
80 |
|
|
int topn = Default_TOPN; |
81 |
|
|
int no_command = Yes; |
82 |
|
|
int old_system = No; |
83 |
|
|
int old_threads = No; |
84 |
|
|
int show_args = No; |
85 |
|
|
pid_t hlpid = -1; |
86 |
|
|
int combine_cpus = 0; |
87 |
|
|
|
88 |
|
|
#if Default_TOPN == Infinity |
89 |
|
|
char topn_specified = No; |
90 |
|
|
#endif |
91 |
|
|
|
92 |
|
|
/* |
93 |
|
|
* these defines enumerate the "strchr"s of the commands in |
94 |
|
|
* command_chars |
95 |
|
|
*/ |
96 |
|
|
#define CMD_redraw 0 |
97 |
|
|
#define CMD_update 1 |
98 |
|
|
#define CMD_quit 2 |
99 |
|
|
#define CMD_help1 3 |
100 |
|
|
#define CMD_help2 4 |
101 |
|
|
#define CMD_OSLIMIT 4 /* terminals with OS can only handle commands */ |
102 |
|
|
#define CMD_errors 5 /* less than or equal to CMD_OSLIMIT */ |
103 |
|
|
#define CMD_number1 6 |
104 |
|
|
#define CMD_number2 7 |
105 |
|
|
#define CMD_delay 8 |
106 |
|
|
#define CMD_displays 9 |
107 |
|
|
#define CMD_kill 10 |
108 |
|
|
#define CMD_renice 11 |
109 |
|
|
#define CMD_idletog 12 |
110 |
|
|
#define CMD_idletog2 13 |
111 |
|
|
#define CMD_user 14 |
112 |
|
|
#define CMD_system 15 |
113 |
|
|
#define CMD_order 16 |
114 |
|
|
#define CMD_pid 17 |
115 |
|
|
#define CMD_command 18 |
116 |
|
|
#define CMD_threads 19 |
117 |
|
|
#define CMD_grep 20 |
118 |
|
|
#define CMD_add 21 |
119 |
|
|
#define CMD_hl 22 |
120 |
|
|
#define CMD_cpus 23 |
121 |
|
|
|
122 |
|
|
static void |
123 |
|
|
usage(void) |
124 |
|
|
{ |
125 |
|
|
extern char *__progname; |
126 |
|
|
|
127 |
|
|
fprintf(stderr, |
128 |
|
|
"usage: %s [-1bCHIinqSu] [-d count] [-g string] [-o field] " |
129 |
|
|
"[-p pid] [-s time]\n\t[-U [-]user] [number]\n", |
130 |
|
|
__progname); |
131 |
|
|
} |
132 |
|
|
|
133 |
|
|
static void |
134 |
|
|
parseargs(int ac, char **av) |
135 |
|
|
{ |
136 |
|
|
char *endp; |
137 |
|
|
int i; |
138 |
|
|
|
139 |
|
|
while ((i = getopt(ac, av, "1SHICbinqus:d:p:U:o:g:")) != -1) { |
140 |
|
|
switch (i) { |
141 |
|
|
case '1': |
142 |
|
|
combine_cpus = 1; |
143 |
|
|
break; |
144 |
|
|
case 'C': |
145 |
|
|
show_args = Yes; |
146 |
|
|
break; |
147 |
|
|
case 'u': /* toggle uid/username display */ |
148 |
|
|
do_unames = !do_unames; |
149 |
|
|
break; |
150 |
|
|
|
151 |
|
|
case 'U': /* display only username's processes */ |
152 |
|
|
if (optarg[0] == '-') { |
153 |
|
|
if ((ps.huid = userid(optarg+1)) == (uid_t)-1) |
154 |
|
|
new_message(MT_delayed, "%s: unknown user", |
155 |
|
|
optarg); |
156 |
|
|
else |
157 |
|
|
ps.uid = (uid_t)-1; |
158 |
|
|
} else if ((ps.uid = userid(optarg)) == (uid_t)-1) |
159 |
|
|
new_message(MT_delayed, "%s: unknown user", |
160 |
|
|
optarg); |
161 |
|
|
else |
162 |
|
|
ps.huid = (uid_t)-1; |
163 |
|
|
break; |
164 |
|
|
|
165 |
|
|
case 'p': { /* display only process id */ |
166 |
|
|
const char *errstr; |
167 |
|
|
|
168 |
|
|
i = strtonum(optarg, 0, INT_MAX, &errstr); |
169 |
|
|
if (errstr != NULL || !find_pid(i)) |
170 |
|
|
new_message(MT_delayed, "%s: unknown pid", |
171 |
|
|
optarg); |
172 |
|
|
else { |
173 |
|
|
ps.pid = (pid_t)i; |
174 |
|
|
ps.system = Yes; |
175 |
|
|
} |
176 |
|
|
break; |
177 |
|
|
} |
178 |
|
|
|
179 |
|
|
case 'S': /* show system processes */ |
180 |
|
|
ps.system = !ps.system; |
181 |
|
|
old_system = !old_system; |
182 |
|
|
break; |
183 |
|
|
|
184 |
|
|
case 'H': /* show threads */ |
185 |
|
|
ps.threads = Yes; |
186 |
|
|
old_threads = Yes; |
187 |
|
|
break; |
188 |
|
|
|
189 |
|
|
case 'I': /* show idle processes */ |
190 |
|
|
ps.idle = !ps.idle; |
191 |
|
|
break; |
192 |
|
|
|
193 |
|
|
case 'i': /* go interactive regardless */ |
194 |
|
|
interactive = Yes; |
195 |
|
|
break; |
196 |
|
|
|
197 |
|
|
case 'n': /* batch, or non-interactive */ |
198 |
|
|
case 'b': |
199 |
|
|
interactive = No; |
200 |
|
|
break; |
201 |
|
|
|
202 |
|
|
case 'd': /* number of displays to show */ |
203 |
|
|
if ((i = atoiwi(optarg)) != Invalid && i != 0) { |
204 |
|
|
displays = i; |
205 |
|
|
if (displays == 1) |
206 |
|
|
interactive = No; |
207 |
|
|
break; |
208 |
|
|
} |
209 |
|
|
new_message(MT_delayed, |
210 |
|
|
"warning: display count should be positive " |
211 |
|
|
"-- option ignored"); |
212 |
|
|
break; |
213 |
|
|
|
214 |
|
|
case 's': |
215 |
|
|
delay = strtod(optarg, &endp); |
216 |
|
|
|
217 |
|
|
if (delay >= 0 && delay <= 1000000 && *endp == '\0') |
218 |
|
|
break; |
219 |
|
|
|
220 |
|
|
new_message(MT_delayed, |
221 |
|
|
"warning: delay should be a non-negative number" |
222 |
|
|
" -- using default"); |
223 |
|
|
delay = Default_DELAY; |
224 |
|
|
break; |
225 |
|
|
|
226 |
|
|
case 'q': /* be quick about it */ |
227 |
|
|
/* only allow this if user is really root */ |
228 |
|
|
if (getuid() == 0) { |
229 |
|
|
/* be very un-nice! */ |
230 |
|
|
(void) nice(-20); |
231 |
|
|
break; |
232 |
|
|
} |
233 |
|
|
new_message(MT_delayed, |
234 |
|
|
"warning: `-q' option can only be used by root"); |
235 |
|
|
break; |
236 |
|
|
|
237 |
|
|
case 'o': /* select sort order */ |
238 |
|
|
order_name = optarg; |
239 |
|
|
break; |
240 |
|
|
|
241 |
|
|
case 'g': /* grep command name */ |
242 |
|
|
free(ps.command); |
243 |
|
|
if ((ps.command = strdup(optarg)) == NULL) |
244 |
|
|
err(1, NULL); |
245 |
|
|
break; |
246 |
|
|
|
247 |
|
|
default: |
248 |
|
|
usage(); |
249 |
|
|
exit(1); |
250 |
|
|
} |
251 |
|
|
} |
252 |
|
|
|
253 |
|
|
i = getncpu(); |
254 |
|
|
if (i == -1) |
255 |
|
|
err(1, NULL); |
256 |
|
|
|
257 |
|
|
if (i > 8) |
258 |
|
|
combine_cpus = 1; |
259 |
|
|
|
260 |
|
|
/* get count of top processes to display (if any) */ |
261 |
|
|
if (optind < ac) { |
262 |
|
|
if ((topn = atoiwi(av[optind])) == Invalid) { |
263 |
|
|
new_message(MT_delayed, |
264 |
|
|
"warning: process count should " |
265 |
|
|
"be a non-negative number -- using default"); |
266 |
|
|
topn = Infinity; |
267 |
|
|
} |
268 |
|
|
#if Default_TOPN == Infinity |
269 |
|
|
else |
270 |
|
|
topn_specified = Yes; |
271 |
|
|
#endif |
272 |
|
|
} |
273 |
|
|
} |
274 |
|
|
|
275 |
|
|
struct system_info system_info; |
276 |
|
|
struct statics statics; |
277 |
|
|
|
278 |
|
|
int |
279 |
|
|
main(int argc, char *argv[]) |
280 |
|
|
{ |
281 |
|
|
char *uname_field = "USERNAME", *header_text, *env_top; |
282 |
|
|
char *(*get_userid)(uid_t) = username; |
283 |
|
|
char **preset_argv = NULL, **av = argv; |
284 |
|
|
int preset_argc = 0, ac = argc, active_procs, i; |
285 |
|
|
sigset_t mask, oldmask; |
286 |
|
|
time_t curr_time; |
287 |
|
|
caddr_t processes; |
288 |
|
|
|
289 |
|
|
/* set the buffer for stdout */ |
290 |
|
|
#ifdef DEBUG |
291 |
|
|
setvbuf(stdout, NULL, _IONBUF, 0); |
292 |
|
|
#else |
293 |
|
|
setvbuf(stdout, stdoutbuf, _IOFBF, sizeof stdoutbuf); |
294 |
|
|
#endif |
295 |
|
|
|
296 |
|
|
/* initialize some selection options */ |
297 |
|
|
ps.idle = Yes; |
298 |
|
|
ps.system = No; |
299 |
|
|
ps.uid = (uid_t)-1; |
300 |
|
|
ps.huid = (uid_t)-1; |
301 |
|
|
ps.pid = (pid_t)-1; |
302 |
|
|
ps.command = NULL; |
303 |
|
|
|
304 |
|
|
/* get preset options from the environment */ |
305 |
|
|
if ((env_top = getenv("TOP")) != NULL) { |
306 |
|
|
av = preset_argv = argparse(env_top, &preset_argc); |
307 |
|
|
ac = preset_argc; |
308 |
|
|
|
309 |
|
|
/* |
310 |
|
|
* set the dummy argument to an explanatory message, in case |
311 |
|
|
* getopt encounters a bad argument |
312 |
|
|
*/ |
313 |
|
|
preset_argv[0] = "while processing environment"; |
314 |
|
|
} |
315 |
|
|
/* process options */ |
316 |
|
|
do { |
317 |
|
|
/* |
318 |
|
|
* if we're done doing the presets, then process the real |
319 |
|
|
* arguments |
320 |
|
|
*/ |
321 |
|
|
if (preset_argc == 0) { |
322 |
|
|
ac = argc; |
323 |
|
|
av = argv; |
324 |
|
|
optind = 1; |
325 |
|
|
} |
326 |
|
|
parseargs(ac, av); |
327 |
|
|
i = preset_argc; |
328 |
|
|
preset_argc = 0; |
329 |
|
|
} while (i != 0); |
330 |
|
|
|
331 |
|
|
if (pledge("stdio rpath getpw tty proc ps vminfo flock cpath wpath", NULL) == -1) |
332 |
|
|
err(1, "pledge"); |
333 |
|
|
|
334 |
|
|
/* set constants for username/uid display correctly */ |
335 |
|
|
if (!do_unames) { |
336 |
|
|
uname_field = " UID "; |
337 |
|
|
get_userid = format_uid; |
338 |
|
|
} |
339 |
|
|
/* initialize the kernel memory interface */ |
340 |
|
|
if (machine_init(&statics) == -1) |
341 |
|
|
exit(1); |
342 |
|
|
|
343 |
|
|
/* determine sorting order index, if necessary */ |
344 |
|
|
if (order_name != NULL) { |
345 |
|
|
if ((order_index = string_index(order_name, |
346 |
|
|
statics.order_names)) == -1) { |
347 |
|
|
char **pp, msg[512]; |
348 |
|
|
|
349 |
|
|
snprintf(msg, sizeof(msg), |
350 |
|
|
"'%s' is not a recognized sorting order", |
351 |
|
|
order_name); |
352 |
|
|
strlcat(msg, ". Valid are:", sizeof(msg)); |
353 |
|
|
pp = statics.order_names; |
354 |
|
|
while (*pp != NULL) { |
355 |
|
|
strlcat(msg, " ", sizeof(msg)); |
356 |
|
|
strlcat(msg, *pp++, sizeof(msg)); |
357 |
|
|
} |
358 |
|
|
new_message(MT_delayed, msg); |
359 |
|
|
order_index = 0; |
360 |
|
|
} |
361 |
|
|
} |
362 |
|
|
|
363 |
|
|
/* initialize termcap */ |
364 |
|
|
init_termcap(interactive); |
365 |
|
|
|
366 |
|
|
/* initialize display interface */ |
367 |
|
|
max_topn = display_init(&statics); |
368 |
|
|
|
369 |
|
|
/* print warning if user requested more processes than we can display */ |
370 |
|
|
if (topn > max_topn) |
371 |
|
|
new_message(MT_delayed, |
372 |
|
|
"warning: this terminal can only display %d processes", |
373 |
|
|
max_topn); |
374 |
|
|
/* adjust for topn == Infinity */ |
375 |
|
|
if (topn == Infinity) { |
376 |
|
|
/* |
377 |
|
|
* For smart terminals, infinity really means everything that can |
378 |
|
|
* be displayed, or Largest. |
379 |
|
|
* On dumb terminals, infinity means every process in the system! |
380 |
|
|
* We only really want to do that if it was explicitly specified. |
381 |
|
|
* This is always the case when "Default_TOPN != Infinity". But if |
382 |
|
|
* topn wasn't explicitly specified and we are on a dumb terminal |
383 |
|
|
* and the default is Infinity, then (and only then) we use |
384 |
|
|
* "Nominal_TOPN" instead. |
385 |
|
|
*/ |
386 |
|
|
#if Default_TOPN == Infinity |
387 |
|
|
topn = smart_terminal ? Largest : |
388 |
|
|
(topn_specified ? Largest : Nominal_TOPN); |
389 |
|
|
#else |
390 |
|
|
topn = Largest; |
391 |
|
|
#endif |
392 |
|
|
} |
393 |
|
|
/* set header display accordingly */ |
394 |
|
|
display_header(topn > 0); |
395 |
|
|
|
396 |
|
|
/* determine interactive state */ |
397 |
|
|
if (interactive == Maybe) |
398 |
|
|
interactive = smart_terminal; |
399 |
|
|
|
400 |
|
|
/* if # of displays not specified, fill it in */ |
401 |
|
|
if (displays == 0) |
402 |
|
|
displays = smart_terminal ? Infinity : 1; |
403 |
|
|
|
404 |
|
|
/* |
405 |
|
|
* block interrupt signals while setting up the screen and the |
406 |
|
|
* handlers |
407 |
|
|
*/ |
408 |
|
|
sigemptyset(&mask); |
409 |
|
|
sigaddset(&mask, SIGINT); |
410 |
|
|
sigaddset(&mask, SIGQUIT); |
411 |
|
|
sigaddset(&mask, SIGTSTP); |
412 |
|
|
sigprocmask(SIG_BLOCK, &mask, &oldmask); |
413 |
|
|
if (interactive) |
414 |
|
|
init_screen(); |
415 |
|
|
(void) signal(SIGINT, leave); |
416 |
|
|
siginterrupt(SIGINT, 1); |
417 |
|
|
(void) signal(SIGQUIT, leave); |
418 |
|
|
(void) signal(SIGTSTP, tstop); |
419 |
|
|
if (smart_terminal) |
420 |
|
|
(void) signal(SIGWINCH, sigwinch); |
421 |
|
|
sigprocmask(SIG_SETMASK, &oldmask, NULL); |
422 |
|
|
restart: |
423 |
|
|
|
424 |
|
|
/* |
425 |
|
|
* main loop -- repeat while display count is positive or while it |
426 |
|
|
* indicates infinity (by being -1) |
427 |
|
|
*/ |
428 |
|
|
while ((displays == -1) || (displays-- > 0)) { |
429 |
|
|
if (winchflag) { |
430 |
|
|
/* |
431 |
|
|
* reascertain the screen |
432 |
|
|
* dimensions |
433 |
|
|
*/ |
434 |
|
|
get_screensize(); |
435 |
|
|
resizeterm(screen_length, screen_width + 1); |
436 |
|
|
|
437 |
|
|
/* tell display to resize */ |
438 |
|
|
max_topn = display_resize(); |
439 |
|
|
|
440 |
|
|
/* reset the signal handler */ |
441 |
|
|
(void) signal(SIGWINCH, sigwinch); |
442 |
|
|
|
443 |
|
|
reset_display(); |
444 |
|
|
winchflag = 0; |
445 |
|
|
} |
446 |
|
|
|
447 |
|
|
/* get the current stats */ |
448 |
|
|
get_system_info(&system_info); |
449 |
|
|
|
450 |
|
|
/* get the current set of processes */ |
451 |
|
|
processes = get_process_info(&system_info, &ps, |
452 |
|
|
proc_compares[order_index]); |
453 |
|
|
|
454 |
|
|
/* display the load averages */ |
455 |
|
|
i_loadave(system_info.last_pid, system_info.load_avg); |
456 |
|
|
|
457 |
|
|
/* display the current time */ |
458 |
|
|
/* this method of getting the time SHOULD be fairly portable */ |
459 |
|
|
time(&curr_time); |
460 |
|
|
i_timeofday(&curr_time); |
461 |
|
|
|
462 |
|
|
/* display process/threads state breakdown */ |
463 |
|
|
i_procstates(system_info.p_total, system_info.procstates, |
464 |
|
|
ps.threads); |
465 |
|
|
|
466 |
|
|
/* display the cpu state percentage breakdown */ |
467 |
|
|
i_cpustates(system_info.cpustates); |
468 |
|
|
|
469 |
|
|
/* display memory stats */ |
470 |
|
|
i_memory(system_info.memory); |
471 |
|
|
|
472 |
|
|
/* handle message area */ |
473 |
|
|
i_message(); |
474 |
|
|
|
475 |
|
|
/* get the string to use for the process area header */ |
476 |
|
|
header_text = format_header(uname_field, ps.threads); |
477 |
|
|
|
478 |
|
|
/* update the header area */ |
479 |
|
|
i_header(header_text); |
480 |
|
|
|
481 |
|
|
if (topn == Infinity) { |
482 |
|
|
#if Default_TOPN == Infinity |
483 |
|
|
topn = smart_terminal ? Largest : |
484 |
|
|
(topn_specified ? Largest : Nominal_TOPN); |
485 |
|
|
#else |
486 |
|
|
topn = Largest; |
487 |
|
|
#endif |
488 |
|
|
} |
489 |
|
|
|
490 |
|
|
if (topn > 0) { |
491 |
|
|
/* determine number of processes to actually display */ |
492 |
|
|
/* |
493 |
|
|
* this number will be the smallest of: active |
494 |
|
|
* processes, number user requested, number current |
495 |
|
|
* screen accommodates |
496 |
|
|
*/ |
497 |
|
|
active_procs = system_info.p_active; |
498 |
|
|
if (active_procs > topn) |
499 |
|
|
active_procs = topn; |
500 |
|
|
if (active_procs > max_topn) |
501 |
|
|
active_procs = max_topn; |
502 |
|
|
/* now show the top "n" processes. */ |
503 |
|
|
for (i = 0; i < active_procs; i++) { |
504 |
|
|
pid_t pid; |
505 |
|
|
char * s; |
506 |
|
|
|
507 |
|
|
s = format_next_process(processes, get_userid, |
508 |
|
|
&pid, ps.threads); |
509 |
|
|
i_process(i, s, pid == hlpid); |
510 |
|
|
} |
511 |
|
|
} |
512 |
|
|
|
513 |
|
|
/* do end-screen processing */ |
514 |
|
|
u_endscreen(); |
515 |
|
|
|
516 |
|
|
/* now, flush the output buffer */ |
517 |
|
|
fflush(stdout); |
518 |
|
|
|
519 |
|
|
if (smart_terminal) |
520 |
|
|
refresh(); |
521 |
|
|
|
522 |
|
|
/* only do the rest if we have more displays to show */ |
523 |
|
|
if (displays) { |
524 |
|
|
/* switch out for new display on smart terminals */ |
525 |
|
|
no_command = Yes; |
526 |
|
|
if (!interactive) { |
527 |
|
|
/* set up alarm */ |
528 |
|
|
(void) signal(SIGALRM, onalrm); |
529 |
|
|
(void) alarm((unsigned) delay); |
530 |
|
|
|
531 |
|
|
/* wait for the rest of it .... */ |
532 |
|
|
pause(); |
533 |
|
|
if (leaveflag) |
534 |
|
|
exit(0); |
535 |
|
|
if (tstopflag) { |
536 |
|
|
(void) signal(SIGTSTP, SIG_DFL); |
537 |
|
|
(void) kill(0, SIGTSTP); |
538 |
|
|
/* reset the signal handler */ |
539 |
|
|
(void) signal(SIGTSTP, tstop); |
540 |
|
|
tstopflag = 0; |
541 |
|
|
} |
542 |
|
|
} else { |
543 |
|
|
while (no_command) |
544 |
|
|
if (rundisplay()) |
545 |
|
|
goto restart; |
546 |
|
|
} |
547 |
|
|
} |
548 |
|
|
} |
549 |
|
|
|
550 |
|
|
quit(0); |
551 |
|
|
/* NOTREACHED */ |
552 |
|
|
return (0); |
553 |
|
|
} |
554 |
|
|
|
555 |
|
|
int |
556 |
|
|
rundisplay(void) |
557 |
|
|
{ |
558 |
|
|
static char tempbuf[TEMPBUFSIZE]; |
559 |
|
|
sigset_t mask; |
560 |
|
|
char ch, *iptr; |
561 |
|
|
int change, i; |
562 |
|
|
struct pollfd pfd[1]; |
563 |
|
|
uid_t uid, huid; |
564 |
|
|
static char command_chars[] = "\f qh?en#sdkriIuSopCHg+P1"; |
565 |
|
|
|
566 |
|
|
/* |
567 |
|
|
* assume valid command unless told |
568 |
|
|
* otherwise |
569 |
|
|
*/ |
570 |
|
|
no_command = No; |
571 |
|
|
|
572 |
|
|
/* |
573 |
|
|
* set up arguments for select with |
574 |
|
|
* timeout |
575 |
|
|
*/ |
576 |
|
|
pfd[0].fd = STDIN_FILENO; |
577 |
|
|
pfd[0].events = POLLIN; |
578 |
|
|
|
579 |
|
|
if (leaveflag) |
580 |
|
|
quit(0); |
581 |
|
|
if (tstopflag) { |
582 |
|
|
/* move to the lower left */ |
583 |
|
|
end_screen(); |
584 |
|
|
fflush(stdout); |
585 |
|
|
|
586 |
|
|
/* |
587 |
|
|
* default the signal handler |
588 |
|
|
* action |
589 |
|
|
*/ |
590 |
|
|
(void) signal(SIGTSTP, SIG_DFL); |
591 |
|
|
|
592 |
|
|
/* |
593 |
|
|
* unblock the signal and |
594 |
|
|
* send ourselves one |
595 |
|
|
*/ |
596 |
|
|
sigemptyset(&mask); |
597 |
|
|
sigaddset(&mask, SIGTSTP); |
598 |
|
|
sigprocmask(SIG_UNBLOCK, &mask, NULL); |
599 |
|
|
(void) kill(0, SIGTSTP); |
600 |
|
|
|
601 |
|
|
/* reset the signal handler */ |
602 |
|
|
(void) signal(SIGTSTP, tstop); |
603 |
|
|
|
604 |
|
|
/* reinit screen */ |
605 |
|
|
reinit_screen(); |
606 |
|
|
reset_display(); |
607 |
|
|
tstopflag = 0; |
608 |
|
|
return 1; |
609 |
|
|
} |
610 |
|
|
/* |
611 |
|
|
* wait for either input or the end |
612 |
|
|
* of the delay period |
613 |
|
|
*/ |
614 |
|
|
if (poll(pfd, 1, (int)(delay * 1000)) > 0) { |
615 |
|
|
char *errmsg; |
616 |
|
|
ssize_t len; |
617 |
|
|
|
618 |
|
|
if ((pfd[0].revents & (POLLERR|POLLHUP|POLLNVAL))) |
619 |
|
|
exit(1); |
620 |
|
|
|
621 |
|
|
clear_message(); |
622 |
|
|
|
623 |
|
|
/* |
624 |
|
|
* now read it and convert to |
625 |
|
|
* command strchr |
626 |
|
|
*/ |
627 |
|
|
while (1) { |
628 |
|
|
len = read(STDIN_FILENO, &ch, 1); |
629 |
|
|
if (len == -1 && errno == EINTR) |
630 |
|
|
continue; |
631 |
|
|
if (len == 0) |
632 |
|
|
exit(1); |
633 |
|
|
break; |
634 |
|
|
} |
635 |
|
|
if ((iptr = strchr(command_chars, ch)) == NULL) { |
636 |
|
|
/* illegal command */ |
637 |
|
|
new_message(MT_standout, " Command not understood"); |
638 |
|
|
putr(); |
639 |
|
|
no_command = Yes; |
640 |
|
|
fflush(stdout); |
641 |
|
|
return (0); |
642 |
|
|
} |
643 |
|
|
|
644 |
|
|
change = iptr - command_chars; |
645 |
|
|
|
646 |
|
|
switch (change) { |
647 |
|
|
case CMD_redraw: /* redraw screen */ |
648 |
|
|
reset_display(); |
649 |
|
|
break; |
650 |
|
|
|
651 |
|
|
case CMD_update: /* merely update display */ |
652 |
|
|
/* |
653 |
|
|
* is the load average high? |
654 |
|
|
*/ |
655 |
|
|
if (system_info.load_avg[0] > LoadMax) { |
656 |
|
|
/* yes, go home for visual feedback */ |
657 |
|
|
go_home(); |
658 |
|
|
fflush(stdout); |
659 |
|
|
} |
660 |
|
|
break; |
661 |
|
|
|
662 |
|
|
case CMD_quit: /* quit */ |
663 |
|
|
quit(0); |
664 |
|
|
break; |
665 |
|
|
|
666 |
|
|
case CMD_help1: /* help */ |
667 |
|
|
case CMD_help2: |
668 |
|
|
clear(); |
669 |
|
|
show_help(); |
670 |
|
|
anykey(); |
671 |
|
|
clear(); |
672 |
|
|
break; |
673 |
|
|
|
674 |
|
|
case CMD_errors: /* show errors */ |
675 |
|
|
if (error_count() == 0) { |
676 |
|
|
new_message(MT_standout, |
677 |
|
|
" Currently no errors to report."); |
678 |
|
|
putr(); |
679 |
|
|
no_command = Yes; |
680 |
|
|
} else { |
681 |
|
|
clear(); |
682 |
|
|
show_errors(); |
683 |
|
|
anykey(); |
684 |
|
|
clear(); |
685 |
|
|
} |
686 |
|
|
break; |
687 |
|
|
|
688 |
|
|
case CMD_number1: /* new number */ |
689 |
|
|
case CMD_number2: |
690 |
|
|
new_message(MT_standout, |
691 |
|
|
"Number of processes to show: "); |
692 |
|
|
|
693 |
|
|
if (readline(tempbuf, 8) > 0) { |
694 |
|
|
if ((i = atoiwi(tempbuf)) != Invalid) { |
695 |
|
|
if (i > max_topn) { |
696 |
|
|
new_message(MT_standout | |
697 |
|
|
MT_delayed, |
698 |
|
|
" This terminal can only " |
699 |
|
|
"display %d processes.", |
700 |
|
|
max_topn); |
701 |
|
|
putr(); |
702 |
|
|
} |
703 |
|
|
if ((i > topn || i == Infinity) |
704 |
|
|
&& topn == 0) { |
705 |
|
|
/* redraw the header */ |
706 |
|
|
display_header(Yes); |
707 |
|
|
} else if (i == 0) |
708 |
|
|
display_header(No); |
709 |
|
|
topn = i; |
710 |
|
|
} else { |
711 |
|
|
new_message(MT_standout, |
712 |
|
|
"Processes should be a " |
713 |
|
|
"non-negative number"); |
714 |
|
|
putr(); |
715 |
|
|
no_command = Yes; |
716 |
|
|
} |
717 |
|
|
} else |
718 |
|
|
clear_message(); |
719 |
|
|
break; |
720 |
|
|
|
721 |
|
|
case CMD_delay: /* new seconds delay */ |
722 |
|
|
new_message(MT_standout, "Seconds to delay: "); |
723 |
|
|
if (readline(tempbuf, sizeof(tempbuf)) > 0) { |
724 |
|
|
char *endp; |
725 |
|
|
double newdelay = strtod(tempbuf, &endp); |
726 |
|
|
|
727 |
|
|
if (newdelay >= 0 && newdelay <= 1000000 && |
728 |
|
|
*endp == '\0') { |
729 |
|
|
delay = newdelay; |
730 |
|
|
} else { |
731 |
|
|
new_message(MT_standout, |
732 |
|
|
"Delay should be a non-negative number"); |
733 |
|
|
putr(); |
734 |
|
|
no_command = Yes; |
735 |
|
|
} |
736 |
|
|
|
737 |
|
|
} else |
738 |
|
|
clear_message(); |
739 |
|
|
break; |
740 |
|
|
|
741 |
|
|
case CMD_displays: /* change display count */ |
742 |
|
|
new_message(MT_standout, |
743 |
|
|
"Displays to show (currently %s): ", |
744 |
|
|
displays == -1 ? "infinite" : |
745 |
|
|
itoa(displays)); |
746 |
|
|
|
747 |
|
|
if (readline(tempbuf, 10) > 0) { |
748 |
|
|
if ((i = atoiwi(tempbuf)) != Invalid) { |
749 |
|
|
if (i == 0) |
750 |
|
|
quit(0); |
751 |
|
|
displays = i; |
752 |
|
|
} else { |
753 |
|
|
new_message(MT_standout, |
754 |
|
|
"Displays should be a non-negative number"); |
755 |
|
|
putr(); |
756 |
|
|
no_command = Yes; |
757 |
|
|
} |
758 |
|
|
} else |
759 |
|
|
clear_message(); |
760 |
|
|
break; |
761 |
|
|
|
762 |
|
|
case CMD_kill: /* kill program */ |
763 |
|
|
new_message(0, "kill "); |
764 |
|
|
if (readline(tempbuf, sizeof(tempbuf)) > 0) { |
765 |
|
|
if ((errmsg = kill_procs(tempbuf)) != NULL) { |
766 |
|
|
new_message(MT_standout, "%s", errmsg); |
767 |
|
|
putr(); |
768 |
|
|
no_command = Yes; |
769 |
|
|
} |
770 |
|
|
} else |
771 |
|
|
clear_message(); |
772 |
|
|
break; |
773 |
|
|
|
774 |
|
|
case CMD_renice: /* renice program */ |
775 |
|
|
new_message(0, "renice "); |
776 |
|
|
if (readline(tempbuf, sizeof(tempbuf)) > 0) { |
777 |
|
|
if ((errmsg = renice_procs(tempbuf)) != NULL) { |
778 |
|
|
new_message(MT_standout, "%s", errmsg); |
779 |
|
|
putr(); |
780 |
|
|
no_command = Yes; |
781 |
|
|
} |
782 |
|
|
} else |
783 |
|
|
clear_message(); |
784 |
|
|
break; |
785 |
|
|
|
786 |
|
|
case CMD_idletog: |
787 |
|
|
case CMD_idletog2: |
788 |
|
|
ps.idle = !ps.idle; |
789 |
|
|
new_message(MT_standout | MT_delayed, |
790 |
|
|
" %sisplaying idle processes.", |
791 |
|
|
ps.idle ? "D" : "Not d"); |
792 |
|
|
putr(); |
793 |
|
|
break; |
794 |
|
|
|
795 |
|
|
case CMD_user: |
796 |
|
|
new_message(MT_standout, |
797 |
|
|
"Username to show: "); |
798 |
|
|
if (readline(tempbuf, sizeof(tempbuf)) > 0) { |
799 |
|
|
if ((tempbuf[0] == '+' || tempbuf[0] == '-') && |
800 |
|
|
tempbuf[1] == '\0') { |
801 |
|
|
ps.uid = (uid_t)-1; |
802 |
|
|
ps.huid = (uid_t)-1; |
803 |
|
|
} else if (tempbuf[0] == '-') { |
804 |
|
|
if ((huid = userid(tempbuf+1)) == (uid_t)-1) { |
805 |
|
|
new_message(MT_standout, |
806 |
|
|
" %s: unknown user", tempbuf+1); |
807 |
|
|
no_command = Yes; |
808 |
|
|
} else { |
809 |
|
|
ps.huid = huid; |
810 |
|
|
ps.uid = (uid_t)-1; |
811 |
|
|
} |
812 |
|
|
} else if ((uid = userid(tempbuf)) == (uid_t)-1) { |
813 |
|
|
new_message(MT_standout, |
814 |
|
|
" %s: unknown user", tempbuf); |
815 |
|
|
no_command = Yes; |
816 |
|
|
} else { |
817 |
|
|
ps.uid = uid; |
818 |
|
|
ps.huid = (uid_t)-1; |
819 |
|
|
} |
820 |
|
|
putr(); |
821 |
|
|
} else |
822 |
|
|
clear_message(); |
823 |
|
|
break; |
824 |
|
|
|
825 |
|
|
case CMD_system: |
826 |
|
|
ps.system = !ps.system; |
827 |
|
|
old_system = ps.system; |
828 |
|
|
new_message(MT_standout | MT_delayed, |
829 |
|
|
" %sisplaying system processes.", |
830 |
|
|
ps.system ? "D" : "Not d"); |
831 |
|
|
break; |
832 |
|
|
|
833 |
|
|
case CMD_order: |
834 |
|
|
new_message(MT_standout, |
835 |
|
|
"Order to sort: "); |
836 |
|
|
if (readline(tempbuf, sizeof(tempbuf)) > 0) { |
837 |
|
|
if ((i = string_index(tempbuf, |
838 |
|
|
statics.order_names)) == -1) { |
839 |
|
|
new_message(MT_standout, |
840 |
|
|
" %s: unrecognized sorting order", |
841 |
|
|
tempbuf); |
842 |
|
|
no_command = Yes; |
843 |
|
|
} else |
844 |
|
|
order_index = i; |
845 |
|
|
putr(); |
846 |
|
|
} else |
847 |
|
|
clear_message(); |
848 |
|
|
break; |
849 |
|
|
|
850 |
|
|
case CMD_pid: |
851 |
|
|
new_message(MT_standout, "Process ID to show: "); |
852 |
|
|
if (readline(tempbuf, sizeof(tempbuf)) > 0) { |
853 |
|
|
if (tempbuf[0] == '+' && |
854 |
|
|
tempbuf[1] == '\0') { |
855 |
|
|
ps.pid = (pid_t)-1; |
856 |
|
|
ps.system = old_system; |
857 |
|
|
} else { |
858 |
|
|
unsigned long long num; |
859 |
|
|
const char *errstr; |
860 |
|
|
|
861 |
|
|
num = strtonum(tempbuf, 0, INT_MAX, |
862 |
|
|
&errstr); |
863 |
|
|
if (errstr != NULL || !find_pid(num)) { |
864 |
|
|
new_message(MT_standout, |
865 |
|
|
" %s: unknown pid", |
866 |
|
|
tempbuf); |
867 |
|
|
no_command = Yes; |
868 |
|
|
} else { |
869 |
|
|
if (ps.system == No) |
870 |
|
|
old_system = No; |
871 |
|
|
ps.pid = (pid_t)num; |
872 |
|
|
ps.system = Yes; |
873 |
|
|
} |
874 |
|
|
} |
875 |
|
|
putr(); |
876 |
|
|
} else |
877 |
|
|
clear_message(); |
878 |
|
|
break; |
879 |
|
|
|
880 |
|
|
case CMD_command: |
881 |
|
|
show_args = (show_args == No) ? Yes : No; |
882 |
|
|
break; |
883 |
|
|
|
884 |
|
|
case CMD_threads: |
885 |
|
|
ps.threads = !ps.threads; |
886 |
|
|
old_threads = ps.threads; |
887 |
|
|
new_message(MT_standout | MT_delayed, |
888 |
|
|
" %sisplaying threads.", |
889 |
|
|
ps.threads ? "D" : "Not d"); |
890 |
|
|
break; |
891 |
|
|
|
892 |
|
|
case CMD_grep: |
893 |
|
|
new_message(MT_standout, |
894 |
|
|
"Grep command name: "); |
895 |
|
|
if (readline(tempbuf, sizeof(tempbuf)) > 0) { |
896 |
|
|
free(ps.command); |
897 |
|
|
if (tempbuf[0] == '+' && |
898 |
|
|
tempbuf[1] == '\0') |
899 |
|
|
ps.command = NULL; |
900 |
|
|
else |
901 |
|
|
if ((ps.command = strdup(tempbuf)) == |
902 |
|
|
NULL) |
903 |
|
|
err(1, NULL); |
904 |
|
|
putr(); |
905 |
|
|
} else |
906 |
|
|
clear_message(); |
907 |
|
|
break; |
908 |
|
|
|
909 |
|
|
case CMD_hl: |
910 |
|
|
new_message(MT_standout, "Process ID to highlight: "); |
911 |
|
|
if (readline(tempbuf, sizeof(tempbuf)) > 0) { |
912 |
|
|
if (tempbuf[0] == '+' && |
913 |
|
|
tempbuf[1] == '\0') { |
914 |
|
|
hlpid = -1; |
915 |
|
|
} else { |
916 |
|
|
unsigned long long num; |
917 |
|
|
const char *errstr; |
918 |
|
|
|
919 |
|
|
num = strtonum(tempbuf, 0, INT_MAX, |
920 |
|
|
&errstr); |
921 |
|
|
if (errstr != NULL || !find_pid(num)) { |
922 |
|
|
new_message(MT_standout, |
923 |
|
|
" %s: unknown pid", |
924 |
|
|
tempbuf); |
925 |
|
|
no_command = Yes; |
926 |
|
|
} else |
927 |
|
|
hlpid = (pid_t)num; |
928 |
|
|
} |
929 |
|
|
putr(); |
930 |
|
|
} else |
931 |
|
|
clear_message(); |
932 |
|
|
break; |
933 |
|
|
|
934 |
|
|
case CMD_add: |
935 |
|
|
ps.uid = (uid_t)-1; /* uid */ |
936 |
|
|
ps.huid = (uid_t)-1; |
937 |
|
|
ps.pid = (pid_t)-1; /* pid */ |
938 |
|
|
ps.system = old_system; |
939 |
|
|
ps.command = NULL; /* grep */ |
940 |
|
|
hlpid = -1; |
941 |
|
|
break; |
942 |
|
|
case CMD_cpus: |
943 |
|
|
combine_cpus = !combine_cpus; |
944 |
|
|
max_topn = display_resize(); |
945 |
|
|
reset_display(); |
946 |
|
|
break; |
947 |
|
|
default: |
948 |
|
|
new_message(MT_standout, " BAD CASE IN SWITCH!"); |
949 |
|
|
putr(); |
950 |
|
|
} |
951 |
|
|
} |
952 |
|
|
|
953 |
|
|
/* flush out stuff that may have been written */ |
954 |
|
|
fflush(stdout); |
955 |
|
|
return 0; |
956 |
|
|
} |
957 |
|
|
|
958 |
|
|
|
959 |
|
|
/* |
960 |
|
|
* reset_display() - reset all the display routine pointers so that entire |
961 |
|
|
* screen will get redrawn. |
962 |
|
|
*/ |
963 |
|
|
static void |
964 |
|
|
reset_display(void) |
965 |
|
|
{ |
966 |
|
|
if (smart_terminal) { |
967 |
|
|
clear(); |
968 |
|
|
refresh(); |
969 |
|
|
} |
970 |
|
|
} |
971 |
|
|
|
972 |
|
|
/* ARGSUSED */ |
973 |
|
|
void |
974 |
|
|
leave(int signo) |
975 |
|
|
{ |
976 |
|
|
leaveflag = 1; |
977 |
|
|
} |
978 |
|
|
|
979 |
|
|
/* ARGSUSED */ |
980 |
|
|
void |
981 |
|
|
tstop(int signo) |
982 |
|
|
{ |
983 |
|
|
tstopflag = 1; |
984 |
|
|
} |
985 |
|
|
|
986 |
|
|
/* ARGSUSED */ |
987 |
|
|
void |
988 |
|
|
sigwinch(int signo) |
989 |
|
|
{ |
990 |
|
|
winchflag = 1; |
991 |
|
|
} |
992 |
|
|
|
993 |
|
|
/* ARGSUSED */ |
994 |
|
|
void |
995 |
|
|
onalrm(int signo) |
996 |
|
|
{ |
997 |
|
|
} |
998 |
|
|
|
999 |
|
|
void |
1000 |
|
|
quit(int ret) |
1001 |
|
|
{ |
1002 |
|
|
end_screen(); |
1003 |
|
|
exit(ret); |
1004 |
|
|
} |