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