]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journalctl.c
log.h: new log_oom() -> int -ENOMEM, use it
[thirdparty/systemd.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 }