chiark / gitweb /
7d8b8e51c82f7beb0316c0e745e2ffbb643a199f
[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;
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         if (!arg_quiet) {
305                 usec_t start, end;
306                 char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX];
307
308                 r = sd_journal_get_cutoff_realtime_usec(j, &start, &end);
309                 if (r < 0) {
310                         log_error("Failed to get cutoff: %s", strerror(-r));
311                         goto finish;
312                 }
313
314                 if (r > 0) {
315                         if (arg_follow)
316                                 printf("Logs begin at %s.\n", format_timestamp(start_buf, sizeof(start_buf), start));
317                         else
318                                 printf("Logs begin at %s, end at %s.\n",
319                                        format_timestamp(start_buf, sizeof(start_buf), start),
320                                        format_timestamp(end_buf, sizeof(end_buf), end));
321                 }
322         }
323
324         if (arg_lines >= 0) {
325                 r = sd_journal_seek_tail(j);
326                 if (r < 0) {
327                         log_error("Failed to seek to tail: %s", strerror(-r));
328                         goto finish;
329                 }
330
331                 r = sd_journal_previous_skip(j, arg_lines);
332         } else {
333                 r = sd_journal_seek_head(j);
334                 if (r < 0) {
335                         log_error("Failed to seek to head: %s", strerror(-r));
336                         goto finish;
337                 }
338
339                 r = sd_journal_next(j);
340         }
341
342         if (r < 0) {
343                 log_error("Failed to iterate through journal: %s", strerror(-r));
344                 goto finish;
345         }
346
347         if (!arg_no_pager && !arg_follow) {
348                 columns();
349                 pager_open();
350         }
351
352         if (arg_output == OUTPUT_JSON) {
353                 fputc('[', stdout);
354                 fflush(stdout);
355         }
356
357         for (;;) {
358                 for (;;) {
359                         if (need_seek) {
360                                 r = sd_journal_next(j);
361                                 if (r < 0) {
362                                         log_error("Failed to iterate through journal: %s", strerror(-r));
363                                         goto finish;
364                                 }
365                         }
366
367                         if (r == 0)
368                                 break;
369
370                         line ++;
371
372                         r = output_journal(j, arg_output, line, 0, arg_show_all);
373                         if (r < 0)
374                                 goto finish;
375
376                         need_seek = true;
377                 }
378
379                 if (!arg_follow)
380                         break;
381
382                 r = sd_journal_wait(j, (uint64_t) -1);
383                 if (r < 0) {
384                         log_error("Couldn't wait for log event: %s", strerror(-r));
385                         goto finish;
386                 }
387         }
388
389         if (arg_output == OUTPUT_JSON)
390                 fputs("\n]\n", stdout);
391
392 finish:
393         if (j)
394                 sd_journal_close(j);
395
396         pager_close();
397
398         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
399 }