chiark / gitweb /
relicense to LGPLv2.1 (with exceptions)
[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
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 }