]> git.proxmox.com Git - proxmox-mini-journalreader.git/blame - src/mini-journalreader.c
rename convert_argument to arg_to_uint64
[proxmox-mini-journalreader.git] / src / mini-journalreader.c
CommitLineData
4ce2e883 1/*
4ce2e883
DC
2 Copyright (C) 2019 Proxmox Server Solutions GmbH
3
814a5472 4 Copyright: mini-journal is under GNU GPL, the GNU General Public License.
4ce2e883
DC
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; version 2 dated June, 1991.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18 02111-1307, USA.
19
20 Author: Dominik Csapak <d.csapak@proxmox.com>
4ce2e883
DC
21*/
22
23#include <errno.h>
24#include <stdbool.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <systemd/sd-journal.h>
29#include <time.h>
30#include <unistd.h>
31
32#define BUFSIZE 4096
33
34static char buf[BUFSIZE+1];
35static size_t offset = 0;
36
4ce2e883
DC
37uint64_t get_timestamp(sd_journal *j) {
38 uint64_t timestamp;
39 int r = sd_journal_get_realtime_usec(j, &timestamp);
40 if (r < 0) {
4a5869e3
TL
41 fprintf(stderr, "Failed %s\n", strerror(-r));
42 return 0xFFFFFFFFFFFFFFFF;
4ce2e883
DC
43 }
44 return timestamp;
45}
46
47void print_to_buf(const char * string, uint32_t length) {
48 if (!length) {
4a5869e3 49 return;
4ce2e883
DC
50 }
51 size_t string_offset = 0;
52 size_t remaining = length;
53 while (offset + remaining > BUFSIZE) {
4a5869e3
TL
54 strncpy(buf+offset, string+string_offset, BUFSIZE-offset);
55 string_offset += BUFSIZE-offset;
56 remaining = length - string_offset;
57 write (1, buf, BUFSIZE);
58 offset = 0;
4ce2e883
DC
59 }
60 strncpy(buf+offset, string+string_offset, remaining);
61 offset += remaining;
62}
63
64bool printed_first_cursor = false;
65
66void print_cursor(sd_journal *j) {
67 int r;
68 char *cursor = NULL;
69 r = sd_journal_get_cursor(j, &cursor);
70 if (r < 0) {
4a5869e3
TL
71 fprintf(stderr, "Failed to get cursor: %s\n", strerror(-r));
72 exit(1);
4ce2e883
DC
73 }
74 print_to_buf(cursor, strlen(cursor));
75 print_to_buf("\n", 1);
76 free(cursor);
77}
78
79void print_first_cursor(sd_journal *j) {
80 if (!printed_first_cursor) {
4a5869e3
TL
81 print_cursor(j);
82 printed_first_cursor = true;
4ce2e883
DC
83 }
84}
85
86static uint64_t last_timestamp;
87static char timestring[16];
88static char bootid[32];
89
90void print_reboot(sd_journal *j) {
91 const char *d;
92 size_t l;
93 int r = sd_journal_get_data(j, "_BOOT_ID", (const void **)&d, &l);
94 if (r < 0) {
4a5869e3
TL
95 fprintf(stderr, "Failed %s\n", strerror(-r));
96 return;
4ce2e883
DC
97 }
98
99 // remove '_BOOT_ID='
100 d += 9;
101 l -= 9;
102
103 if (bootid[0] != '\0') { // we have some bootid
4a5869e3
TL
104 if (strncmp(bootid, d, l)) { // a new bootid found
105 strncpy(bootid, d, l);
106 print_to_buf("-- Reboot --\n", 13);
107 }
4ce2e883 108 } else {
4a5869e3 109 strncpy(bootid, d, l);
4ce2e883
DC
110 }
111}
112
113void print_timestamp(sd_journal *j) {
114 uint64_t timestamp;
115 int r = sd_journal_get_realtime_usec(j, &timestamp);
116 if (r < 0) {
4a5869e3
TL
117 fprintf(stderr, "Failed %s\n", strerror(-r));
118 return;
4ce2e883
DC
119 }
120
121 if (timestamp >= (last_timestamp+(1000*1000))) {
4a5869e3
TL
122 timestamp = timestamp / (1000*1000); // usec to sec
123 struct tm time;
124 localtime_r((time_t *)&timestamp, &time);
125 strftime(timestring, 16, "%b %d %T", &time);
126 last_timestamp = timestamp;
4ce2e883
DC
127 }
128
129 print_to_buf(timestring, 15);
130}
131
132void print_pid(sd_journal *j) {
133 const char *d;
134 size_t l;
135 int r = sd_journal_get_data(j, "_PID", (const void **)&d, &l);
136 if (r < 0) {
4a5869e3
TL
137 // we sometimes have no pid
138 return;
4ce2e883
DC
139 }
140
141 // remove '_PID='
142 d += 5;
143 l -= 5;
144
145 print_to_buf("[", 1);
146 print_to_buf(d, l);
147 print_to_buf("]", 1);
148}
149
150bool print_field(sd_journal *j, const char *field) {
151 const char *d;
152 size_t l;
153 int r = sd_journal_get_data(j, field, (const void **)&d, &l);
154 if (r < 0) {
4a5869e3
TL
155 // some fields do not exists
156 return false;
4ce2e883
DC
157 }
158
159 int fieldlen = strlen(field)+1;
160 d += fieldlen;
161 l -= fieldlen;
162 print_to_buf(d, l);
163 return true;
164}
165
166
167void print_line(sd_journal *j) {
168 print_reboot(j);
169 print_timestamp(j);
170 print_to_buf(" ", 1);
171 print_field(j, "_HOSTNAME");
172 print_to_buf(" ", 1);
173 if (!print_field(j, "SYSLOG_IDENTIFIER") &&
4a5869e3
TL
174 !print_field(j, "_COMM")) {
175 print_to_buf("unknown", strlen("unknown") - 1);
4ce2e883
DC
176 }
177 print_pid(j);
178 print_to_buf(": ", 2);
179 print_field(j, "MESSAGE");
180 print_to_buf("\n", 1);
181}
182
183void usage(char *progname) {
184 fprintf(stderr, "usage: %s [OPTIONS]\n", progname);
185 fprintf(stderr, " -b begin\tbegin at this epoch\n");
186 fprintf(stderr, " -e end\tend at this epoch\n");
187 fprintf(stderr, " -d directory\tpath to journal directory\n");
188 fprintf(stderr, " -n number\tprint the last number entries\n");
189 fprintf(stderr, " -f from\tprint from this cursor\n");
190 fprintf(stderr, " -t to\tprint to this cursor\n");
191 fprintf(stderr, " -h \t\tthis help\n");
192 fprintf(stderr, "\n");
193 fprintf(stderr, "giving a range conflicts with -n\n");
194 fprintf(stderr, "-b and -f conflict\n");
195 fprintf(stderr, "-e and -t conflict\n");
196}
197
c13468ab
TL
198static uint64_t arg_to_uint64(const char *argument) {
199 errno = 0;
200 char * end;
201 uint64_t value = strtoull(argument, &end, 10);
202 if (errno != 0 || *end != '\0') {
203 fprintf(stderr, "%s is not a valid integer number\n", argument);
204 exit(1);
205 }
206
207 return value;
208}
209
210
4ce2e883
DC
211int main(int argc, char *argv[]) {
212 uint64_t number = 0;
213 const char *directory = NULL;
214 const char *startcursor = NULL;
215 const char *endcursor = NULL;
216 uint64_t begin = 0;
217 uint64_t end = 0;
218 char c;
219
220 while ((c = getopt (argc, argv, "b:e:d:n:f:t:h")) != -1) {
4a5869e3
TL
221 switch (c) {
222 case 'b':
c13468ab
TL
223 begin = arg_to_uint64(optarg);
224 begin = begin * 1000 * 1000; // µs
4a5869e3
TL
225 break;
226 case 'e':
c13468ab
TL
227 end = arg_to_uint64(optarg);
228 end = end * 1000 * 1000; // µs
4a5869e3
TL
229 break;
230 case 'd':
231 directory = optarg;
232 break;
233 case 'n':
c13468ab 234 number = arg_to_uint64(optarg);
4a5869e3
TL
235 break;
236 case 'f':
237 startcursor = optarg;
238 break;
239 case 't':
240 endcursor = optarg;
241 break;
242 case 'h':
243 usage(argv[0]);
244 exit(0);
245 break;
246 case '?':
247 default:
248 usage(argv[0]);
249 exit(1);
250 }
4ce2e883
DC
251 }
252
253 if (number && (begin || startcursor)) {
4a5869e3
TL
254 usage(argv[0]);
255 exit(1);
4ce2e883
DC
256 }
257
258 if (begin && startcursor) {
4a5869e3
TL
259 usage(argv[0]);
260 exit(1);
4ce2e883
DC
261 }
262
263 if (end && endcursor) {
4a5869e3
TL
264 usage(argv[0]);
265 exit(1);
4ce2e883
DC
266 }
267
268 if (argc > optind) {
4a5869e3
TL
269 usage(argv[0]);
270 exit(1);
4ce2e883
DC
271 }
272
273 // to prevent calling it everytime we generate a timestamp
274 tzset();
275
276 int r;
277 sd_journal *j;
278 if (directory == NULL) {
4a5869e3 279 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY);
4ce2e883 280 } else {
4a5869e3 281 r = sd_journal_open_directory(&j, directory, 0);
4ce2e883
DC
282 }
283
284 if (r < 0) {
4a5869e3
TL
285 fprintf(stderr, "Failed to open journal: %s\n", strerror(-r));
286 return 1;
4ce2e883
DC
287 }
288
289 // if we want to print the last x entries, seek to cursor or end,
290 // then x entries back, print the cursor and finally print the
291 // entries until end or cursor
292 if (number) {
4a5869e3
TL
293 if (end) {
294 r = sd_journal_seek_realtime_usec(j, end);
295 } else if (endcursor != NULL) {
296 r = sd_journal_seek_cursor(j, endcursor);
297 number++;
298 } else {
299 r = sd_journal_seek_tail(j);
300 }
301
302 if (r < 0) {
303 fprintf(stderr, "Failed to seek to end/cursor: %s\n", strerror(-r));
304 exit(1);
305 }
306
307 // seek back number entries and print cursor
308 r = sd_journal_previous_skip(j, number + 1);
309 if (r < 0) {
310 fprintf(stderr, "Failed to seek back: %s\n", strerror(-r));
311 exit(1);
312 }
4ce2e883 313 } else {
4a5869e3
TL
314 if (begin) {
315 r = sd_journal_seek_realtime_usec(j, begin);
316 } else if (startcursor) {
317 r = sd_journal_seek_cursor(j, startcursor);
318 } else {
319 r = sd_journal_seek_head(j);
320 }
321
322 if (r < 0) {
323 fprintf(stderr, "Failed to seek to begin/cursor: %s\n", strerror(-r));
324 exit(1);
325 }
326
327 // if we have a start cursor, we want to skip the first entry
328 if (startcursor) {
329 r = sd_journal_next(j);
330 if (r < 0) {
331 fprintf(stderr, "Failed to seek to begin/cursor: %s\n", strerror(-r));
332 exit(1);
333 }
334 print_first_cursor(j);
335 }
4ce2e883
DC
336 }
337
338
339 while ((r = sd_journal_next(j)) > 0 && (end == 0 || get_timestamp(j) < end)) {
4a5869e3
TL
340 print_first_cursor(j);
341 if (endcursor != NULL && sd_journal_test_cursor(j, endcursor)) {
342 break;
343 }
344 print_line(j);
4ce2e883
DC
345 }
346
347 // print optional reboot
348 print_reboot(j);
349
350 // print final cursor
351 print_cursor(j);
352
353 // print remaining buffer
354 write(1, buf, offset);
355 sd_journal_close(j);
356
357 return 0;
358}
359