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