]> git.proxmox.com Git - systemd.git/blame - src/journal/journalctl.c
log.h: new log_oom() -> int -ENOMEM, use it
[systemd.git] / src / journal / journalctl.c
CommitLineData
87d2c1ff
LP
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
5430f7f2
LP
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
87d2c1ff
LP
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
5430f7f2 16 Lesser General Public License for more details.
87d2c1ff 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
87d2c1ff
LP
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>
3fbf9cbb
LP
25#include <string.h>
26#include <stdio.h>
27#include <unistd.h>
28#include <stdlib.h>
50f20cfd 29#include <sys/poll.h>
72f59706 30#include <time.h>
0d43c694 31#include <getopt.h>
50940700 32#include <sys/stat.h>
87d2c1ff 33
81527be1
LP
34#include <systemd/sd-journal.h>
35
3fbf9cbb 36#include "log.h"
72f59706 37#include "util.h"
e5124088 38#include "path-util.h"
0d43c694
LP
39#include "build.h"
40#include "pager.h"
86aa7ba4 41#include "logs-show.h"
a963990f 42#include "strv.h"
dca6219e 43#include "journal-internal.h"
250d54b5 44
df50185b 45static OutputMode arg_output = OUTPUT_SHORT;
72f59706
LP
46static bool arg_follow = false;
47static bool arg_show_all = false;
0d43c694 48static bool arg_no_pager = false;
2100675e 49static int arg_lines = -1;
e91af489 50static bool arg_no_tail = false;
39f7f5c1 51static bool arg_new_id128 = false;
dca6219e 52static bool arg_print_header = false;
43673799 53static bool arg_quiet = false;
2bd3c38a 54static bool arg_local = false;
59cea26a 55static bool arg_this_boot = false;
a963990f 56static const char *arg_directory = NULL;
50f20cfd 57
0d43c694
LP
58static int help(void) {
59
89834a7c 60 printf("%s [OPTIONS...] [MATCH]\n\n"
df50185b 61 "Send control commands to or query the journal.\n\n"
0d43c694
LP
62 " -h --help Show this help\n"
63 " --version Show package version\n"
64 " --no-pager Do not pipe output into a pager\n"
2af777ba 65 " -a --all Show all fields, including long and unprintable\n"
0d43c694 66 " -f --follow Follow journal\n"
df50185b 67 " -n --lines=INTEGER Journal entries to show\n"
e91af489 68 " --no-tail Show all lines, even in follow mode\n"
d3f2bdbf
LP
69 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
70 " verbose, export, json, cat)\n"
43673799 71 " -q --quiet Don't show privilege warning\n"
59cea26a
LP
72 " -l --local Only local entries\n"
73 " -b --this-boot Show data only from current boot\n"
a963990f 74 " -D --directory=PATH Show journal files from directory\n"
dca6219e 75 " --header Show journal header information\n"
59cea26a 76 " --new-id128 Generate a new 128 Bit id\n",
0d43c694
LP
77 program_invocation_short_name);
78
79 return 0;
80}
81
82static int parse_argv(int argc, char *argv[]) {
83
84 enum {
85 ARG_VERSION = 0x100,
e91af489 86 ARG_NO_PAGER,
55ee336c 87 ARG_NO_TAIL,
dca6219e
LP
88 ARG_NEW_ID128,
89 ARG_HEADER
0d43c694
LP
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' },
2100675e 99 { "lines", required_argument, NULL, 'n' },
e91af489 100 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
39f7f5c1 101 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
43673799 102 { "quiet", no_argument, NULL, 'q' },
2bd3c38a 103 { "local", no_argument, NULL, 'l' },
59cea26a 104 { "this-boot", no_argument, NULL, 'b' },
a963990f 105 { "directory", required_argument, NULL, 'D' },
dca6219e 106 { "header", no_argument, NULL, ARG_HEADER },
0d43c694
LP
107 { NULL, 0, NULL, 0 }
108 };
109
2100675e 110 int c, r;
0d43c694
LP
111
112 assert(argc >= 0);
113 assert(argv);
114
a963990f 115 while ((c = getopt_long(argc, argv, "hfo:an:qlbD:", options, NULL)) >= 0) {
0d43c694
LP
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':
df50185b
LP
138 arg_output = output_mode_from_string(optarg);
139 if (arg_output < 0) {
0d43c694
LP
140 log_error("Unknown output '%s'.", optarg);
141 return -EINVAL;
142 }
df50185b 143
0d43c694
LP
144 break;
145
146 case 'a':
147 arg_show_all = true;
148 break;
149
2100675e
LP
150 case 'n':
151 r = safe_atoi(optarg, &arg_lines);
e91af489 152 if (r < 0 || arg_lines < 0) {
2100675e
LP
153 log_error("Failed to parse lines '%s'", optarg);
154 return -EINVAL;
155 }
156 break;
157
e91af489
LP
158 case ARG_NO_TAIL:
159 arg_no_tail = true;
160 break;
161
39f7f5c1
LP
162 case ARG_NEW_ID128:
163 arg_new_id128 = true;
55ee336c
LP
164 break;
165
43673799
LP
166 case 'q':
167 arg_quiet = true;
490e567d 168 break;
43673799 169
2bd3c38a
LP
170 case 'l':
171 arg_local = true;
172 break;
173
59cea26a
LP
174 case 'b':
175 arg_this_boot = true;
176 break;
177
a963990f
LP
178 case 'D':
179 arg_directory = optarg;
180 break;
181
dca6219e
LP
182 case ARG_HEADER:
183 arg_print_header = true;
184 break;
185
0d43c694
LP
186 case '?':
187 return -EINVAL;
188
189 default:
190 log_error("Unknown option code %c", c);
191 return -EINVAL;
192 }
193 }
194
62f21ec9 195 if (arg_follow && !arg_no_tail && arg_lines < 0)
e91af489
LP
196 arg_lines = 10;
197
0d43c694
LP
198 return 1;
199}
200
39f7f5c1 201static int generate_new_id128(void) {
55ee336c
LP
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
a963990f
LP
229static int add_matches(sd_journal *j, char **args) {
230 char **i;
231 int r;
59cea26a 232
a963990f 233 assert(j);
59cea26a 234
a963990f 235 STRV_FOREACH(i, args) {
59cea26a 236
cbdca852
LP
237 if (streq(*i, "+"))
238 r = sd_journal_add_disjunction(j);
239 else if (path_is_absolute(*i)) {
a963990f 240 char *p;
e5124088 241 const char *path;
a963990f 242 struct stat st;
e5124088 243
a963990f
LP
244 p = canonicalize_file_name(*i);
245 path = p ? p : *i;
e5124088
LP
246
247 if (stat(path, &st) < 0) {
248 free(p);
249 log_error("Couldn't stat file: %m");
a963990f 250 return -errno;
e5124088
LP
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);
0d0f0c50 259 return log_oom();
e5124088
LP
260 }
261
cbdca852 262 r = sd_journal_add_match(j, t, 0);
e5124088 263 free(t);
50940700 264 } else {
e5124088 265 free(p);
a963990f
LP
266 log_error("File is not a regular file or is not executable: %s", *i);
267 return -EINVAL;
50940700 268 }
e5124088
LP
269
270 free(p);
271 } else
cbdca852 272 r = sd_journal_add_match(j, *i, 0);
e5124088 273
de7b95cd 274 if (r < 0) {
cbdca852 275 log_error("Failed to add match '%s': %s", *i, strerror(-r));
a963990f 276 return r;
de7b95cd
LP
277 }
278 }
279
a963990f
LP
280 return 0;
281}
282
283static 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
307int main(int argc, char *argv[]) {
308 int r;
309 sd_journal *j = NULL;
310 unsigned line = 0;
311 bool need_seek = false;
14a65d65
LP
312 sd_id128_t previous_boot_id;
313 bool previous_boot_id_valid = false;
92a1fd9e 314 bool have_pager;
a963990f
LP
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
dca6219e
LP
343 if (arg_print_header) {
344 journal_print_header(j);
345 r = 0;
346 goto finish;
347 }
348
a963990f
LP
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
08984293
LP
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
2100675e
LP
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);
2100675e
LP
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 }
6f003b43
LP
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;
50f20cfd 398 }
87d2c1ff 399
fafb6ecc 400 have_pager = !arg_no_pager && !arg_follow && pager_open();
0d43c694
LP
401
402 if (arg_output == OUTPUT_JSON) {
72f59706 403 fputc('[', stdout);
0d43c694
LP
404 fflush(stdout);
405 }
72f59706 406
50f20cfd 407 for (;;) {
0d43c694 408 for (;;) {
14a65d65 409 sd_id128_t boot_id;
92a1fd9e
ZJS
410 int flags = (arg_show_all*OUTPUT_SHOW_ALL |
411 have_pager*OUTPUT_FULL_WIDTH);
14a65d65 412
6f003b43
LP
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 }
0d43c694
LP
419 }
420
421 if (r == 0)
422 break;
423
14a65d65
LP
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
72f59706 434 line ++;
50f20cfd 435
92a1fd9e 436 r = output_journal(j, arg_output, line, 0, flags);
72f59706
LP
437 if (r < 0)
438 goto finish;
6f003b43
LP
439
440 need_seek = true;
87d2c1ff
LP
441 }
442
50f20cfd
LP
443 if (!arg_follow)
444 break;
445
e02d1cf7 446 r = sd_journal_wait(j, (uint64_t) -1);
50f20cfd 447 if (r < 0) {
e02d1cf7 448 log_error("Couldn't wait for log event: %s", strerror(-r));
50f20cfd
LP
449 goto finish;
450 }
de190aef 451 }
87d2c1ff 452
72f59706
LP
453 if (arg_output == OUTPUT_JSON)
454 fputs("\n]\n", stdout);
455
87d2c1ff 456finish:
3fbf9cbb
LP
457 if (j)
458 sd_journal_close(j);
87d2c1ff 459
0d43c694
LP
460 pager_close();
461
3fbf9cbb 462 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
87d2c1ff 463}