From: Lennart Poettering Date: Tue, 3 Jan 2012 20:08:28 +0000 (+0100) Subject: systemctl: hook up systemctl with the journal X-Git-Tag: v38~96 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=86aa7ba4f9969bbfc75ebd51f944313695f1a0a1 systemctl: hook up systemctl with the journal --- diff --git a/Makefile.am b/Makefile.am index 8bb5b9d94..6c0fd055d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -98,7 +98,8 @@ AM_CPPFLAGS = \ -DSYSTEMD_KBD_MODEL_MAP=\"$(pkgdatadir)/kbd-model-map\" \ -I $(top_srcdir)/src \ -I $(top_srcdir)/src/readahead \ - -I $(top_srcdir)/src/login + -I $(top_srcdir)/src/login \ + -I $(top_srcdir)/src/journal if TARGET_GENTOO AM_CPPFLAGS += \ @@ -923,7 +924,8 @@ systemctl_SOURCES = \ src/unit-name.c \ src/pager.c \ src/install.c \ - src/spawn-agent.c + src/spawn-agent.c \ + src/logs-show.c systemctl_CFLAGS = \ $(AM_CFLAGS) \ @@ -932,6 +934,7 @@ systemctl_CFLAGS = \ systemctl_LDADD = \ libsystemd-basic.la \ libsystemd-daemon.la \ + libsystemd-journal.la \ $(DBUS_LIBS) systemd_notify_SOURCES = \ @@ -1189,14 +1192,12 @@ endif systemd_journalctl_SOURCES = \ src/journal/journalctl.c \ - src/journal/sd-journal.c \ - src/journal/journal-file.c \ - src/journal/lookup3.c \ - src/sd-id128.c \ - src/pager.c + src/pager.c \ + src/logs-show.c systemd_journalctl_LDADD = \ - libsystemd-basic.la + libsystemd-basic.la \ + libsystemd-journal.la if HAVE_XZ systemd_journalctl_SOURCES += \ diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 701518244..4c20ff66a 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -35,319 +35,14 @@ #include "util.h" #include "build.h" #include "pager.h" +#include "logs-show.h" -#define PRINT_THRESHOLD 128 - -static enum { - OUTPUT_SHORT, - OUTPUT_VERBOSE, - OUTPUT_EXPORT, - OUTPUT_JSON, - _OUTPUT_MAX -} arg_output = OUTPUT_SHORT; +static output_mode arg_output = OUTPUT_SHORT; static bool arg_follow = false; static bool arg_show_all = false; static bool arg_no_pager = false; -static bool contains_unprintable(const void *p, size_t l) { - const char *j; - - for (j = p; j < (const char *) p + l; j++) - if (*j < ' ' || *j >= 127) - return true; - - return false; -} - -static int output_short(sd_journal *j, unsigned line) { - int r; - uint64_t realtime; - time_t t; - struct tm tm; - char buf[64]; - const void *data; - size_t length; - size_t n = 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; - } - - 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; - } - - fputs(buf, stdout); - n += strlen(buf); - - if (sd_journal_get_data(j, "_HOSTNAME", &data, &length) >= 0 && - (arg_show_all || (!contains_unprintable(data, length) && - length < PRINT_THRESHOLD))) { - printf(" %.*s", (int) length - 10, ((const char*) data) + 10); - n += length - 10 + 1; - } - - if (sd_journal_get_data(j, "MESSAGE", &data, &length) >= 0) { - if (arg_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; - - e = ellipsize_mem((const char *) data + 8, length - 8, columns() - n - 2, 90); - - if (!e) - printf(" %.*s", (int) length - 8, ((const char*) data) + 8); - else - printf(" %s", e); - - free(e); - } - } - - fputc('\n', stdout); - - return 0; -} - -static int output_verbose(sd_journal *j, unsigned line) { - const void *data; - size_t length; - char *cursor; - uint64_t realtime; - char ts[FORMAT_TIMESTAMP_MAX]; - int r; - - assert(j); - - r = sd_journal_get_realtime_usec(j, &realtime); - if (r < 0) { - log_error("Failed to get realtime timestamp: %s", strerror(-r)); - return r; - } - - r = sd_journal_get_cursor(j, &cursor); - if (r < 0) { - log_error("Failed to get cursor: %s", strerror(-r)); - return r; - } - - printf("%s [%s]\n", - format_timestamp(ts, sizeof(ts), realtime), - cursor); - - free(cursor); - - SD_JOURNAL_FOREACH_DATA(j, data, length) { - if (!arg_show_all && (length > PRINT_THRESHOLD || - contains_unprintable(data, length))) { - const char *c; - - c = memchr(data, '=', length); - if (!c) { - log_error("Invalid field."); - return -EINVAL; - } - - printf("\t%.*s=[blob data]\n", - (int) (c - (const char*) data), - (const char*) data); - } else - printf("\t%.*s\n", (int) length, (const char*) data); - } - - return 0; -} - -static int output_export(sd_journal *j, unsigned line) { - sd_id128_t boot_id; - char sid[33]; - int r; - usec_t realtime, monotonic; - char *cursor; - const void *data; - size_t length; - - assert(j); - - r = sd_journal_get_realtime_usec(j, &realtime); - if (r < 0) { - log_error("Failed to get realtime timestamp: %s", strerror(-r)); - return r; - } - - r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id); - if (r < 0) { - log_error("Failed to get monotonic timestamp: %s", strerror(-r)); - return r; - } - - r = sd_journal_get_cursor(j, &cursor); - if (r < 0) { - log_error("Failed to get cursor: %s", strerror(-r)); - return r; - } - - printf(".cursor=%s\n" - ".realtime=%llu\n" - ".monotonic=%llu\n" - ".boot_id=%s\n", - cursor, - (unsigned long long) realtime, - (unsigned long long) monotonic, - sd_id128_to_string(boot_id, sid)); - - free(cursor); - - SD_JOURNAL_FOREACH_DATA(j, data, length) { - - if (contains_unprintable(data, length)) { - const char *c; - uint64_t le64; - - c = memchr(data, '=', length); - if (!c) { - log_error("Invalid field."); - return -EINVAL; - } - - fwrite(data, c - (const char*) data, 1, stdout); - fputc('\n', stdout); - le64 = htole64(length - (c - (const char*) data) - 1); - fwrite(&le64, sizeof(le64), 1, stdout); - fwrite(c + 1, length - (c - (const char*) data) - 1, 1, stdout); - } else - fwrite(data, length, 1, stdout); - - fputc('\n', stdout); - } - - fputc('\n', stdout); - - return 0; -} - -static void json_escape(const char* p, size_t l) { - - if (contains_unprintable(p, l)) { - bool not_first = false; - - fputs("[ ", stdout); - - while (l > 0) { - if (not_first) - printf(", %u", (uint8_t) *p); - else { - not_first = true; - printf("%u", (uint8_t) *p); - } - - p++; - l--; - } - - fputs(" ]", stdout); - } else { - fputc('\"', stdout); - - while (l > 0) { - if (*p == '"' || *p == '\\') { - fputc('\\', stdout); - fputc(*p, stdout); - } else - fputc(*p, stdout); - - p++; - l--; - } - - fputc('\"', stdout); - } -} - -static int output_json(sd_journal *j, unsigned line) { - uint64_t realtime, monotonic; - char *cursor; - const void *data; - size_t length; - sd_id128_t boot_id; - char sid[33]; - int r; - - assert(j); - - r = sd_journal_get_realtime_usec(j, &realtime); - if (r < 0) { - log_error("Failed to get realtime timestamp: %s", strerror(-r)); - return r; - } - - r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id); - if (r < 0) { - log_error("Failed to get monotonic timestamp: %s", strerror(-r)); - return r; - } - - r = sd_journal_get_cursor(j, &cursor); - if (r < 0) { - log_error("Failed to get cursor: %s", strerror(-r)); - return r; - } - - if (line == 1) - fputc('\n', stdout); - else - fputs(",\n", stdout); - - printf("{\n" - "\t\".cursor\" : \"%s\",\n" - "\t\".realtime\" : %llu,\n" - "\t\".monotonic\" : %llu,\n" - "\t\".boot_id\" : \"%s\"", - cursor, - (unsigned long long) realtime, - (unsigned long long) monotonic, - sd_id128_to_string(boot_id, sid)); - - free(cursor); - - SD_JOURNAL_FOREACH_DATA(j, data, length) { - const char *c; - - c = memchr(data, '=', length); - if (!c) { - log_error("Invalid field."); - return -EINVAL; - } - - fputs(",\n\t", stdout); - json_escape(data, c - (const char*) data); - fputs(" : ", stdout); - json_escape(c + 1, length - (c - (const char*) data) - 1); - } - - fputs("\n}", stdout); - fflush(stdout); - - return 0; -} - -static int (*output_funcs[_OUTPUT_MAX])(sd_journal*j, unsigned line) = { - [OUTPUT_SHORT] = output_short, - [OUTPUT_VERBOSE] = output_verbose, - [OUTPUT_EXPORT] = output_export, - [OUTPUT_JSON] = output_json -}; static int help(void) { @@ -503,7 +198,7 @@ int main(int argc, char *argv[]) { line ++; - r = output_funcs[arg_output](j, line); + r = output_journal(j, arg_output, line, arg_show_all); if (r < 0) goto finish; } diff --git a/src/logs-show.c b/src/logs-show.c new file mode 100644 index 000000000..d178f95f9 --- /dev/null +++ b/src/logs-show.c @@ -0,0 +1,404 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "logs-show.h" +#include "log.h" +#include "util.h" + +#define PRINT_THRESHOLD 128 + +static bool contains_unprintable(const void *p, size_t l) { + const char *j; + + for (j = p; j < (const char *) p + l; j++) + if (*j < ' ' || *j >= 127) + return true; + + return false; +} + +static int output_short(sd_journal *j, unsigned line, bool show_all) { + int r; + uint64_t realtime; + time_t t; + struct tm tm; + char buf[64]; + const void *data; + size_t length; + size_t n = 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; + } + + 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; + } + + fputs(buf, stdout); + n += strlen(buf); + + 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; + } + + 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; + + e = ellipsize_mem((const char *) data + 8, length - 8, columns() - n - 2, 90); + + if (!e) + printf(" %.*s", (int) length - 8, ((const char*) data) + 8); + else + printf(" %s", e); + + free(e); + } + } + + fputc('\n', stdout); + + return 0; +} + +static int output_verbose(sd_journal *j, unsigned line, bool show_all) { + const void *data; + size_t length; + char *cursor; + uint64_t realtime; + char ts[FORMAT_TIMESTAMP_MAX]; + int r; + + assert(j); + + r = sd_journal_get_realtime_usec(j, &realtime); + if (r < 0) { + log_error("Failed to get realtime timestamp: %s", strerror(-r)); + return r; + } + + r = sd_journal_get_cursor(j, &cursor); + if (r < 0) { + log_error("Failed to get cursor: %s", strerror(-r)); + return r; + } + + printf("%s [%s]\n", + format_timestamp(ts, sizeof(ts), realtime), + cursor); + + free(cursor); + + SD_JOURNAL_FOREACH_DATA(j, data, length) { + if (!show_all && (length > PRINT_THRESHOLD || + contains_unprintable(data, length))) { + const char *c; + + c = memchr(data, '=', length); + if (!c) { + log_error("Invalid field."); + return -EINVAL; + } + + printf("\t%.*s=[blob data]\n", + (int) (c - (const char*) data), + (const char*) data); + } else + printf("\t%.*s\n", (int) length, (const char*) data); + } + + return 0; +} + +static int output_export(sd_journal *j, unsigned line, bool show_all) { + sd_id128_t boot_id; + char sid[33]; + int r; + usec_t realtime, monotonic; + char *cursor; + const void *data; + size_t length; + + assert(j); + + r = sd_journal_get_realtime_usec(j, &realtime); + if (r < 0) { + log_error("Failed to get realtime timestamp: %s", strerror(-r)); + return r; + } + + r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id); + if (r < 0) { + log_error("Failed to get monotonic timestamp: %s", strerror(-r)); + return r; + } + + r = sd_journal_get_cursor(j, &cursor); + if (r < 0) { + log_error("Failed to get cursor: %s", strerror(-r)); + return r; + } + + printf(".cursor=%s\n" + ".realtime=%llu\n" + ".monotonic=%llu\n" + ".boot_id=%s\n", + cursor, + (unsigned long long) realtime, + (unsigned long long) monotonic, + sd_id128_to_string(boot_id, sid)); + + free(cursor); + + SD_JOURNAL_FOREACH_DATA(j, data, length) { + + if (contains_unprintable(data, length)) { + const char *c; + uint64_t le64; + + c = memchr(data, '=', length); + if (!c) { + log_error("Invalid field."); + return -EINVAL; + } + + fwrite(data, c - (const char*) data, 1, stdout); + fputc('\n', stdout); + le64 = htole64(length - (c - (const char*) data) - 1); + fwrite(&le64, sizeof(le64), 1, stdout); + fwrite(c + 1, length - (c - (const char*) data) - 1, 1, stdout); + } else + fwrite(data, length, 1, stdout); + + fputc('\n', stdout); + } + + fputc('\n', stdout); + + return 0; +} + +static void json_escape(const char* p, size_t l) { + + if (contains_unprintable(p, l)) { + bool not_first = false; + + fputs("[ ", stdout); + + while (l > 0) { + if (not_first) + printf(", %u", (uint8_t) *p); + else { + not_first = true; + printf("%u", (uint8_t) *p); + } + + p++; + l--; + } + + fputs(" ]", stdout); + } else { + fputc('\"', stdout); + + while (l > 0) { + if (*p == '"' || *p == '\\') { + fputc('\\', stdout); + fputc(*p, stdout); + } else + fputc(*p, stdout); + + p++; + l--; + } + + fputc('\"', stdout); + } +} + +static int output_json(sd_journal *j, unsigned line, bool show_all) { + uint64_t realtime, monotonic; + char *cursor; + const void *data; + size_t length; + sd_id128_t boot_id; + char sid[33]; + int r; + + assert(j); + + r = sd_journal_get_realtime_usec(j, &realtime); + if (r < 0) { + log_error("Failed to get realtime timestamp: %s", strerror(-r)); + return r; + } + + r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id); + if (r < 0) { + log_error("Failed to get monotonic timestamp: %s", strerror(-r)); + return r; + } + + r = sd_journal_get_cursor(j, &cursor); + if (r < 0) { + log_error("Failed to get cursor: %s", strerror(-r)); + return r; + } + + if (line == 1) + fputc('\n', stdout); + else + fputs(",\n", stdout); + + printf("{\n" + "\t\".cursor\" : \"%s\",\n" + "\t\".realtime\" : %llu,\n" + "\t\".monotonic\" : %llu,\n" + "\t\".boot_id\" : \"%s\"", + cursor, + (unsigned long long) realtime, + (unsigned long long) monotonic, + sd_id128_to_string(boot_id, sid)); + + free(cursor); + + SD_JOURNAL_FOREACH_DATA(j, data, length) { + const char *c; + + c = memchr(data, '=', length); + if (!c) { + log_error("Invalid field."); + return -EINVAL; + } + + fputs(",\n\t", stdout); + json_escape(data, c - (const char*) data); + fputs(" : ", stdout); + json_escape(c + 1, length - (c - (const char*) data) - 1); + } + + fputs("\n}", stdout); + fflush(stdout); + + return 0; +} + +static int (*output_funcs[_OUTPUT_MODE_MAX])(sd_journal*j, unsigned line, bool show_all) = { + [OUTPUT_SHORT] = output_short, + [OUTPUT_VERBOSE] = output_verbose, + [OUTPUT_EXPORT] = output_export, + [OUTPUT_JSON] = output_json +}; + +int output_journal(sd_journal *j, output_mode mode, unsigned line, bool show_all) { + assert(mode < _OUTPUT_MODE_MAX); + + return output_funcs[mode](j, line, show_all); +} + +int show_journal_by_service( + const char *service, + output_mode mode, + const char *prefix, + unsigned n_columns, + usec_t not_before, + unsigned how_many, + bool show_all) { + + char *m = NULL; + sd_journal *j; + int r; + unsigned i; + + assert(service); + + if (n_columns <= 0) + n_columns = columns(); + + if (how_many <= 0) + how_many = 10; + + if (!prefix) + prefix = ""; + + if (asprintf(&m, "_SYSTEMD_SERVICE=%s", service) < 0) { + r = -ENOMEM; + goto finish; + } + + r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM_ONLY); + if (r < 0) + goto finish; + + r = sd_journal_add_match(j, m, strlen(m)); + if (r < 0) + goto finish; + + r = sd_journal_seek_tail(j); + if (r < 0) + goto finish; + + for (i = 0; i < how_many; i++) + sd_journal_previous(j); + + for (i = 0; i < how_many; i++) { + + r = sd_journal_next(j); + if (r < 0) + goto finish; + + if (r == 0) + break; + + r = output_journal(j, mode, i+1, show_all); + if (r < 0) + goto finish; + } + +finish: + if (m) + free(m); + + if (j) + sd_journal_close(j); + + return r; +} diff --git a/src/logs-show.h b/src/logs-show.h new file mode 100644 index 000000000..f83df069c --- /dev/null +++ b/src/logs-show.h @@ -0,0 +1,49 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foologsshowhfoo +#define foologsshowhfoo + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include + +#include "sd-journal.h" +#include "util.h" + +typedef enum output_mode { + OUTPUT_SHORT, + OUTPUT_VERBOSE, + OUTPUT_EXPORT, + OUTPUT_JSON, + _OUTPUT_MODE_MAX +} output_mode; + +int output_journal(sd_journal *j, output_mode mode, unsigned line, bool show_all); + +int show_journal_by_service( + const char *service, + output_mode mode, + const char *prefix, + unsigned n_columns, + usec_t not_before, + unsigned how_many, + bool show_all); + +#endif diff --git a/src/systemctl.c b/src/systemctl.c index 1142200e4..10e399174 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -58,6 +58,7 @@ #include "pager.h" #include "spawn-agent.h" #include "install.h" +#include "logs-show.h" static const char *arg_type = NULL; static char **arg_property = NULL; @@ -2261,6 +2262,9 @@ static void print_status_info(UnitStatusInfo *i) { } } + if (i->id && arg_transport != TRANSPORT_SSH) + show_journal_by_service(i->id, OUTPUT_SHORT, NULL, 0, 0, 0, arg_all); + if (i->need_daemon_reload) printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %s daemon-reload' recommended.\n", ansi_highlight(true),