]>
git.proxmox.com Git - proxmox-mini-journalreader.git/blob - src/mini-journalreader.c
508de0260ec263119e9cfecb62a949c063b7b471
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 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 void print_to_buf(const char * string
, uint32_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 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 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 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 void print_timestamp(sd_journal
*j
) {
114 int r
= sd_journal_get_realtime_usec(j
, ×tamp
);
116 fprintf(stderr
, "Failed %s\n", strerror(-r
));
120 static uint64_t last_timestamp
;
121 static char timestring
[16];
122 if (timestamp
>= (last_timestamp
+(1000*1000))) {
123 timestamp
= timestamp
/ (1000*1000); // usec to sec
125 localtime_r((time_t *)×tamp
, &time
);
126 strftime(timestring
, 16, "%b %d %T", &time
);
127 last_timestamp
= timestamp
;
130 print_to_buf(timestring
, 15);
133 void print_pid(sd_journal
*j
) {
136 int r
= sd_journal_get_data(j
, "_PID", (const void **)&d
, &l
);
138 // we sometimes have no pid, e.g., kernel messages
146 print_to_buf("[", 1);
148 print_to_buf("]", 1);
151 bool print_field(sd_journal
*j
, const char *field
) {
154 int r
= sd_journal_get_data(j
, field
, (const void **)&d
, &l
);
156 // some fields do not exists
160 int fieldlen
= strlen(field
)+1;
168 void print_line(sd_journal
*j
) {
171 print_to_buf(" ", 1);
172 print_field(j
, "_HOSTNAME");
173 print_to_buf(" ", 1);
174 if (!print_field(j
, "SYSLOG_IDENTIFIER") &&
175 !print_field(j
, "_COMM")) {
176 print_to_buf("unknown", strlen("unknown") - 1);
179 print_to_buf(": ", 2);
180 print_field(j
, "MESSAGE");
181 print_to_buf("\n", 1);
186 void usage(char *error
) {
188 fprintf(stderr
, "ERROR: %s\n", error
);
190 fprintf(stderr
, "usage: %s [OPTIONS]\n", progname
);
192 " -b <timestamp>\tbegin at this UNIX epoch based timestamp\n"
193 " -e <timestamp>\tend at this UNIX epoch based timestamp\n"
194 " -d <directory>\tpath to a journal directory\n"
195 " -n <integer>\t\tprint the last number entries logged\n"
196 " -f <cursor>\t\tprint from this cursor\n"
197 " -t <cursor>\t\tprint to this cursor\n"
198 " -h \t\t\tthis help\n"
200 "Passing no range option will dump all the available journal\n"
201 "Giving a range conflicts with -n\n"
202 "-b and -f conflict\n"
203 "-e and -t conflict\n");
207 static uint64_t arg_to_uint64(const char *argument
) {
210 uint64_t value
= strtoull(argument
, &end
, 10);
211 if (errno
!= 0 || *end
!= '\0') {
212 fprintf(stderr
, "%s is not a valid integer number\n", argument
);
220 int main(int argc
, char *argv
[]) {
222 const char *directory
= NULL
;
223 const char *startcursor
= NULL
;
224 const char *endcursor
= NULL
;
231 while ((c
= getopt (argc
, argv
, "b:e:d:n:f:t:h")) != -1) {
234 begin
= arg_to_uint64(optarg
);
235 begin
= begin
* 1000 * 1000; // µs
238 end
= arg_to_uint64(optarg
);
239 end
= end
* 1000 * 1000; // µs
245 number
= arg_to_uint64(optarg
);
248 startcursor
= optarg
;
258 usage("invalid option or missing argument");
262 if (number
&& (begin
|| startcursor
)) {
263 usage("-n conflicts with -b and/or -f");
266 if (begin
&& startcursor
) {
267 usage("-b and -f conflict");
270 if (end
&& endcursor
) {
271 usage("-e and -t conflict");
275 usage("unkown, or to many arguments");
278 // to prevent calling it everytime we generate a timestamp
283 if (directory
== NULL
) {
284 r
= sd_journal_open(&j
, SD_JOURNAL_LOCAL_ONLY
);
286 r
= sd_journal_open_directory(&j
, directory
, 0);
290 fprintf(stderr
, "Failed to open journal: %s\n", strerror(-r
));
294 // if we want to print the last x entries, seek to cursor or end,
295 // then x entries back, print the cursor and finally print the
296 // entries until end or cursor
299 r
= sd_journal_seek_realtime_usec(j
, end
);
300 } else if (endcursor
!= NULL
) {
301 r
= sd_journal_seek_cursor(j
, endcursor
);
304 r
= sd_journal_seek_tail(j
);
308 fprintf(stderr
, "Failed to seek to end/cursor: %s\n", strerror(-r
));
312 // seek back number entries and print cursor
313 r
= sd_journal_previous_skip(j
, number
+ 1);
315 fprintf(stderr
, "Failed to seek back: %s\n", strerror(-r
));
320 r
= sd_journal_seek_realtime_usec(j
, begin
);
321 } else if (startcursor
) {
322 r
= sd_journal_seek_cursor(j
, startcursor
);
324 r
= sd_journal_seek_head(j
);
328 fprintf(stderr
, "Failed to seek to begin/cursor: %s\n", strerror(-r
));
332 // if we have a start cursor, we want to skip the first entry
334 r
= sd_journal_next(j
);
336 fprintf(stderr
, "Failed to seek to begin/cursor: %s\n", strerror(-r
));
339 print_first_cursor(j
);
344 while ((r
= sd_journal_next(j
)) > 0 && (end
== 0 || get_timestamp(j
) < end
)) {
345 print_first_cursor(j
);
346 if (endcursor
!= NULL
&& sd_journal_test_cursor(j
, endcursor
)) {
352 // print optional reboot
355 // print final cursor
359 // print remaining buffer
360 if (write (1, buf
, offset
) <= 0) {
361 perror("write to stdout failed");