chiark / gitweb /
systemctl: hook up systemctl with the journal
[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 General Public License as published by
10   the Free Software Foundation; either version 2 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   General Public License for more details.
17
18   You should have received a copy of the GNU 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 "sd-journal.h"
34 #include "log.h"
35 #include "util.h"
36 #include "build.h"
37 #include "pager.h"
38 #include "logs-show.h"
39
40 static output_mode arg_output = OUTPUT_SHORT;
41
42 static bool arg_follow = false;
43 static bool arg_show_all = false;
44 static bool arg_no_pager = false;
45
46
47 static int help(void) {
48
49         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
50                "Send control commands to or query the login manager.\n\n"
51                "  -h --help           Show this help\n"
52                "     --version        Show package version\n"
53                "     --no-pager       Do not pipe output into a pager\n"
54                "  -a --all            Show all properties, including long and unprintable\n"
55                "  -f --follow         Follow journal\n"
56                "  -o --output=STRING  Change output mode (short, verbose, export, json)\n",
57                program_invocation_short_name);
58
59         return 0;
60 }
61
62 static int parse_argv(int argc, char *argv[]) {
63
64         enum {
65                 ARG_VERSION = 0x100,
66                 ARG_NO_PAGER
67         };
68
69         static const struct option options[] = {
70                 { "help",      no_argument,       NULL, 'h'           },
71                 { "version" ,  no_argument,       NULL, ARG_VERSION   },
72                 { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
73                 { "follow",    no_argument,       NULL, 'f'           },
74                 { "output",    required_argument, NULL, 'o'           },
75                 { "all",       no_argument,       NULL, 'a'           },
76                 { NULL,        0,                 NULL, 0             }
77         };
78
79         int c;
80
81         assert(argc >= 0);
82         assert(argv);
83
84         while ((c = getopt_long(argc, argv, "hfo:a", options, NULL)) >= 0) {
85
86                 switch (c) {
87
88                 case 'h':
89                         help();
90                         return 0;
91
92                 case ARG_VERSION:
93                         puts(PACKAGE_STRING);
94                         puts(DISTRIBUTION);
95                         puts(SYSTEMD_FEATURES);
96                         return 0;
97
98                 case ARG_NO_PAGER:
99                         arg_no_pager = true;
100                         break;
101
102                 case 'f':
103                         arg_follow = true;
104                         break;
105
106                 case 'o':
107                         if (streq(optarg, "short"))
108                                 arg_output = OUTPUT_SHORT;
109                         else if (streq(optarg, "verbose"))
110                                 arg_output = OUTPUT_VERBOSE;
111                         else if (streq(optarg, "export"))
112                                 arg_output = OUTPUT_EXPORT;
113                         else if (streq(optarg, "json"))
114                                 arg_output = OUTPUT_JSON;
115                         else {
116                                 log_error("Unknown output '%s'.", optarg);
117                                 return -EINVAL;
118                         }
119                         break;
120
121                 case 'a':
122                         arg_show_all = true;
123                         break;
124
125                 case '?':
126                         return -EINVAL;
127
128                 default:
129                         log_error("Unknown option code %c", c);
130                         return -EINVAL;
131                 }
132         }
133
134         return 1;
135 }
136
137 int main(int argc, char *argv[]) {
138         int r, i, fd;
139         sd_journal *j = NULL;
140         unsigned line = 0;
141
142         log_parse_environment();
143         log_open();
144
145         r = parse_argv(argc, argv);
146         if (r <= 0)
147                 goto finish;
148
149         r = sd_journal_open(&j, 0);
150         if (r < 0) {
151                 log_error("Failed to open journal: %s", strerror(-r));
152                 goto finish;
153         }
154
155         for (i = optind; i < argc; i++) {
156                 r = sd_journal_add_match(j, argv[i], strlen(argv[i]));
157                 if (r < 0) {
158                         log_error("Failed to add match: %s", strerror(-r));
159                         goto finish;
160                 }
161         }
162
163         fd = sd_journal_get_fd(j);
164         if (fd < 0) {
165                 log_error("Failed to get wakeup fd: %s", strerror(-fd));
166                 goto finish;
167         }
168
169         r = sd_journal_seek_head(j);
170         if (r < 0) {
171                 log_error("Failed to seek to head: %s", strerror(-r));
172                 goto finish;
173         }
174
175         if (!arg_no_pager && !arg_follow) {
176                 columns();
177                 pager_open();
178         }
179
180         if (arg_output == OUTPUT_JSON) {
181                 fputc('[', stdout);
182                 fflush(stdout);
183         }
184
185         for (;;) {
186                 struct pollfd pollfd;
187
188                 for (;;) {
189                         r = sd_journal_next(j);
190
191                         if (r < 0) {
192                                 log_error("Failed to iterate through journal: %s", strerror(-r));
193                                 goto finish;
194                         }
195
196                         if (r == 0)
197                                 break;
198
199                         line ++;
200
201                         r = output_journal(j, arg_output, line, arg_show_all);
202                         if (r < 0)
203                                 goto finish;
204                 }
205
206                 if (!arg_follow)
207                         break;
208
209                 zero(pollfd);
210                 pollfd.fd = fd;
211                 pollfd.events = POLLIN;
212
213                 if (poll(&pollfd, 1, -1) < 0) {
214                         if (errno == EINTR)
215                                 break;
216
217                         log_error("poll(): %m");
218                         r = -errno;
219                         goto finish;
220                 }
221
222                 r = sd_journal_process(j);
223                 if (r < 0) {
224                         log_error("Failed to process: %s", strerror(-r));
225                         goto finish;
226                 }
227         }
228
229         if (arg_output == OUTPUT_JSON)
230                 fputs("\n]\n", stdout);
231
232 finish:
233         if (j)
234                 sd_journal_close(j);
235
236         pager_close();
237
238         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
239 }