]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journalctl.c
journalctl: add --local switch
[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 General Public License as published by
10 the Free Software Foundation; either version 2 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 General Public License for more details.
17
18 You should have received a copy of the GNU 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
33 #include <systemd/sd-journal.h>
34
35 #include "log.h"
36 #include "util.h"
37 #include "build.h"
38 #include "pager.h"
39 #include "logs-show.h"
40
41 static OutputMode arg_output = OUTPUT_SHORT;
42 static bool arg_follow = false;
43 static bool arg_show_all = false;
44 static bool arg_no_pager = false;
45 static int arg_lines = -1;
46 static bool arg_no_tail = false;
47 static bool arg_new_id128 = false;
48 static bool arg_quiet = false;
49 static bool arg_local = false;
50
51 static int help(void) {
52
53 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
54 "Send control commands to or query the journal.\n\n"
55 " -h --help Show this help\n"
56 " --version Show package version\n"
57 " --no-pager Do not pipe output into a pager\n"
58 " -a --all Show all fields, including long and unprintable\n"
59 " -f --follow Follow journal\n"
60 " -n --lines=INTEGER Journal entries to show\n"
61 " --no-tail Show all lines, even in follow mode\n"
62 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
63 " verbose, export, json, cat)\n"
64 " -q --quiet Don't show privilege warning\n"
65 " --new-id128 Generate a new 128 Bit id\n"
66 " -l --local Only local entries\n",
67 program_invocation_short_name);
68
69 return 0;
70 }
71
72 static int parse_argv(int argc, char *argv[]) {
73
74 enum {
75 ARG_VERSION = 0x100,
76 ARG_NO_PAGER,
77 ARG_NO_TAIL,
78 ARG_NEW_ID128
79 };
80
81 static const struct option options[] = {
82 { "help", no_argument, NULL, 'h' },
83 { "version" , no_argument, NULL, ARG_VERSION },
84 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
85 { "follow", no_argument, NULL, 'f' },
86 { "output", required_argument, NULL, 'o' },
87 { "all", no_argument, NULL, 'a' },
88 { "lines", required_argument, NULL, 'n' },
89 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
90 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
91 { "quiet", no_argument, NULL, 'q' },
92 { "local", no_argument, NULL, 'l' },
93 { NULL, 0, NULL, 0 }
94 };
95
96 int c, r;
97
98 assert(argc >= 0);
99 assert(argv);
100
101 while ((c = getopt_long(argc, argv, "hfo:an:ql", options, NULL)) >= 0) {
102
103 switch (c) {
104
105 case 'h':
106 help();
107 return 0;
108
109 case ARG_VERSION:
110 puts(PACKAGE_STRING);
111 puts(DISTRIBUTION);
112 puts(SYSTEMD_FEATURES);
113 return 0;
114
115 case ARG_NO_PAGER:
116 arg_no_pager = true;
117 break;
118
119 case 'f':
120 arg_follow = true;
121 break;
122
123 case 'o':
124 arg_output = output_mode_from_string(optarg);
125 if (arg_output < 0) {
126 log_error("Unknown output '%s'.", optarg);
127 return -EINVAL;
128 }
129
130 break;
131
132 case 'a':
133 arg_show_all = true;
134 break;
135
136 case 'n':
137 r = safe_atoi(optarg, &arg_lines);
138 if (r < 0 || arg_lines < 0) {
139 log_error("Failed to parse lines '%s'", optarg);
140 return -EINVAL;
141 }
142 break;
143
144 case ARG_NO_TAIL:
145 arg_no_tail = true;
146 break;
147
148 case ARG_NEW_ID128:
149 arg_new_id128 = true;
150 break;
151
152 case 'q':
153 arg_quiet = true;
154 break;
155
156 case 'l':
157 arg_local = true;
158 break;
159
160 case '?':
161 return -EINVAL;
162
163 default:
164 log_error("Unknown option code %c", c);
165 return -EINVAL;
166 }
167 }
168
169 if (arg_follow && !arg_no_tail && arg_lines < 0)
170 arg_lines = 10;
171
172 return 1;
173 }
174
175 static int generate_new_id128(void) {
176 sd_id128_t id;
177 int r;
178 unsigned i;
179
180 r = sd_id128_randomize(&id);
181 if (r < 0) {
182 log_error("Failed to generate ID: %s", strerror(-r));
183 return r;
184 }
185
186 printf("As string:\n"
187 SD_ID128_FORMAT_STR "\n\n"
188 "As UUID:\n"
189 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
190 "As macro:\n"
191 "#define MESSAGE_XYZ SD_ID128_MAKE(",
192 SD_ID128_FORMAT_VAL(id),
193 SD_ID128_FORMAT_VAL(id));
194
195 for (i = 0; i < 16; i++)
196 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
197
198 fputs(")\n", stdout);
199
200 return 0;
201 }
202
203 int main(int argc, char *argv[]) {
204 int r, i, fd;
205 sd_journal *j = NULL;
206 unsigned line = 0;
207 bool need_seek = false;
208
209 log_parse_environment();
210 log_open();
211
212 r = parse_argv(argc, argv);
213 if (r <= 0)
214 goto finish;
215
216 if (arg_new_id128) {
217 r = generate_new_id128();
218 goto finish;
219 }
220
221 #ifdef HAVE_ACL
222 if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
223 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
224 #endif
225
226 r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
227 if (r < 0) {
228 log_error("Failed to open journal: %s", strerror(-r));
229 goto finish;
230 }
231
232 for (i = optind; i < argc; i++) {
233 r = sd_journal_add_match(j, argv[i], strlen(argv[i]));
234 if (r < 0) {
235 log_error("Failed to add match: %s", strerror(-r));
236 goto finish;
237 }
238 }
239
240 fd = sd_journal_get_fd(j);
241 if (fd < 0) {
242 log_error("Failed to get wakeup fd: %s", strerror(-fd));
243 goto finish;
244 }
245
246 if (arg_lines >= 0) {
247 r = sd_journal_seek_tail(j);
248 if (r < 0) {
249 log_error("Failed to seek to tail: %s", strerror(-r));
250 goto finish;
251 }
252
253 r = sd_journal_previous_skip(j, arg_lines);
254 } else {
255 r = sd_journal_seek_head(j);
256 if (r < 0) {
257 log_error("Failed to seek to head: %s", strerror(-r));
258 goto finish;
259 }
260
261 r = sd_journal_next(j);
262 }
263
264 if (r < 0) {
265 log_error("Failed to iterate through journal: %s", strerror(-r));
266 goto finish;
267 }
268
269 if (!arg_no_pager && !arg_follow) {
270 columns();
271 pager_open();
272 }
273
274 if (arg_output == OUTPUT_JSON) {
275 fputc('[', stdout);
276 fflush(stdout);
277 }
278
279 for (;;) {
280 for (;;) {
281 if (need_seek) {
282 r = sd_journal_next(j);
283 if (r < 0) {
284 log_error("Failed to iterate through journal: %s", strerror(-r));
285 goto finish;
286 }
287 }
288
289 if (r == 0)
290 break;
291
292 line ++;
293
294 r = output_journal(j, arg_output, line, 0, arg_show_all);
295 if (r < 0)
296 goto finish;
297
298 need_seek = true;
299 }
300
301 if (!arg_follow)
302 break;
303
304 r = fd_wait_for_event(fd, POLLIN, (usec_t) -1);
305 if (r < 0) {
306 log_error("Couldn't wait for event: %s", strerror(-r));
307 goto finish;
308 }
309
310 r = sd_journal_process(j);
311 if (r < 0) {
312 log_error("Failed to process: %s", strerror(-r));
313 goto finish;
314 }
315 }
316
317 if (arg_output == OUTPUT_JSON)
318 fputs("\n]\n", stdout);
319
320 finish:
321 if (j)
322 sd_journal_close(j);
323
324 pager_close();
325
326 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
327 }