]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cgtop/cgtop.c
resolved: avoid possible dereference of null pointer
[thirdparty/systemd.git] / src / cgtop / cgtop.c
CommitLineData
8f2d43a0
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
8f2d43a0
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
8f2d43a0 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
8f2d43a0
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
1e913bcb 22#define __STDC_FORMAT_MACROS
8f2d43a0
LP
23#include <errno.h>
24#include <string.h>
25#include <stdlib.h>
1e913bcb 26#include <stdint.h>
8f2d43a0
LP
27#include <unistd.h>
28#include <alloca.h>
29#include <getopt.h>
30
9eb977db 31#include "path-util.h"
8f2d43a0
LP
32#include "util.h"
33#include "hashmap.h"
34#include "cgroup-util.h"
0d7e32fa 35#include "build.h"
a5c32cff 36#include "fileio.h"
8f2d43a0
LP
37
38typedef struct Group {
39 char *path;
40
41 bool n_tasks_valid:1;
42 bool cpu_valid:1;
43 bool memory_valid:1;
44 bool io_valid:1;
45
46 unsigned n_tasks;
47
48 unsigned cpu_iteration;
49 uint64_t cpu_usage;
50 struct timespec cpu_timestamp;
51 double cpu_fraction;
52
53 uint64_t memory;
54
55 unsigned io_iteration;
56 uint64_t io_input, io_output;
57 struct timespec io_timestamp;
58 uint64_t io_input_bps, io_output_bps;
59} Group;
60
30edf116 61static unsigned arg_depth = 3;
a152771a 62static unsigned arg_iterations = 0;
e66bb58b 63static bool arg_batch = false;
8f2d43a0
LP
64static usec_t arg_delay = 1*USEC_PER_SEC;
65
66static enum {
67 ORDER_PATH,
68 ORDER_TASKS,
69 ORDER_CPU,
70 ORDER_MEMORY,
71 ORDER_IO
72} arg_order = ORDER_CPU;
73
1e913bcb
UTL
74static enum {
75 CPU_PERCENT,
76 CPU_TIME,
77} arg_cpu_type = CPU_PERCENT;
78
8f2d43a0
LP
79static void group_free(Group *g) {
80 assert(g);
81
82 free(g->path);
83 free(g);
84}
85
86static void group_hashmap_clear(Hashmap *h) {
87 Group *g;
88
89 while ((g = hashmap_steal_first(h)))
90 group_free(g);
91}
92
93static void group_hashmap_free(Hashmap *h) {
94 group_hashmap_clear(h);
95 hashmap_free(h);
96}
97
98static int process(const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration) {
99 Group *g;
100 int r;
39883f62 101 FILE *f = NULL;
8f2d43a0
LP
102 pid_t pid;
103 unsigned n;
104
105 assert(controller);
106 assert(path);
107 assert(a);
108
109 g = hashmap_get(a, path);
110 if (!g) {
111 g = hashmap_get(b, path);
112 if (!g) {
113 g = new0(Group, 1);
114 if (!g)
115 return -ENOMEM;
116
117 g->path = strdup(path);
118 if (!g->path) {
119 group_free(g);
120 return -ENOMEM;
121 }
122
123 r = hashmap_put(a, g->path, g);
124 if (r < 0) {
125 group_free(g);
126 return r;
127 }
128 } else {
129 assert_se(hashmap_move_one(a, b, path) == 0);
130 g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false;
131 }
132 }
133
134 /* Regardless which controller, let's find the maximum number
135 * of processes in any of it */
136
b043cd0b 137 r = cg_enumerate_processes(controller, path, &f);
8f2d43a0
LP
138 if (r < 0)
139 return r;
140
141 n = 0;
142 while (cg_read_pid(f, &pid) > 0)
143 n++;
144 fclose(f);
145
146 if (n > 0) {
147 if (g->n_tasks_valid)
148 g->n_tasks = MAX(g->n_tasks, n);
149 else
150 g->n_tasks = n;
151
152 g->n_tasks_valid = true;
153 }
154
155 if (streq(controller, "cpuacct")) {
156 uint64_t new_usage;
157 char *p, *v;
158 struct timespec ts;
159
160 r = cg_get_path(controller, path, "cpuacct.usage", &p);
161 if (r < 0)
162 return r;
163
164 r = read_one_line_file(p, &v);
165 free(p);
166 if (r < 0)
167 return r;
168
169 r = safe_atou64(v, &new_usage);
170 free(v);
171 if (r < 0)
172 return r;
173
174 assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
175
176 if (g->cpu_iteration == iteration - 1) {
177 uint64_t x, y;
178
179 x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) -
180 ((uint64_t) g->cpu_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->cpu_timestamp.tv_nsec);
181
182 y = new_usage - g->cpu_usage;
183
184 if (y > 0) {
185 g->cpu_fraction = (double) y / (double) x;
186 g->cpu_valid = true;
187 }
188 }
189
190 g->cpu_usage = new_usage;
191 g->cpu_timestamp = ts;
192 g->cpu_iteration = iteration;
193
194 } else if (streq(controller, "memory")) {
195 char *p, *v;
196
197 r = cg_get_path(controller, path, "memory.usage_in_bytes", &p);
198 if (r < 0)
199 return r;
200
201 r = read_one_line_file(p, &v);
202 free(p);
203 if (r < 0)
204 return r;
205
206 r = safe_atou64(v, &g->memory);
207 free(v);
208 if (r < 0)
209 return r;
210
211 if (g->memory > 0)
212 g->memory_valid = true;
213
214 } else if (streq(controller, "blkio")) {
215 char *p;
216 uint64_t wr = 0, rd = 0;
217 struct timespec ts;
218
219 r = cg_get_path(controller, path, "blkio.io_service_bytes", &p);
220 if (r < 0)
221 return r;
222
223 f = fopen(p, "re");
224 free(p);
225
226 if (!f)
227 return -errno;
228
229 for (;;) {
230 char line[LINE_MAX], *l;
231 uint64_t k, *q;
232
233 if (!fgets(line, sizeof(line), f))
234 break;
235
236 l = strstrip(line);
237 l += strcspn(l, WHITESPACE);
238 l += strspn(l, WHITESPACE);
239
240 if (first_word(l, "Read")) {
241 l += 4;
242 q = &rd;
243 } else if (first_word(l, "Write")) {
244 l += 5;
245 q = &wr;
246 } else
247 continue;
248
249 l += strspn(l, WHITESPACE);
250 r = safe_atou64(l, &k);
251 if (r < 0)
252 continue;
253
254 *q += k;
255 }
256
257 fclose(f);
258
259 assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
260
261 if (g->io_iteration == iteration - 1) {
262 uint64_t x, yr, yw;
263
264 x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) -
265 ((uint64_t) g->io_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->io_timestamp.tv_nsec);
266
267 yr = rd - g->io_input;
268 yw = wr - g->io_output;
269
270 if (yr > 0 || yw > 0) {
271 g->io_input_bps = (yr * 1000000000ULL) / x;
272 g->io_output_bps = (yw * 1000000000ULL) / x;
273 g->io_valid = true;
274
275 }
276 }
277
278 g->io_input = rd;
279 g->io_output = wr;
280 g->io_timestamp = ts;
281 g->io_iteration = iteration;
282 }
283
284 return 0;
285}
286
287static int refresh_one(
288 const char *controller,
289 const char *path,
290 Hashmap *a,
291 Hashmap *b,
292 unsigned iteration,
293 unsigned depth) {
294
295 DIR *d = NULL;
296 int r;
297
298 assert(controller);
299 assert(path);
300 assert(a);
301
302 if (depth > arg_depth)
303 return 0;
304
305 r = process(controller, path, a, b, iteration);
306 if (r < 0)
307 return r;
308
309 r = cg_enumerate_subgroups(controller, path, &d);
310 if (r < 0) {
2f29c419 311 if (r == -ENOENT)
8f2d43a0
LP
312 return 0;
313
314 return r;
315 }
316
317 for (;;) {
318 char *fn, *p;
319
320 r = cg_read_subgroup(d, &fn);
321 if (r <= 0)
322 goto finish;
323
b7def684 324 p = strjoin(path, "/", fn, NULL);
8f2d43a0
LP
325 free(fn);
326
327 if (!p) {
328 r = -ENOMEM;
329 goto finish;
330 }
331
332 path_kill_slashes(p);
333
334 r = refresh_one(controller, p, a, b, iteration, depth + 1);
335 free(p);
336
337 if (r < 0)
338 goto finish;
339 }
340
341finish:
342 if (d)
343 closedir(d);
344
345 return r;
346}
347
348static int refresh(Hashmap *a, Hashmap *b, unsigned iteration) {
349 int r;
350
351 assert(a);
352
353 r = refresh_one("name=systemd", "/", a, b, iteration, 0);
354 if (r < 0)
63210a15
SL
355 if (r != -ENOENT)
356 return r;
8f2d43a0
LP
357 r = refresh_one("cpuacct", "/", a, b, iteration, 0);
358 if (r < 0)
63210a15
SL
359 if (r != -ENOENT)
360 return r;
8f2d43a0
LP
361 r = refresh_one("memory", "/", a, b, iteration, 0);
362 if (r < 0)
63210a15
SL
363 if (r != -ENOENT)
364 return r;
8f2d43a0 365
63210a15
SL
366 r = refresh_one("blkio", "/", a, b, iteration, 0);
367 if (r < 0)
368 if (r != -ENOENT)
369 return r;
370 return 0;
8f2d43a0
LP
371}
372
373static int group_compare(const void*a, const void *b) {
374 const Group *x = *(Group**)a, *y = *(Group**)b;
375
376 if (path_startswith(y->path, x->path))
377 return -1;
378 if (path_startswith(x->path, y->path))
379 return 1;
380
381 if (arg_order == ORDER_CPU) {
1e913bcb
UTL
382 if (arg_cpu_type == CPU_PERCENT) {
383 if (x->cpu_valid && y->cpu_valid) {
384 if (x->cpu_fraction > y->cpu_fraction)
385 return -1;
386 else if (x->cpu_fraction < y->cpu_fraction)
387 return 1;
388 } else if (x->cpu_valid)
8f2d43a0 389 return -1;
1e913bcb 390 else if (y->cpu_valid)
8f2d43a0 391 return 1;
1e913bcb
UTL
392 } else {
393 if (x->cpu_usage > y->cpu_usage)
394 return -1;
395 else if (x->cpu_usage < y->cpu_usage)
396 return 1;
397 }
8f2d43a0
LP
398 }
399
400 if (arg_order == ORDER_TASKS) {
401
402 if (x->n_tasks_valid && y->n_tasks_valid) {
403 if (x->n_tasks > y->n_tasks)
404 return -1;
405 else if (x->n_tasks < y->n_tasks)
406 return 1;
407 } else if (x->n_tasks_valid)
408 return -1;
409 else if (y->n_tasks_valid)
410 return 1;
411 }
412
413 if (arg_order == ORDER_MEMORY) {
414 if (x->memory_valid && y->memory_valid) {
415 if (x->memory > y->memory)
416 return -1;
417 else if (x->memory < y->memory)
418 return 1;
419 } else if (x->memory_valid)
420 return -1;
421 else if (y->memory_valid)
422 return 1;
423 }
424
425 if (arg_order == ORDER_IO) {
426 if (x->io_valid && y->io_valid) {
427 if (x->io_input_bps + x->io_output_bps > y->io_input_bps + y->io_output_bps)
428 return -1;
429 else if (x->io_input_bps + x->io_output_bps < y->io_input_bps + y->io_output_bps)
430 return 1;
431 } else if (x->io_valid)
432 return -1;
433 else if (y->io_valid)
434 return 1;
435 }
436
437 return strcmp(x->path, y->path);
438}
439
1e913bcb
UTL
440#define ON ANSI_HIGHLIGHT_ON
441#define OFF ANSI_HIGHLIGHT_OFF
442
8f2d43a0
LP
443static int display(Hashmap *a) {
444 Iterator i;
445 Group *g;
446 Group **array;
1e913bcb
UTL
447 signed path_columns;
448 unsigned rows, n = 0, j, maxtcpu = 0, maxtpath = 0;
62b95b8b 449 char buffer[MAX3(21, FORMAT_BYTES_MAX, FORMAT_TIMESPAN_MAX)];
8f2d43a0
LP
450
451 assert(a);
452
453 /* Set cursor to top left corner and clear screen */
1e913bcb
UTL
454 if (on_tty())
455 fputs("\033[H"
456 "\033[2J", stdout);
8f2d43a0
LP
457
458 array = alloca(sizeof(Group*) * hashmap_size(a));
459
460 HASHMAP_FOREACH(g, a, i)
461 if (g->n_tasks_valid || g->cpu_valid || g->memory_valid || g->io_valid)
462 array[n++] = g;
463
7ff7394d 464 qsort_safe(array, n, sizeof(Group*), group_compare);
8f2d43a0 465
1e913bcb
UTL
466 /* Find the longest names in one run */
467 for (j = 0; j < n; j++) {
468 unsigned cputlen, pathtlen;
62b95b8b
LP
469
470 format_timespan(buffer, sizeof(buffer), (nsec_t) (array[j]->cpu_usage / NSEC_PER_USEC), 0);
471 cputlen = strlen(buffer);
1e913bcb
UTL
472 maxtcpu = MAX(maxtcpu, cputlen);
473 pathtlen = strlen(array[j]->path);
474 maxtpath = MAX(maxtpath, pathtlen);
475 }
476
477 if (arg_cpu_type == CPU_PERCENT)
62b95b8b 478 snprintf(buffer, sizeof(buffer), "%6s", "%CPU");
1e913bcb 479 else
62b95b8b 480 snprintf(buffer, sizeof(buffer), "%*s", maxtcpu, "CPU Time");
1e913bcb 481
ed757c0c
LP
482 rows = lines();
483 if (rows <= 10)
484 rows = 10;
8f2d43a0 485
1e913bcb 486 if (on_tty()) {
62b95b8b 487 path_columns = columns() - 36 - strlen(buffer);
1e913bcb
UTL
488 if (path_columns < 10)
489 path_columns = 10;
490
491 printf("%s%-*s%s %s%7s%s %s%s%s %s%8s%s %s%8s%s %s%8s%s\n\n",
492 arg_order == ORDER_PATH ? ON : "", path_columns, "Path",
493 arg_order == ORDER_PATH ? OFF : "",
494 arg_order == ORDER_TASKS ? ON : "", "Tasks",
495 arg_order == ORDER_TASKS ? OFF : "",
62b95b8b 496 arg_order == ORDER_CPU ? ON : "", buffer,
1e913bcb
UTL
497 arg_order == ORDER_CPU ? OFF : "",
498 arg_order == ORDER_MEMORY ? ON : "", "Memory",
499 arg_order == ORDER_MEMORY ? OFF : "",
500 arg_order == ORDER_IO ? ON : "", "Input/s",
501 arg_order == ORDER_IO ? OFF : "",
502 arg_order == ORDER_IO ? ON : "", "Output/s",
503 arg_order == ORDER_IO ? OFF : "");
504 } else
505 path_columns = maxtpath;
8f2d43a0
LP
506
507 for (j = 0; j < n; j++) {
508 char *p;
8f2d43a0 509
1e913bcb 510 if (on_tty() && j + 5 > rows)
8f2d43a0
LP
511 break;
512
513 g = array[j];
514
11f96fac
ZJS
515 p = ellipsize(g->path, path_columns, 33);
516 printf("%-*s", path_columns, p ? p : g->path);
8f2d43a0
LP
517 free(p);
518
519 if (g->n_tasks_valid)
520 printf(" %7u", g->n_tasks);
521 else
522 fputs(" -", stdout);
523
62b95b8b 524 if (arg_cpu_type == CPU_PERCENT) {
1e913bcb
UTL
525 if (g->cpu_valid)
526 printf(" %6.1f", g->cpu_fraction*100);
527 else
528 fputs(" -", stdout);
62b95b8b
LP
529 } else
530 printf(" %*s", maxtcpu, format_timespan(buffer, sizeof(buffer), (nsec_t) (g->cpu_usage / NSEC_PER_USEC), 0));
8f2d43a0
LP
531
532 if (g->memory_valid)
62b95b8b 533 printf(" %8s", format_bytes(buffer, sizeof(buffer), g->memory));
8f2d43a0
LP
534 else
535 fputs(" -", stdout);
536
537 if (g->io_valid) {
538 printf(" %8s",
62b95b8b 539 format_bytes(buffer, sizeof(buffer), g->io_input_bps));
8f2d43a0 540 printf(" %8s",
62b95b8b 541 format_bytes(buffer, sizeof(buffer), g->io_output_bps));
8f2d43a0
LP
542 } else
543 fputs(" - -", stdout);
544
545 putchar('\n');
546 }
547
548 return 0;
549}
550
eb9da376 551static int help(void) {
8f2d43a0
LP
552
553 printf("%s [OPTIONS...]\n\n"
554 "Show top control groups by their resource usage.\n\n"
555 " -h --help Show this help\n"
0d7e32fa 556 " --version Print version and exit\n"
8f2d43a0
LP
557 " -p Order by path\n"
558 " -t Order by number of tasks\n"
559 " -c Order by CPU load\n"
560 " -m Order by memory load\n"
561 " -i Order by IO load\n"
1e913bcb
UTL
562 " --cpu[=TYPE] Show CPU usage as time or percentage (default)\n"
563 " -d --delay=DELAY Delay between updates\n"
a152771a 564 " -n --iterations=N Run for N iterations before exiting\n"
e66bb58b 565 " -b --batch Run in batch mode, accepting no input\n"
43a99a7a 566 " --depth=DEPTH Maximum traversal depth (default: %u)\n",
813ac7d3 567 program_invocation_short_name, arg_depth);
8f2d43a0 568
eb9da376 569 return 0;
0d7e32fa
ZJS
570}
571
8f2d43a0
LP
572static int parse_argv(int argc, char *argv[]) {
573
574 enum {
0d7e32fa
ZJS
575 ARG_VERSION = 0x100,
576 ARG_DEPTH,
1e913bcb 577 ARG_CPU_TYPE
8f2d43a0
LP
578 };
579
580 static const struct option options[] = {
0d7e32fa
ZJS
581 { "help", no_argument, NULL, 'h' },
582 { "version", no_argument, NULL, ARG_VERSION },
583 { "delay", required_argument, NULL, 'd' },
584 { "iterations", required_argument, NULL, 'n' },
585 { "batch", no_argument, NULL, 'b' },
586 { "depth", required_argument, NULL, ARG_DEPTH },
1e913bcb 587 { "cpu", optional_argument, NULL, ARG_CPU_TYPE},
eb9da376 588 {}
8f2d43a0
LP
589 };
590
591 int c;
592 int r;
593
594 assert(argc >= 1);
595 assert(argv);
596
e66bb58b 597 while ((c = getopt_long(argc, argv, "hptcmin:bd:", options, NULL)) >= 0) {
8f2d43a0
LP
598
599 switch (c) {
600
601 case 'h':
eb9da376 602 return help();
8f2d43a0 603
0d7e32fa 604 case ARG_VERSION:
eb9da376
LP
605 puts(PACKAGE_STRING);
606 puts(SYSTEMD_FEATURES);
0d7e32fa
ZJS
607 return 0;
608
1e913bcb
UTL
609 case ARG_CPU_TYPE:
610 if (optarg) {
611 if (strcmp(optarg, "time") == 0)
612 arg_cpu_type = CPU_TIME;
613 else if (strcmp(optarg, "percentage") == 0)
614 arg_cpu_type = CPU_PERCENT;
615 else
616 return -EINVAL;
617 }
618 break;
619
8f2d43a0
LP
620 case ARG_DEPTH:
621 r = safe_atou(optarg, &arg_depth);
622 if (r < 0) {
623 log_error("Failed to parse depth parameter.");
624 return -EINVAL;
625 }
626
627 break;
628
629 case 'd':
7f602784 630 r = parse_sec(optarg, &arg_delay);
8f2d43a0
LP
631 if (r < 0 || arg_delay <= 0) {
632 log_error("Failed to parse delay parameter.");
633 return -EINVAL;
634 }
635
636 break;
637
a152771a
DS
638 case 'n':
639 r = safe_atou(optarg, &arg_iterations);
640 if (r < 0) {
641 log_error("Failed to parse iterations parameter.");
642 return -EINVAL;
643 }
644
645 break;
646
e66bb58b
DS
647 case 'b':
648 arg_batch = true;
649 break;
650
8f2d43a0
LP
651 case 'p':
652 arg_order = ORDER_PATH;
653 break;
654
655 case 't':
656 arg_order = ORDER_TASKS;
657 break;
658
659 case 'c':
660 arg_order = ORDER_CPU;
661 break;
662
663 case 'm':
664 arg_order = ORDER_MEMORY;
665 break;
666
667 case 'i':
668 arg_order = ORDER_IO;
669 break;
670
671 case '?':
672 return -EINVAL;
673
674 default:
eb9da376 675 assert_not_reached("Unhandled option");
8f2d43a0
LP
676 }
677 }
678
679 if (optind < argc) {
680 log_error("Too many arguments.");
681 return -EINVAL;
682 }
683
684 return 1;
685}
686
687int main(int argc, char *argv[]) {
688 int r;
689 Hashmap *a = NULL, *b = NULL;
690 unsigned iteration = 0;
691 usec_t last_refresh = 0;
692 bool quit = false, immediate_refresh = false;
693
694 log_parse_environment();
695 log_open();
696
697 r = parse_argv(argc, argv);
698 if (r <= 0)
699 goto finish;
700
701 a = hashmap_new(string_hash_func, string_compare_func);
702 b = hashmap_new(string_hash_func, string_compare_func);
703 if (!a || !b) {
0d0f0c50 704 r = log_oom();
8f2d43a0
LP
705 goto finish;
706 }
707
ed757c0c 708 signal(SIGWINCH, columns_lines_cache_reset);
28917d7d 709
1e913bcb
UTL
710 if (!on_tty())
711 arg_iterations = 1;
712
8f2d43a0
LP
713 while (!quit) {
714 Hashmap *c;
715 usec_t t;
716 char key;
717 char h[FORMAT_TIMESPAN_MAX];
718
719 t = now(CLOCK_MONOTONIC);
720
721 if (t >= last_refresh + arg_delay || immediate_refresh) {
722
723 r = refresh(a, b, iteration++);
724 if (r < 0)
725 goto finish;
726
727 group_hashmap_clear(b);
728
729 c = a;
730 a = b;
731 b = c;
732
733 last_refresh = t;
734 immediate_refresh = false;
735 }
736
737 r = display(b);
738 if (r < 0)
739 goto finish;
740
a152771a
DS
741 if (arg_iterations && iteration >= arg_iterations)
742 break;
743
e66bb58b
DS
744 if (arg_batch) {
745 usleep(last_refresh + arg_delay - t);
746 } else {
747 r = read_one_char(stdin, &key,
748 last_refresh + arg_delay - t, NULL);
749 if (r == -ETIMEDOUT)
750 continue;
751 if (r < 0) {
752 log_error("Couldn't read key: %s", strerror(-r));
753 goto finish;
754 }
8f2d43a0
LP
755 }
756
757 fputs("\r \r", stdout);
758 fflush(stdout);
759
e66bb58b
DS
760 if (arg_batch)
761 continue;
762
8f2d43a0
LP
763 switch (key) {
764
765 case ' ':
766 immediate_refresh = true;
767 break;
768
769 case 'q':
770 quit = true;
771 break;
772
773 case 'p':
774 arg_order = ORDER_PATH;
775 break;
776
777 case 't':
778 arg_order = ORDER_TASKS;
779 break;
780
781 case 'c':
782 arg_order = ORDER_CPU;
783 break;
784
785 case 'm':
786 arg_order = ORDER_MEMORY;
787 break;
788
789 case 'i':
790 arg_order = ORDER_IO;
791 break;
792
2bfc1eda
ZJS
793 case '%':
794 arg_cpu_type = arg_cpu_type == CPU_TIME ? CPU_PERCENT : CPU_TIME;
795 break;
796
8f2d43a0
LP
797 case '+':
798 if (arg_delay < USEC_PER_SEC)
799 arg_delay += USEC_PER_MSEC*250;
800 else
801 arg_delay += USEC_PER_SEC;
802
2fa4092c 803 fprintf(stdout, "\nIncreased delay to %s.", format_timespan(h, sizeof(h), arg_delay, 0));
8f2d43a0
LP
804 fflush(stdout);
805 sleep(1);
806 break;
807
808 case '-':
809 if (arg_delay <= USEC_PER_MSEC*500)
810 arg_delay = USEC_PER_MSEC*250;
811 else if (arg_delay < USEC_PER_MSEC*1250)
812 arg_delay -= USEC_PER_MSEC*250;
813 else
814 arg_delay -= USEC_PER_SEC;
815
2fa4092c 816 fprintf(stdout, "\nDecreased delay to %s.", format_timespan(h, sizeof(h), arg_delay, 0));
8f2d43a0
LP
817 fflush(stdout);
818 sleep(1);
819 break;
820
821 case '?':
822 case 'h':
823 fprintf(stdout,
c851f34b 824 "\t<" ON "p" OFF "> By path; <" ON "t" OFF "> By tasks; <" ON "c" OFF "> By CPU; <" ON "m" OFF "> By memory; <" ON "i" OFF "> By I/O\n"
2bfc1eda 825 "\t<" ON "+" OFF "> Increase delay; <" ON "-" OFF "> Decrease delay; <" ON "%%" OFF "> Toggle time\n"
c851f34b 826 "\t<" ON "q" OFF "> Quit; <" ON "SPACE" OFF "> Refresh");
8f2d43a0
LP
827 fflush(stdout);
828 sleep(3);
829 break;
830
831 default:
832 fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key);
833 fflush(stdout);
834 sleep(1);
835 break;
836 }
837 }
838
8f2d43a0
LP
839 r = 0;
840
841finish:
842 group_hashmap_free(a);
843 group_hashmap_free(b);
844
14212119
SL
845 if (r < 0) {
846 log_error("Exiting with failure: %s", strerror(-r));
847 return EXIT_FAILURE;
848 }
849
850 return EXIT_SUCCESS;
8f2d43a0 851}