]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journalctl.c
relicense to LGPLv2.1 (with exceptions)
[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
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 }