]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journalctl.c
journalctl: support /usr/bin/nginx, etc
[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 #include <sys/stat.h>
33
34 #include <systemd/sd-journal.h>
35
36 #include "log.h"
37 #include "util.h"
38 #include "build.h"
39 #include "pager.h"
40 #include "logs-show.h"
41
42 #define SD_JOURNALCTL_EXE "_EXE="
43
44 static OutputMode arg_output = OUTPUT_SHORT;
45 static bool arg_follow = false;
46 static bool arg_show_all = false;
47 static bool arg_no_pager = false;
48 static int arg_lines = -1;
49 static bool arg_no_tail = false;
50 static bool arg_new_id128 = false;
51 static bool arg_quiet = false;
52 static bool arg_local = false;
53
54 static int help(void) {
55
56 printf("%s [OPTIONS...] [MATCH]\n\n"
57 "Send control commands to or query the journal.\n\n"
58 " -h --help Show this help\n"
59 " --version Show package version\n"
60 " --no-pager Do not pipe output into a pager\n"
61 " -a --all Show all fields, including long and unprintable\n"
62 " -f --follow Follow journal\n"
63 " -n --lines=INTEGER Journal entries to show\n"
64 " --no-tail Show all lines, even in follow mode\n"
65 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
66 " verbose, export, json, cat)\n"
67 " -q --quiet Don't show privilege warning\n"
68 " --new-id128 Generate a new 128 Bit id\n"
69 " -l --local Only local entries\n",
70 program_invocation_short_name);
71
72 return 0;
73 }
74
75 static int parse_argv(int argc, char *argv[]) {
76
77 enum {
78 ARG_VERSION = 0x100,
79 ARG_NO_PAGER,
80 ARG_NO_TAIL,
81 ARG_NEW_ID128
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' },
91 { "lines", required_argument, NULL, 'n' },
92 { "no-tail", no_argument, NULL, ARG_NO_TAIL },
93 { "new-id128", no_argument, NULL, ARG_NEW_ID128 },
94 { "quiet", no_argument, NULL, 'q' },
95 { "local", no_argument, NULL, 'l' },
96 { NULL, 0, NULL, 0 }
97 };
98
99 int c, r;
100
101 assert(argc >= 0);
102 assert(argv);
103
104 while ((c = getopt_long(argc, argv, "hfo:an:ql", options, NULL)) >= 0) {
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':
127 arg_output = output_mode_from_string(optarg);
128 if (arg_output < 0) {
129 log_error("Unknown output '%s'.", optarg);
130 return -EINVAL;
131 }
132
133 break;
134
135 case 'a':
136 arg_show_all = true;
137 break;
138
139 case 'n':
140 r = safe_atoi(optarg, &arg_lines);
141 if (r < 0 || arg_lines < 0) {
142 log_error("Failed to parse lines '%s'", optarg);
143 return -EINVAL;
144 }
145 break;
146
147 case ARG_NO_TAIL:
148 arg_no_tail = true;
149 break;
150
151 case ARG_NEW_ID128:
152 arg_new_id128 = true;
153 break;
154
155 case 'q':
156 arg_quiet = true;
157 break;
158
159 case 'l':
160 arg_local = true;
161 break;
162
163 case '?':
164 return -EINVAL;
165
166 default:
167 log_error("Unknown option code %c", c);
168 return -EINVAL;
169 }
170 }
171
172 if (arg_follow && !arg_no_tail && arg_lines < 0)
173 arg_lines = 10;
174
175 return 1;
176 }
177
178 static int generate_new_id128(void) {
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
206 int main(int argc, char *argv[]) {
207 int r, i, fd;
208 sd_journal *j = NULL;
209 unsigned line = 0;
210 bool need_seek = false;
211 struct stat st;
212 char* journal_exe_buff;
213
214 log_parse_environment();
215 log_open();
216
217 r = parse_argv(argc, argv);
218 if (r <= 0)
219 goto finish;
220
221 if (arg_new_id128) {
222 r = generate_new_id128();
223 goto finish;
224 }
225
226 #ifdef HAVE_ACL
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.");
229 #endif
230
231 r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
232 if (r < 0) {
233 log_error("Failed to open journal: %s", strerror(-r));
234 goto finish;
235 }
236
237 for (i = optind; i < argc; i++) {
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 }
257 if (r < 0) {
258 log_error("Failed to add match: %s", strerror(-r));
259 goto finish;
260 }
261 }
262
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 }
268
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);
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 }
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;
290 }
291
292 if (!arg_no_pager && !arg_follow) {
293 columns();
294 pager_open();
295 }
296
297 if (arg_output == OUTPUT_JSON) {
298 fputc('[', stdout);
299 fflush(stdout);
300 }
301
302 for (;;) {
303 for (;;) {
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 }
310 }
311
312 if (r == 0)
313 break;
314
315 line ++;
316
317 r = output_journal(j, arg_output, line, 0, arg_show_all);
318 if (r < 0)
319 goto finish;
320
321 need_seek = true;
322 }
323
324 if (!arg_follow)
325 break;
326
327 r = fd_wait_for_event(fd, POLLIN, (usec_t) -1);
328 if (r < 0) {
329 log_error("Couldn't wait for event: %s", strerror(-r));
330 goto finish;
331 }
332
333 r = sd_journal_process(j);
334 if (r < 0) {
335 log_error("Failed to process: %s", strerror(-r));
336 goto finish;
337 }
338 }
339
340 if (arg_output == OUTPUT_JSON)
341 fputs("\n]\n", stdout);
342
343 finish:
344 if (j)
345 sd_journal_close(j);
346
347 pager_close();
348
349 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
350 }