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