]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/logs-show.c
journal: it's not a problem if the realtime jumps, hence don't ensure monotonicity...
[thirdparty/systemd.git] / src / logs-show.c
CommitLineData
86aa7ba4
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2012 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 <time.h>
23#include <assert.h>
24#include <errno.h>
25
26#include "logs-show.h"
27#include "log.h"
28#include "util.h"
29
30#define PRINT_THRESHOLD 128
31
32static bool contains_unprintable(const void *p, size_t l) {
33 const char *j;
34
35 for (j = p; j < (const char *) p + l; j++)
36 if (*j < ' ' || *j >= 127)
37 return true;
38
39 return false;
40}
41
42static int output_short(sd_journal *j, unsigned line, bool show_all) {
43 int r;
44 uint64_t realtime;
45 time_t t;
46 struct tm tm;
47 char buf[64];
48 const void *data;
49 size_t length;
50 size_t n = 0;
51
52 assert(j);
53
54 r = sd_journal_get_realtime_usec(j, &realtime);
55 if (r < 0) {
56 log_error("Failed to get realtime: %s", strerror(-r));
57 return r;
58 }
59
60 t = (time_t) (realtime / USEC_PER_SEC);
61 if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) {
62 log_error("Failed to format time.");
63 return -EINVAL;
64 }
65
66 fputs(buf, stdout);
67 n += strlen(buf);
68
69 if (sd_journal_get_data(j, "_HOSTNAME", &data, &length) >= 0 &&
70 (show_all || (!contains_unprintable(data, length) &&
71 length < PRINT_THRESHOLD))) {
72 printf(" %.*s", (int) length - 10, ((const char*) data) + 10);
73 n += length - 10 + 1;
74 }
75
76 if (sd_journal_get_data(j, "MESSAGE", &data, &length) >= 0) {
77 if (show_all)
78 printf(" %.*s", (int) length - 8, ((const char*) data) + 8);
79 else if (contains_unprintable(data, length))
80 fputs(" [blob data]", stdout);
81 else if (length - 8 + n < columns())
82 printf(" %.*s", (int) length - 8, ((const char*) data) + 8);
83 else if (n < columns()) {
84 char *e;
85
86 e = ellipsize_mem((const char *) data + 8, length - 8, columns() - n - 2, 90);
87
88 if (!e)
89 printf(" %.*s", (int) length - 8, ((const char*) data) + 8);
90 else
91 printf(" %s", e);
92
93 free(e);
94 }
95 }
96
97 fputc('\n', stdout);
98
99 return 0;
100}
101
102static int output_verbose(sd_journal *j, unsigned line, bool show_all) {
103 const void *data;
104 size_t length;
105 char *cursor;
106 uint64_t realtime;
107 char ts[FORMAT_TIMESTAMP_MAX];
108 int r;
109
110 assert(j);
111
112 r = sd_journal_get_realtime_usec(j, &realtime);
113 if (r < 0) {
114 log_error("Failed to get realtime timestamp: %s", strerror(-r));
115 return r;
116 }
117
118 r = sd_journal_get_cursor(j, &cursor);
119 if (r < 0) {
120 log_error("Failed to get cursor: %s", strerror(-r));
121 return r;
122 }
123
124 printf("%s [%s]\n",
125 format_timestamp(ts, sizeof(ts), realtime),
126 cursor);
127
128 free(cursor);
129
130 SD_JOURNAL_FOREACH_DATA(j, data, length) {
131 if (!show_all && (length > PRINT_THRESHOLD ||
132 contains_unprintable(data, length))) {
133 const char *c;
134
135 c = memchr(data, '=', length);
136 if (!c) {
137 log_error("Invalid field.");
138 return -EINVAL;
139 }
140
141 printf("\t%.*s=[blob data]\n",
142 (int) (c - (const char*) data),
143 (const char*) data);
144 } else
145 printf("\t%.*s\n", (int) length, (const char*) data);
146 }
147
148 return 0;
149}
150
151static int output_export(sd_journal *j, unsigned line, bool show_all) {
152 sd_id128_t boot_id;
153 char sid[33];
154 int r;
155 usec_t realtime, monotonic;
156 char *cursor;
157 const void *data;
158 size_t length;
159
160 assert(j);
161
162 r = sd_journal_get_realtime_usec(j, &realtime);
163 if (r < 0) {
164 log_error("Failed to get realtime timestamp: %s", strerror(-r));
165 return r;
166 }
167
168 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
169 if (r < 0) {
170 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
171 return r;
172 }
173
174 r = sd_journal_get_cursor(j, &cursor);
175 if (r < 0) {
176 log_error("Failed to get cursor: %s", strerror(-r));
177 return r;
178 }
179
180 printf(".cursor=%s\n"
181 ".realtime=%llu\n"
182 ".monotonic=%llu\n"
183 ".boot_id=%s\n",
184 cursor,
185 (unsigned long long) realtime,
186 (unsigned long long) monotonic,
187 sd_id128_to_string(boot_id, sid));
188
189 free(cursor);
190
191 SD_JOURNAL_FOREACH_DATA(j, data, length) {
192
193 if (contains_unprintable(data, length)) {
194 const char *c;
195 uint64_t le64;
196
197 c = memchr(data, '=', length);
198 if (!c) {
199 log_error("Invalid field.");
200 return -EINVAL;
201 }
202
203 fwrite(data, c - (const char*) data, 1, stdout);
204 fputc('\n', stdout);
205 le64 = htole64(length - (c - (const char*) data) - 1);
206 fwrite(&le64, sizeof(le64), 1, stdout);
207 fwrite(c + 1, length - (c - (const char*) data) - 1, 1, stdout);
208 } else
209 fwrite(data, length, 1, stdout);
210
211 fputc('\n', stdout);
212 }
213
214 fputc('\n', stdout);
215
216 return 0;
217}
218
219static void json_escape(const char* p, size_t l) {
220
221 if (contains_unprintable(p, l)) {
222 bool not_first = false;
223
224 fputs("[ ", stdout);
225
226 while (l > 0) {
227 if (not_first)
228 printf(", %u", (uint8_t) *p);
229 else {
230 not_first = true;
231 printf("%u", (uint8_t) *p);
232 }
233
234 p++;
235 l--;
236 }
237
238 fputs(" ]", stdout);
239 } else {
240 fputc('\"', stdout);
241
242 while (l > 0) {
243 if (*p == '"' || *p == '\\') {
244 fputc('\\', stdout);
245 fputc(*p, stdout);
246 } else
247 fputc(*p, stdout);
248
249 p++;
250 l--;
251 }
252
253 fputc('\"', stdout);
254 }
255}
256
257static int output_json(sd_journal *j, unsigned line, bool show_all) {
258 uint64_t realtime, monotonic;
259 char *cursor;
260 const void *data;
261 size_t length;
262 sd_id128_t boot_id;
263 char sid[33];
264 int r;
265
266 assert(j);
267
268 r = sd_journal_get_realtime_usec(j, &realtime);
269 if (r < 0) {
270 log_error("Failed to get realtime timestamp: %s", strerror(-r));
271 return r;
272 }
273
274 r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
275 if (r < 0) {
276 log_error("Failed to get monotonic timestamp: %s", strerror(-r));
277 return r;
278 }
279
280 r = sd_journal_get_cursor(j, &cursor);
281 if (r < 0) {
282 log_error("Failed to get cursor: %s", strerror(-r));
283 return r;
284 }
285
286 if (line == 1)
287 fputc('\n', stdout);
288 else
289 fputs(",\n", stdout);
290
291 printf("{\n"
292 "\t\".cursor\" : \"%s\",\n"
293 "\t\".realtime\" : %llu,\n"
294 "\t\".monotonic\" : %llu,\n"
295 "\t\".boot_id\" : \"%s\"",
296 cursor,
297 (unsigned long long) realtime,
298 (unsigned long long) monotonic,
299 sd_id128_to_string(boot_id, sid));
300
301 free(cursor);
302
303 SD_JOURNAL_FOREACH_DATA(j, data, length) {
304 const char *c;
305
306 c = memchr(data, '=', length);
307 if (!c) {
308 log_error("Invalid field.");
309 return -EINVAL;
310 }
311
312 fputs(",\n\t", stdout);
313 json_escape(data, c - (const char*) data);
314 fputs(" : ", stdout);
315 json_escape(c + 1, length - (c - (const char*) data) - 1);
316 }
317
318 fputs("\n}", stdout);
319 fflush(stdout);
320
321 return 0;
322}
323
324static int (*output_funcs[_OUTPUT_MODE_MAX])(sd_journal*j, unsigned line, bool show_all) = {
325 [OUTPUT_SHORT] = output_short,
326 [OUTPUT_VERBOSE] = output_verbose,
327 [OUTPUT_EXPORT] = output_export,
328 [OUTPUT_JSON] = output_json
329};
330
331int output_journal(sd_journal *j, output_mode mode, unsigned line, bool show_all) {
332 assert(mode < _OUTPUT_MODE_MAX);
333
334 return output_funcs[mode](j, line, show_all);
335}
336
337int show_journal_by_service(
338 const char *service,
339 output_mode mode,
340 const char *prefix,
341 unsigned n_columns,
342 usec_t not_before,
343 unsigned how_many,
344 bool show_all) {
345
346 char *m = NULL;
347 sd_journal *j;
348 int r;
349 unsigned i;
350
351 assert(service);
352
f4fb21c1
LP
353 if (!endswith(service, ".service") &&
354 !endswith(service, ".socket") &&
355 !endswith(service, ".mount") &&
356 !endswith(service, ".swap"))
357 return 0;
358
86aa7ba4
LP
359 if (n_columns <= 0)
360 n_columns = columns();
361
362 if (how_many <= 0)
363 how_many = 10;
364
365 if (!prefix)
366 prefix = "";
367
94fb446e 368 if (asprintf(&m, "_SYSTEMD_UNIT=%s", service) < 0) {
86aa7ba4
LP
369 r = -ENOMEM;
370 goto finish;
371 }
372
373 r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY);
374 if (r < 0)
375 goto finish;
376
377 r = sd_journal_add_match(j, m, strlen(m));
378 if (r < 0)
379 goto finish;
380
381 r = sd_journal_seek_tail(j);
382 if (r < 0)
383 goto finish;
384
385 for (i = 0; i < how_many; i++)
386 sd_journal_previous(j);
387
388 for (i = 0; i < how_many; i++) {
389
390 r = sd_journal_next(j);
391 if (r < 0)
392 goto finish;
393
394 if (r == 0)
395 break;
396
397 r = output_journal(j, mode, i+1, show_all);
398 if (r < 0)
399 goto finish;
400 }
401
402finish:
403 if (m)
404 free(m);
405
406 if (j)
407 sd_journal_close(j);
408
409 return r;
410}