]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/tools/lxc_top.c
change defines for return value of handlers
[mirror_lxc.git] / src / lxc / tools / lxc_top.c
CommitLineData
7dc6f6e2
DE
1/*
2 * lxc: linux Container library
3 *
4 * Copyright © 2014 Oracle.
5 *
6 * Authors:
7 * Dwight Engen <dwight.engen@oracle.com>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
adc09141 24#define _GNU_SOURCE
9dba726e 25#define __STDC_FORMAT_MACROS /* Required for PRIu64 to work. */
7dc6f6e2 26#include <errno.h>
9dba726e 27#include <inttypes.h>
7dc6f6e2
DE
28#include <signal.h>
29#include <stdbool.h>
adc09141 30#include <stdint.h>
7dc6f6e2
DE
31#include <stdio.h>
32#include <stdlib.h>
adc09141 33#include <string.h>
7dc6f6e2
DE
34#include <termios.h>
35#include <unistd.h>
36#include <sys/epoll.h>
37#include <sys/ioctl.h>
adc09141
CB
38#include <sys/time.h>
39
7dc6f6e2
DE
40#include <lxc/lxccontainer.h>
41
42#include "arguments.h"
adc09141 43#include "tool_utils.h"
7dc6f6e2 44
7dc6f6e2
DE
45#define USER_HZ 100
46#define ESC "\033"
47#define TERMCLEAR ESC "[H" ESC "[J"
48#define TERMNORM ESC "[0m"
49#define TERMBOLD ESC "[1m"
50#define TERMRVRS ESC "[7m"
51
7c9d733f 52struct blkio_stats {
53 uint64_t read;
54 uint64_t write;
55 uint64_t total;
56};
57
7dc6f6e2
DE
58struct stats {
59 uint64_t mem_used;
60 uint64_t mem_limit;
6b6e702a
NA
61 uint64_t memsw_used;
62 uint64_t memsw_limit;
7dc6f6e2
DE
63 uint64_t kmem_used;
64 uint64_t kmem_limit;
65 uint64_t cpu_use_nanos;
66 uint64_t cpu_use_user;
67 uint64_t cpu_use_sys;
7c9d733f 68 struct blkio_stats io_service_bytes;
69 struct blkio_stats io_serviced;
7dc6f6e2
DE
70};
71
72struct ct {
73 struct lxc_container *c;
74 struct stats *stats;
75};
76
b420058d 77static int batch = 0;
78static int delay_set = 0;
7dc6f6e2
DE
79static int delay = 3;
80static char sort_by = 'n';
81static int sort_reverse = 0;
7dc6f6e2
DE
82static struct termios oldtios;
83static struct ct *ct = NULL;
84static int ct_alloc_cnt = 0;
85
86static int my_parser(struct lxc_arguments* args, int c, char* arg)
87{
88 switch (c) {
45419e74 89 case 'd':
b420058d 90 delay_set = 1;
45419e74
CB
91 if (lxc_safe_int(arg, &delay) < 0)
92 return -1;
93 break;
b420058d 94 case 'b':
95 batch=1;
96 break;
45419e74
CB
97 case 's':
98 sort_by = arg[0];
99 break;
100 case 'r':
101 sort_reverse = 1;
102 break;
7dc6f6e2
DE
103 }
104 return 0;
105}
106
107static const struct option my_longopts[] = {
108 {"delay", required_argument, 0, 'd'},
b420058d 109 {"batch", no_argument, 0, 'b'},
7dc6f6e2
DE
110 {"sort", required_argument, 0, 's'},
111 {"reverse", no_argument, 0, 'r'},
112 LXC_COMMON_OPTIONS
113};
114
115static struct lxc_arguments my_args = {
116 .progname = "lxc-top",
117 .help = "\
8564baf9 118\n\
7dc6f6e2
DE
119\n\
120lxc-top monitors the state of the active containers\n\
121\n\
122Options :\n\
123 -d, --delay delay in seconds between refreshes (default: 3.0)\n\
b420058d 124 -b, --batch output designed to capture to a file\n\
7dc6f6e2
DE
125 -s, --sort sort by [n,c,b,m] (default: n) where\n\
126 n = Name\n\
127 c = CPU use\n\
128 b = Block I/O use\n\
129 m = Memory use\n\
6b6e702a 130 s = Memory + Swap use\n\
7dc6f6e2
DE
131 k = Kernel memory use\n\
132 -r, --reverse sort in reverse (descending) order\n",
133 .name = ".*",
134 .options = my_longopts,
135 .parser = my_parser,
136 .checker = NULL,
137 .lxcpath_additional = -1,
138};
139
140static void stdin_tios_restore(void)
141{
142 tcsetattr(0, TCSAFLUSH, &oldtios);
4db667c4 143 fprintf(stderr, "\n");
7dc6f6e2
DE
144}
145
146static int stdin_tios_setup(void)
147{
148 struct termios newtios;
149
150 if (!isatty(0)) {
8052112c 151 fprintf(stderr, "stdin is not a tty\n");
7dc6f6e2
DE
152 return -1;
153 }
154
155 if (tcgetattr(0, &oldtios)) {
8052112c 156 fprintf(stderr, "failed to get current terminal settings\n");
7dc6f6e2
DE
157 return -1;
158 }
159
160 newtios = oldtios;
161
162 /* turn off echo and line buffering */
163 newtios.c_iflag &= ~IGNBRK;
164 newtios.c_iflag &= BRKINT;
165 newtios.c_lflag &= ~(ECHO|ICANON);
166 newtios.c_cc[VMIN] = 1;
167 newtios.c_cc[VTIME] = 0;
168
169 if (tcsetattr(0, TCSAFLUSH, &newtios)) {
8052112c 170 fprintf(stderr, "failed to set new terminal settings\n");
7dc6f6e2
DE
171 return -1;
172 }
173
174 return 0;
175}
176
177static int stdin_tios_rows(void)
178{
179 struct winsize wsz;
180 if (isatty(0) && ioctl(0, TIOCGWINSZ, &wsz) == 0)
181 return wsz.ws_row;
182 return 25;
183}
184
7dc6f6e2
DE
185static void sig_handler(int sig)
186{
187 exit(EXIT_SUCCESS);
188}
189
190static void size_humanize(unsigned long long val, char *buf, size_t bufsz)
191{
de429548
CB
192 int ret;
193
7dc6f6e2 194 if (val > 1 << 30) {
de429548 195 ret = snprintf(buf, bufsz, "%u.%2.2u GiB",
6fc7d8b6 196 (unsigned int)(val >> 30),
197 (unsigned int)(val & ((1 << 30) - 1)) / 10737419);
7dc6f6e2 198 } else if (val > 1 << 20) {
6fc7d8b6 199 unsigned int x = val + 5243; /* for rounding */
de429548 200 ret = snprintf(buf, bufsz, "%u.%2.2u MiB",
7dc6f6e2
DE
201 x >> 20, ((x & ((1 << 20) - 1)) * 100) >> 20);
202 } else if (val > 1 << 10) {
6fc7d8b6 203 unsigned int x = val + 5; /* for rounding */
de429548 204 ret = snprintf(buf, bufsz, "%u.%2.2u KiB",
7dc6f6e2
DE
205 x >> 10, ((x & ((1 << 10) - 1)) * 100) >> 10);
206 } else {
de429548 207 ret = snprintf(buf, bufsz, "%3u.00 ", (unsigned int)val);
7dc6f6e2 208 }
de429548
CB
209
210 if (ret < 0 || (size_t)ret >= bufsz)
211 fprintf(stderr, "Failed to create string\n");
7dc6f6e2
DE
212}
213
214static uint64_t stat_get_int(struct lxc_container *c, const char *item)
215{
216 char buf[80];
217 int len;
218 uint64_t val;
219
220 len = c->get_cgroup_item(c, item, buf, sizeof(buf));
221 if (len <= 0) {
8052112c 222 fprintf(stderr, "unable to read cgroup item %s\n", item);
7dc6f6e2
DE
223 return 0;
224 }
225
226 val = strtoull(buf, NULL, 0);
227 return val;
228}
229
230static uint64_t stat_match_get_int(struct lxc_container *c, const char *item,
231 const char *match, int column)
232{
233 char buf[4096];
234 int i,j,len;
235 uint64_t val = 0;
236 char **lines, **cols;
237 size_t matchlen;
238
239 len = c->get_cgroup_item(c, item, buf, sizeof(buf));
240 if (len <= 0) {
8052112c 241 fprintf(stderr, "unable to read cgroup item %s\n", item);
7dc6f6e2
DE
242 goto out;
243 }
244
245 lines = lxc_string_split_and_trim(buf, '\n');
246 if (!lines)
247 goto out;
248
249 matchlen = strlen(match);
250 for (i = 0; lines[i]; i++) {
251 if (strncmp(lines[i], match, matchlen) == 0) {
252 cols = lxc_string_split_and_trim(lines[i], ' ');
253 if (!cols)
254 goto err1;
255 for (j = 0; cols[j]; j++) {
256 if (j == column) {
257 val = strtoull(cols[j], NULL, 0);
258 break;
259 }
260 }
261 lxc_free_array((void **)cols, free);
262 break;
263 }
264 }
265err1:
266 lxc_free_array((void **)lines, free);
267out:
268 return val;
269}
270
7c9d733f 271/*
272examples:
273 blkio.throttle.io_serviced
274 8:0 Read 4259
275 8:0 Write 835
276 8:0 Sync 292
277 8:0 Async 4802
278 8:0 Total 5094
279 Total 5094
280
281 blkio.throttle.io_service_bytes
282 8:0 Read 110309376
283 8:0 Write 39018496
284 8:0 Sync 2818048
285 8:0 Async 146509824
286 8:0 Total 149327872
287 Total 149327872
288*/
289static void stat_get_blk_stats(struct lxc_container *c, const char *item,
290 struct blkio_stats *stats) {
291 char buf[4096];
292 int i, len;
293 char **lines, **cols;
294
295 len = c->get_cgroup_item(c, item, buf, sizeof(buf));
adc09141 296 if (len <= 0 || (size_t)len >= sizeof(buf)) {
8052112c 297 fprintf(stderr, "unable to read cgroup item %s\n", item);
7c9d733f 298 return;
299 }
300
301 lines = lxc_string_split_and_trim(buf, '\n');
302 if (!lines)
303 return;
304
305 memset(stats, 0, sizeof(struct blkio_stats));
306 for (i = 0; lines[i]; i++) {
307 cols = lxc_string_split_and_trim(lines[i], ' ');
308 if (!cols)
309 goto out;
310 if (strcmp(cols[1], "Read") == 0)
311 stats->read += strtoull(cols[2], NULL, 0);
312 else if (strcmp(cols[1], "Write") == 0)
313 stats->write += strtoull(cols[2], NULL, 0);
314 if (strcmp(cols[0], "Total") == 0)
315 stats->total = strtoull(cols[1], NULL, 0);
316
317 lxc_free_array((void **)cols, free);
318 }
319out:
320 lxc_free_array((void **)lines, free);
321 return;
322}
323
7dc6f6e2
DE
324static void stats_get(struct lxc_container *c, struct ct *ct, struct stats *total)
325{
326 ct->c = c;
327 ct->stats->mem_used = stat_get_int(c, "memory.usage_in_bytes");
328 ct->stats->mem_limit = stat_get_int(c, "memory.limit_in_bytes");
6b6e702a
NA
329 ct->stats->memsw_used = stat_get_int(c, "memory.memsw.usage_in_bytes");
330 ct->stats->memsw_limit = stat_get_int(c, "memory.memsw.limit_in_bytes");
7dc6f6e2
DE
331 ct->stats->kmem_used = stat_get_int(c, "memory.kmem.usage_in_bytes");
332 ct->stats->kmem_limit = stat_get_int(c, "memory.kmem.limit_in_bytes");
333 ct->stats->cpu_use_nanos = stat_get_int(c, "cpuacct.usage");
334 ct->stats->cpu_use_user = stat_match_get_int(c, "cpuacct.stat", "user", 1);
335 ct->stats->cpu_use_sys = stat_match_get_int(c, "cpuacct.stat", "system", 1);
7c9d733f 336 stat_get_blk_stats(c, "blkio.throttle.io_service_bytes", &ct->stats->io_service_bytes);
337 stat_get_blk_stats(c, "blkio.throttle.io_serviced", &ct->stats->io_serviced);
7dc6f6e2
DE
338
339 if (total) {
340 total->mem_used = total->mem_used + ct->stats->mem_used;
341 total->mem_limit = total->mem_limit + ct->stats->mem_limit;
6b6e702a
NA
342 total->memsw_used = total->memsw_used + ct->stats->memsw_used;
343 total->memsw_limit = total->memsw_limit + ct->stats->memsw_limit;
7dc6f6e2
DE
344 total->kmem_used = total->kmem_used + ct->stats->kmem_used;
345 total->kmem_limit = total->kmem_limit + ct->stats->kmem_limit;
346 total->cpu_use_nanos = total->cpu_use_nanos + ct->stats->cpu_use_nanos;
347 total->cpu_use_user = total->cpu_use_user + ct->stats->cpu_use_user;
348 total->cpu_use_sys = total->cpu_use_sys + ct->stats->cpu_use_sys;
7c9d733f 349 total->io_service_bytes.total += ct->stats->io_service_bytes.total;
350 total->io_service_bytes.read += ct->stats->io_service_bytes.read;
351 total->io_service_bytes.write += ct->stats->io_service_bytes.write;
7dc6f6e2
DE
352 }
353}
354
355static void stats_print_header(struct stats *stats)
356{
357 printf(TERMRVRS TERMBOLD);
7c9d733f 358 printf("%-18s %12s %12s %12s %36s %10s", "Container", "CPU", "CPU", "CPU", "BlkIO", "Mem");
6b6e702a
NA
359 if (stats->memsw_used > 0)
360 printf(" %10s", "MemSw");
7dc6f6e2
DE
361 if (stats->kmem_used > 0)
362 printf(" %10s", "KMem");
363 printf("\n");
364
7c9d733f 365 printf("%-18s %12s %12s %12s %36s %10s", "Name", "Used", "Sys", "User", "Total(Read/Write)", "Used");
6b6e702a
NA
366 if (stats->memsw_used > 0)
367 printf(" %10s", "Used");
7dc6f6e2
DE
368 if (stats->kmem_used > 0)
369 printf(" %10s", "Used");
370 printf("\n");
371 printf(TERMNORM);
372}
373
374static void stats_print(const char *name, const struct stats *stats,
375 const struct stats *total)
376{
7c9d733f 377 char iosb_str[63];
378 char iosb_total_str[20];
379 char iosb_read_str[20];
380 char iosb_write_str[20];
7dc6f6e2 381 char mem_used_str[20];
6b6e702a 382 char memsw_used_str[20];
7dc6f6e2 383 char kmem_used_str[20];
b420058d 384 struct timeval time_val;
385 unsigned long long time_ms;
7c9d733f 386 int ret;
9dba726e 387
b420058d 388 if (!batch) {
7c9d733f 389 size_humanize(stats->io_service_bytes.total, iosb_total_str, sizeof(iosb_total_str));
390 size_humanize(stats->io_service_bytes.read, iosb_read_str, sizeof(iosb_read_str));
391 size_humanize(stats->io_service_bytes.write, iosb_write_str, sizeof(iosb_write_str));
b420058d 392 size_humanize(stats->mem_used, mem_used_str, sizeof(mem_used_str));
9dba726e 393
7c9d733f 394 ret = snprintf(iosb_str, sizeof(iosb_str), "%s(%s/%s)", iosb_total_str, iosb_read_str, iosb_write_str);
adc09141 395 if (ret < 0 || (size_t)ret >= sizeof(iosb_str))
8052112c 396 printf("snprintf'd too many characters: %d\n", ret);
7c9d733f 397
398 printf("%-18.18s %12.2f %12.2f %12.2f %36s %10s",
b420058d 399 name,
400 (float)stats->cpu_use_nanos / 1000000000,
401 (float)stats->cpu_use_sys / USER_HZ,
402 (float)stats->cpu_use_user / USER_HZ,
7c9d733f 403 iosb_str,
b420058d 404 mem_used_str);
6b6e702a
NA
405
406 if (total->memsw_used > 0) {
407 size_humanize(stats->memsw_used, memsw_used_str, sizeof(memsw_used_str));
408 printf(" %10s", memsw_used_str);
409 }
b420058d 410 if (total->kmem_used > 0) {
411 size_humanize(stats->kmem_used, kmem_used_str, sizeof(kmem_used_str));
412 printf(" %10s", kmem_used_str);
413 }
414 } else {
adc09141 415 (void)gettimeofday(&time_val, NULL);
b420058d 416 time_ms = (unsigned long long) (time_val.tv_sec) * 1000 + (unsigned long long) (time_val.tv_usec) / 1000;
9dba726e 417 printf("%" PRIu64 ",%s,%" PRIu64 ",%" PRIu64 ",%" PRIu64
1b67456c 418 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64,
9dba726e
CB
419 (uint64_t)time_ms, name, (uint64_t)stats->cpu_use_nanos,
420 (uint64_t)stats->cpu_use_sys,
7c9d733f 421 (uint64_t)stats->cpu_use_user, (uint64_t)stats->io_service_bytes.total,
422 (uint64_t)stats->io_serviced.total, (uint64_t)stats->mem_used,
1b67456c 423 (uint64_t)stats->memsw_used, (uint64_t)stats->kmem_used);
7dc6f6e2 424 }
9dba726e 425
7dc6f6e2
DE
426}
427
428static int cmp_name(const void *sct1, const void *sct2)
429{
430 const struct ct *ct1 = sct1;
431 const struct ct *ct2 = sct2;
432
433 if (sort_reverse)
434 return strcmp(ct2->c->name, ct1->c->name);
435 return strcmp(ct1->c->name, ct2->c->name);
436}
437
438static int cmp_cpuuse(const void *sct1, const void *sct2)
439{
440 const struct ct *ct1 = sct1;
441 const struct ct *ct2 = sct2;
442
443 if (sort_reverse)
444 return ct2->stats->cpu_use_nanos < ct1->stats->cpu_use_nanos;
445 return ct1->stats->cpu_use_nanos < ct2->stats->cpu_use_nanos;
446}
447
448static int cmp_blkio(const void *sct1, const void *sct2)
449{
450 const struct ct *ct1 = sct1;
451 const struct ct *ct2 = sct2;
452
453 if (sort_reverse)
7c9d733f 454 return ct2->stats->io_service_bytes.total < ct1->stats->io_service_bytes.total;
455 return ct1->stats->io_service_bytes.total < ct2->stats->io_service_bytes.total;
7dc6f6e2
DE
456}
457
458static int cmp_memory(const void *sct1, const void *sct2)
459{
460 const struct ct *ct1 = sct1;
461 const struct ct *ct2 = sct2;
462
463 if (sort_reverse)
464 return ct2->stats->mem_used < ct1->stats->mem_used;
465 return ct1->stats->mem_used < ct2->stats->mem_used;
466}
467
6b6e702a
NA
468static int cmp_memorysw(const void *sct1, const void *sct2)
469{
470 const struct ct *ct1 = sct1;
471 const struct ct *ct2 = sct2;
472
473 if (sort_reverse)
474 return ct2->stats->memsw_used < ct1->stats->memsw_used;
475 return ct1->stats->memsw_used < ct2->stats->memsw_used;
476}
477
7dc6f6e2
DE
478static int cmp_kmemory(const void *sct1, const void *sct2)
479{
480 const struct ct *ct1 = sct1;
481 const struct ct *ct2 = sct2;
482
483 if (sort_reverse)
484 return ct2->stats->kmem_used < ct1->stats->kmem_used;
485 return ct1->stats->kmem_used < ct2->stats->kmem_used;
486}
487
488static void ct_sort(int active)
489{
490 int (*cmp_func)(const void *, const void *);
491
492 switch(sort_by) {
493 default:
494 case 'n': cmp_func = cmp_name; break;
495 case 'c': cmp_func = cmp_cpuuse; break;
496 case 'b': cmp_func = cmp_blkio; break;
497 case 'm': cmp_func = cmp_memory; break;
6b6e702a 498 case 's': cmp_func = cmp_memorysw; break;
7dc6f6e2
DE
499 case 'k': cmp_func = cmp_kmemory; break;
500 }
501 qsort(ct, active, sizeof(*ct), (int (*)(const void *,const void *))cmp_func);
502}
503
504static void ct_free(void)
505{
506 int i;
507
508 for (i = 0; i < ct_alloc_cnt; i++) {
509 if (ct[i].c) {
510 lxc_container_put(ct[i].c);
511 ct[i].c = NULL;
512 }
f10fad2f
ME
513 free(ct[i].stats);
514 ct[i].stats = NULL;
7dc6f6e2
DE
515 }
516}
517
518static void ct_realloc(int active_cnt)
519{
520 int i;
521
522 if (active_cnt > ct_alloc_cnt) {
523 ct_free();
524 ct = realloc(ct, sizeof(*ct) * active_cnt);
525 if (!ct) {
8052112c 526 fprintf(stderr, "cannot alloc mem\n");
7dc6f6e2
DE
527 exit(EXIT_FAILURE);
528 }
529 for (i = 0; i < active_cnt; i++) {
530 ct[i].stats = malloc(sizeof(*ct[0].stats));
531 if (!ct[i].stats) {
8052112c 532 fprintf(stderr, "cannot alloc mem\n");
7dc6f6e2
DE
533 exit(EXIT_FAILURE);
534 }
535 }
536 ct_alloc_cnt = active_cnt;
537 }
538}
539
adc09141
CB
540#define LXC_MAINLOOP_CONTINUE 0
541#define LXC_MAINLOOP_CLOSE 1
542
543struct lxc_epoll_descr {
544 int epfd;
545 struct lxc_list handlers;
546};
547
548typedef int (*lxc_mainloop_callback_t)(int fd, uint32_t event, void *data,
549 struct lxc_epoll_descr *descr);
550
551struct mainloop_handler {
552 lxc_mainloop_callback_t callback;
553 int fd;
554 void *data;
555};
556
557#define MAX_EVENTS 10
558
559int lxc_mainloop(struct lxc_epoll_descr *descr, int timeout_ms)
560{
561 int i, nfds, ret;
562 struct mainloop_handler *handler;
563 struct epoll_event events[MAX_EVENTS];
564
565 for (;;) {
566 nfds = epoll_wait(descr->epfd, events, MAX_EVENTS, timeout_ms);
567 if (nfds < 0) {
568 if (errno == EINTR)
569 continue;
570
571 return -1;
572 }
573
574 for (i = 0; i < nfds; i++) {
575 handler = events[i].data.ptr;
576
577 /* If the handler returns a positive value, exit the
578 * mainloop.
579 */
580 ret = handler->callback(handler->fd, events[i].events,
581 handler->data, descr);
582 if (ret == LXC_MAINLOOP_CLOSE)
583 return 0;
584 }
585
586 if (nfds == 0)
587 return 0;
588
589 if (lxc_list_empty(&descr->handlers))
590 return 0;
591 }
592}
593
594int lxc_mainloop_open(struct lxc_epoll_descr *descr)
595{
596 /* hint value passed to epoll create */
597 descr->epfd = epoll_create1(EPOLL_CLOEXEC);
598 if (descr->epfd < 0)
599 return -1;
600
601 lxc_list_init(&descr->handlers);
602 return 0;
603}
604
605int lxc_mainloop_add_handler(struct lxc_epoll_descr *descr, int fd,
606 lxc_mainloop_callback_t callback, void *data)
607{
608 struct epoll_event ev;
609 struct mainloop_handler *handler;
610 struct lxc_list *item;
611
612 handler = malloc(sizeof(*handler));
613 if (!handler)
614 return -1;
615
616 handler->callback = callback;
617 handler->fd = fd;
618 handler->data = data;
619
620 ev.events = EPOLLIN;
621 ev.data.ptr = handler;
622
623 if (epoll_ctl(descr->epfd, EPOLL_CTL_ADD, fd, &ev) < 0)
624 goto out_free_handler;
625
626 item = malloc(sizeof(*item));
627 if (!item)
628 goto out_free_handler;
629
630 item->elem = handler;
631 lxc_list_add(&descr->handlers, item);
632 return 0;
633
634out_free_handler:
635 free(handler);
636 return -1;
637}
638
639int lxc_mainloop_close(struct lxc_epoll_descr *descr)
640{
641 struct lxc_list *iterator, *next;
642
643 iterator = descr->handlers.next;
644 while (iterator != &descr->handlers) {
645 next = iterator->next;
646
647 lxc_list_del(iterator);
648 free(iterator->elem);
649 free(iterator);
650 iterator = next;
651 }
652
653 if (descr->epfd >= 0)
654 return close(descr->epfd);
655
656 return 0;
657}
658
659static int stdin_handler(int fd, uint32_t events, void *data,
660 struct lxc_epoll_descr *descr)
661{
662 char *in_char = data;
663
664 if (events & EPOLLIN) {
665 int rc;
666
667 rc = read(fd, in_char, sizeof(*in_char));
668 if (rc <= 0)
669 *in_char = '\0';
670 }
671
672 if (events & EPOLLHUP)
673 *in_char = 'q';
b2a48508 674 return LXC_MAINLOOP_CLOSE;
adc09141
CB
675}
676
7dc6f6e2
DE
677int main(int argc, char *argv[])
678{
679 struct lxc_epoll_descr descr;
680 int ret, ct_print_cnt;
681 char in_char;
9dba726e 682
7dc6f6e2
DE
683 ret = EXIT_FAILURE;
684 if (lxc_arguments_parse(&my_args, argc, argv))
685 goto out;
686
687 ct_print_cnt = stdin_tios_rows() - 3; /* 3 -> header and total */
688 if (stdin_tios_setup() < 0) {
8052112c 689 fprintf(stderr, "failed to setup terminal\n");
7dc6f6e2
DE
690 goto out;
691 }
692
693 /* ensure the terminal gets restored */
694 atexit(stdin_tios_restore);
695 signal(SIGINT, sig_handler);
696 signal(SIGQUIT, sig_handler);
697
698 if (lxc_mainloop_open(&descr)) {
8052112c 699 fprintf(stderr, "failed to create mainloop\n");
7dc6f6e2
DE
700 goto out;
701 }
702
703 ret = lxc_mainloop_add_handler(&descr, 0, stdin_handler, &in_char);
704 if (ret) {
8052112c 705 fprintf(stderr, "failed to add stdin handler\n");
7dc6f6e2
DE
706 ret = EXIT_FAILURE;
707 goto err1;
708 }
709
b420058d 710 if (batch && !delay_set) {
711 delay = 300;
712 }
713 if (batch) {
6b6e702a 714 printf("time_ms,container,cpu_nanos,cpu_sys_userhz,cpu_user_userhz,blkio_bytes,blkio_iops,mem_used_bytes,memsw_used_bytes,kernel_mem_used_bytes\n");
b420058d 715 }
9dba726e 716
7dc6f6e2
DE
717 for(;;) {
718 struct lxc_container **active;
719 int i, active_cnt;
720 struct stats total;
721 char total_name[30];
722
723 active_cnt = list_active_containers(my_args.lxcpath[0], NULL, &active);
724 ct_realloc(active_cnt);
725
726 memset(&total, 0, sizeof(total));
727 for (i = 0; i < active_cnt; i++)
728 stats_get(active[i], &ct[i], &total);
729
730 ct_sort(active_cnt);
731
b420058d 732 if (!batch) {
733 printf(TERMCLEAR);
734 stats_print_header(&total);
735 }
7dc6f6e2
DE
736 for (i = 0; i < active_cnt && i < ct_print_cnt; i++) {
737 stats_print(ct[i].c->name, ct[i].stats, &total);
738 printf("\n");
739 }
b420058d 740 if (!batch) {
741 sprintf(total_name, "TOTAL %d of %d", i, active_cnt);
742 stats_print(total_name, &total, &total);
743 }
7dc6f6e2 744 fflush(stdout);
9dba726e 745
7dc6f6e2
DE
746 for (i = 0; i < active_cnt; i++) {
747 lxc_container_put(ct[i].c);
748 ct[i].c = NULL;
749 }
9dba726e 750
7dc6f6e2 751 in_char = '\0';
b420058d 752 if (!batch) {
753 ret = lxc_mainloop(&descr, 1000 * delay);
754 if (ret != 0 || in_char == 'q')
755 break;
756 switch(in_char) {
757 case 'r':
7dc6f6e2 758 sort_reverse ^= 1;
b420058d 759 break;
760 case 'n':
761 case 'c':
762 case 'b':
763 case 'm':
6b6e702a 764 case 's':
b420058d 765 case 'k':
766 if (sort_by == in_char)
767 sort_reverse ^= 1;
768 else
769 sort_reverse = 0;
770 sort_by = in_char;
771 }
772 } else {
773 sleep(delay);
7dc6f6e2
DE
774 }
775 }
776 ret = EXIT_SUCCESS;
9dba726e 777
7dc6f6e2
DE
778err1:
779 lxc_mainloop_close(&descr);
780out:
9fd8b8a7 781 exit(ret);
7dc6f6e2 782}