chiark / gitweb /
a70de0684ec97047ad2aafa6596fd2ca1a491f87
[elogind.git] / src / journal / journalctl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2011 Lennart Poettering
7
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.
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
16   Lesser General Public License for more details.
17
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/>.
20 ***/
21
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <stddef.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <sys/poll.h>
30 #include <time.h>
31 #include <getopt.h>
32 #include <sys/stat.h>
33
34 #include <systemd/sd-journal.h>
35
36 #include "log.h"
37 #include "util.h"
38 #include "path-util.h"
39 #include "build.h"
40 #include "pager.h"
41 #include "logs-show.h"
42 #include "strv.h"
43 #include "journal-internal.h"
44 #include "fsprg.h"
45 #include "journal-def.h"
46 #include "journal-verify.h"
47
48 #define DEFAULT_FSPRG_INTERVAL_USEC (15*USEC_PER_MINUTE)
49
50 static OutputMode arg_output = OUTPUT_SHORT;
51 static bool arg_follow = false;
52 static bool arg_show_all = false;
53 static bool arg_no_pager = false;
54 static int arg_lines = -1;
55 static bool arg_no_tail = false;
56 static bool arg_quiet = false;
57 static bool arg_local = false;
58 static bool arg_this_boot = false;
59 static const char *arg_directory = NULL;
60 static int arg_priorities = 0xFF;
61
62 static enum {
63         ACTION_SHOW,
64         ACTION_NEW_ID128,
65         ACTION_PRINT_HEADER,
66         ACTION_SETUP_KEYS,
67         ACTION_VERIFY
68 } arg_action = ACTION_SHOW;
69
70 static int help(void) {
71
72         printf("%s [OPTIONS...] [MATCH]\n\n"
73                "Send control commands to or query the journal.\n\n"
74                "  -h --help           Show this help\n"
75                "     --version        Show package version\n"
76                "     --no-pager       Do not pipe output into a pager\n"
77                "  -a --all            Show all fields, including long and unprintable\n"
78                "  -f --follow         Follow journal\n"
79                "  -n --lines=INTEGER  Journal entries to show\n"
80                "     --no-tail        Show all lines, even in follow mode\n"
81                "  -o --output=STRING  Change journal output mode (short, short-monotonic,\n"
82                "                      verbose, export, json, cat)\n"
83                "  -q --quiet          Don't show privilege warning\n"
84                "  -l --local          Only local entries\n"
85                "  -b --this-boot      Show data only from current boot\n"
86                "  -D --directory=PATH Show journal files from directory\n"
87                "  -p --priority=RANGE Show only messages within the specified priority range\n\n"
88                "Commands:\n"
89                "     --new-id128      Generate a new 128 Bit ID\n"
90                "     --header         Show journal header information\n"
91                "     --setup-keys     Generate new FSPRG key pair\n"
92                "     --verify         Verify journal file consistency\n",
93                program_invocation_short_name);
94
95         return 0;
96 }
97
98 static int parse_argv(int argc, char *argv[]) {
99
100         enum {
101                 ARG_VERSION = 0x100,
102                 ARG_NO_PAGER,
103                 ARG_NO_TAIL,
104                 ARG_NEW_ID128,
105                 ARG_HEADER,
106                 ARG_SETUP_KEYS,
107                 ARG_VERIFY
108         };
109
110         static const struct option options[] = {
111                 { "help",      no_argument,       NULL, 'h'           },
112                 { "version" ,  no_argument,       NULL, ARG_VERSION   },
113                 { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
114                 { "follow",    no_argument,       NULL, 'f'           },
115                 { "output",    required_argument, NULL, 'o'           },
116                 { "all",       no_argument,       NULL, 'a'           },
117                 { "lines",     required_argument, NULL, 'n'           },
118                 { "no-tail",   no_argument,       NULL, ARG_NO_TAIL   },
119                 { "new-id128", no_argument,       NULL, ARG_NEW_ID128 },
120                 { "quiet",     no_argument,       NULL, 'q'           },
121                 { "local",     no_argument,       NULL, 'l'           },
122                 { "this-boot", no_argument,       NULL, 'b'           },
123                 { "directory", required_argument, NULL, 'D'           },
124                 { "header",    no_argument,       NULL, ARG_HEADER    },
125                 { "priority",  no_argument,       NULL, 'p'           },
126                 { "setup-keys",no_argument,       NULL, ARG_SETUP_KEYS},
127                 { "verify",    no_argument,       NULL, ARG_VERIFY    },
128                 { NULL,        0,                 NULL, 0             }
129         };
130
131         int c, r;
132
133         assert(argc >= 0);
134         assert(argv);
135
136         while ((c = getopt_long(argc, argv, "hfo:an:qlbD:p:", options, NULL)) >= 0) {
137
138                 switch (c) {
139
140                 case 'h':
141                         help();
142                         return 0;
143
144                 case ARG_VERSION:
145                         puts(PACKAGE_STRING);
146                         puts(DISTRIBUTION);
147                         puts(SYSTEMD_FEATURES);
148                         return 0;
149
150                 case ARG_NO_PAGER:
151                         arg_no_pager = true;
152                         break;
153
154                 case 'f':
155                         arg_follow = true;
156                         break;
157
158                 case 'o':
159                         arg_output =  output_mode_from_string(optarg);
160                         if (arg_output < 0) {
161                                 log_error("Unknown output '%s'.", optarg);
162                                 return -EINVAL;
163                         }
164
165                         break;
166
167                 case 'a':
168                         arg_show_all = true;
169                         break;
170
171                 case 'n':
172                         r = safe_atoi(optarg, &arg_lines);
173                         if (r < 0 || arg_lines < 0) {
174                                 log_error("Failed to parse lines '%s'", optarg);
175                                 return -EINVAL;
176                         }
177                         break;
178
179                 case ARG_NO_TAIL:
180                         arg_no_tail = true;
181                         break;
182
183                 case ARG_NEW_ID128:
184                         arg_action = ACTION_NEW_ID128;
185                         break;
186
187                 case 'q':
188                         arg_quiet = true;
189                         break;
190
191                 case 'l':
192                         arg_local = true;
193                         break;
194
195                 case 'b':
196                         arg_this_boot = true;
197                         break;
198
199                 case 'D':
200                         arg_directory = optarg;
201                         break;
202
203                 case ARG_HEADER:
204                         arg_action = ACTION_PRINT_HEADER;
205                         break;
206
207                 case ARG_SETUP_KEYS:
208                         arg_action = ACTION_SETUP_KEYS;
209                         break;
210
211                 case ARG_VERIFY:
212                         arg_action = ACTION_VERIFY;
213                         break;
214
215                 case 'p': {
216                         const char *dots;
217
218                         dots = strstr(optarg, "..");
219                         if (dots) {
220                                 char *a;
221                                 int from, to, i;
222
223                                 /* a range */
224                                 a = strndup(optarg, dots - optarg);
225                                 if (!a)
226                                         return log_oom();
227
228                                 from = log_level_from_string(a);
229                                 to = log_level_from_string(dots + 2);
230                                 free(a);
231
232                                 if (from < 0 || to < 0) {
233                                         log_error("Failed to parse log level range %s", optarg);
234                                         return -EINVAL;
235                                 }
236
237                                 arg_priorities = 0;
238
239                                 if (from < to) {
240                                         for (i = from; i <= to; i++)
241                                                 arg_priorities |= 1 << i;
242                                 } else {
243                                         for (i = to; i <= from; i++)
244                                                 arg_priorities |= 1 << i;
245                                 }
246
247                         } else {
248                                 int p, i;
249
250                                 p = log_level_from_string(optarg);
251                                 if (p < 0) {
252                                         log_error("Unknown log level %s", optarg);
253                                         return -EINVAL;
254                                 }
255
256                                 arg_priorities = 0;
257
258                                 for (i = 0; i <= p; i++)
259                                         arg_priorities |= 1 << i;
260                         }
261
262                         break;
263                 }
264
265                 case '?':
266                         return -EINVAL;
267
268                 default:
269                         log_error("Unknown option code %c", c);
270                         return -EINVAL;
271                 }
272         }
273
274         if (arg_follow && !arg_no_tail && arg_lines < 0)
275                 arg_lines = 10;
276
277         return 1;
278 }
279
280 static bool on_tty(void) {
281         static int t = -1;
282
283         /* Note that this is invoked relatively early, before we start
284          * the pager. That means the value we return reflects whether
285          * we originally were started on a tty, not if we currently
286          * are. But this is intended, since we want colour and so on
287          * when run in our own pager. */
288
289         if (_unlikely_(t < 0))
290                 t = isatty(STDOUT_FILENO) > 0;
291
292         return t;
293 }
294
295 static int generate_new_id128(void) {
296         sd_id128_t id;
297         int r;
298         unsigned i;
299
300         r = sd_id128_randomize(&id);
301         if (r < 0) {
302                 log_error("Failed to generate ID: %s", strerror(-r));
303                 return r;
304         }
305
306         printf("As string:\n"
307                SD_ID128_FORMAT_STR "\n\n"
308                "As UUID:\n"
309                "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
310                "As macro:\n"
311               "#define MESSAGE_XYZ SD_ID128_MAKE(",
312                SD_ID128_FORMAT_VAL(id),
313                SD_ID128_FORMAT_VAL(id));
314
315         for (i = 0; i < 16; i++)
316                 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
317
318         fputs(")\n", stdout);
319
320         return 0;
321 }
322
323 static int add_matches(sd_journal *j, char **args) {
324         char **i;
325         int r;
326
327         assert(j);
328
329         STRV_FOREACH(i, args) {
330
331                 if (streq(*i, "+"))
332                         r = sd_journal_add_disjunction(j);
333                 else if (path_is_absolute(*i)) {
334                         char *p, *t = NULL;
335                         const char *path;
336                         struct stat st;
337
338                         p = canonicalize_file_name(*i);
339                         path = p ? p : *i;
340
341                         if (stat(path, &st) < 0)  {
342                                 free(p);
343                                 log_error("Couldn't stat file: %m");
344                                 return -errno;
345                         }
346
347                         if (S_ISREG(st.st_mode) && (0111 & st.st_mode))
348                                 t = strappend("_EXE=", path);
349                         else if (S_ISCHR(st.st_mode))
350                                 asprintf(&t, "_KERNEL_DEVICE=c%u:%u", major(st.st_rdev), minor(st.st_rdev));
351                         else if (S_ISBLK(st.st_mode))
352                                 asprintf(&t, "_KERNEL_DEVICE=b%u:%u", major(st.st_rdev), minor(st.st_rdev));
353                         else {
354                                 free(p);
355                                 log_error("File is not a device node, regular file or is not executable: %s", *i);
356                                 return -EINVAL;
357                         }
358
359                         free(p);
360
361                         if (!t)
362                                 return log_oom();
363
364                         r = sd_journal_add_match(j, t, 0);
365                         free(t);
366                 } else
367                         r = sd_journal_add_match(j, *i, 0);
368
369                 if (r < 0) {
370                         log_error("Failed to add match '%s': %s", *i, strerror(-r));
371                         return r;
372                 }
373         }
374
375         return 0;
376 }
377
378 static int add_this_boot(sd_journal *j) {
379         char match[9+32+1] = "_BOOT_ID=";
380         sd_id128_t boot_id;
381         int r;
382
383         assert(j);
384
385         if (!arg_this_boot)
386                 return 0;
387
388         r = sd_id128_get_boot(&boot_id);
389         if (r < 0) {
390                 log_error("Failed to get boot id: %s", strerror(-r));
391                 return r;
392         }
393
394         sd_id128_to_string(boot_id, match + 9);
395         r = sd_journal_add_match(j, match, strlen(match));
396         if (r < 0) {
397                 log_error("Failed to add match: %s", strerror(-r));
398                 return r;
399         }
400
401         return 0;
402 }
403
404 static int add_priorities(sd_journal *j) {
405         char match[] = "PRIORITY=0";
406         int i, r;
407
408         assert(j);
409
410         if (arg_priorities == 0xFF)
411                 return 0;
412
413         for (i = LOG_EMERG; i <= LOG_DEBUG; i++)
414                 if (arg_priorities & (1 << i)) {
415                         match[sizeof(match)-2] = '0' + i;
416
417                         log_info("adding match %s", match);
418
419                         r = sd_journal_add_match(j, match, strlen(match));
420                         if (r < 0) {
421                                 log_error("Failed to add match: %s", strerror(-r));
422                                 return r;
423                         }
424                 }
425
426         return 0;
427 }
428
429 static int setup_keys(void) {
430 #ifdef HAVE_GCRYPT
431         size_t mpk_size, seed_size, state_size, i;
432         uint8_t *mpk, *seed, *state;
433         ssize_t l;
434         int fd = -1, r;
435         sd_id128_t machine, boot;
436         char *p = NULL, *k = NULL;
437         struct FSPRGHeader h;
438         uint64_t n, interval;
439
440         r = sd_id128_get_machine(&machine);
441         if (r < 0) {
442                 log_error("Failed to get machine ID: %s", strerror(-r));
443                 return r;
444         }
445
446         r = sd_id128_get_boot(&boot);
447         if (r < 0) {
448                 log_error("Failed to get boot ID: %s", strerror(-r));
449                 return r;
450         }
451
452         if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fsprg",
453                      SD_ID128_FORMAT_VAL(machine)) < 0)
454                 return log_oom();
455
456         if (access(p, F_OK) >= 0) {
457                 log_error("Evolving key file %s exists already.", p);
458                 r = -EEXIST;
459                 goto finish;
460         }
461
462         if (asprintf(&k, "/var/log/journal/" SD_ID128_FORMAT_STR "/fsprg.tmp.XXXXXX",
463                      SD_ID128_FORMAT_VAL(machine)) < 0) {
464                 r = log_oom();
465                 goto finish;
466         }
467
468         mpk_size = FSPRG_mskinbytes(FSPRG_RECOMMENDED_SECPAR);
469         mpk = alloca(mpk_size);
470
471         seed_size = FSPRG_RECOMMENDED_SEEDLEN;
472         seed = alloca(seed_size);
473
474         state_size = FSPRG_stateinbytes(FSPRG_RECOMMENDED_SECPAR);
475         state = alloca(state_size);
476
477         fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY);
478         if (fd < 0) {
479                 log_error("Failed to open /dev/random: %m");
480                 r = -errno;
481                 goto finish;
482         }
483
484         log_info("Generating seed...");
485         l = loop_read(fd, seed, seed_size, true);
486         if (l < 0 || (size_t) l != seed_size) {
487                 log_error("Failed to read random seed: %s", strerror(EIO));
488                 r = -EIO;
489                 goto finish;
490         }
491
492         log_info("Generating key pair...");
493         FSPRG_GenMK(NULL, mpk, seed, seed_size, FSPRG_RECOMMENDED_SECPAR);
494
495         log_info("Generating evolving key...");
496         FSPRG_GenState0(state, mpk, seed, seed_size);
497
498         interval = DEFAULT_FSPRG_INTERVAL_USEC;
499         n = now(CLOCK_REALTIME);
500         n /= interval;
501
502         close_nointr_nofail(fd);
503         fd = mkostemp(k, O_WRONLY|O_CLOEXEC|O_NOCTTY);
504         if (fd < 0) {
505                 log_error("Failed to open %s: %m", k);
506                 r = -errno;
507                 goto finish;
508         }
509
510         zero(h);
511         memcpy(h.signature, "KSHHRHLP", 8);
512         h.machine_id = machine;
513         h.boot_id = boot;
514         h.header_size = htole64(sizeof(h));
515         h.fsprg_start_usec = htole64(n * interval);
516         h.fsprg_interval_usec = htole64(interval);
517         h.secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
518         h.state_size = htole64(state_size);
519
520         l = loop_write(fd, &h, sizeof(h), false);
521         if (l < 0 || (size_t) l != sizeof(h)) {
522                 log_error("Failed to write header: %s", strerror(EIO));
523                 r = -EIO;
524                 goto finish;
525         }
526
527         l = loop_write(fd, state, state_size, false);
528         if (l < 0 || (size_t) l != state_size) {
529                 log_error("Failed to write state: %s", strerror(EIO));
530                 r = -EIO;
531                 goto finish;
532         }
533
534         if (link(k, p) < 0) {
535                 log_error("Failed to link file: %m");
536                 r = -errno;
537                 goto finish;
538         }
539
540         if (isatty(STDOUT_FILENO)) {
541                 fprintf(stderr,
542                         "\n"
543                         "The new key pair has been generated. The evolving key has been written to the\n"
544                         "following file. It will be used to protect local journal files. This file does\n"
545                         "not need to be kept secret. It should not be used on multiple hosts.\n"
546                         "\n"
547                         "\t%s\n"
548                         "\n"
549                         "Please write down the following " ANSI_HIGHLIGHT_ON "secret" ANSI_HIGHLIGHT_OFF " seed value. It should not be stored\n"
550                         "locally on disk, and may be used to verify journal files from this host.\n"
551                         "\n\t" ANSI_HIGHLIGHT_RED_ON, p);
552                 fflush(stderr);
553         }
554         for (i = 0; i < seed_size; i++) {
555                 if (i > 0 && i % 3 == 0)
556                         putchar('-');
557                 printf("%02x", ((uint8_t*) seed)[i]);
558         }
559
560         printf("/%llx-%llx\n", (unsigned long long) n, (unsigned long long) interval);
561
562         if (isatty(STDOUT_FILENO))
563                 fputs(ANSI_HIGHLIGHT_OFF "\n", stderr);
564
565         r = 0;
566
567 finish:
568         if (fd >= 0)
569                 close_nointr_nofail(fd);
570
571         if (k) {
572                 unlink(k);
573                 free(k);
574         }
575
576         free(p);
577
578         return r;
579 #else
580         log_error("Forward-secure journal verification not available.");
581 #endif
582 }
583
584 static int verify(sd_journal *j) {
585         int r = 0;
586         Iterator i;
587         JournalFile *f;
588
589         assert(j);
590
591         HASHMAP_FOREACH(f, j->files, i) {
592                 int k;
593
594                 k = journal_file_verify(f, NULL);
595                 if (k < 0) {
596                         log_warning("FAIL: %s (%s)", f->path, strerror(-k));
597                         r = -r;
598                 } else
599                         log_info("PASS: %s", f->path);
600         }
601
602         return r;
603 }
604
605 int main(int argc, char *argv[]) {
606         int r;
607         sd_journal *j = NULL;
608         unsigned line = 0;
609         bool need_seek = false;
610         sd_id128_t previous_boot_id;
611         bool previous_boot_id_valid = false;
612         bool have_pager;
613
614         log_parse_environment();
615         log_open();
616
617         r = parse_argv(argc, argv);
618         if (r <= 0)
619                 goto finish;
620
621         if (arg_action == ACTION_NEW_ID128) {
622                 r = generate_new_id128();
623                 goto finish;
624         }
625
626         if (arg_action == ACTION_SETUP_KEYS) {
627                 r = setup_keys();
628                 goto finish;
629         }
630
631         if (arg_directory)
632                 r = sd_journal_open_directory(&j, arg_directory, 0);
633         else
634                 r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
635
636         if (r < 0) {
637                 log_error("Failed to open journal: %s", strerror(-r));
638                 goto finish;
639         }
640
641         if (arg_action == ACTION_VERIFY) {
642                 r = verify(j);
643                 goto finish;
644         }
645
646         if (arg_action == ACTION_PRINT_HEADER) {
647                 journal_print_header(j);
648                 r = 0;
649                 goto finish;
650         }
651
652 #ifdef HAVE_ACL
653         if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
654                 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
655 #endif
656
657         r = add_this_boot(j);
658         if (r < 0)
659                 goto finish;
660
661         r = add_matches(j, argv + optind);
662         if (r < 0)
663                 goto finish;
664
665         r = add_priorities(j);
666         if (r < 0)
667                 goto finish;
668
669         if (!arg_quiet) {
670                 usec_t start, end;
671                 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
672
673                 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
674                 if (r < 0) {
675                         log_error("Failed to get cutoff: %s", strerror(-r));
676                         goto finish;
677                 }
678
679                 if (r > 0) {
680                         if (arg_follow)
681                                 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
682                         else
683                                 printf("Logs begin at %s, end at %s.\n",
684                                        format_timestamp(start_buf, sizeof(start_buf), start),
685                                        format_timestamp(end_buf, sizeof(end_buf), end));
686                 }
687         }
688
689         if (arg_lines >= 0) {
690                 r = sd_journal_seek_tail(j);
691                 if (r < 0) {
692                         log_error("Failed to seek to tail: %s", strerror(-r));
693                         goto finish;
694                 }
695
696                 r = sd_journal_previous_skip(j, arg_lines);
697         } else {
698                 r = sd_journal_seek_head(j);
699                 if (r < 0) {
700                         log_error("Failed to seek to head: %s", strerror(-r));
701                         goto finish;
702                 }
703
704                 r = sd_journal_next(j);
705         }
706
707         if (r < 0) {
708                 log_error("Failed to iterate through journal: %s", strerror(-r));
709                 goto finish;
710         }
711
712         on_tty();
713         have_pager = !arg_no_pager && !arg_follow && pager_open();
714
715         if (arg_output == OUTPUT_JSON) {
716                 fputc('[', stdout);
717                 fflush(stdout);
718         }
719
720         for (;;) {
721                 for (;;) {
722                         sd_id128_t boot_id;
723                         int flags =
724                                 arg_show_all * OUTPUT_SHOW_ALL |
725                                 have_pager * OUTPUT_FULL_WIDTH |
726                                 on_tty() * OUTPUT_COLOR;
727
728                         if (need_seek) {
729                                 r = sd_journal_next(j);
730                                 if (r < 0) {
731                                         log_error("Failed to iterate through journal: %s", strerror(-r));
732                                         goto finish;
733                                 }
734                         }
735
736                         if (r == 0)
737                                 break;
738
739                         r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
740                         if (r >= 0) {
741                                 if (previous_boot_id_valid &&
742                                     !sd_id128_equal(boot_id, previous_boot_id))
743                                         printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n");
744
745                                 previous_boot_id = boot_id;
746                                 previous_boot_id_valid = true;
747                         }
748
749                         line ++;
750
751                         r = output_journal(j, arg_output, line, 0, flags);
752                         if (r < 0)
753                                 goto finish;
754
755                         need_seek = true;
756                 }
757
758                 if (!arg_follow)
759                         break;
760
761                 r = sd_journal_wait(j, (uint64_t) -1);
762                 if (r < 0) {
763                         log_error("Couldn't wait for log event: %s", strerror(-r));
764                         goto finish;
765                 }
766         }
767
768         if (arg_output == OUTPUT_JSON)
769                 fputs("\n]\n", stdout);
770
771 finish:
772         if (j)
773                 sd_journal_close(j);
774
775         pager_close();
776
777         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
778 }