#include <time.h>
#include <assert.h>
#include <errno.h>
+#include <sys/poll.h>
+#include <string.h>
#include "logs-show.h"
#include "log.h"
return false;
}
-static int output_short(sd_journal *j, unsigned line, bool show_all) {
+static int parse_field(const void *data, size_t length, const char *field, char **target, size_t *target_size) {
+ size_t fl, nl;
+ void *buf;
+
+ assert(data);
+ assert(field);
+ assert(target);
+ assert(target_size);
+
+ fl = strlen(field);
+ if (length < fl)
+ return 0;
+
+ if (memcmp(data, field, fl))
+ return 0;
+
+ nl = length - fl;
+ buf = malloc(nl+1);
+ memcpy(buf, (const char*) data + fl, nl);
+ ((char*)buf)[nl] = 0;
+ if (!buf) {
+ log_error("Out of memory");
+ return -ENOMEM;
+ }
+
+ free(*target);
+ *target = buf;
+ *target_size = nl;
+
+ return 1;
+}
+
+static bool shall_print(bool show_all, char *p, size_t l) {
+ if (show_all)
+ return true;
+
+ if (l > PRINT_THRESHOLD)
+ return false;
+
+ if (contains_unprintable(p, l))
+ return false;
+
+ return true;
+}
+
+static int output_short(sd_journal *j, unsigned line, bool show_all, bool monotonic_mode) {
int r;
- uint64_t realtime;
- time_t t;
- struct tm tm;
- char buf[64];
const void *data;
size_t length;
size_t n = 0;
+ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL;
+ size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0, realtime_len = 0, monotonic_len = 0;
assert(j);
- r = sd_journal_get_realtime_usec(j, &realtime);
- if (r < 0) {
- log_error("Failed to get realtime: %s", strerror(-r));
- return r;
+ SD_JOURNAL_FOREACH_DATA(j, data, length) {
+
+ r = parse_field(data, length, "_HOSTNAME=", &hostname, &hostname_len);
+ if (r < 0)
+ goto finish;
+ else if (r > 0)
+ continue;
+
+ r = parse_field(data, length, "SYSLOG_IDENTIFIER=", &identifier, &identifier_len);
+ if (r < 0)
+ goto finish;
+ else if (r > 0)
+ continue;
+
+ r = parse_field(data, length, "_COMM=", &comm, &comm_len);
+ if (r < 0)
+ goto finish;
+ else if (r > 0)
+ continue;
+
+ r = parse_field(data, length, "_PID=", &pid, &pid_len);
+ if (r < 0)
+ goto finish;
+ else if (r > 0)
+ continue;
+
+ r = parse_field(data, length, "SYSLOG_PID=", &fake_pid, &fake_pid_len);
+ if (r < 0)
+ goto finish;
+ else if (r > 0)
+ continue;
+
+ r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len);
+ if (r < 0)
+ goto finish;
+ else if (r > 0)
+ continue;
+
+ r = parse_field(data, length, "_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len);
+ if (r < 0)
+ goto finish;
+ else if (r > 0)
+ continue;
+
+ r = parse_field(data, length, "MESSAGE=", &message, &message_len);
+ if (r < 0)
+ goto finish;
}
- t = (time_t) (realtime / USEC_PER_SEC);
- if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) {
- log_error("Failed to format time.");
- return -EINVAL;
+ if (!message) {
+ r = 0;
+ goto finish;
}
- fputs(buf, stdout);
- n += strlen(buf);
+ if (monotonic_mode) {
+ uint64_t t;
+ sd_id128_t boot_id;
- if (sd_journal_get_data(j, "_HOSTNAME", &data, &length) >= 0 &&
- (show_all || (!contains_unprintable(data, length) &&
- length < PRINT_THRESHOLD))) {
- printf(" %.*s", (int) length - 10, ((const char*) data) + 10);
- n += length - 10 + 1;
- }
+ r = -ENOENT;
+
+ if (monotonic)
+ r = safe_atou64(monotonic, &t);
- if (sd_journal_get_data(j, "MESSAGE", &data, &length) >= 0) {
- if (show_all)
- printf(" %.*s", (int) length - 8, ((const char*) data) + 8);
- else if (contains_unprintable(data, length))
- fputs(" [blob data]", stdout);
- else if (length - 8 + n < columns())
- printf(" %.*s", (int) length - 8, ((const char*) data) + 8);
- else if (n < columns()) {
- char *e;
+ if (r < 0)
+ r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
+
+ if (r < 0) {
+ log_error("Failed to get monotonic: %s", strerror(-r));
+ goto finish;
+ }
+
+ printf("[%5llu.%06llu]",
+ (unsigned long long) (t / USEC_PER_SEC),
+ (unsigned long long) (t % USEC_PER_SEC));
+
+ n += 1 + 5 + 1 + 6 + 1;
+
+ } else {
+ char buf[64];
+ uint64_t x;
+ time_t t;
+ struct tm tm;
- e = ellipsize_mem((const char *) data + 8, length - 8, columns() - n - 2, 90);
+ r = -ENOENT;
- if (!e)
- printf(" %.*s", (int) length - 8, ((const char*) data) + 8);
- else
- printf(" %s", e);
+ if (realtime)
+ r = safe_atou64(realtime, &x);
- free(e);
+ if (r < 0)
+ r = sd_journal_get_realtime_usec(j, &x);
+
+ if (r < 0) {
+ log_error("Failed to get realtime: %s", strerror(-r));
+ goto finish;
+ }
+
+ t = (time_t) (x / USEC_PER_SEC);
+ if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", localtime_r(&t, &tm)) <= 0) {
+ log_error("Failed to format time.");
+ goto finish;
}
+
+ fputs(buf, stdout);
+ n += strlen(buf);
}
- fputc('\n', stdout);
+ if (hostname && shall_print(show_all, hostname, hostname_len)) {
+ printf(" %.*s", (int) hostname_len, hostname);
+ n += hostname_len + 1;
+ }
- return 0;
+ if (identifier && shall_print(show_all, identifier, identifier_len)) {
+ printf(" %.*s", (int) identifier_len, identifier);
+ n += identifier_len + 1;
+ } else if (comm && shall_print(show_all, comm, comm_len)) {
+ printf(" %.*s", (int) comm_len, comm);
+ n += comm_len + 1;
+ }
+
+ if (pid && shall_print(show_all, pid, pid_len)) {
+ printf("[%.*s]", (int) pid_len, pid);
+ n += pid_len + 2;
+ } else if (fake_pid && shall_print(show_all, fake_pid, fake_pid_len)) {
+ printf("[%.*s]", (int) fake_pid_len, fake_pid);
+ n += fake_pid_len + 2;
+ }
+
+ if (show_all)
+ printf(": %.*s\n", (int) message_len, message);
+ else if (contains_unprintable(message, message_len)) {
+ char bytes[FORMAT_BYTES_MAX];
+ printf(": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
+ } else if (message_len + n < columns())
+ printf(": %.*s\n", (int) message_len, message);
+ else if (n < columns()) {
+ char *e;
+
+ e = ellipsize_mem(message, message_len, columns() - n - 2, 90);
+
+ if (!e)
+ printf(": %.*s\n", (int) message_len, message);
+ else
+ printf(": %s\n", e);
+
+ free(e);
+ } else
+ fputs("\n", stdout);
+
+ r = 0;
+
+finish:
+ free(hostname);
+ free(identifier);
+ free(comm);
+ free(pid);
+ free(fake_pid);
+ free(message);
+ free(monotonic);
+ free(realtime);
+
+ return r;
+}
+
+static int output_short_realtime(sd_journal *j, unsigned line, bool show_all) {
+ return output_short(j, line, show_all, false);
+}
+
+static int output_short_monotonic(sd_journal *j, unsigned line, bool show_all) {
+ return output_short(j, line, show_all, true);
}
static int output_verbose(sd_journal *j, unsigned line, bool show_all) {
if (!show_all && (length > PRINT_THRESHOLD ||
contains_unprintable(data, length))) {
const char *c;
+ char bytes[FORMAT_BYTES_MAX];
c = memchr(data, '=', length);
if (!c) {
return -EINVAL;
}
- printf("\t%.*s=[blob data]\n",
+ printf("\t%.*s=[%s blob data]\n",
(int) (c - (const char*) data),
- (const char*) data);
+ (const char*) data,
+ format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1));
} else
printf("\t%.*s\n", (int) length, (const char*) data);
}
return 0;
}
+static int output_cat(sd_journal *j, unsigned line, bool show_all) {
+ const void *data;
+ size_t l;
+ int r;
+
+ assert(j);
+
+ r = sd_journal_get_data(j, "MESSAGE", &data, &l);
+ if (r < 0) {
+ log_error("Failed to get data: %s", strerror(-r));
+ return r;
+ }
+
+ assert(l >= 8);
+
+ fwrite((const char*) data + 8, 1, l - 8, stdout);
+ putchar('\n');
+
+ return 0;
+}
+
static int (*output_funcs[_OUTPUT_MODE_MAX])(sd_journal*j, unsigned line, bool show_all) = {
- [OUTPUT_SHORT] = output_short,
+ [OUTPUT_SHORT] = output_short_realtime,
+ [OUTPUT_SHORT_MONOTONIC] = output_short_monotonic,
[OUTPUT_VERBOSE] = output_verbose,
[OUTPUT_EXPORT] = output_export,
- [OUTPUT_JSON] = output_json
+ [OUTPUT_JSON] = output_json,
+ [OUTPUT_CAT] = output_cat
};
-int output_journal(sd_journal *j, output_mode mode, unsigned line, bool show_all) {
+int output_journal(sd_journal *j, OutputMode mode, unsigned line, bool show_all) {
+ assert(mode >= 0);
assert(mode < _OUTPUT_MODE_MAX);
return output_funcs[mode](j, line, show_all);
}
-int show_journal_by_service(
- const char *service,
- output_mode mode,
+int show_journal_by_unit(
+ const char *unit,
+ OutputMode mode,
const char *prefix,
unsigned n_columns,
usec_t not_before,
unsigned how_many,
- bool show_all) {
+ bool show_all,
+ bool follow) {
char *m = NULL;
sd_journal *j;
int r;
- unsigned i;
+ int fd;
+ unsigned line = 0;
+ bool need_seek = false;
- assert(service);
+ assert(mode >= 0);
+ assert(mode < _OUTPUT_MODE_MAX);
+ assert(unit);
+
+ if (!endswith(unit, ".service") &&
+ !endswith(unit, ".socket") &&
+ !endswith(unit, ".mount") &&
+ !endswith(unit, ".swap"))
+ return 0;
- if (!endswith(service, ".service") &&
- !endswith(service, ".socket") &&
- !endswith(service, ".mount") &&
- !endswith(service, ".swap"))
+ if (how_many <= 0)
return 0;
if (n_columns <= 0)
n_columns = columns();
- if (how_many <= 0)
- how_many = 10;
-
if (!prefix)
prefix = "";
- if (asprintf(&m, "_SYSTEMD_UNIT=%s", service) < 0) {
+ if (asprintf(&m, "_SYSTEMD_UNIT=%s", unit) < 0) {
r = -ENOMEM;
goto finish;
}
if (r < 0)
goto finish;
+ fd = sd_journal_get_fd(j);
+ if (fd < 0)
+ goto finish;
+
r = sd_journal_add_match(j, m, strlen(m));
if (r < 0)
goto finish;
if (r < 0)
goto finish;
- for (i = 0; i < how_many; i++)
- sd_journal_previous(j);
+ r = sd_journal_previous_skip(j, how_many);
+ if (r < 0)
+ goto finish;
- for (i = 0; i < how_many; i++) {
+ if (mode == OUTPUT_JSON) {
+ fputc('[', stdout);
+ fflush(stdout);
+ }
- r = sd_journal_next(j);
- if (r < 0)
- goto finish;
+ for (;;) {
+ for (;;) {
+ usec_t usec;
+
+ if (need_seek) {
+ r = sd_journal_next(j);
+ if (r < 0)
+ goto finish;
+ }
+
+ if (r == 0)
+ break;
- if (r == 0)
+ need_seek = true;
+
+ if (not_before > 0) {
+ r = sd_journal_get_monotonic_usec(j, &usec, NULL);
+
+ /* -ESTALE is returned if the
+ timestamp is not from this boot */
+ if (r == -ESTALE)
+ continue;
+ else if (r < 0)
+ goto finish;
+
+ if (usec < not_before)
+ continue;
+ }
+
+ line ++;
+
+ r = output_journal(j, mode, line, show_all);
+ if (r < 0)
+ goto finish;
+ }
+
+ if (!follow)
break;
- r = output_journal(j, mode, i+1, show_all);
+ r = fd_wait_for_event(fd, POLLIN, (usec_t) -1);
if (r < 0)
goto finish;
+
+ r = sd_journal_process(j);
+ if (r < 0)
+ goto finish;
+
}
+ if (mode == OUTPUT_JSON)
+ fputs("\n]\n", stdout);
+
finish:
if (m)
free(m);
return r;
}
+
+static const char *const output_mode_table[_OUTPUT_MODE_MAX] = {
+ [OUTPUT_SHORT] = "short",
+ [OUTPUT_SHORT_MONOTONIC] = "short-monotonic",
+ [OUTPUT_VERBOSE] = "verbose",
+ [OUTPUT_EXPORT] = "export",
+ [OUTPUT_JSON] = "json",
+ [OUTPUT_CAT] = "cat"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode);