1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
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
11 (at your option) any later version.
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
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
31 #include "path-util.h"
32 #include "terminal-util.h"
33 #include "process-util.h"
36 #include "cgroup-util.h"
41 #include "bus-error.h"
42 #include "unit-name.h"
44 typedef struct Group
{
54 unsigned cpu_iteration
;
61 unsigned io_iteration
;
62 uint64_t io_input
, io_output
;
64 uint64_t io_input_bps
, io_output_bps
;
67 static unsigned arg_depth
= 3;
68 static unsigned arg_iterations
= (unsigned) -1;
69 static bool arg_batch
= false;
70 static bool arg_raw
= false;
71 static usec_t arg_delay
= 1*USEC_PER_SEC
;
72 static char* arg_machine
= NULL
;
76 COUNT_USERSPACE_PROCESSES
,
78 } arg_count
= COUNT_PIDS
;
79 static bool arg_recursive
= true;
87 } arg_order
= ORDER_CPU
;
92 } arg_cpu_type
= CPU_PERCENT
;
94 static void group_free(Group
*g
) {
101 static void group_hashmap_clear(Hashmap
*h
) {
104 while ((g
= hashmap_steal_first(h
)))
108 static void group_hashmap_free(Hashmap
*h
) {
109 group_hashmap_clear(h
);
113 static const char *maybe_format_bytes(char *buf
, size_t l
, bool is_valid
, uint64_t t
) {
117 snprintf(buf
, l
, "%jd", t
);
120 return format_bytes(buf
, l
, t
);
124 const char *controller
,
138 g
= hashmap_get(a
, path
);
140 g
= hashmap_get(b
, path
);
146 g
->path
= strdup(path
);
152 r
= hashmap_put(a
, g
->path
, g
);
158 r
= hashmap_move_one(a
, b
, path
);
162 g
->cpu_valid
= g
->memory_valid
= g
->io_valid
= g
->n_tasks_valid
= false;
166 if (streq(controller
, SYSTEMD_CGROUP_CONTROLLER
) && IN_SET(arg_count
, COUNT_ALL_PROCESSES
, COUNT_USERSPACE_PROCESSES
)) {
167 _cleanup_fclose_
FILE *f
= NULL
;
170 r
= cg_enumerate_processes(controller
, path
, &f
);
177 while (cg_read_pid(f
, &pid
) > 0) {
179 if (arg_count
== COUNT_USERSPACE_PROCESSES
&& is_kernel_thread(pid
) > 0)
186 g
->n_tasks_valid
= true;
188 } else if (streq(controller
, "pids") && arg_count
== COUNT_PIDS
) {
189 _cleanup_free_
char *p
= NULL
, *v
= NULL
;
191 r
= cg_get_path(controller
, path
, "pids.current", &p
);
195 r
= read_one_line_file(p
, &v
);
201 r
= safe_atou64(v
, &g
->n_tasks
);
206 g
->n_tasks_valid
= true;
208 } else if (streq(controller
, "cpuacct") && cg_unified() <= 0) {
209 _cleanup_free_
char *p
= NULL
, *v
= NULL
;
213 r
= cg_get_path(controller
, path
, "cpuacct.usage", &p
);
217 r
= read_one_line_file(p
, &v
);
223 r
= safe_atou64(v
, &new_usage
);
227 timestamp
= now_nsec(CLOCK_MONOTONIC
);
229 if (g
->cpu_iteration
== iteration
- 1 &&
230 (nsec_t
) new_usage
> g
->cpu_usage
) {
234 x
= timestamp
- g
->cpu_timestamp
;
238 y
= (nsec_t
) new_usage
- g
->cpu_usage
;
239 g
->cpu_fraction
= (double) y
/ (double) x
;
243 g
->cpu_usage
= (nsec_t
) new_usage
;
244 g
->cpu_timestamp
= timestamp
;
245 g
->cpu_iteration
= iteration
;
247 } else if (streq(controller
, "memory")) {
248 _cleanup_free_
char *p
= NULL
, *v
= NULL
;
250 if (cg_unified() <= 0)
251 r
= cg_get_path(controller
, path
, "memory.usage_in_bytes", &p
);
253 r
= cg_get_path(controller
, path
, "memory.current", &p
);
257 r
= read_one_line_file(p
, &v
);
263 r
= safe_atou64(v
, &g
->memory
);
268 g
->memory_valid
= true;
270 } else if (streq(controller
, "blkio") && cg_unified() <= 0) {
271 _cleanup_fclose_
FILE *f
= NULL
;
272 _cleanup_free_
char *p
= NULL
;
273 uint64_t wr
= 0, rd
= 0;
276 r
= cg_get_path(controller
, path
, "blkio.io_service_bytes", &p
);
288 char line
[LINE_MAX
], *l
;
291 if (!fgets(line
, sizeof(line
), f
))
295 l
+= strcspn(l
, WHITESPACE
);
296 l
+= strspn(l
, WHITESPACE
);
298 if (first_word(l
, "Read")) {
301 } else if (first_word(l
, "Write")) {
307 l
+= strspn(l
, WHITESPACE
);
308 r
= safe_atou64(l
, &k
);
315 timestamp
= now_nsec(CLOCK_MONOTONIC
);
317 if (g
->io_iteration
== iteration
- 1) {
320 x
= (uint64_t) (timestamp
- g
->io_timestamp
);
324 if (rd
> g
->io_input
)
325 yr
= rd
- g
->io_input
;
329 if (wr
> g
->io_output
)
330 yw
= wr
- g
->io_output
;
334 if (yr
> 0 || yw
> 0) {
335 g
->io_input_bps
= (yr
* 1000000000ULL) / x
;
336 g
->io_output_bps
= (yw
* 1000000000ULL) / x
;
343 g
->io_timestamp
= timestamp
;
344 g
->io_iteration
= iteration
;
353 static int refresh_one(
354 const char *controller
,
362 _cleanup_closedir_
DIR *d
= NULL
;
370 if (depth
> arg_depth
)
373 r
= process(controller
, path
, a
, b
, iteration
, &ours
);
377 r
= cg_enumerate_subgroups(controller
, path
, &d
);
384 _cleanup_free_
char *fn
= NULL
, *p
= NULL
;
387 r
= cg_read_subgroup(d
, &fn
);
393 p
= strjoin(path
, "/", fn
, NULL
);
397 path_kill_slashes(p
);
399 r
= refresh_one(controller
, p
, a
, b
, iteration
, depth
+ 1, &child
);
404 IN_SET(arg_count
, COUNT_ALL_PROCESSES
, COUNT_USERSPACE_PROCESSES
) &&
406 child
->n_tasks_valid
&&
407 streq(controller
, SYSTEMD_CGROUP_CONTROLLER
)) {
409 /* Recursively sum up processes */
411 if (ours
->n_tasks_valid
)
412 ours
->n_tasks
+= child
->n_tasks
;
414 ours
->n_tasks
= child
->n_tasks
;
415 ours
->n_tasks_valid
= true;
426 static int refresh(const char *root
, Hashmap
*a
, Hashmap
*b
, unsigned iteration
) {
431 r
= refresh_one(SYSTEMD_CGROUP_CONTROLLER
, root
, a
, b
, iteration
, 0, NULL
);
434 r
= refresh_one("cpuacct", root
, a
, b
, iteration
, 0, NULL
);
437 r
= refresh_one("memory", root
, a
, b
, iteration
, 0, NULL
);
440 r
= refresh_one("blkio", root
, a
, b
, iteration
, 0, NULL
);
443 r
= refresh_one("pids", root
, a
, b
, iteration
, 0, NULL
);
450 static int group_compare(const void*a
, const void *b
) {
451 const Group
*x
= *(Group
**)a
, *y
= *(Group
**)b
;
453 if (arg_order
!= ORDER_TASKS
|| arg_recursive
) {
454 /* Let's make sure that the parent is always before
455 * the child. Except when ordering by tasks and
456 * recursive summing is off, since that is actually
457 * not accumulative for all children. */
459 if (path_startswith(y
->path
, x
->path
))
461 if (path_startswith(x
->path
, y
->path
))
471 if (arg_cpu_type
== CPU_PERCENT
) {
472 if (x
->cpu_valid
&& y
->cpu_valid
) {
473 if (x
->cpu_fraction
> y
->cpu_fraction
)
475 else if (x
->cpu_fraction
< y
->cpu_fraction
)
477 } else if (x
->cpu_valid
)
479 else if (y
->cpu_valid
)
482 if (x
->cpu_usage
> y
->cpu_usage
)
484 else if (x
->cpu_usage
< y
->cpu_usage
)
491 if (x
->n_tasks_valid
&& y
->n_tasks_valid
) {
492 if (x
->n_tasks
> y
->n_tasks
)
494 else if (x
->n_tasks
< y
->n_tasks
)
496 } else if (x
->n_tasks_valid
)
498 else if (y
->n_tasks_valid
)
504 if (x
->memory_valid
&& y
->memory_valid
) {
505 if (x
->memory
> y
->memory
)
507 else if (x
->memory
< y
->memory
)
509 } else if (x
->memory_valid
)
511 else if (y
->memory_valid
)
517 if (x
->io_valid
&& y
->io_valid
) {
518 if (x
->io_input_bps
+ x
->io_output_bps
> y
->io_input_bps
+ y
->io_output_bps
)
520 else if (x
->io_input_bps
+ x
->io_output_bps
< y
->io_input_bps
+ y
->io_output_bps
)
522 } else if (x
->io_valid
)
524 else if (y
->io_valid
)
528 return path_compare(x
->path
, y
->path
);
531 static void display(Hashmap
*a
) {
536 unsigned rows
, n
= 0, j
, maxtcpu
= 0, maxtpath
= 3; /* 3 for ellipsize() to work properly */
537 char buffer
[MAX3(21, FORMAT_BYTES_MAX
, FORMAT_TIMESPAN_MAX
)];
542 fputs(ANSI_HOME_CLEAR
, stdout
);
544 array
= alloca(sizeof(Group
*) * hashmap_size(a
));
546 HASHMAP_FOREACH(g
, a
, i
)
547 if (g
->n_tasks_valid
|| g
->cpu_valid
|| g
->memory_valid
|| g
->io_valid
)
550 qsort_safe(array
, n
, sizeof(Group
*), group_compare
);
552 /* Find the longest names in one run */
553 for (j
= 0; j
< n
; j
++) {
554 unsigned cputlen
, pathtlen
;
556 format_timespan(buffer
, sizeof(buffer
), (usec_t
) (array
[j
]->cpu_usage
/ NSEC_PER_USEC
), 0);
557 cputlen
= strlen(buffer
);
558 maxtcpu
= MAX(maxtcpu
, cputlen
);
560 pathtlen
= strlen(array
[j
]->path
);
561 maxtpath
= MAX(maxtpath
, pathtlen
);
564 if (arg_cpu_type
== CPU_PERCENT
)
565 snprintf(buffer
, sizeof(buffer
), "%6s", "%CPU");
567 snprintf(buffer
, sizeof(buffer
), "%*s", maxtcpu
, "CPU Time");
574 const char *on
, *off
;
576 path_columns
= columns() - 36 - strlen(buffer
);
577 if (path_columns
< 10)
580 on
= ansi_highlight_underline();
581 off
= ansi_underline();
583 printf("%s%s%-*s%s %s%7s%s %s%s%s %s%8s%s %s%8s%s %s%8s%s%s\n",
585 arg_order
== ORDER_PATH
? on
: "", path_columns
, "Control Group",
586 arg_order
== ORDER_PATH
? off
: "",
587 arg_order
== ORDER_TASKS
? on
: "", arg_count
== COUNT_PIDS
? "Tasks" : arg_count
== COUNT_USERSPACE_PROCESSES
? "Procs" : "Proc+",
588 arg_order
== ORDER_TASKS
? off
: "",
589 arg_order
== ORDER_CPU
? on
: "", buffer
,
590 arg_order
== ORDER_CPU
? off
: "",
591 arg_order
== ORDER_MEMORY
? on
: "", "Memory",
592 arg_order
== ORDER_MEMORY
? off
: "",
593 arg_order
== ORDER_IO
? on
: "", "Input/s",
594 arg_order
== ORDER_IO
? off
: "",
595 arg_order
== ORDER_IO
? on
: "", "Output/s",
596 arg_order
== ORDER_IO
? off
: "",
599 path_columns
= maxtpath
;
601 for (j
= 0; j
< n
; j
++) {
602 _cleanup_free_
char *ellipsized
= NULL
;
605 if (on_tty() && j
+ 6 > rows
)
610 path
= isempty(g
->path
) ? "/" : g
->path
;
611 ellipsized
= ellipsize(path
, path_columns
, 33);
612 printf("%-*s", path_columns
, ellipsized
?: path
);
614 if (g
->n_tasks_valid
)
615 printf(" %7" PRIu64
, g
->n_tasks
);
619 if (arg_cpu_type
== CPU_PERCENT
) {
621 printf(" %6.1f", g
->cpu_fraction
*100);
625 printf(" %*s", maxtcpu
, format_timespan(buffer
, sizeof(buffer
), (usec_t
) (g
->cpu_usage
/ NSEC_PER_USEC
), 0));
627 printf(" %8s", maybe_format_bytes(buffer
, sizeof(buffer
), g
->memory_valid
, g
->memory
));
628 printf(" %8s", maybe_format_bytes(buffer
, sizeof(buffer
), g
->io_valid
, g
->io_input_bps
));
629 printf(" %8s", maybe_format_bytes(buffer
, sizeof(buffer
), g
->io_valid
, g
->io_output_bps
));
635 static void help(void) {
636 printf("%s [OPTIONS...]\n\n"
637 "Show top control groups by their resource usage.\n\n"
638 " -h --help Show this help\n"
639 " --version Show package version\n"
640 " -p --order=path Order by path\n"
641 " -t --order=tasks Order by number of tasks/processes\n"
642 " -c --order=cpu Order by CPU load (default)\n"
643 " -m --order=memory Order by memory load\n"
644 " -i --order=io Order by IO load\n"
645 " -r --raw Provide raw (not human-readable) numbers\n"
646 " --cpu=percentage Show CPU usage as percentage (default)\n"
647 " --cpu=time Show CPU usage as time\n"
648 " -P Count userspace processes instead of tasks (excl. kernel)\n"
649 " -k Count all processes instead of tasks (incl. kernel)\n"
650 " --recursive=BOOL Sum up process count recursively\n"
651 " -d --delay=DELAY Delay between updates\n"
652 " -n --iterations=N Run for N iterations before exiting\n"
653 " -b --batch Run in batch mode, accepting no input\n"
654 " --depth=DEPTH Maximum traversal depth (default: %u)\n"
655 " -M --machine= Show container\n"
656 , program_invocation_short_name
, arg_depth
);
659 static int parse_argv(int argc
, char *argv
[]) {
669 static const struct option options
[] = {
670 { "help", no_argument
, NULL
, 'h' },
671 { "version", no_argument
, NULL
, ARG_VERSION
},
672 { "delay", required_argument
, NULL
, 'd' },
673 { "iterations", required_argument
, NULL
, 'n' },
674 { "batch", no_argument
, NULL
, 'b' },
675 { "raw", no_argument
, NULL
, 'r' },
676 { "depth", required_argument
, NULL
, ARG_DEPTH
},
677 { "cpu", optional_argument
, NULL
, ARG_CPU_TYPE
},
678 { "order", required_argument
, NULL
, ARG_ORDER
},
679 { "recursive", required_argument
, NULL
, ARG_RECURSIVE
},
680 { "machine", required_argument
, NULL
, 'M' },
684 bool recursive_unset
= false;
690 while ((c
= getopt_long(argc
, argv
, "hptcmin:brd:kPM:", options
, NULL
)) >= 0)
699 puts(PACKAGE_STRING
);
700 puts(SYSTEMD_FEATURES
);
705 if (streq(optarg
, "time"))
706 arg_cpu_type
= CPU_TIME
;
707 else if (streq(optarg
, "percentage"))
708 arg_cpu_type
= CPU_PERCENT
;
710 log_error("Unknown argument to --cpu=: %s", optarg
);
714 arg_cpu_type
= CPU_TIME
;
719 r
= safe_atou(optarg
, &arg_depth
);
721 log_error("Failed to parse depth parameter.");
728 r
= parse_sec(optarg
, &arg_delay
);
729 if (r
< 0 || arg_delay
<= 0) {
730 log_error("Failed to parse delay parameter.");
737 r
= safe_atou(optarg
, &arg_iterations
);
739 log_error("Failed to parse iterations parameter.");
754 arg_order
= ORDER_PATH
;
758 arg_order
= ORDER_TASKS
;
762 arg_order
= ORDER_CPU
;
766 arg_order
= ORDER_MEMORY
;
770 arg_order
= ORDER_IO
;
774 if (streq(optarg
, "path"))
775 arg_order
= ORDER_PATH
;
776 else if (streq(optarg
, "tasks"))
777 arg_order
= ORDER_TASKS
;
778 else if (streq(optarg
, "cpu"))
779 arg_order
= ORDER_CPU
;
780 else if (streq(optarg
, "memory"))
781 arg_order
= ORDER_MEMORY
;
782 else if (streq(optarg
, "io"))
783 arg_order
= ORDER_IO
;
785 log_error("Invalid argument to --order=: %s", optarg
);
791 arg_count
= COUNT_ALL_PROCESSES
;
795 arg_count
= COUNT_USERSPACE_PROCESSES
;
799 r
= parse_boolean(optarg
);
801 log_error("Failed to parse --recursive= argument: %s", optarg
);
806 recursive_unset
= r
== 0;
810 arg_machine
= optarg
;
817 assert_not_reached("Unhandled option");
821 log_error("Too many arguments.");
825 if (recursive_unset
&& arg_count
== COUNT_PIDS
) {
826 log_error("Non-recursive counting is only supported when counting processes, not tasks. Use -P or -k.");
833 static const char* counting_what(void) {
834 if (arg_count
== COUNT_PIDS
)
836 else if (arg_count
== COUNT_ALL_PROCESSES
)
837 return "all processes (incl. kernel)";
839 return "userspace processes (excl. kernel)";
842 static int get_cgroup_root(char **ret
) {
843 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
844 _cleanup_bus_flush_close_unref_ sd_bus
*bus
= NULL
;
845 _cleanup_free_
char *unit
= NULL
, *path
= NULL
;
850 r
= cg_get_root_path(ret
);
852 return log_error_errno(r
, "Failed to get root control group path: %m");
857 m
= strjoina("/run/systemd/machines/", arg_machine
);
858 r
= parse_env_file(m
, NEWLINE
, "SCOPE", &unit
, NULL
);
860 return log_error_errno(r
, "Failed to load machine data: %m");
862 path
= unit_dbus_path_from_name(unit
);
866 r
= bus_open_transport(BUS_TRANSPORT_LOCAL
, NULL
, false, &bus
);
868 return log_error_errno(r
, "Failed to create bus connection: %m");
870 r
= sd_bus_get_property_string(
872 "org.freedesktop.systemd1",
874 unit_dbus_interface_from_name(unit
),
879 return log_error_errno(r
, "Failed to query unit control group path: %s", bus_error_message(&error
, r
));
884 int main(int argc
, char *argv
[]) {
886 Hashmap
*a
= NULL
, *b
= NULL
;
887 unsigned iteration
= 0;
888 usec_t last_refresh
= 0;
889 bool quit
= false, immediate_refresh
= false;
890 _cleanup_free_
char *root
= NULL
;
893 log_parse_environment();
896 r
= cg_mask_supported(&mask
);
898 log_error_errno(r
, "Failed to determine supported controllers: %m");
902 arg_count
= (mask
& CGROUP_MASK_PIDS
) ? COUNT_PIDS
: COUNT_USERSPACE_PROCESSES
;
904 r
= parse_argv(argc
, argv
);
908 r
= get_cgroup_root(&root
);
910 log_error_errno(r
, "Failed to get root control group path: %m");
914 a
= hashmap_new(&string_hash_ops
);
915 b
= hashmap_new(&string_hash_ops
);
921 signal(SIGWINCH
, columns_lines_cache_reset
);
923 if (arg_iterations
== (unsigned) -1)
924 arg_iterations
= on_tty() ? 0 : 1;
930 char h
[FORMAT_TIMESPAN_MAX
];
932 t
= now(CLOCK_MONOTONIC
);
934 if (t
>= last_refresh
+ arg_delay
|| immediate_refresh
) {
936 r
= refresh(root
, a
, b
, iteration
++);
938 log_error_errno(r
, "Failed to refresh: %m");
942 group_hashmap_clear(b
);
949 immediate_refresh
= false;
954 if (arg_iterations
&& iteration
>= arg_iterations
)
957 if (!on_tty()) /* non-TTY: Empty newline as delimiter between polls */
962 (void) usleep(last_refresh
+ arg_delay
- t
);
964 r
= read_one_char(stdin
, &key
, last_refresh
+ arg_delay
- t
, NULL
);
968 log_error_errno(r
, "Couldn't read key: %m");
973 if (on_tty()) { /* TTY: Clear any user keystroke */
974 fputs("\r \r", stdout
);
984 immediate_refresh
= true;
992 arg_order
= ORDER_PATH
;
996 arg_order
= ORDER_TASKS
;
1000 arg_order
= ORDER_CPU
;
1004 arg_order
= ORDER_MEMORY
;
1008 arg_order
= ORDER_IO
;
1012 arg_cpu_type
= arg_cpu_type
== CPU_TIME
? CPU_PERCENT
: CPU_TIME
;
1016 arg_count
= arg_count
!= COUNT_ALL_PROCESSES
? COUNT_ALL_PROCESSES
: COUNT_PIDS
;
1017 fprintf(stdout
, "\nCounting: %s.", counting_what());
1023 arg_count
= arg_count
!= COUNT_USERSPACE_PROCESSES
? COUNT_USERSPACE_PROCESSES
: COUNT_PIDS
;
1024 fprintf(stdout
, "\nCounting: %s.", counting_what());
1030 if (arg_count
== COUNT_PIDS
)
1031 fprintf(stdout
, "\n\aCannot toggle recursive counting, not available in task counting mode.");
1033 arg_recursive
= !arg_recursive
;
1034 fprintf(stdout
, "\nRecursive process counting: %s", yes_no(arg_recursive
));
1041 if (arg_delay
< USEC_PER_SEC
)
1042 arg_delay
+= USEC_PER_MSEC
*250;
1044 arg_delay
+= USEC_PER_SEC
;
1046 fprintf(stdout
, "\nIncreased delay to %s.", format_timespan(h
, sizeof(h
), arg_delay
, 0));
1052 if (arg_delay
<= USEC_PER_MSEC
*500)
1053 arg_delay
= USEC_PER_MSEC
*250;
1054 else if (arg_delay
< USEC_PER_MSEC
*1250)
1055 arg_delay
-= USEC_PER_MSEC
*250;
1057 arg_delay
-= USEC_PER_SEC
;
1059 fprintf(stdout
, "\nDecreased delay to %s.", format_timespan(h
, sizeof(h
), arg_delay
, 0));
1067 #define ON ANSI_HIGHLIGHT
1068 #define OFF ANSI_NORMAL
1071 "\t<" ON
"p" OFF
"> By path; <" ON
"t" OFF
"> By tasks/procs; <" ON
"c" OFF
"> By CPU; <" ON
"m" OFF
"> By memory; <" ON
"i" OFF
"> By I/O\n"
1072 "\t<" ON
"+" OFF
"> Inc. delay; <" ON
"-" OFF
"> Dec. delay; <" ON
"%%" OFF
"> Toggle time; <" ON
"SPACE" OFF
"> Refresh\n"
1073 "\t<" ON
"P" OFF
"> Toggle count userspace processes; <" ON
"k" OFF
"> Toggle count all processes\n"
1074 "\t<" ON
"r" OFF
"> Count processes recursively; <" ON
"q" OFF
"> Quit");
1081 fprintf(stdout
, "\nUnknown key '\\x%x'. Ignoring.", key
);
1083 fprintf(stdout
, "\nUnknown key '%c'. Ignoring.", key
);
1093 group_hashmap_free(a
);
1094 group_hashmap_free(b
);
1096 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;