chiark / gitweb /
log.h: new log_oom() -> int -ENOMEM, use it
[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
45 static OutputMode arg_output = OUTPUT_SHORT;
46 static bool arg_follow = false;
47 static bool arg_show_all = false;
48 static bool arg_no_pager = false;
49 static int arg_lines = -1;
50 static bool arg_no_tail = false;
51 static bool arg_new_id128 = false;
52 static bool arg_print_header = false;
53 static bool arg_quiet = false;
54 static bool arg_local = false;
55 static bool arg_this_boot = false;
56 static const char *arg_directory = NULL;
57
58 static int help(void) {
59
60         printf("%s [OPTIONS...] [MATCH]\n\n"
61                "Send control commands to or query the journal.\n\n"
62                "  -h --help           Show this help\n"
63                "     --version        Show package version\n"
64                "     --no-pager       Do not pipe output into a pager\n"
65                "  -a --all            Show all fields, including long and unprintable\n"
66                "  -f --follow         Follow journal\n"
67                "  -n --lines=INTEGER  Journal entries to show\n"
68                "     --no-tail        Show all lines, even in follow mode\n"
69                "  -o --output=STRING  Change journal output mode (short, short-monotonic,\n"
70                "                      verbose, export, json, cat)\n"
71                "  -q --quiet          Don't show privilege warning\n"
72                "  -l --local          Only local entries\n"
73                "  -b --this-boot      Show data only from current boot\n"
74                "  -D --directory=PATH Show journal files from directory\n"
75                "     --header         Show journal header information\n"
76                "     --new-id128      Generate a new 128 Bit id\n",
77                program_invocation_short_name);
78
79         return 0;
80 }
81
82 static int parse_argv(int argc, char *argv[]) {
83
84         enum {
85                 ARG_VERSION = 0x100,
86                 ARG_NO_PAGER,
87                 ARG_NO_TAIL,
88                 ARG_NEW_ID128,
89                 ARG_HEADER
90         };
91
92         static const struct option options[] = {
93                 { "help",      no_argument,       NULL, 'h'           },
94                 { "version" ,  no_argument,       NULL, ARG_VERSION   },
95                 { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
96                 { "follow",    no_argument,       NULL, 'f'           },
97                 { "output",    required_argument, NULL, 'o'           },
98                 { "all",       no_argument,       NULL, 'a'           },
99                 { "lines",     required_argument, NULL, 'n'           },
100                 { "no-tail",   no_argument,       NULL, ARG_NO_TAIL   },
101                 { "new-id128", no_argument,       NULL, ARG_NEW_ID128 },
102                 { "quiet",     no_argument,       NULL, 'q'           },
103                 { "local",     no_argument,       NULL, 'l'           },
104                 { "this-boot", no_argument,       NULL, 'b'           },
105                 { "directory", required_argument, NULL, 'D'           },
106                 { "header",    no_argument,       NULL, ARG_HEADER    },
107                 { NULL,        0,                 NULL, 0             }
108         };
109
110         int c, r;
111
112         assert(argc >= 0);
113         assert(argv);
114
115         while ((c = getopt_long(argc, argv, "hfo:an:qlbD:", options, NULL)) >= 0) {
116
117                 switch (c) {
118
119                 case 'h':
120                         help();
121                         return 0;
122
123                 case ARG_VERSION:
124                         puts(PACKAGE_STRING);
125                         puts(DISTRIBUTION);
126                         puts(SYSTEMD_FEATURES);
127                         return 0;
128
129                 case ARG_NO_PAGER:
130                         arg_no_pager = true;
131                         break;
132
133                 case 'f':
134                         arg_follow = true;
135                         break;
136
137                 case 'o':
138                         arg_output =  output_mode_from_string(optarg);
139                         if (arg_output < 0) {
140                                 log_error("Unknown output '%s'.", optarg);
141                                 return -EINVAL;
142                         }
143
144                         break;
145
146                 case 'a':
147                         arg_show_all = true;
148                         break;
149
150                 case 'n':
151                         r = safe_atoi(optarg, &arg_lines);
152                         if (r < 0 || arg_lines < 0) {
153                                 log_error("Failed to parse lines '%s'", optarg);
154                                 return -EINVAL;
155                         }
156                         break;
157
158                 case ARG_NO_TAIL:
159                         arg_no_tail = true;
160                         break;
161
162                 case ARG_NEW_ID128:
163                         arg_new_id128 = true;
164                         break;
165
166                 case 'q':
167                         arg_quiet = true;
168                         break;
169
170                 case 'l':
171                         arg_local = true;
172                         break;
173
174                 case 'b':
175                         arg_this_boot = true;
176                         break;
177
178                 case 'D':
179                         arg_directory = optarg;
180                         break;
181
182                 case ARG_HEADER:
183                         arg_print_header = true;
184                         break;
185
186                 case '?':
187                         return -EINVAL;
188
189                 default:
190                         log_error("Unknown option code %c", c);
191                         return -EINVAL;
192                 }
193         }
194
195         if (arg_follow && !arg_no_tail && arg_lines < 0)
196                 arg_lines = 10;
197
198         return 1;
199 }
200
201 static int generate_new_id128(void) {
202         sd_id128_t id;
203         int r;
204         unsigned i;
205
206         r = sd_id128_randomize(&id);
207         if (r < 0) {
208                 log_error("Failed to generate ID: %s", strerror(-r));
209                 return r;
210         }
211
212         printf("As string:\n"
213                SD_ID128_FORMAT_STR "\n\n"
214                "As UUID:\n"
215                "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
216                "As macro:\n"
217               "#define MESSAGE_XYZ SD_ID128_MAKE(",
218                SD_ID128_FORMAT_VAL(id),
219                SD_ID128_FORMAT_VAL(id));
220
221         for (i = 0; i < 16; i++)
222                 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
223
224         fputs(")\n", stdout);
225
226         return 0;
227 }
228
229 static int add_matches(sd_journal *j, char **args) {
230         char **i;
231         int r;
232
233         assert(j);
234
235         STRV_FOREACH(i, args) {
236
237                 if (streq(*i, "+"))
238                         r = sd_journal_add_disjunction(j);
239                 else if (path_is_absolute(*i)) {
240                         char *p;
241                         const char *path;
242                         struct stat st;
243
244                         p = canonicalize_file_name(*i);
245                         path = p ? p : *i;
246
247                         if (stat(path, &st) < 0)  {
248                                 free(p);
249                                 log_error("Couldn't stat file: %m");
250                                 return -errno;
251                         }
252
253                         if (S_ISREG(st.st_mode) && (0111 & st.st_mode)) {
254                                 char *t;
255
256                                 t = strappend("_EXE=", path);
257                                 if (!t) {
258                                         free(p);
259                                         return log_oom();
260                                 }
261
262                                 r = sd_journal_add_match(j, t, 0);
263                                 free(t);
264                         } else {
265                                 free(p);
266                                 log_error("File is not a regular file or is not executable: %s", *i);
267                                 return -EINVAL;
268                         }
269
270                         free(p);
271                 } else
272                         r = sd_journal_add_match(j, *i, 0);
273
274                 if (r < 0) {
275                         log_error("Failed to add match '%s': %s", *i, strerror(-r));
276                         return r;
277                 }
278         }
279
280         return 0;
281 }
282
283 static int add_this_boot(sd_journal *j) {
284         char match[9+32+1] = "_BOOT_ID=";
285         sd_id128_t boot_id;
286         int r;
287
288         if (!arg_this_boot)
289                 return 0;
290
291         r = sd_id128_get_boot(&boot_id);
292         if (r < 0) {
293                 log_error("Failed to get boot id: %s", strerror(-r));
294                 return r;
295         }
296
297         sd_id128_to_string(boot_id, match + 9);
298         r = sd_journal_add_match(j, match, strlen(match));
299         if (r < 0) {
300                 log_error("Failed to add match: %s", strerror(-r));
301                 return r;
302         }
303
304         return 0;
305 }
306
307 int main(int argc, char *argv[]) {
308         int r;
309         sd_journal *j = NULL;
310         unsigned line = 0;
311         bool need_seek = false;
312         sd_id128_t previous_boot_id;
313         bool previous_boot_id_valid = false;
314         bool have_pager;
315
316         log_parse_environment();
317         log_open();
318
319         r = parse_argv(argc, argv);
320         if (r <= 0)
321                 goto finish;
322
323         if (arg_new_id128) {
324                 r = generate_new_id128();
325                 goto finish;
326         }
327
328 #ifdef HAVE_ACL
329         if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
330                 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
331 #endif
332
333         if (arg_directory)
334                 r = sd_journal_open_directory(&j, arg_directory, 0);
335         else
336                 r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
337
338         if (r < 0) {
339                 log_error("Failed to open journal: %s", strerror(-r));
340                 goto finish;
341         }
342
343         if (arg_print_header) {
344                 journal_print_header(j);
345                 r = 0;
346                 goto finish;
347         }
348
349         r = add_this_boot(j);
350         if (r < 0)
351                 goto finish;
352
353         r = add_matches(j, argv + optind);
354         if (r < 0)
355                 goto finish;
356
357         if (!arg_quiet) {
358                 usec_t start, end;
359                 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
360
361                 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
362                 if (r < 0) {
363                         log_error("Failed to get cutoff: %s", strerror(-r));
364                         goto finish;
365                 }
366
367                 if (r > 0) {
368                         if (arg_follow)
369                                 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
370                         else
371                                 printf("Logs begin at %s, end at %s.\n",
372                                        format_timestamp(start_buf, sizeof(start_buf), start),
373                                        format_timestamp(end_buf, sizeof(end_buf), end));
374                 }
375         }
376
377         if (arg_lines >= 0) {
378                 r = sd_journal_seek_tail(j);
379                 if (r < 0) {
380                         log_error("Failed to seek to tail: %s", strerror(-r));
381                         goto finish;
382                 }
383
384                 r = sd_journal_previous_skip(j, arg_lines);
385         } else {
386                 r = sd_journal_seek_head(j);
387                 if (r < 0) {
388                         log_error("Failed to seek to head: %s", strerror(-r));
389                         goto finish;
390                 }
391
392                 r = sd_journal_next(j);
393         }
394
395         if (r < 0) {
396                 log_error("Failed to iterate through journal: %s", strerror(-r));
397                 goto finish;
398         }
399
400         have_pager = !arg_no_pager && !arg_follow && pager_open();
401
402         if (arg_output == OUTPUT_JSON) {
403                 fputc('[', stdout);
404                 fflush(stdout);
405         }
406
407         for (;;) {
408                 for (;;) {
409                         sd_id128_t boot_id;
410                         int flags = (arg_show_all*OUTPUT_SHOW_ALL |
411                                      have_pager*OUTPUT_FULL_WIDTH);
412
413                         if (need_seek) {
414                                 r = sd_journal_next(j);
415                                 if (r < 0) {
416                                         log_error("Failed to iterate through journal: %s", strerror(-r));
417                                         goto finish;
418                                 }
419                         }
420
421                         if (r == 0)
422                                 break;
423
424                         r = sd_journal_get_monotonic_usec(j, NULL, &boot_id);
425                         if (r >= 0) {
426                                 if (previous_boot_id_valid &&
427                                     !sd_id128_equal(boot_id, previous_boot_id))
428                                         printf(ANSI_HIGHLIGHT_ON "----- Reboot -----" ANSI_HIGHLIGHT_OFF "\n");
429
430                                 previous_boot_id = boot_id;
431                                 previous_boot_id_valid = true;
432                         }
433
434                         line ++;
435
436                         r = output_journal(j, arg_output, line, 0, flags);
437                         if (r < 0)
438                                 goto finish;
439
440                         need_seek = true;
441                 }
442
443                 if (!arg_follow)
444                         break;
445
446                 r = sd_journal_wait(j, (uint64_t) -1);
447                 if (r < 0) {
448                         log_error("Couldn't wait for log event: %s", strerror(-r));
449                         goto finish;
450                 }
451         }
452
453         if (arg_output == OUTPUT_JSON)
454                 fputs("\n]\n", stdout);
455
456 finish:
457         if (j)
458                 sd_journal_close(j);
459
460         pager_close();
461
462         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
463 }