chiark / gitweb /
6929b7601867302d466abf960f3324eb4acb5016
[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 "path-util.h"
39 #include "build.h"
40 #include "pager.h"
41 #include "logs-show.h"
42
43 static OutputMode arg_output = OUTPUT_SHORT;
44 static bool arg_follow = false;
45 static bool arg_show_all = false;
46 static bool arg_no_pager = false;
47 static int arg_lines = -1;
48 static bool arg_no_tail = false;
49 static bool arg_new_id128 = false;
50 static bool arg_quiet = false;
51 static bool arg_local = false;
52 static bool arg_this_boot = 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                "  -l --local          Only local entries\n"
69                "  -b --this-boot      Show data only from current boot\n"
70                "     --new-id128      Generate a new 128 Bit id\n",
71                program_invocation_short_name);
72
73         return 0;
74 }
75
76 static int parse_argv(int argc, char *argv[]) {
77
78         enum {
79                 ARG_VERSION = 0x100,
80                 ARG_NO_PAGER,
81                 ARG_NO_TAIL,
82                 ARG_NEW_ID128
83         };
84
85         static const struct option options[] = {
86                 { "help",      no_argument,       NULL, 'h'           },
87                 { "version" ,  no_argument,       NULL, ARG_VERSION   },
88                 { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
89                 { "follow",    no_argument,       NULL, 'f'           },
90                 { "output",    required_argument, NULL, 'o'           },
91                 { "all",       no_argument,       NULL, 'a'           },
92                 { "lines",     required_argument, NULL, 'n'           },
93                 { "no-tail",   no_argument,       NULL, ARG_NO_TAIL   },
94                 { "new-id128", no_argument,       NULL, ARG_NEW_ID128 },
95                 { "quiet",     no_argument,       NULL, 'q'           },
96                 { "local",     no_argument,       NULL, 'l'           },
97                 { "this-boot", no_argument,       NULL, 'b'           },
98                 { NULL,        0,                 NULL, 0             }
99         };
100
101         int c, r;
102
103         assert(argc >= 0);
104         assert(argv);
105
106         while ((c = getopt_long(argc, argv, "hfo:an:qlb", options, NULL)) >= 0) {
107
108                 switch (c) {
109
110                 case 'h':
111                         help();
112                         return 0;
113
114                 case ARG_VERSION:
115                         puts(PACKAGE_STRING);
116                         puts(DISTRIBUTION);
117                         puts(SYSTEMD_FEATURES);
118                         return 0;
119
120                 case ARG_NO_PAGER:
121                         arg_no_pager = true;
122                         break;
123
124                 case 'f':
125                         arg_follow = true;
126                         break;
127
128                 case 'o':
129                         arg_output =  output_mode_from_string(optarg);
130                         if (arg_output < 0) {
131                                 log_error("Unknown output '%s'.", optarg);
132                                 return -EINVAL;
133                         }
134
135                         break;
136
137                 case 'a':
138                         arg_show_all = true;
139                         break;
140
141                 case 'n':
142                         r = safe_atoi(optarg, &arg_lines);
143                         if (r < 0 || arg_lines < 0) {
144                                 log_error("Failed to parse lines '%s'", optarg);
145                                 return -EINVAL;
146                         }
147                         break;
148
149                 case ARG_NO_TAIL:
150                         arg_no_tail = true;
151                         break;
152
153                 case ARG_NEW_ID128:
154                         arg_new_id128 = true;
155                         break;
156
157                 case 'q':
158                         arg_quiet = true;
159                         break;
160
161                 case 'l':
162                         arg_local = true;
163                         break;
164
165                 case 'b':
166                         arg_this_boot = true;
167                         break;
168
169                 case '?':
170                         return -EINVAL;
171
172                 default:
173                         log_error("Unknown option code %c", c);
174                         return -EINVAL;
175                 }
176         }
177
178         if (arg_follow && !arg_no_tail && arg_lines < 0)
179                 arg_lines = 10;
180
181         return 1;
182 }
183
184 static int generate_new_id128(void) {
185         sd_id128_t id;
186         int r;
187         unsigned i;
188
189         r = sd_id128_randomize(&id);
190         if (r < 0) {
191                 log_error("Failed to generate ID: %s", strerror(-r));
192                 return r;
193         }
194
195         printf("As string:\n"
196                SD_ID128_FORMAT_STR "\n\n"
197                "As UUID:\n"
198                "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n"
199                "As macro:\n"
200               "#define MESSAGE_XYZ SD_ID128_MAKE(",
201                SD_ID128_FORMAT_VAL(id),
202                SD_ID128_FORMAT_VAL(id));
203
204         for (i = 0; i < 16; i++)
205                 printf("%02x%s", id.bytes[i], i != 15 ? "," : "");
206
207         fputs(")\n", stdout);
208
209         return 0;
210 }
211
212 int main(int argc, char *argv[]) {
213         int r, i, fd;
214         sd_journal *j = NULL;
215         unsigned line = 0;
216         bool need_seek = false;
217         struct stat st;
218
219         log_parse_environment();
220         log_open();
221
222         r = parse_argv(argc, argv);
223         if (r <= 0)
224                 goto finish;
225
226         if (arg_new_id128) {
227                 r = generate_new_id128();
228                 goto finish;
229         }
230
231 #ifdef HAVE_ACL
232         if (!arg_quiet && geteuid() != 0 && in_group("adm") <= 0)
233                 log_warning("Showing user generated messages only. Users in the group 'adm' can see all messages. Pass -q to turn this message off.");
234 #endif
235
236         r = sd_journal_open(&j, arg_local ? SD_JOURNAL_LOCAL_ONLY : 0);
237         if (r < 0) {
238                 log_error("Failed to open journal: %s", strerror(-r));
239                 goto finish;
240         }
241
242         if (arg_this_boot) {
243                 char match[9+32+1] = "_BOOT_ID=";
244                 sd_id128_t boot_id;
245
246                 r = sd_id128_get_boot(&boot_id);
247                 if (r < 0) {
248                         log_error("Failed to get boot id: %s", strerror(-r));
249                         goto finish;
250                 }
251
252                 sd_id128_to_string(boot_id, match + 9);
253
254                 r = sd_journal_add_match(j, match, strlen(match));
255                 if (r < 0) {
256                         log_error("Failed to add match: %s", strerror(-r));
257                         goto finish;
258                 }
259         }
260
261         for (i = optind; i < argc; i++) {
262                 if (path_is_absolute(argv[i])) {
263                         char *p = NULL;
264                         const char *path;
265
266                         p = canonicalize_file_name(argv[i]);
267                         path = p ? p : argv[i];
268
269                         if (stat(path, &st) < 0)  {
270                                 free(p);
271                                 log_error("Couldn't stat file: %m");
272                                 r = -errno;
273                                 goto finish;
274                         }
275
276                         if (S_ISREG(st.st_mode) && (0111 & st.st_mode)) {
277                                 char *t;
278
279                                 t = strappend("_EXE=", path);
280                                 if (!t) {
281                                         free(p);
282                                         log_error("Out of memory");
283                                         goto finish;
284                                 }
285
286                                 r = sd_journal_add_match(j, t, strlen(t));
287                                 free(t);
288                         } else {
289                                 free(p);
290                                 log_error("File is not a regular file or is not executable: %s", argv[i]);
291                                 goto finish;
292                         }
293
294                         free(p);
295                 } else
296                         r = sd_journal_add_match(j, argv[i], strlen(argv[i]));
297
298                 if (r < 0) {
299                         log_error("Failed to add match: %s", strerror(-r));
300                         goto finish;
301                 }
302         }
303
304         fd = sd_journal_get_fd(j);
305         if (fd < 0) {
306                 log_error("Failed to get wakeup fd: %s", strerror(-fd));
307                 goto finish;
308         }
309
310         if (!arg_quiet) {
311                 usec_t start, end;
312                 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
313
314                 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
315                 if (r < 0) {
316                         log_error("Failed to get cutoff: %s", strerror(-r));
317                         goto finish;
318                 }
319
320                 if (r > 0) {
321                         if (arg_follow)
322                                 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
323                         else
324                                 printf("Logs begin at %s, end at %s.\n",
325                                        format_timestamp(start_buf, sizeof(start_buf), start),
326                                        format_timestamp(end_buf, sizeof(end_buf), end));
327                 }
328         }
329
330         if (arg_lines >= 0) {
331                 r = sd_journal_seek_tail(j);
332                 if (r < 0) {
333                         log_error("Failed to seek to tail: %s", strerror(-r));
334                         goto finish;
335                 }
336
337                 r = sd_journal_previous_skip(j, arg_lines);
338         } else {
339                 r = sd_journal_seek_head(j);
340                 if (r < 0) {
341                         log_error("Failed to seek to head: %s", strerror(-r));
342                         goto finish;
343                 }
344
345                 r = sd_journal_next(j);
346         }
347
348         if (r < 0) {
349                 log_error("Failed to iterate through journal: %s", strerror(-r));
350                 goto finish;
351         }
352
353         if (!arg_no_pager && !arg_follow) {
354                 columns();
355                 pager_open();
356         }
357
358         if (arg_output == OUTPUT_JSON) {
359                 fputc('[', stdout);
360                 fflush(stdout);
361         }
362
363         for (;;) {
364                 for (;;) {
365                         if (need_seek) {
366                                 r = sd_journal_next(j);
367                                 if (r < 0) {
368                                         log_error("Failed to iterate through journal: %s", strerror(-r));
369                                         goto finish;
370                                 }
371                         }
372
373                         if (r == 0)
374                                 break;
375
376                         line ++;
377
378                         r = output_journal(j, arg_output, line, 0, arg_show_all);
379                         if (r < 0)
380                                 goto finish;
381
382                         need_seek = true;
383                 }
384
385                 if (!arg_follow)
386                         break;
387
388                 r = fd_wait_for_event(fd, POLLIN, (usec_t) -1);
389                 if (r < 0) {
390                         log_error("Couldn't wait for event: %s", strerror(-r));
391                         goto finish;
392                 }
393
394                 r = sd_journal_process(j);
395                 if (r < 0) {
396                         log_error("Failed to process: %s", strerror(-r));
397                         goto finish;
398                 }
399         }
400
401         if (arg_output == OUTPUT_JSON)
402                 fputs("\n]\n", stdout);
403
404 finish:
405         if (j)
406                 sd_journal_close(j);
407
408         pager_close();
409
410         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
411 }