]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cgtop/cgtop.c
treewide: auto-convert the simple cases to log_*_errno()
[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 {
2d5c93c7
MS
129 r = hashmap_move_one(a, b, path);
130 if (r < 0)
131 return r;
8f2d43a0
LP
132 g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false;
133 }
134 }
135
136 /* Regardless which controller, let's find the maximum number
137 * of processes in any of it */
138
b043cd0b 139 r = cg_enumerate_processes(controller, path, &f);
8f2d43a0
LP
140 if (r < 0)
141 return r;
142
143 n = 0;
144 while (cg_read_pid(f, &pid) > 0)
145 n++;
146 fclose(f);
147
148 if (n > 0) {
149 if (g->n_tasks_valid)
150 g->n_tasks = MAX(g->n_tasks, n);
151 else
152 g->n_tasks = n;
153
154 g->n_tasks_valid = true;
155 }
156
157 if (streq(controller, "cpuacct")) {
158 uint64_t new_usage;
159 char *p, *v;
160 struct timespec ts;
161
162 r = cg_get_path(controller, path, "cpuacct.usage", &p);
163 if (r < 0)
164 return r;
165
166 r = read_one_line_file(p, &v);
167 free(p);
168 if (r < 0)
169 return r;
170
171 r = safe_atou64(v, &new_usage);
172 free(v);
173 if (r < 0)
174 return r;
175
176 assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
177
178 if (g->cpu_iteration == iteration - 1) {
179 uint64_t x, y;
180
181 x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) -
182 ((uint64_t) g->cpu_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->cpu_timestamp.tv_nsec);
183
184 y = new_usage - g->cpu_usage;
185
186 if (y > 0) {
187 g->cpu_fraction = (double) y / (double) x;
188 g->cpu_valid = true;
189 }
190 }
191
192 g->cpu_usage = new_usage;
193 g->cpu_timestamp = ts;
194 g->cpu_iteration = iteration;
195
196 } else if (streq(controller, "memory")) {
197 char *p, *v;
198
199 r = cg_get_path(controller, path, "memory.usage_in_bytes", &p);
200 if (r < 0)
201 return r;
202
203 r = read_one_line_file(p, &v);
204 free(p);
205 if (r < 0)
206 return r;
207
208 r = safe_atou64(v, &g->memory);
209 free(v);
210 if (r < 0)
211 return r;
212
213 if (g->memory > 0)
214 g->memory_valid = true;
215
216 } else if (streq(controller, "blkio")) {
217 char *p;
218 uint64_t wr = 0, rd = 0;
219 struct timespec ts;
220
221 r = cg_get_path(controller, path, "blkio.io_service_bytes", &p);
222 if (r < 0)
223 return r;
224
225 f = fopen(p, "re");
226 free(p);
227
228 if (!f)
229 return -errno;
230
231 for (;;) {
232 char line[LINE_MAX], *l;
233 uint64_t k, *q;
234
235 if (!fgets(line, sizeof(line), f))
236 break;
237
238 l = strstrip(line);
239 l += strcspn(l, WHITESPACE);
240 l += strspn(l, WHITESPACE);
241
242 if (first_word(l, "Read")) {
243 l += 4;
244 q = &rd;
245 } else if (first_word(l, "Write")) {
246 l += 5;
247 q = &wr;
248 } else
249 continue;
250
251 l += strspn(l, WHITESPACE);
252 r = safe_atou64(l, &k);
253 if (r < 0)
254 continue;
255
256 *q += k;
257 }
258
259 fclose(f);
260
261 assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
262
263 if (g->io_iteration == iteration - 1) {
264 uint64_t x, yr, yw;
265
266 x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) -
267 ((uint64_t) g->io_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->io_timestamp.tv_nsec);
268
269 yr = rd - g->io_input;
270 yw = wr - g->io_output;
271
272 if (yr > 0 || yw > 0) {
273 g->io_input_bps = (yr * 1000000000ULL) / x;
274 g->io_output_bps = (yw * 1000000000ULL) / x;
275 g->io_valid = true;
276
277 }
278 }
279
280 g->io_input = rd;
281 g->io_output = wr;
282 g->io_timestamp = ts;
283 g->io_iteration = iteration;
284 }
285
286 return 0;
287}
288
289static int refresh_one(
290 const char *controller,
291 const char *path,
292 Hashmap *a,
293 Hashmap *b,
294 unsigned iteration,
295 unsigned depth) {
296
297 DIR *d = NULL;
298 int r;
299
300 assert(controller);
301 assert(path);
302 assert(a);
303
304 if (depth > arg_depth)
305 return 0;
306
307 r = process(controller, path, a, b, iteration);
308 if (r < 0)
309 return r;
310
311 r = cg_enumerate_subgroups(controller, path, &d);
312 if (r < 0) {
2f29c419 313 if (r == -ENOENT)
8f2d43a0
LP
314 return 0;
315
316 return r;
317 }
318
319 for (;;) {
320 char *fn, *p;
321
322 r = cg_read_subgroup(d, &fn);
323 if (r <= 0)
324 goto finish;
325
b7def684 326 p = strjoin(path, "/", fn, NULL);
8f2d43a0
LP
327 free(fn);
328
329 if (!p) {
330 r = -ENOMEM;
331 goto finish;
332 }
333
334 path_kill_slashes(p);
335
336 r = refresh_one(controller, p, a, b, iteration, depth + 1);
337 free(p);
338
339 if (r < 0)
340 goto finish;
341 }
342
343finish:
344 if (d)
345 closedir(d);
346
347 return r;
348}
349
350static int refresh(Hashmap *a, Hashmap *b, unsigned iteration) {
351 int r;
352
353 assert(a);
354
355 r = refresh_one("name=systemd", "/", a, b, iteration, 0);
356 if (r < 0)
63210a15
SL
357 if (r != -ENOENT)
358 return r;
8f2d43a0
LP
359 r = refresh_one("cpuacct", "/", a, b, iteration, 0);
360 if (r < 0)
63210a15
SL
361 if (r != -ENOENT)
362 return r;
8f2d43a0
LP
363 r = refresh_one("memory", "/", a, b, iteration, 0);
364 if (r < 0)
63210a15
SL
365 if (r != -ENOENT)
366 return r;
8f2d43a0 367
63210a15
SL
368 r = refresh_one("blkio", "/", a, b, iteration, 0);
369 if (r < 0)
370 if (r != -ENOENT)
371 return r;
372 return 0;
8f2d43a0
LP
373}
374
375static int group_compare(const void*a, const void *b) {
376 const Group *x = *(Group**)a, *y = *(Group**)b;
377
378 if (path_startswith(y->path, x->path))
379 return -1;
380 if (path_startswith(x->path, y->path))
381 return 1;
382
383 if (arg_order == ORDER_CPU) {
1e913bcb
UTL
384 if (arg_cpu_type == CPU_PERCENT) {
385 if (x->cpu_valid && y->cpu_valid) {
386 if (x->cpu_fraction > y->cpu_fraction)
387 return -1;
388 else if (x->cpu_fraction < y->cpu_fraction)
389 return 1;
390 } else if (x->cpu_valid)
8f2d43a0 391 return -1;
1e913bcb 392 else if (y->cpu_valid)
8f2d43a0 393 return 1;
1e913bcb
UTL
394 } else {
395 if (x->cpu_usage > y->cpu_usage)
396 return -1;
397 else if (x->cpu_usage < y->cpu_usage)
398 return 1;
399 }
8f2d43a0
LP
400 }
401
402 if (arg_order == ORDER_TASKS) {
403
404 if (x->n_tasks_valid && y->n_tasks_valid) {
405 if (x->n_tasks > y->n_tasks)
406 return -1;
407 else if (x->n_tasks < y->n_tasks)
408 return 1;
409 } else if (x->n_tasks_valid)
410 return -1;
411 else if (y->n_tasks_valid)
412 return 1;
413 }
414
415 if (arg_order == ORDER_MEMORY) {
416 if (x->memory_valid && y->memory_valid) {
417 if (x->memory > y->memory)
418 return -1;
419 else if (x->memory < y->memory)
420 return 1;
421 } else if (x->memory_valid)
422 return -1;
423 else if (y->memory_valid)
424 return 1;
425 }
426
427 if (arg_order == ORDER_IO) {
428 if (x->io_valid && y->io_valid) {
429 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_input_bps + x->io_output_bps < y->io_input_bps + y->io_output_bps)
432 return 1;
433 } else if (x->io_valid)
434 return -1;
435 else if (y->io_valid)
436 return 1;
437 }
438
439 return strcmp(x->path, y->path);
440}
441
1e913bcb
UTL
442#define ON ANSI_HIGHLIGHT_ON
443#define OFF ANSI_HIGHLIGHT_OFF
444
8f2d43a0
LP
445static int display(Hashmap *a) {
446 Iterator i;
447 Group *g;
448 Group **array;
1e913bcb
UTL
449 signed path_columns;
450 unsigned rows, n = 0, j, maxtcpu = 0, maxtpath = 0;
62b95b8b 451 char buffer[MAX3(21, FORMAT_BYTES_MAX, FORMAT_TIMESPAN_MAX)];
8f2d43a0
LP
452
453 assert(a);
454
455 /* Set cursor to top left corner and clear screen */
1e913bcb
UTL
456 if (on_tty())
457 fputs("\033[H"
458 "\033[2J", stdout);
8f2d43a0
LP
459
460 array = alloca(sizeof(Group*) * hashmap_size(a));
461
462 HASHMAP_FOREACH(g, a, i)
463 if (g->n_tasks_valid || g->cpu_valid || g->memory_valid || g->io_valid)
464 array[n++] = g;
465
7ff7394d 466 qsort_safe(array, n, sizeof(Group*), group_compare);
8f2d43a0 467
1e913bcb
UTL
468 /* Find the longest names in one run */
469 for (j = 0; j < n; j++) {
470 unsigned cputlen, pathtlen;
62b95b8b
LP
471
472 format_timespan(buffer, sizeof(buffer), (nsec_t) (array[j]->cpu_usage / NSEC_PER_USEC), 0);
473 cputlen = strlen(buffer);
1e913bcb
UTL
474 maxtcpu = MAX(maxtcpu, cputlen);
475 pathtlen = strlen(array[j]->path);
476 maxtpath = MAX(maxtpath, pathtlen);
477 }
478
479 if (arg_cpu_type == CPU_PERCENT)
62b95b8b 480 snprintf(buffer, sizeof(buffer), "%6s", "%CPU");
1e913bcb 481 else
62b95b8b 482 snprintf(buffer, sizeof(buffer), "%*s", maxtcpu, "CPU Time");
1e913bcb 483
ed757c0c
LP
484 rows = lines();
485 if (rows <= 10)
486 rows = 10;
8f2d43a0 487
1e913bcb 488 if (on_tty()) {
62b95b8b 489 path_columns = columns() - 36 - strlen(buffer);
1e913bcb
UTL
490 if (path_columns < 10)
491 path_columns = 10;
492
493 printf("%s%-*s%s %s%7s%s %s%s%s %s%8s%s %s%8s%s %s%8s%s\n\n",
494 arg_order == ORDER_PATH ? ON : "", path_columns, "Path",
495 arg_order == ORDER_PATH ? OFF : "",
496 arg_order == ORDER_TASKS ? ON : "", "Tasks",
497 arg_order == ORDER_TASKS ? OFF : "",
62b95b8b 498 arg_order == ORDER_CPU ? ON : "", buffer,
1e913bcb
UTL
499 arg_order == ORDER_CPU ? OFF : "",
500 arg_order == ORDER_MEMORY ? ON : "", "Memory",
501 arg_order == ORDER_MEMORY ? OFF : "",
502 arg_order == ORDER_IO ? ON : "", "Input/s",
503 arg_order == ORDER_IO ? OFF : "",
504 arg_order == ORDER_IO ? ON : "", "Output/s",
505 arg_order == ORDER_IO ? OFF : "");
506 } else
507 path_columns = maxtpath;
8f2d43a0
LP
508
509 for (j = 0; j < n; j++) {
510 char *p;
8f2d43a0 511
1e913bcb 512 if (on_tty() && j + 5 > rows)
8f2d43a0
LP
513 break;
514
515 g = array[j];
516
11f96fac
ZJS
517 p = ellipsize(g->path, path_columns, 33);
518 printf("%-*s", path_columns, p ? p : g->path);
8f2d43a0
LP
519 free(p);
520
521 if (g->n_tasks_valid)
522 printf(" %7u", g->n_tasks);
523 else
524 fputs(" -", stdout);
525
62b95b8b 526 if (arg_cpu_type == CPU_PERCENT) {
1e913bcb
UTL
527 if (g->cpu_valid)
528 printf(" %6.1f", g->cpu_fraction*100);
529 else
530 fputs(" -", stdout);
62b95b8b
LP
531 } else
532 printf(" %*s", maxtcpu, format_timespan(buffer, sizeof(buffer), (nsec_t) (g->cpu_usage / NSEC_PER_USEC), 0));
8f2d43a0
LP
533
534 if (g->memory_valid)
62b95b8b 535 printf(" %8s", format_bytes(buffer, sizeof(buffer), g->memory));
8f2d43a0
LP
536 else
537 fputs(" -", stdout);
538
539 if (g->io_valid) {
540 printf(" %8s",
62b95b8b 541 format_bytes(buffer, sizeof(buffer), g->io_input_bps));
8f2d43a0 542 printf(" %8s",
62b95b8b 543 format_bytes(buffer, sizeof(buffer), g->io_output_bps));
8f2d43a0
LP
544 } else
545 fputs(" - -", stdout);
546
547 putchar('\n');
548 }
549
550 return 0;
551}
552
601185b4 553static void help(void) {
8f2d43a0
LP
554 printf("%s [OPTIONS...]\n\n"
555 "Show top control groups by their resource usage.\n\n"
556 " -h --help Show this help\n"
0d7e32fa 557 " --version Print version and exit\n"
8f2d43a0
LP
558 " -p Order by path\n"
559 " -t Order by number of tasks\n"
560 " -c Order by CPU load\n"
561 " -m Order by memory load\n"
562 " -i Order by IO load\n"
1e913bcb
UTL
563 " --cpu[=TYPE] Show CPU usage as time or percentage (default)\n"
564 " -d --delay=DELAY Delay between updates\n"
a152771a 565 " -n --iterations=N Run for N iterations before exiting\n"
e66bb58b 566 " -b --batch Run in batch mode, accepting no input\n"
601185b4
ZJS
567 " --depth=DEPTH Maximum traversal depth (default: %u)\n"
568 , program_invocation_short_name, arg_depth);
0d7e32fa
ZJS
569}
570
8f2d43a0
LP
571static int parse_argv(int argc, char *argv[]) {
572
573 enum {
0d7e32fa
ZJS
574 ARG_VERSION = 0x100,
575 ARG_DEPTH,
1e913bcb 576 ARG_CPU_TYPE
8f2d43a0
LP
577 };
578
579 static const struct option options[] = {
0d7e32fa
ZJS
580 { "help", no_argument, NULL, 'h' },
581 { "version", no_argument, NULL, ARG_VERSION },
582 { "delay", required_argument, NULL, 'd' },
583 { "iterations", required_argument, NULL, 'n' },
584 { "batch", no_argument, NULL, 'b' },
585 { "depth", required_argument, NULL, ARG_DEPTH },
1e913bcb 586 { "cpu", optional_argument, NULL, ARG_CPU_TYPE},
eb9da376 587 {}
8f2d43a0
LP
588 };
589
590 int c;
591 int r;
592
593 assert(argc >= 1);
594 assert(argv);
595
601185b4 596 while ((c = getopt_long(argc, argv, "hptcmin:bd:", options, NULL)) >= 0)
8f2d43a0
LP
597
598 switch (c) {
599
600 case 'h':
601185b4
ZJS
601 help();
602 return 0;
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 676 }
8f2d43a0
LP
677
678 if (optind < argc) {
679 log_error("Too many arguments.");
680 return -EINVAL;
681 }
682
683 return 1;
684}
685
686int main(int argc, char *argv[]) {
687 int r;
688 Hashmap *a = NULL, *b = NULL;
689 unsigned iteration = 0;
690 usec_t last_refresh = 0;
691 bool quit = false, immediate_refresh = false;
692
693 log_parse_environment();
694 log_open();
695
696 r = parse_argv(argc, argv);
697 if (r <= 0)
698 goto finish;
699
d5099efc
MS
700 a = hashmap_new(&string_hash_ops);
701 b = hashmap_new(&string_hash_ops);
8f2d43a0 702 if (!a || !b) {
0d0f0c50 703 r = log_oom();
8f2d43a0
LP
704 goto finish;
705 }
706
ed757c0c 707 signal(SIGWINCH, columns_lines_cache_reset);
28917d7d 708
1e913bcb
UTL
709 if (!on_tty())
710 arg_iterations = 1;
711
8f2d43a0
LP
712 while (!quit) {
713 Hashmap *c;
714 usec_t t;
715 char key;
716 char h[FORMAT_TIMESPAN_MAX];
717
718 t = now(CLOCK_MONOTONIC);
719
720 if (t >= last_refresh + arg_delay || immediate_refresh) {
721
722 r = refresh(a, b, iteration++);
723 if (r < 0)
724 goto finish;
725
726 group_hashmap_clear(b);
727
728 c = a;
729 a = b;
730 b = c;
731
732 last_refresh = t;
733 immediate_refresh = false;
734 }
735
736 r = display(b);
737 if (r < 0)
738 goto finish;
739
a152771a
DS
740 if (arg_iterations && iteration >= arg_iterations)
741 break;
742
e66bb58b
DS
743 if (arg_batch) {
744 usleep(last_refresh + arg_delay - t);
745 } else {
746 r = read_one_char(stdin, &key,
747 last_refresh + arg_delay - t, NULL);
748 if (r == -ETIMEDOUT)
749 continue;
750 if (r < 0) {
0a1beeb6 751 log_error_errno(-r, "Couldn't read key: %m");
e66bb58b
DS
752 goto finish;
753 }
8f2d43a0
LP
754 }
755
756 fputs("\r \r", stdout);
757 fflush(stdout);
758
e66bb58b
DS
759 if (arg_batch)
760 continue;
761
8f2d43a0
LP
762 switch (key) {
763
764 case ' ':
765 immediate_refresh = true;
766 break;
767
768 case 'q':
769 quit = true;
770 break;
771
772 case 'p':
773 arg_order = ORDER_PATH;
774 break;
775
776 case 't':
777 arg_order = ORDER_TASKS;
778 break;
779
780 case 'c':
781 arg_order = ORDER_CPU;
782 break;
783
784 case 'm':
785 arg_order = ORDER_MEMORY;
786 break;
787
788 case 'i':
789 arg_order = ORDER_IO;
790 break;
791
2bfc1eda
ZJS
792 case '%':
793 arg_cpu_type = arg_cpu_type == CPU_TIME ? CPU_PERCENT : CPU_TIME;
794 break;
795
8f2d43a0
LP
796 case '+':
797 if (arg_delay < USEC_PER_SEC)
798 arg_delay += USEC_PER_MSEC*250;
799 else
800 arg_delay += USEC_PER_SEC;
801
2fa4092c 802 fprintf(stdout, "\nIncreased delay to %s.", format_timespan(h, sizeof(h), arg_delay, 0));
8f2d43a0
LP
803 fflush(stdout);
804 sleep(1);
805 break;
806
807 case '-':
808 if (arg_delay <= USEC_PER_MSEC*500)
809 arg_delay = USEC_PER_MSEC*250;
810 else if (arg_delay < USEC_PER_MSEC*1250)
811 arg_delay -= USEC_PER_MSEC*250;
812 else
813 arg_delay -= USEC_PER_SEC;
814
2fa4092c 815 fprintf(stdout, "\nDecreased delay to %s.", format_timespan(h, sizeof(h), arg_delay, 0));
8f2d43a0
LP
816 fflush(stdout);
817 sleep(1);
818 break;
819
820 case '?':
821 case 'h':
822 fprintf(stdout,
c851f34b 823 "\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 824 "\t<" ON "+" OFF "> Increase delay; <" ON "-" OFF "> Decrease delay; <" ON "%%" OFF "> Toggle time\n"
c851f34b 825 "\t<" ON "q" OFF "> Quit; <" ON "SPACE" OFF "> Refresh");
8f2d43a0
LP
826 fflush(stdout);
827 sleep(3);
828 break;
829
830 default:
831 fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key);
832 fflush(stdout);
833 sleep(1);
834 break;
835 }
836 }
837
8f2d43a0
LP
838 r = 0;
839
840finish:
841 group_hashmap_free(a);
842 group_hashmap_free(b);
843
14212119 844 if (r < 0) {
0a1beeb6 845 log_error_errno(-r, "Exiting with failure: %m");
14212119
SL
846 return EXIT_FAILURE;
847 }
848
849 return EXIT_SUCCESS;
8f2d43a0 850}