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