chiark / gitweb /
daf428106518a55632d0297e282aafd419250b19
[elogind.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 }