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