]>
git.proxmox.com Git - proxmox-mini-journalreader.git/blob - src/mini-journalreader.c
15865921df4db12927471b8799d44cc847a72d5c
2 Copyright (C) 2019 Proxmox Server Solutions GmbH
4 Copyright: mini-journal is under GNU GPL, the GNU General Public License.
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.
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.
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
20 Author: Dominik Csapak <d.csapak@proxmox.com>
28 #include <systemd/sd-journal.h>
34 static char buf
[BUFSIZE
+ 1];
35 static size_t offset
= 0;
37 static uint64_t get_timestamp(sd_journal
*j
) {
39 int r
= sd_journal_get_realtime_usec(j
, ×tamp
);
41 fprintf(stderr
, "Failed %s\n", strerror(-r
));
47 static void print_to_buf(const char * string
, size_t length
) {
51 size_t string_offset
= 0;
52 size_t remaining
= length
;
53 while (offset
+ remaining
> BUFSIZE
) {
54 strncpy(buf
+ offset
, string
+ string_offset
, BUFSIZE
- offset
);
55 string_offset
+= BUFSIZE
- offset
;
56 remaining
= length
- string_offset
;
57 if (write (1, buf
, BUFSIZE
) <= 0) {
58 perror("write to stdout failed");
63 strncpy(buf
+ offset
, string
+ string_offset
, remaining
);
67 static void print_cursor(sd_journal
*j
) {
70 r
= sd_journal_get_cursor(j
, &cursor
);
72 fprintf(stderr
, "Failed to get cursor: %s\n", strerror(-r
));
75 print_to_buf(cursor
, strlen(cursor
));
76 print_to_buf("\n", 1);
80 static void print_first_cursor(sd_journal
*j
) {
81 static bool printed_first_cursor
= false;
82 if (!printed_first_cursor
) {
84 printed_first_cursor
= true;
88 static void print_reboot(sd_journal
*j
) {
91 int r
= sd_journal_get_data(j
, "_BOOT_ID", (const void **)&d
, &l
);
93 fprintf(stderr
, "Failed %s\n", strerror(-r
));
101 static char bootid
[32];
102 if (bootid
[0] != '\0') { // we have some bootid
103 if (strncmp(bootid
, d
, l
)) { // a new bootid found
104 strncpy(bootid
, d
, l
);
105 print_to_buf("-- Reboot --\n", 13);
108 strncpy(bootid
, d
, l
);
112 static void print_timestamp(sd_journal
*j
) {
113 uint64_t timestamp
= get_timestamp(j
);
114 if (timestamp
== (uint64_t)-1) {
118 static uint64_t last_timestamp
;
119 static char timestring
[16];
120 if (timestamp
>= (last_timestamp
+(1000*1000))) {
121 timestamp
= timestamp
/ (1000*1000); // usec to sec
123 localtime_r((time_t *)×tamp
, &time
);
124 strftime(timestring
, 16, "%b %d %T", &time
);
125 last_timestamp
= timestamp
;
128 print_to_buf(timestring
, 15);
131 static void print_pid(sd_journal
*j
) {
134 int r
= sd_journal_get_data(j
, "_PID", (const void **)&d
, &l
);
136 // we sometimes have no pid, e.g., kernel messages
144 print_to_buf("[", 1);
146 print_to_buf("]", 1);
149 static bool print_field(sd_journal
*j
, const char *field
) {
152 int r
= sd_journal_get_data(j
, field
, (const void **)&d
, &l
);
154 // some fields do not exists
158 size_t fieldlen
= strlen(field
)+1;
166 static void print_line(sd_journal
*j
) {
169 print_to_buf(" ", 1);
170 print_field(j
, "_HOSTNAME");
171 print_to_buf(" ", 1);
172 if (!print_field(j
, "SYSLOG_IDENTIFIER") &&
173 !print_field(j
, "_COMM")) {
174 print_to_buf("unknown", strlen("unknown") - 1);
177 print_to_buf(": ", 2);
178 print_field(j
, "MESSAGE");
179 print_to_buf("\n", 1);
182 static char *progname
;
184 _Noreturn
static void usage(char *error
) {
186 fprintf(stderr
, "ERROR: %s\n", error
);
188 fprintf(stderr
, "usage: %s [OPTIONS]\n", progname
);
190 " -b <timestamp>\tbegin at this UNIX epoch based timestamp\n"
191 " -e <timestamp>\tend at this UNIX epoch based timestamp\n"
192 " -d <directory>\tpath to a journal directory\n"
193 " -n <integer>\t\tprint the last number entries logged\n"
194 " -f <cursor>\t\tprint from this cursor\n"
195 " -t <cursor>\t\tprint to this cursor\n"
196 " -h \t\t\tthis help\n"
198 "Passing no range option will dump all the available journal\n"
199 "Giving a range conflicts with -n\n"
200 "-b and -f conflict\n"
201 "-e and -t conflict\n");
205 static uint64_t arg_to_uint64(const char *argument
) {
208 uint64_t value
= strtoull(argument
, &end
, 10);
209 if (errno
!= 0 || *end
!= '\0') {
210 fprintf(stderr
, "%s is not a valid integer number\n", argument
);
218 int main(int argc
, char *argv
[]) {
220 const char *directory
= NULL
;
221 const char *startcursor
= NULL
;
222 const char *endcursor
= NULL
;
229 while ((c
= (char)getopt (argc
, argv
, "b:e:d:n:f:t:h")) != -1) {
232 begin
= arg_to_uint64(optarg
);
233 begin
= begin
* 1000 * 1000; // µs
236 end
= arg_to_uint64(optarg
);
237 end
= end
* 1000 * 1000; // µs
243 number
= arg_to_uint64(optarg
);
246 startcursor
= optarg
;
255 usage("invalid option or missing argument");
259 if (number
&& (begin
|| startcursor
)) {
260 usage("-n conflicts with -b and/or -f");
263 if (begin
&& startcursor
) {
264 usage("-b and -f conflict");
267 if (end
&& endcursor
) {
268 usage("-e and -t conflict");
272 usage("unkown, or to many arguments");
275 // to prevent calling it everytime we generate a timestamp
280 if (directory
== NULL
) {
281 r
= sd_journal_open(&j
, SD_JOURNAL_LOCAL_ONLY
);
283 r
= sd_journal_open_directory(&j
, directory
, 0);
287 fprintf(stderr
, "Failed to open journal: %s\n", strerror(-r
));
291 // if we want to print the last x entries, seek to cursor or end,
292 // then x entries back, print the cursor and finally print the
293 // entries until end or cursor
296 r
= sd_journal_seek_realtime_usec(j
, end
);
297 } else if (endcursor
!= NULL
) {
298 r
= sd_journal_seek_cursor(j
, endcursor
);
301 r
= sd_journal_seek_tail(j
);
305 fprintf(stderr
, "Failed to seek to end/cursor: %s\n", strerror(-r
));
309 // seek back number entries and print cursor
310 r
= sd_journal_previous_skip(j
, number
+ 1);
312 fprintf(stderr
, "Failed to seek back: %s\n", strerror(-r
));
317 r
= sd_journal_seek_realtime_usec(j
, begin
);
318 } else if (startcursor
) {
319 r
= sd_journal_seek_cursor(j
, startcursor
);
321 r
= sd_journal_seek_head(j
);
325 fprintf(stderr
, "Failed to seek to begin/cursor: %s\n", strerror(-r
));
329 // if we have a start cursor, we want to skip the first entry
331 r
= sd_journal_next(j
);
333 fprintf(stderr
, "Failed to seek to begin/cursor: %s\n", strerror(-r
));
336 print_first_cursor(j
);
341 while ((r
= sd_journal_next(j
)) > 0 && (end
== 0 || get_timestamp(j
) < end
)) {
342 print_first_cursor(j
);
343 if (endcursor
!= NULL
&& sd_journal_test_cursor(j
, endcursor
)) {
349 // print optional reboot
352 // print final cursor
356 // print remaining buffer
357 if (write (1, buf
, offset
) <= 0) {
358 perror("write to stdout failed");