2017-11-18 17:09:20 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
2012-01-03 21:08:28 +01:00
|
|
|
/***
|
|
|
|
This file is part of systemd.
|
|
|
|
|
|
|
|
Copyright 2012 Lennart Poettering
|
|
|
|
|
|
|
|
systemd is free software; you can redistribute it and/or modify it
|
2012-04-12 00:20:58 +02:00
|
|
|
under the terms of the GNU Lesser General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
2012-01-03 21:08:28 +01:00
|
|
|
(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
|
2012-04-12 00:20:58 +02:00
|
|
|
Lesser General Public License for more details.
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2012-04-12 00:20:58 +02:00
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
2012-01-03 21:08:28 +01:00
|
|
|
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
***/
|
|
|
|
|
|
|
|
#include <errno.h>
|
2013-12-11 22:04:03 +01:00
|
|
|
#include <fcntl.h>
|
2015-12-03 21:13:37 +01:00
|
|
|
#include <signal.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdlib.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <string.h>
|
|
|
|
#include <sys/socket.h>
|
2015-12-03 21:13:37 +01:00
|
|
|
#include <syslog.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <time.h>
|
2015-12-03 21:13:37 +01:00
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "sd-id128.h"
|
|
|
|
#include "sd-journal.h"
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "fd-util.h"
|
2016-11-07 16:14:59 +01:00
|
|
|
#include "format-util.h"
|
2012-10-25 01:19:24 +02:00
|
|
|
#include "hashmap.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "hostname-util.h"
|
2015-10-25 14:08:25 +01:00
|
|
|
#include "io-util.h"
|
2013-04-16 05:25:57 +02:00
|
|
|
#include "journal-internal.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "log.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "logs-show.h"
|
2015-12-03 21:13:37 +01:00
|
|
|
#include "macro.h"
|
|
|
|
#include "output-mode.h"
|
2015-10-26 16:18:16 +01:00
|
|
|
#include "parse-util.h"
|
2015-04-10 19:10:00 +02:00
|
|
|
#include "process-util.h"
|
2015-12-03 21:13:37 +01:00
|
|
|
#include "sparse-endian.h"
|
2016-08-04 01:45:07 +02:00
|
|
|
#include "stdio-util.h"
|
2015-10-26 22:31:05 +01:00
|
|
|
#include "string-table.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "string-util.h"
|
2017-10-27 05:10:47 +02:00
|
|
|
#include "strv.h"
|
2015-04-10 23:15:59 +02:00
|
|
|
#include "terminal-util.h"
|
2015-12-03 21:13:37 +01:00
|
|
|
#include "time-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "utf8.h"
|
|
|
|
#include "util.h"
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2015-10-24 22:58:24 +02:00
|
|
|
/* up to three lines (each up to 100 characters) or 300 characters, whichever is less */
|
logs-show: limit to 3 lines and use dots if not showing full message
So far, we would show up to 128 bytes from a message, simply
cutting of the rest. With multiline messages, it is quite common
for a message to be longer than that, and this model doesn't really
work anymore.
A new limit is added: up to 3 lines will be shown, unless --full is
used (c.f. first line below). The limit for bytes is extended to 300
bytes. An ellipsis will always be used, if some form of truncation
occurs. If the tail of the message is cut off, either because of
length or line limit, dots will be shown at the end of the last
line. If this last line is short, the dots will be simply appended. If
the last line is too long for that, it will be ellipsized with dots at
the very end.
Note that the limits are in bytes, not characters, and we suck at
outputting unicode strings (c.f. last three lines below).
Aug 11 10:46:21 fedora python[67]: test message
line
line...
Aug 11 10:50:47 fedora python[76]: test message word word word word word word word word word word word wor...
Aug 11 10:55:11 fedora python[83]: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...
Aug 11 11:03:21 fedora python[90]: ąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:03:53 fedora python[97]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:25:45 fedora python[121]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą�...
2013-08-11 16:56:09 +02:00
|
|
|
#define PRINT_LINE_THRESHOLD 3
|
|
|
|
#define PRINT_CHAR_THRESHOLD 300
|
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
#define JSON_THRESHOLD 4096
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2012-11-15 23:03:31 +01:00
|
|
|
static int print_catalog(FILE *f, sd_journal *j) {
|
|
|
|
int r;
|
|
|
|
_cleanup_free_ char *t = NULL, *z = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
r = sd_journal_get_catalog(j, &t);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
z = strreplace(strstrip(t), "\n", "\n-- ");
|
|
|
|
if (!z)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
fputs("-- ", f);
|
|
|
|
fputs(z, f);
|
|
|
|
fputc('\n', f);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-10-12 08:14:07 +02:00
|
|
|
static int parse_field(const void *data, size_t length, const char *field, size_t field_len, char **target, size_t *target_len) {
|
|
|
|
size_t nl;
|
2016-01-28 16:28:01 +01:00
|
|
|
char *buf;
|
2012-01-05 20:11:47 +01:00
|
|
|
|
|
|
|
assert(data);
|
|
|
|
assert(field);
|
|
|
|
assert(target);
|
|
|
|
|
2017-10-12 08:14:07 +02:00
|
|
|
if (length < field_len)
|
2012-01-05 20:11:47 +01:00
|
|
|
return 0;
|
|
|
|
|
2017-10-12 08:14:07 +02:00
|
|
|
if (memcmp(data, field, field_len))
|
2012-01-05 20:11:47 +01:00
|
|
|
return 0;
|
|
|
|
|
2017-10-12 08:14:07 +02:00
|
|
|
nl = length - field_len;
|
2017-07-20 14:14:55 +02:00
|
|
|
|
|
|
|
|
2017-10-12 08:14:07 +02:00
|
|
|
buf = newdup_suffix0(char, (const char*) data + field_len, nl);
|
2012-07-25 23:55:59 +02:00
|
|
|
if (!buf)
|
|
|
|
return log_oom();
|
2012-01-05 20:11:47 +01:00
|
|
|
|
2012-01-07 04:09:59 +01:00
|
|
|
free(*target);
|
2012-01-05 20:11:47 +01:00
|
|
|
*target = buf;
|
2016-01-28 16:28:01 +01:00
|
|
|
|
2017-10-12 08:14:07 +02:00
|
|
|
if (target_len)
|
|
|
|
*target_len = nl;
|
2012-01-05 20:11:47 +01:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-10-12 08:14:07 +02:00
|
|
|
typedef struct ParseFieldVec {
|
|
|
|
const char *field;
|
|
|
|
size_t field_len;
|
|
|
|
char **target;
|
|
|
|
size_t *target_len;
|
|
|
|
} ParseFieldVec;
|
|
|
|
|
|
|
|
#define PARSE_FIELD_VEC_ENTRY(_field, _target, _target_len) \
|
|
|
|
{ .field = _field, .field_len = strlen(_field), .target = _target, .target_len = _target_len }
|
|
|
|
|
|
|
|
static int parse_fieldv(const void *data, size_t length, const ParseFieldVec *fields, unsigned n_fields) {
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
for (i = 0; i < n_fields; i++) {
|
|
|
|
const ParseFieldVec *f = &fields[i];
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = parse_field(data, length, f->field, f->field_len, f->target, f->target_len);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
else if (r > 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-10-27 05:10:47 +02:00
|
|
|
static int field_set_test(Set *fields, const char *name, size_t n) {
|
|
|
|
char *s = NULL;
|
|
|
|
|
|
|
|
if (!fields)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
s = strndupa(name, n);
|
|
|
|
if (!s)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
return set_get(fields, s) ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
static bool shall_print(const char *p, size_t l, OutputFlags flags) {
|
|
|
|
assert(p);
|
|
|
|
|
|
|
|
if (flags & OUTPUT_SHOW_ALL)
|
2012-01-05 20:11:47 +01:00
|
|
|
return true;
|
|
|
|
|
logs-show: limit to 3 lines and use dots if not showing full message
So far, we would show up to 128 bytes from a message, simply
cutting of the rest. With multiline messages, it is quite common
for a message to be longer than that, and this model doesn't really
work anymore.
A new limit is added: up to 3 lines will be shown, unless --full is
used (c.f. first line below). The limit for bytes is extended to 300
bytes. An ellipsis will always be used, if some form of truncation
occurs. If the tail of the message is cut off, either because of
length or line limit, dots will be shown at the end of the last
line. If this last line is short, the dots will be simply appended. If
the last line is too long for that, it will be ellipsized with dots at
the very end.
Note that the limits are in bytes, not characters, and we suck at
outputting unicode strings (c.f. last three lines below).
Aug 11 10:46:21 fedora python[67]: test message
line
line...
Aug 11 10:50:47 fedora python[76]: test message word word word word word word word word word word word wor...
Aug 11 10:55:11 fedora python[83]: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...
Aug 11 11:03:21 fedora python[90]: ąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:03:53 fedora python[97]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:25:45 fedora python[121]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą�...
2013-08-11 16:56:09 +02:00
|
|
|
if (l >= PRINT_CHAR_THRESHOLD)
|
2012-01-05 20:11:47 +01:00
|
|
|
return false;
|
|
|
|
|
2013-04-24 04:40:26 +02:00
|
|
|
if (!utf8_is_printable(p, l))
|
2012-01-05 20:11:47 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-09-18 15:36:27 +02:00
|
|
|
static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, OutputFlags flags, int priority, const char* message, size_t message_len) {
|
2013-04-24 04:40:26 +02:00
|
|
|
const char *color_on = "", *color_off = "";
|
|
|
|
const char *pos, *end;
|
2013-08-04 01:38:13 +02:00
|
|
|
bool ellipsized = false;
|
logs-show: limit to 3 lines and use dots if not showing full message
So far, we would show up to 128 bytes from a message, simply
cutting of the rest. With multiline messages, it is quite common
for a message to be longer than that, and this model doesn't really
work anymore.
A new limit is added: up to 3 lines will be shown, unless --full is
used (c.f. first line below). The limit for bytes is extended to 300
bytes. An ellipsis will always be used, if some form of truncation
occurs. If the tail of the message is cut off, either because of
length or line limit, dots will be shown at the end of the last
line. If this last line is short, the dots will be simply appended. If
the last line is too long for that, it will be ellipsized with dots at
the very end.
Note that the limits are in bytes, not characters, and we suck at
outputting unicode strings (c.f. last three lines below).
Aug 11 10:46:21 fedora python[67]: test message
line
line...
Aug 11 10:50:47 fedora python[76]: test message word word word word word word word word word word word wor...
Aug 11 10:55:11 fedora python[83]: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...
Aug 11 11:03:21 fedora python[90]: ąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:03:53 fedora python[97]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:25:45 fedora python[121]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą�...
2013-08-11 16:56:09 +02:00
|
|
|
int line = 0;
|
2013-04-24 04:40:26 +02:00
|
|
|
|
|
|
|
if (flags & OUTPUT_COLOR) {
|
|
|
|
if (priority <= LOG_ERR) {
|
2015-09-19 00:45:05 +02:00
|
|
|
color_on = ANSI_HIGHLIGHT_RED;
|
|
|
|
color_off = ANSI_NORMAL;
|
2013-04-24 04:40:26 +02:00
|
|
|
} else if (priority <= LOG_NOTICE) {
|
2015-09-19 00:45:05 +02:00
|
|
|
color_on = ANSI_HIGHLIGHT;
|
|
|
|
color_off = ANSI_NORMAL;
|
2013-04-24 04:40:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-20 02:00:09 +01:00
|
|
|
/* A special case: make sure that we print a newline when
|
|
|
|
the message is empty. */
|
|
|
|
if (message_len == 0)
|
|
|
|
fputs("\n", f);
|
|
|
|
|
logs-show: limit to 3 lines and use dots if not showing full message
So far, we would show up to 128 bytes from a message, simply
cutting of the rest. With multiline messages, it is quite common
for a message to be longer than that, and this model doesn't really
work anymore.
A new limit is added: up to 3 lines will be shown, unless --full is
used (c.f. first line below). The limit for bytes is extended to 300
bytes. An ellipsis will always be used, if some form of truncation
occurs. If the tail of the message is cut off, either because of
length or line limit, dots will be shown at the end of the last
line. If this last line is short, the dots will be simply appended. If
the last line is too long for that, it will be ellipsized with dots at
the very end.
Note that the limits are in bytes, not characters, and we suck at
outputting unicode strings (c.f. last three lines below).
Aug 11 10:46:21 fedora python[67]: test message
line
line...
Aug 11 10:50:47 fedora python[76]: test message word word word word word word word word word word word wor...
Aug 11 10:55:11 fedora python[83]: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...
Aug 11 11:03:21 fedora python[90]: ąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:03:53 fedora python[97]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:25:45 fedora python[121]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą�...
2013-08-11 16:56:09 +02:00
|
|
|
for (pos = message;
|
|
|
|
pos < message + message_len;
|
|
|
|
pos = end + 1, line++) {
|
|
|
|
bool continuation = line > 0;
|
|
|
|
bool tail_line;
|
2013-04-24 04:40:26 +02:00
|
|
|
int len;
|
|
|
|
for (end = pos; end < message + message_len && *end != '\n'; end++)
|
|
|
|
;
|
|
|
|
len = end - pos;
|
|
|
|
assert(len >= 0);
|
|
|
|
|
2013-08-21 07:34:57 +02:00
|
|
|
/* We need to figure out when we are showing not-last line, *and*
|
logs-show: limit to 3 lines and use dots if not showing full message
So far, we would show up to 128 bytes from a message, simply
cutting of the rest. With multiline messages, it is quite common
for a message to be longer than that, and this model doesn't really
work anymore.
A new limit is added: up to 3 lines will be shown, unless --full is
used (c.f. first line below). The limit for bytes is extended to 300
bytes. An ellipsis will always be used, if some form of truncation
occurs. If the tail of the message is cut off, either because of
length or line limit, dots will be shown at the end of the last
line. If this last line is short, the dots will be simply appended. If
the last line is too long for that, it will be ellipsized with dots at
the very end.
Note that the limits are in bytes, not characters, and we suck at
outputting unicode strings (c.f. last three lines below).
Aug 11 10:46:21 fedora python[67]: test message
line
line...
Aug 11 10:50:47 fedora python[76]: test message word word word word word word word word word word word wor...
Aug 11 10:55:11 fedora python[83]: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...
Aug 11 11:03:21 fedora python[90]: ąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:03:53 fedora python[97]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:25:45 fedora python[121]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą�...
2013-08-11 16:56:09 +02:00
|
|
|
* will skip subsequent lines. In that case, we will put the dots
|
|
|
|
* at the end of the line, instead of putting dots in the middle
|
|
|
|
* or not at all.
|
|
|
|
*/
|
|
|
|
tail_line =
|
|
|
|
line + 1 == PRINT_LINE_THRESHOLD ||
|
2013-08-21 07:34:57 +02:00
|
|
|
end + 1 >= message + PRINT_CHAR_THRESHOLD;
|
logs-show: limit to 3 lines and use dots if not showing full message
So far, we would show up to 128 bytes from a message, simply
cutting of the rest. With multiline messages, it is quite common
for a message to be longer than that, and this model doesn't really
work anymore.
A new limit is added: up to 3 lines will be shown, unless --full is
used (c.f. first line below). The limit for bytes is extended to 300
bytes. An ellipsis will always be used, if some form of truncation
occurs. If the tail of the message is cut off, either because of
length or line limit, dots will be shown at the end of the last
line. If this last line is short, the dots will be simply appended. If
the last line is too long for that, it will be ellipsized with dots at
the very end.
Note that the limits are in bytes, not characters, and we suck at
outputting unicode strings (c.f. last three lines below).
Aug 11 10:46:21 fedora python[67]: test message
line
line...
Aug 11 10:50:47 fedora python[76]: test message word word word word word word word word word word word wor...
Aug 11 10:55:11 fedora python[83]: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...
Aug 11 11:03:21 fedora python[90]: ąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:03:53 fedora python[97]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:25:45 fedora python[121]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą�...
2013-08-11 16:56:09 +02:00
|
|
|
|
|
|
|
if (flags & (OUTPUT_FULL_WIDTH | OUTPUT_SHOW_ALL) ||
|
|
|
|
(prefix + len + 1 < n_columns && !tail_line)) {
|
2013-04-24 04:40:26 +02:00
|
|
|
fprintf(f, "%*s%s%.*s%s\n",
|
|
|
|
continuation * prefix, "",
|
|
|
|
color_on, len, pos, color_off);
|
logs-show: limit to 3 lines and use dots if not showing full message
So far, we would show up to 128 bytes from a message, simply
cutting of the rest. With multiline messages, it is quite common
for a message to be longer than that, and this model doesn't really
work anymore.
A new limit is added: up to 3 lines will be shown, unless --full is
used (c.f. first line below). The limit for bytes is extended to 300
bytes. An ellipsis will always be used, if some form of truncation
occurs. If the tail of the message is cut off, either because of
length or line limit, dots will be shown at the end of the last
line. If this last line is short, the dots will be simply appended. If
the last line is too long for that, it will be ellipsized with dots at
the very end.
Note that the limits are in bytes, not characters, and we suck at
outputting unicode strings (c.f. last three lines below).
Aug 11 10:46:21 fedora python[67]: test message
line
line...
Aug 11 10:50:47 fedora python[76]: test message word word word word word word word word word word word wor...
Aug 11 10:55:11 fedora python[83]: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...
Aug 11 11:03:21 fedora python[90]: ąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:03:53 fedora python[97]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:25:45 fedora python[121]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą�...
2013-08-11 16:56:09 +02:00
|
|
|
continue;
|
|
|
|
}
|
2013-04-24 04:40:26 +02:00
|
|
|
|
logs-show: limit to 3 lines and use dots if not showing full message
So far, we would show up to 128 bytes from a message, simply
cutting of the rest. With multiline messages, it is quite common
for a message to be longer than that, and this model doesn't really
work anymore.
A new limit is added: up to 3 lines will be shown, unless --full is
used (c.f. first line below). The limit for bytes is extended to 300
bytes. An ellipsis will always be used, if some form of truncation
occurs. If the tail of the message is cut off, either because of
length or line limit, dots will be shown at the end of the last
line. If this last line is short, the dots will be simply appended. If
the last line is too long for that, it will be ellipsized with dots at
the very end.
Note that the limits are in bytes, not characters, and we suck at
outputting unicode strings (c.f. last three lines below).
Aug 11 10:46:21 fedora python[67]: test message
line
line...
Aug 11 10:50:47 fedora python[76]: test message word word word word word word word word word word word wor...
Aug 11 10:55:11 fedora python[83]: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...
Aug 11 11:03:21 fedora python[90]: ąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:03:53 fedora python[97]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:25:45 fedora python[121]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą�...
2013-08-11 16:56:09 +02:00
|
|
|
/* Beyond this point, ellipsization will happen. */
|
|
|
|
ellipsized = true;
|
2013-04-24 04:40:26 +02:00
|
|
|
|
logs-show: limit to 3 lines and use dots if not showing full message
So far, we would show up to 128 bytes from a message, simply
cutting of the rest. With multiline messages, it is quite common
for a message to be longer than that, and this model doesn't really
work anymore.
A new limit is added: up to 3 lines will be shown, unless --full is
used (c.f. first line below). The limit for bytes is extended to 300
bytes. An ellipsis will always be used, if some form of truncation
occurs. If the tail of the message is cut off, either because of
length or line limit, dots will be shown at the end of the last
line. If this last line is short, the dots will be simply appended. If
the last line is too long for that, it will be ellipsized with dots at
the very end.
Note that the limits are in bytes, not characters, and we suck at
outputting unicode strings (c.f. last three lines below).
Aug 11 10:46:21 fedora python[67]: test message
line
line...
Aug 11 10:50:47 fedora python[76]: test message word word word word word word word word word word word wor...
Aug 11 10:55:11 fedora python[83]: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...
Aug 11 11:03:21 fedora python[90]: ąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:03:53 fedora python[97]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:25:45 fedora python[121]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą�...
2013-08-11 16:56:09 +02:00
|
|
|
if (prefix < n_columns && n_columns - prefix >= 3) {
|
|
|
|
if (n_columns - prefix > (unsigned) len + 3)
|
|
|
|
fprintf(f, "%*s%s%.*s...%s\n",
|
2013-08-08 14:32:43 +02:00
|
|
|
continuation * prefix, "",
|
|
|
|
color_on, len, pos, color_off);
|
logs-show: limit to 3 lines and use dots if not showing full message
So far, we would show up to 128 bytes from a message, simply
cutting of the rest. With multiline messages, it is quite common
for a message to be longer than that, and this model doesn't really
work anymore.
A new limit is added: up to 3 lines will be shown, unless --full is
used (c.f. first line below). The limit for bytes is extended to 300
bytes. An ellipsis will always be used, if some form of truncation
occurs. If the tail of the message is cut off, either because of
length or line limit, dots will be shown at the end of the last
line. If this last line is short, the dots will be simply appended. If
the last line is too long for that, it will be ellipsized with dots at
the very end.
Note that the limits are in bytes, not characters, and we suck at
outputting unicode strings (c.f. last three lines below).
Aug 11 10:46:21 fedora python[67]: test message
line
line...
Aug 11 10:50:47 fedora python[76]: test message word word word word word word word word word word word wor...
Aug 11 10:55:11 fedora python[83]: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...
Aug 11 11:03:21 fedora python[90]: ąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:03:53 fedora python[97]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:25:45 fedora python[121]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą�...
2013-08-11 16:56:09 +02:00
|
|
|
else {
|
|
|
|
_cleanup_free_ char *e;
|
|
|
|
|
|
|
|
e = ellipsize_mem(pos, len, n_columns - prefix,
|
|
|
|
tail_line ? 100 : 90);
|
|
|
|
if (!e)
|
|
|
|
fprintf(f, "%*s%s%.*s%s\n",
|
|
|
|
continuation * prefix, "",
|
|
|
|
color_on, len, pos, color_off);
|
|
|
|
else
|
|
|
|
fprintf(f, "%*s%s%s%s\n",
|
|
|
|
continuation * prefix, "",
|
|
|
|
color_on, e, color_off);
|
|
|
|
}
|
|
|
|
} else
|
2013-04-24 04:40:26 +02:00
|
|
|
fputs("...\n", f);
|
|
|
|
|
logs-show: limit to 3 lines and use dots if not showing full message
So far, we would show up to 128 bytes from a message, simply
cutting of the rest. With multiline messages, it is quite common
for a message to be longer than that, and this model doesn't really
work anymore.
A new limit is added: up to 3 lines will be shown, unless --full is
used (c.f. first line below). The limit for bytes is extended to 300
bytes. An ellipsis will always be used, if some form of truncation
occurs. If the tail of the message is cut off, either because of
length or line limit, dots will be shown at the end of the last
line. If this last line is short, the dots will be simply appended. If
the last line is too long for that, it will be ellipsized with dots at
the very end.
Note that the limits are in bytes, not characters, and we suck at
outputting unicode strings (c.f. last three lines below).
Aug 11 10:46:21 fedora python[67]: test message
line
line...
Aug 11 10:50:47 fedora python[76]: test message word word word word word word word word word word word wor...
Aug 11 10:55:11 fedora python[83]: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...
Aug 11 11:03:21 fedora python[90]: ąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:03:53 fedora python[97]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:25:45 fedora python[121]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą�...
2013-08-11 16:56:09 +02:00
|
|
|
if (tail_line)
|
|
|
|
break;
|
2013-04-24 04:40:26 +02:00
|
|
|
}
|
2013-08-04 01:38:13 +02:00
|
|
|
|
|
|
|
return ellipsized;
|
2013-04-24 04:40:26 +02:00
|
|
|
}
|
|
|
|
|
2016-08-04 01:45:07 +02:00
|
|
|
static int output_timestamp_monotonic(FILE *f, sd_journal *j, const char *monotonic) {
|
|
|
|
sd_id128_t boot_id;
|
|
|
|
uint64_t t;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(f);
|
|
|
|
assert(j);
|
|
|
|
|
|
|
|
r = -ENXIO;
|
|
|
|
if (monotonic)
|
|
|
|
r = safe_atou64(monotonic, &t);
|
|
|
|
if (r < 0)
|
|
|
|
r = sd_journal_get_monotonic_usec(j, &t, &boot_id);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to get monotonic timestamp: %m");
|
|
|
|
|
2016-11-07 17:49:25 +01:00
|
|
|
fprintf(f, "[%5"PRI_USEC".%06"PRI_USEC"]", t / USEC_PER_SEC, t % USEC_PER_SEC);
|
2016-08-04 01:45:07 +02:00
|
|
|
return 1 + 5 + 1 + 6 + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, OutputFlags flags, const char *realtime) {
|
|
|
|
char buf[MAX(FORMAT_TIMESTAMP_MAX, 64)];
|
|
|
|
struct tm *(*gettime_r)(const time_t *, struct tm *);
|
|
|
|
struct tm tm;
|
|
|
|
uint64_t x;
|
|
|
|
time_t t;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(f);
|
|
|
|
assert(j);
|
|
|
|
|
|
|
|
r = -ENXIO;
|
|
|
|
if (realtime)
|
|
|
|
r = safe_atou64(realtime, &x);
|
|
|
|
if (r < 0)
|
|
|
|
r = sd_journal_get_realtime_usec(j, &x);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to get realtime timestamp: %m");
|
|
|
|
|
2017-02-02 18:30:29 +01:00
|
|
|
if (x > USEC_TIMESTAMP_FORMATTABLE_MAX) {
|
|
|
|
log_error("Timestamp cannot be printed");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2016-08-04 01:45:07 +02:00
|
|
|
if (mode == OUTPUT_SHORT_FULL) {
|
|
|
|
const char *k;
|
|
|
|
|
|
|
|
if (flags & OUTPUT_UTC)
|
|
|
|
k = format_timestamp_utc(buf, sizeof(buf), x);
|
|
|
|
else
|
|
|
|
k = format_timestamp(buf, sizeof(buf), x);
|
|
|
|
if (!k) {
|
|
|
|
log_error("Failed to format timestamp.");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
2017-05-08 02:23:49 +02:00
|
|
|
char usec[7];
|
|
|
|
|
2016-08-04 01:45:07 +02:00
|
|
|
gettime_r = (flags & OUTPUT_UTC) ? gmtime_r : localtime_r;
|
|
|
|
t = (time_t) (x / USEC_PER_SEC);
|
|
|
|
|
|
|
|
switch (mode) {
|
|
|
|
|
|
|
|
case OUTPUT_SHORT_UNIX:
|
2016-11-07 17:49:25 +01:00
|
|
|
xsprintf(buf, "%10"PRI_TIME".%06"PRIu64, t, x % USEC_PER_SEC);
|
2016-08-04 01:45:07 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OUTPUT_SHORT_ISO:
|
|
|
|
if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm)) <= 0) {
|
2017-05-08 02:23:49 +02:00
|
|
|
log_error("Failed to format ISO time");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OUTPUT_SHORT_ISO_PRECISE:
|
|
|
|
/* No usec in strftime, so we leave space and copy over */
|
|
|
|
if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S.xxxxxx%z", gettime_r(&t, &tm)) <= 0) {
|
|
|
|
log_error("Failed to format ISO-precise time");
|
2016-08-04 01:45:07 +02:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2017-05-08 02:23:49 +02:00
|
|
|
xsprintf(usec, "%06"PRI_USEC, x % USEC_PER_SEC);
|
|
|
|
memcpy(buf + 20, usec, 6);
|
2016-08-04 01:45:07 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OUTPUT_SHORT:
|
|
|
|
case OUTPUT_SHORT_PRECISE:
|
|
|
|
|
|
|
|
if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)) <= 0) {
|
|
|
|
log_error("Failed to format syslog time");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mode == OUTPUT_SHORT_PRECISE) {
|
|
|
|
size_t k;
|
|
|
|
|
|
|
|
assert(sizeof(buf) > strlen(buf));
|
|
|
|
k = sizeof(buf) - strlen(buf);
|
|
|
|
|
2016-11-07 17:49:25 +01:00
|
|
|
r = snprintf(buf + strlen(buf), k, ".%06"PRIu64, x % USEC_PER_SEC);
|
2016-08-04 01:45:07 +02:00
|
|
|
if (r <= 0 || (size_t) r >= k) { /* too long? */
|
|
|
|
log_error("Failed to format precise time");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
assert_not_reached("Unknown time format");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fputs(buf, f);
|
|
|
|
return (int) strlen(buf);
|
|
|
|
}
|
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
static int output_short(
|
|
|
|
FILE *f,
|
|
|
|
sd_journal *j,
|
|
|
|
OutputMode mode,
|
|
|
|
unsigned n_columns,
|
2017-10-27 05:10:47 +02:00
|
|
|
OutputFlags flags,
|
|
|
|
Set *output_fields) {
|
2012-09-27 23:27:10 +02:00
|
|
|
|
2012-01-03 21:08:28 +01:00
|
|
|
int r;
|
|
|
|
const void *data;
|
|
|
|
size_t length;
|
|
|
|
size_t n = 0;
|
2012-09-27 23:27:10 +02:00
|
|
|
_cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL;
|
2012-07-26 16:50:35 +02:00
|
|
|
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, priority_len = 0;
|
|
|
|
int p = LOG_INFO;
|
2013-08-04 01:38:13 +02:00
|
|
|
bool ellipsized = false;
|
2017-10-12 08:14:07 +02:00
|
|
|
const ParseFieldVec fields[] = {
|
|
|
|
PARSE_FIELD_VEC_ENTRY("_PID=", &pid, &pid_len),
|
|
|
|
PARSE_FIELD_VEC_ENTRY("_COMM=", &comm, &comm_len),
|
|
|
|
PARSE_FIELD_VEC_ENTRY("MESSAGE=", &message, &message_len),
|
|
|
|
PARSE_FIELD_VEC_ENTRY("PRIORITY=", &priority, &priority_len),
|
|
|
|
PARSE_FIELD_VEC_ENTRY("_HOSTNAME=", &hostname, &hostname_len),
|
|
|
|
PARSE_FIELD_VEC_ENTRY("SYSLOG_PID=", &fake_pid, &fake_pid_len),
|
|
|
|
PARSE_FIELD_VEC_ENTRY("SYSLOG_IDENTIFIER=", &identifier, &identifier_len),
|
|
|
|
PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len),
|
|
|
|
PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len),
|
|
|
|
};
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
assert(f);
|
2012-01-03 21:08:28 +01:00
|
|
|
assert(j);
|
|
|
|
|
logs-show: limit to 3 lines and use dots if not showing full message
So far, we would show up to 128 bytes from a message, simply
cutting of the rest. With multiline messages, it is quite common
for a message to be longer than that, and this model doesn't really
work anymore.
A new limit is added: up to 3 lines will be shown, unless --full is
used (c.f. first line below). The limit for bytes is extended to 300
bytes. An ellipsis will always be used, if some form of truncation
occurs. If the tail of the message is cut off, either because of
length or line limit, dots will be shown at the end of the last
line. If this last line is short, the dots will be simply appended. If
the last line is too long for that, it will be ellipsized with dots at
the very end.
Note that the limits are in bytes, not characters, and we suck at
outputting unicode strings (c.f. last three lines below).
Aug 11 10:46:21 fedora python[67]: test message
line
line...
Aug 11 10:50:47 fedora python[76]: test message word word word word word word word word word word word wor...
Aug 11 10:55:11 fedora python[83]: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...
Aug 11 11:03:21 fedora python[90]: ąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:03:53 fedora python[97]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:25:45 fedora python[121]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą�...
2013-08-11 16:56:09 +02:00
|
|
|
/* Set the threshold to one bigger than the actual print
|
2013-09-14 01:41:52 +02:00
|
|
|
* threshold, so that if the line is actually longer than what
|
logs-show: limit to 3 lines and use dots if not showing full message
So far, we would show up to 128 bytes from a message, simply
cutting of the rest. With multiline messages, it is quite common
for a message to be longer than that, and this model doesn't really
work anymore.
A new limit is added: up to 3 lines will be shown, unless --full is
used (c.f. first line below). The limit for bytes is extended to 300
bytes. An ellipsis will always be used, if some form of truncation
occurs. If the tail of the message is cut off, either because of
length or line limit, dots will be shown at the end of the last
line. If this last line is short, the dots will be simply appended. If
the last line is too long for that, it will be ellipsized with dots at
the very end.
Note that the limits are in bytes, not characters, and we suck at
outputting unicode strings (c.f. last three lines below).
Aug 11 10:46:21 fedora python[67]: test message
line
line...
Aug 11 10:50:47 fedora python[76]: test message word word word word word word word word word word word wor...
Aug 11 10:55:11 fedora python[83]: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...
Aug 11 11:03:21 fedora python[90]: ąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:03:53 fedora python[97]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:25:45 fedora python[121]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą�...
2013-08-11 16:56:09 +02:00
|
|
|
* we're willing to print, ellipsization will occur. This way
|
|
|
|
* we won't output a misleading line without any indication of
|
|
|
|
* truncation.
|
|
|
|
*/
|
|
|
|
sd_journal_set_data_threshold(j, flags & (OUTPUT_SHOW_ALL|OUTPUT_FULL_WIDTH) ? 0 : PRINT_CHAR_THRESHOLD + 1);
|
2012-11-21 00:28:00 +01:00
|
|
|
|
2013-06-10 03:50:56 +02:00
|
|
|
JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
|
2012-01-05 20:11:47 +01:00
|
|
|
|
2017-10-12 08:14:07 +02:00
|
|
|
r = parse_fieldv(data, length, fields, ELEMENTSOF(fields));
|
2012-01-05 20:11:47 +01:00
|
|
|
if (r < 0)
|
2012-09-27 23:27:10 +02:00
|
|
|
return r;
|
2012-01-05 20:11:47 +01:00
|
|
|
}
|
2016-04-25 21:43:12 +02:00
|
|
|
if (r == -EBADMSG) {
|
|
|
|
log_debug_errno(r, "Skipping message we can't read: %m");
|
|
|
|
return 0;
|
|
|
|
}
|
2013-06-10 03:50:56 +02:00
|
|
|
if (r < 0)
|
2015-05-19 00:25:45 +02:00
|
|
|
return log_error_errno(r, "Failed to get journal fields: %m");
|
2013-06-10 03:50:56 +02:00
|
|
|
|
2015-06-17 20:28:20 +02:00
|
|
|
if (!message) {
|
|
|
|
log_debug("Skipping message without MESSAGE= field.");
|
2012-09-27 23:27:10 +02:00
|
|
|
return 0;
|
2015-06-17 20:28:20 +02:00
|
|
|
}
|
2012-01-05 20:11:47 +01:00
|
|
|
|
2012-12-23 11:23:59 +01:00
|
|
|
if (!(flags & OUTPUT_SHOW_ALL))
|
|
|
|
strip_tab_ansi(&message, &message_len);
|
|
|
|
|
2012-07-26 16:50:35 +02:00
|
|
|
if (priority_len == 1 && *priority >= '0' && *priority <= '7')
|
|
|
|
p = *priority - '0';
|
|
|
|
|
2016-08-04 01:45:07 +02:00
|
|
|
if (mode == OUTPUT_SHORT_MONOTONIC)
|
|
|
|
r = output_timestamp_monotonic(f, j, monotonic);
|
|
|
|
else
|
|
|
|
r = output_timestamp_realtime(f, j, mode, flags, realtime);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
n += r;
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2016-08-04 01:45:07 +02:00
|
|
|
if (flags & OUTPUT_NO_HOSTNAME) {
|
2016-04-20 20:09:57 +02:00
|
|
|
/* Suppress display of the hostname if this is requested. */
|
2016-11-03 22:23:22 +01:00
|
|
|
hostname = mfree(hostname);
|
2016-04-20 20:09:57 +02:00
|
|
|
hostname_len = 0;
|
|
|
|
}
|
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
if (hostname && shall_print(hostname, hostname_len, flags)) {
|
|
|
|
fprintf(f, " %.*s", (int) hostname_len, hostname);
|
2012-01-05 20:11:47 +01:00
|
|
|
n += hostname_len + 1;
|
|
|
|
}
|
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
if (identifier && shall_print(identifier, identifier_len, flags)) {
|
|
|
|
fprintf(f, " %.*s", (int) identifier_len, identifier);
|
2012-01-05 21:43:49 +01:00
|
|
|
n += identifier_len + 1;
|
2012-09-27 23:27:10 +02:00
|
|
|
} else if (comm && shall_print(comm, comm_len, flags)) {
|
|
|
|
fprintf(f, " %.*s", (int) comm_len, comm);
|
2012-01-05 20:11:47 +01:00
|
|
|
n += comm_len + 1;
|
2012-04-11 00:34:06 +02:00
|
|
|
} else
|
2014-11-04 00:28:33 +01:00
|
|
|
fputs(" unknown", f);
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
if (pid && shall_print(pid, pid_len, flags)) {
|
|
|
|
fprintf(f, "[%.*s]", (int) pid_len, pid);
|
2012-01-05 20:11:47 +01:00
|
|
|
n += pid_len + 2;
|
2012-09-27 23:27:10 +02:00
|
|
|
} else if (fake_pid && shall_print(fake_pid, fake_pid_len, flags)) {
|
|
|
|
fprintf(f, "[%.*s]", (int) fake_pid_len, fake_pid);
|
2012-01-07 04:09:59 +01:00
|
|
|
n += fake_pid_len + 2;
|
2012-01-03 21:08:28 +01:00
|
|
|
}
|
|
|
|
|
2013-04-24 04:40:26 +02:00
|
|
|
if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(message, message_len)) {
|
2012-01-14 03:07:47 +01:00
|
|
|
char bytes[FORMAT_BYTES_MAX];
|
2012-09-27 23:27:10 +02:00
|
|
|
fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
|
2013-04-24 04:40:26 +02:00
|
|
|
} else {
|
|
|
|
fputs(": ", f);
|
2013-08-04 01:38:13 +02:00
|
|
|
ellipsized |=
|
|
|
|
print_multiline(f, n + 2, n_columns, flags, p, message, message_len);
|
2013-04-24 04:40:26 +02:00
|
|
|
}
|
2012-01-05 20:11:47 +01:00
|
|
|
|
2012-11-15 23:03:31 +01:00
|
|
|
if (flags & OUTPUT_CATALOG)
|
|
|
|
print_catalog(f, j);
|
|
|
|
|
2013-08-04 01:38:13 +02:00
|
|
|
return ellipsized;
|
2012-01-03 21:08:28 +01:00
|
|
|
}
|
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
static int output_verbose(
|
|
|
|
FILE *f,
|
|
|
|
sd_journal *j,
|
|
|
|
OutputMode mode,
|
|
|
|
unsigned n_columns,
|
2017-10-27 05:10:47 +02:00
|
|
|
OutputFlags flags,
|
|
|
|
Set *output_fields) {
|
2012-09-27 23:27:10 +02:00
|
|
|
|
2012-01-03 21:08:28 +01:00
|
|
|
const void *data;
|
|
|
|
size_t length;
|
2013-04-18 09:11:22 +02:00
|
|
|
_cleanup_free_ char *cursor = NULL;
|
2016-01-28 16:28:01 +01:00
|
|
|
uint64_t realtime = 0;
|
2013-08-04 15:04:20 +02:00
|
|
|
char ts[FORMAT_TIMESTAMP_MAX + 7];
|
2017-05-13 22:40:09 +02:00
|
|
|
const char *timestamp;
|
2012-01-03 21:08:28 +01:00
|
|
|
int r;
|
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
assert(f);
|
2012-01-03 21:08:28 +01:00
|
|
|
assert(j);
|
|
|
|
|
2012-11-21 00:28:00 +01:00
|
|
|
sd_journal_set_data_threshold(j, 0);
|
|
|
|
|
2013-08-08 04:55:29 +02:00
|
|
|
r = sd_journal_get_data(j, "_SOURCE_REALTIME_TIMESTAMP", &data, &length);
|
|
|
|
if (r == -ENOENT)
|
|
|
|
log_debug("Source realtime timestamp not found");
|
2015-05-19 00:25:45 +02:00
|
|
|
else if (r < 0)
|
|
|
|
return log_full_errno(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR, r, "Failed to get source realtime timestamp: %m");
|
|
|
|
else {
|
2013-08-08 04:55:29 +02:00
|
|
|
_cleanup_free_ char *value = NULL;
|
|
|
|
|
2017-12-14 19:02:29 +01:00
|
|
|
r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=",
|
|
|
|
STRLEN("_SOURCE_REALTIME_TIMESTAMP="), &value,
|
|
|
|
NULL);
|
2013-08-08 04:55:29 +02:00
|
|
|
if (r < 0)
|
2015-12-10 03:35:49 +01:00
|
|
|
return r;
|
2016-01-28 16:28:01 +01:00
|
|
|
assert(r > 0);
|
|
|
|
|
|
|
|
r = safe_atou64(value, &realtime);
|
|
|
|
if (r < 0)
|
|
|
|
log_debug_errno(r, "Failed to parse realtime timestamp: %m");
|
2013-08-08 04:55:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (r < 0) {
|
|
|
|
r = sd_journal_get_realtime_usec(j, &realtime);
|
2015-05-19 00:25:45 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_full_errno(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_ERR, r, "Failed to get realtime timestamp: %m");
|
2012-01-03 21:08:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_journal_get_cursor(j, &cursor);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to get cursor: %m");
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2017-05-13 22:40:09 +02:00
|
|
|
timestamp = flags & OUTPUT_UTC ? format_timestamp_us_utc(ts, sizeof ts, realtime)
|
|
|
|
: format_timestamp_us(ts, sizeof ts, realtime);
|
2012-09-27 23:27:10 +02:00
|
|
|
fprintf(f, "%s [%s]\n",
|
2017-05-13 22:40:09 +02:00
|
|
|
timestamp ?: "(no timestamp)",
|
2012-09-27 23:27:10 +02:00
|
|
|
cursor);
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2013-06-10 03:50:56 +02:00
|
|
|
JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
|
2013-04-24 04:40:26 +02:00
|
|
|
const char *c;
|
|
|
|
int fieldlen;
|
2013-06-27 01:48:24 +02:00
|
|
|
const char *on = "", *off = "";
|
|
|
|
|
2013-04-24 04:40:26 +02:00
|
|
|
c = memchr(data, '=', length);
|
|
|
|
if (!c) {
|
|
|
|
log_error("Invalid field.");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
fieldlen = c - (const char*) data;
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2017-10-27 05:10:47 +02:00
|
|
|
r = field_set_test(output_fields, data, fieldlen);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (!r)
|
|
|
|
continue;
|
|
|
|
|
2013-06-27 01:48:24 +02:00
|
|
|
if (flags & OUTPUT_COLOR && startswith(data, "MESSAGE=")) {
|
2015-09-19 00:45:05 +02:00
|
|
|
on = ANSI_HIGHLIGHT;
|
|
|
|
off = ANSI_NORMAL;
|
2013-06-27 01:48:24 +02:00
|
|
|
}
|
|
|
|
|
2016-07-01 02:41:19 +02:00
|
|
|
if ((flags & OUTPUT_SHOW_ALL) ||
|
logs-show: limit to 3 lines and use dots if not showing full message
So far, we would show up to 128 bytes from a message, simply
cutting of the rest. With multiline messages, it is quite common
for a message to be longer than that, and this model doesn't really
work anymore.
A new limit is added: up to 3 lines will be shown, unless --full is
used (c.f. first line below). The limit for bytes is extended to 300
bytes. An ellipsis will always be used, if some form of truncation
occurs. If the tail of the message is cut off, either because of
length or line limit, dots will be shown at the end of the last
line. If this last line is short, the dots will be simply appended. If
the last line is too long for that, it will be ellipsized with dots at
the very end.
Note that the limits are in bytes, not characters, and we suck at
outputting unicode strings (c.f. last three lines below).
Aug 11 10:46:21 fedora python[67]: test message
line
line...
Aug 11 10:50:47 fedora python[76]: test message word word word word word word word word word word word wor...
Aug 11 10:55:11 fedora python[83]: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...
Aug 11 11:03:21 fedora python[90]: ąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:03:53 fedora python[97]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą...
Aug 11 11:25:45 fedora python[121]: aąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąąą�...
2013-08-11 16:56:09 +02:00
|
|
|
(((length < PRINT_CHAR_THRESHOLD) || flags & OUTPUT_FULL_WIDTH)
|
|
|
|
&& utf8_is_printable(data, length))) {
|
2013-06-27 01:48:24 +02:00
|
|
|
fprintf(f, " %s%.*s=", on, fieldlen, (const char*)data);
|
2013-04-24 04:40:26 +02:00
|
|
|
print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1);
|
2013-06-27 01:48:24 +02:00
|
|
|
fputs(off, f);
|
2013-04-24 04:40:26 +02:00
|
|
|
} else {
|
|
|
|
char bytes[FORMAT_BYTES_MAX];
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2013-06-27 01:48:24 +02:00
|
|
|
fprintf(f, " %s%.*s=[%s blob data]%s\n",
|
|
|
|
on,
|
2013-04-24 04:40:26 +02:00
|
|
|
(int) (c - (const char*) data),
|
|
|
|
(const char*) data,
|
2013-06-27 01:48:24 +02:00
|
|
|
format_bytes(bytes, sizeof(bytes), length - (c - (const char *) data) - 1),
|
|
|
|
off);
|
2013-04-24 04:40:26 +02:00
|
|
|
}
|
2012-01-03 21:08:28 +01:00
|
|
|
}
|
|
|
|
|
2013-06-10 03:50:56 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2012-11-15 23:03:31 +01:00
|
|
|
if (flags & OUTPUT_CATALOG)
|
|
|
|
print_catalog(f, j);
|
|
|
|
|
2012-01-03 21:08:28 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
static int output_export(
|
|
|
|
FILE *f,
|
|
|
|
sd_journal *j,
|
|
|
|
OutputMode mode,
|
|
|
|
unsigned n_columns,
|
2017-10-27 05:10:47 +02:00
|
|
|
OutputFlags flags,
|
|
|
|
Set *output_fields) {
|
2012-09-27 23:27:10 +02:00
|
|
|
|
2012-01-03 21:08:28 +01:00
|
|
|
sd_id128_t boot_id;
|
|
|
|
char sid[33];
|
|
|
|
int r;
|
|
|
|
usec_t realtime, monotonic;
|
2013-04-18 09:11:22 +02:00
|
|
|
_cleanup_free_ char *cursor = NULL;
|
2012-01-03 21:08:28 +01:00
|
|
|
const void *data;
|
|
|
|
size_t length;
|
|
|
|
|
|
|
|
assert(j);
|
|
|
|
|
2012-11-21 00:28:00 +01:00
|
|
|
sd_journal_set_data_threshold(j, 0);
|
|
|
|
|
2012-01-03 21:08:28 +01:00
|
|
|
r = sd_journal_get_realtime_usec(j, &realtime);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to get realtime timestamp: %m");
|
2012-01-03 21:08:28 +01:00
|
|
|
|
|
|
|
r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to get monotonic timestamp: %m");
|
2012-01-03 21:08:28 +01:00
|
|
|
|
|
|
|
r = sd_journal_get_cursor(j, &cursor);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to get cursor: %m");
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
fprintf(f,
|
|
|
|
"__CURSOR=%s\n"
|
2014-04-25 13:45:15 +02:00
|
|
|
"__REALTIME_TIMESTAMP="USEC_FMT"\n"
|
|
|
|
"__MONOTONIC_TIMESTAMP="USEC_FMT"\n"
|
2012-09-27 23:27:10 +02:00
|
|
|
"_BOOT_ID=%s\n",
|
|
|
|
cursor,
|
2014-04-25 13:45:15 +02:00
|
|
|
realtime,
|
|
|
|
monotonic,
|
2012-09-27 23:27:10 +02:00
|
|
|
sd_id128_to_string(boot_id, sid));
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2013-06-10 03:50:56 +02:00
|
|
|
JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
|
2017-10-27 05:10:47 +02:00
|
|
|
const char *c;
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2012-04-04 01:00:09 +02:00
|
|
|
/* We already printed the boot id, from the data in
|
|
|
|
* the header, hence let's suppress it here */
|
|
|
|
if (length >= 9 &&
|
2013-08-22 03:20:55 +02:00
|
|
|
startswith(data, "_BOOT_ID="))
|
2012-04-04 01:00:09 +02:00
|
|
|
continue;
|
|
|
|
|
2017-10-27 05:10:47 +02:00
|
|
|
c = memchr(data, '=', length);
|
|
|
|
if (!c) {
|
|
|
|
log_error("Invalid field.");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = field_set_test(output_fields, data, c - (const char *) data);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (!r)
|
|
|
|
continue;
|
|
|
|
|
2014-03-31 14:57:28 +02:00
|
|
|
if (utf8_is_printable_newline(data, length, false))
|
|
|
|
fwrite(data, length, 1, f);
|
|
|
|
else {
|
2012-01-03 21:08:28 +01:00
|
|
|
uint64_t le64;
|
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
fwrite(data, c - (const char*) data, 1, f);
|
|
|
|
fputc('\n', f);
|
2012-01-03 21:08:28 +01:00
|
|
|
le64 = htole64(length - (c - (const char*) data) - 1);
|
2012-09-27 23:27:10 +02:00
|
|
|
fwrite(&le64, sizeof(le64), 1, f);
|
|
|
|
fwrite(c + 1, length - (c - (const char*) data) - 1, 1, f);
|
2014-03-31 14:57:28 +02:00
|
|
|
}
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
fputc('\n', f);
|
2012-01-03 21:08:28 +01:00
|
|
|
}
|
|
|
|
|
2013-06-10 03:50:56 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
fputc('\n', f);
|
2012-01-03 21:08:28 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-18 22:31:27 +02:00
|
|
|
void json_escape(
|
2012-09-27 23:27:10 +02:00
|
|
|
FILE *f,
|
|
|
|
const char* p,
|
|
|
|
size_t l,
|
|
|
|
OutputFlags flags) {
|
|
|
|
|
|
|
|
assert(f);
|
|
|
|
assert(p);
|
|
|
|
|
2012-11-21 00:28:00 +01:00
|
|
|
if (!(flags & OUTPUT_SHOW_ALL) && l >= JSON_THRESHOLD)
|
2012-09-27 23:27:10 +02:00
|
|
|
fputs("null", f);
|
|
|
|
|
2016-07-01 02:41:19 +02:00
|
|
|
else if (!(flags & OUTPUT_SHOW_ALL) && !utf8_is_printable(p, l)) {
|
2012-01-03 21:08:28 +01:00
|
|
|
bool not_first = false;
|
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
fputs("[ ", f);
|
2012-01-03 21:08:28 +01:00
|
|
|
|
|
|
|
while (l > 0) {
|
|
|
|
if (not_first)
|
2012-09-27 23:27:10 +02:00
|
|
|
fprintf(f, ", %u", (uint8_t) *p);
|
2012-01-03 21:08:28 +01:00
|
|
|
else {
|
|
|
|
not_first = true;
|
2012-09-27 23:27:10 +02:00
|
|
|
fprintf(f, "%u", (uint8_t) *p);
|
2012-01-03 21:08:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
p++;
|
|
|
|
l--;
|
|
|
|
}
|
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
fputs(" ]", f);
|
2012-01-03 21:08:28 +01:00
|
|
|
} else {
|
2012-09-27 23:27:10 +02:00
|
|
|
fputc('\"', f);
|
2012-01-03 21:08:28 +01:00
|
|
|
|
|
|
|
while (l > 0) {
|
2017-10-04 16:01:32 +02:00
|
|
|
if (IN_SET(*p, '"', '\\')) {
|
2012-09-27 23:27:10 +02:00
|
|
|
fputc('\\', f);
|
|
|
|
fputc(*p, f);
|
2013-04-24 04:40:26 +02:00
|
|
|
} else if (*p == '\n')
|
|
|
|
fputs("\\n", f);
|
2015-08-18 16:26:11 +02:00
|
|
|
else if ((uint8_t) *p < ' ')
|
|
|
|
fprintf(f, "\\u%04x", (uint8_t) *p);
|
2012-09-27 23:27:10 +02:00
|
|
|
else
|
|
|
|
fputc(*p, f);
|
2012-01-03 21:08:28 +01:00
|
|
|
|
|
|
|
p++;
|
|
|
|
l--;
|
|
|
|
}
|
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
fputc('\"', f);
|
2012-01-03 21:08:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
static int output_json(
|
|
|
|
FILE *f,
|
|
|
|
sd_journal *j,
|
|
|
|
OutputMode mode,
|
|
|
|
unsigned n_columns,
|
2017-10-27 05:10:47 +02:00
|
|
|
OutputFlags flags,
|
|
|
|
Set *output_fields) {
|
2012-09-27 23:27:10 +02:00
|
|
|
|
2012-01-03 21:08:28 +01:00
|
|
|
uint64_t realtime, monotonic;
|
2013-04-18 09:11:22 +02:00
|
|
|
_cleanup_free_ char *cursor = NULL;
|
2012-01-03 21:08:28 +01:00
|
|
|
const void *data;
|
|
|
|
size_t length;
|
|
|
|
sd_id128_t boot_id;
|
2013-01-15 21:02:00 +01:00
|
|
|
char sid[33], *k;
|
2012-01-03 21:08:28 +01:00
|
|
|
int r;
|
2012-10-25 01:19:24 +02:00
|
|
|
Hashmap *h = NULL;
|
|
|
|
bool done, separator;
|
2012-01-03 21:08:28 +01:00
|
|
|
|
|
|
|
assert(j);
|
|
|
|
|
2012-11-21 00:28:00 +01:00
|
|
|
sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD);
|
|
|
|
|
2012-01-03 21:08:28 +01:00
|
|
|
r = sd_journal_get_realtime_usec(j, &realtime);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to get realtime timestamp: %m");
|
2012-01-03 21:08:28 +01:00
|
|
|
|
|
|
|
r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to get monotonic timestamp: %m");
|
2012-01-03 21:08:28 +01:00
|
|
|
|
|
|
|
r = sd_journal_get_cursor(j, &cursor);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to get cursor: %m");
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2012-09-06 00:25:32 +02:00
|
|
|
if (mode == OUTPUT_JSON_PRETTY)
|
2012-09-27 23:27:10 +02:00
|
|
|
fprintf(f,
|
|
|
|
"{\n"
|
|
|
|
"\t\"__CURSOR\" : \"%s\",\n"
|
2014-04-25 13:45:15 +02:00
|
|
|
"\t\"__REALTIME_TIMESTAMP\" : \""USEC_FMT"\",\n"
|
|
|
|
"\t\"__MONOTONIC_TIMESTAMP\" : \""USEC_FMT"\",\n"
|
2012-09-27 23:27:10 +02:00
|
|
|
"\t\"_BOOT_ID\" : \"%s\"",
|
|
|
|
cursor,
|
2014-04-25 13:45:15 +02:00
|
|
|
realtime,
|
|
|
|
monotonic,
|
2012-09-27 23:27:10 +02:00
|
|
|
sd_id128_to_string(boot_id, sid));
|
2012-10-11 02:37:10 +02:00
|
|
|
else {
|
|
|
|
if (mode == OUTPUT_JSON_SSE)
|
|
|
|
fputs("data: ", f);
|
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
fprintf(f,
|
|
|
|
"{ \"__CURSOR\" : \"%s\", "
|
2014-04-25 13:45:15 +02:00
|
|
|
"\"__REALTIME_TIMESTAMP\" : \""USEC_FMT"\", "
|
|
|
|
"\"__MONOTONIC_TIMESTAMP\" : \""USEC_FMT"\", "
|
2012-09-27 23:27:10 +02:00
|
|
|
"\"_BOOT_ID\" : \"%s\"",
|
|
|
|
cursor,
|
2014-04-25 13:45:15 +02:00
|
|
|
realtime,
|
|
|
|
monotonic,
|
2012-09-27 23:27:10 +02:00
|
|
|
sd_id128_to_string(boot_id, sid));
|
2012-10-11 02:37:10 +02:00
|
|
|
}
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2014-08-13 01:00:18 +02:00
|
|
|
h = hashmap_new(&string_hash_ops);
|
2012-10-25 01:19:24 +02:00
|
|
|
if (!h)
|
2015-05-19 00:25:45 +02:00
|
|
|
return log_oom();
|
2012-10-25 01:19:24 +02:00
|
|
|
|
|
|
|
/* First round, iterate through the entry and count how often each field appears */
|
2013-06-10 03:50:56 +02:00
|
|
|
JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
|
2012-10-25 01:19:24 +02:00
|
|
|
const char *eq;
|
|
|
|
char *n;
|
|
|
|
unsigned u;
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2012-04-04 01:00:09 +02:00
|
|
|
if (length >= 9 &&
|
|
|
|
memcmp(data, "_BOOT_ID=", 9) == 0)
|
|
|
|
continue;
|
|
|
|
|
2012-10-25 01:19:24 +02:00
|
|
|
eq = memchr(data, '=', length);
|
|
|
|
if (!eq)
|
|
|
|
continue;
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2012-10-25 01:19:24 +02:00
|
|
|
n = strndup(data, eq - (const char*) data);
|
|
|
|
if (!n) {
|
2015-05-19 00:25:45 +02:00
|
|
|
r = log_oom();
|
2012-10-25 01:19:24 +02:00
|
|
|
goto finish;
|
|
|
|
}
|
2012-09-06 00:25:32 +02:00
|
|
|
|
2012-10-25 01:19:24 +02:00
|
|
|
u = PTR_TO_UINT(hashmap_get(h, n));
|
|
|
|
if (u == 0) {
|
|
|
|
r = hashmap_put(h, n, UINT_TO_PTR(1));
|
|
|
|
if (r < 0) {
|
|
|
|
free(n);
|
2015-05-19 00:25:45 +02:00
|
|
|
log_oom();
|
2012-10-25 01:19:24 +02:00
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
r = hashmap_update(h, n, UINT_TO_PTR(u + 1));
|
|
|
|
free(n);
|
2015-05-19 00:25:45 +02:00
|
|
|
if (r < 0) {
|
|
|
|
log_oom();
|
2012-10-25 01:19:24 +02:00
|
|
|
goto finish;
|
2015-05-19 00:25:45 +02:00
|
|
|
}
|
2012-10-25 01:19:24 +02:00
|
|
|
}
|
2012-01-03 21:08:28 +01:00
|
|
|
}
|
|
|
|
|
2013-06-10 03:50:56 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2012-10-25 01:19:24 +02:00
|
|
|
separator = true;
|
|
|
|
do {
|
|
|
|
done = true;
|
|
|
|
|
|
|
|
SD_JOURNAL_FOREACH_DATA(j, data, length) {
|
|
|
|
const char *eq;
|
|
|
|
char *kk, *n;
|
|
|
|
size_t m;
|
|
|
|
unsigned u;
|
|
|
|
|
|
|
|
/* We already printed the boot id, from the data in
|
|
|
|
* the header, hence let's suppress it here */
|
|
|
|
if (length >= 9 &&
|
|
|
|
memcmp(data, "_BOOT_ID=", 9) == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
eq = memchr(data, '=', length);
|
|
|
|
if (!eq)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
m = eq - (const char*) data;
|
|
|
|
|
|
|
|
n = strndup(data, m);
|
|
|
|
if (!n) {
|
2015-05-19 00:25:45 +02:00
|
|
|
r = log_oom();
|
2012-10-25 01:19:24 +02:00
|
|
|
goto finish;
|
|
|
|
}
|
|
|
|
|
2017-10-27 05:10:47 +02:00
|
|
|
if (output_fields && !set_get(output_fields, n)) {
|
|
|
|
free(n);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (separator) {
|
|
|
|
if (mode == OUTPUT_JSON_PRETTY)
|
|
|
|
fputs(",\n\t", f);
|
|
|
|
else
|
|
|
|
fputs(", ", f);
|
|
|
|
}
|
|
|
|
|
2012-10-25 01:19:24 +02:00
|
|
|
u = PTR_TO_UINT(hashmap_get2(h, n, (void**) &kk));
|
|
|
|
if (u == 0) {
|
|
|
|
/* We already printed this, let's jump to the next */
|
|
|
|
free(n);
|
|
|
|
separator = false;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
} else if (u == 1) {
|
|
|
|
/* Field only appears once, output it directly */
|
|
|
|
|
|
|
|
json_escape(f, data, m, flags);
|
|
|
|
fputs(" : ", f);
|
|
|
|
|
|
|
|
json_escape(f, eq + 1, length - m - 1, flags);
|
|
|
|
|
|
|
|
hashmap_remove(h, n);
|
|
|
|
free(kk);
|
|
|
|
free(n);
|
|
|
|
|
|
|
|
separator = true;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/* Field appears multiple times, output it as array */
|
|
|
|
json_escape(f, data, m, flags);
|
|
|
|
fputs(" : [ ", f);
|
|
|
|
json_escape(f, eq + 1, length - m - 1, flags);
|
|
|
|
|
|
|
|
/* Iterate through the end of the list */
|
|
|
|
|
|
|
|
while (sd_journal_enumerate_data(j, &data, &length) > 0) {
|
|
|
|
if (length < m + 1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (memcmp(data, n, m) != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (((const char*) data)[m] != '=')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
fputs(", ", f);
|
|
|
|
json_escape(f, (const char*) data + m + 1, length - m - 1, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
fputs(" ]", f);
|
|
|
|
|
|
|
|
hashmap_remove(h, n);
|
|
|
|
free(kk);
|
|
|
|
free(n);
|
|
|
|
|
|
|
|
/* Iterate data fields form the beginning */
|
|
|
|
done = false;
|
|
|
|
separator = true;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} while (!done);
|
|
|
|
|
2012-09-06 00:25:32 +02:00
|
|
|
if (mode == OUTPUT_JSON_PRETTY)
|
2012-09-27 23:27:10 +02:00
|
|
|
fputs("\n}\n", f);
|
2012-10-11 02:37:10 +02:00
|
|
|
else if (mode == OUTPUT_JSON_SSE)
|
|
|
|
fputs("}\n\n", f);
|
2012-09-06 00:25:32 +02:00
|
|
|
else
|
2012-09-27 23:27:10 +02:00
|
|
|
fputs(" }\n", f);
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2012-10-25 01:19:24 +02:00
|
|
|
r = 0;
|
|
|
|
|
|
|
|
finish:
|
|
|
|
while ((k = hashmap_steal_first_key(h)))
|
|
|
|
free(k);
|
|
|
|
|
|
|
|
hashmap_free(h);
|
|
|
|
|
|
|
|
return r;
|
2012-01-03 21:08:28 +01:00
|
|
|
}
|
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
static int output_cat(
|
|
|
|
FILE *f,
|
|
|
|
sd_journal *j,
|
|
|
|
OutputMode mode,
|
|
|
|
unsigned n_columns,
|
2017-10-27 05:10:47 +02:00
|
|
|
OutputFlags flags,
|
|
|
|
Set *output_fields) {
|
2012-09-27 23:27:10 +02:00
|
|
|
|
2012-01-13 02:58:45 +01:00
|
|
|
const void *data;
|
|
|
|
size_t l;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(j);
|
2012-09-27 23:27:10 +02:00
|
|
|
assert(f);
|
2012-01-13 02:58:45 +01:00
|
|
|
|
2012-11-21 00:28:00 +01:00
|
|
|
sd_journal_set_data_threshold(j, 0);
|
|
|
|
|
2012-01-13 02:58:45 +01:00
|
|
|
r = sd_journal_get_data(j, "MESSAGE", &data, &l);
|
|
|
|
if (r < 0) {
|
2012-09-18 11:12:31 +02:00
|
|
|
/* An entry without MESSAGE=? */
|
|
|
|
if (r == -ENOENT)
|
|
|
|
return 0;
|
|
|
|
|
2014-11-28 19:13:53 +01:00
|
|
|
return log_error_errno(r, "Failed to get data: %m");
|
2012-01-13 02:58:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
assert(l >= 8);
|
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
fwrite((const char*) data + 8, 1, l - 8, f);
|
|
|
|
fputc('\n', f);
|
2012-01-13 02:58:45 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
static int (*output_funcs[_OUTPUT_MODE_MAX])(
|
|
|
|
FILE *f,
|
|
|
|
sd_journal*j,
|
|
|
|
OutputMode mode,
|
|
|
|
unsigned n_columns,
|
2017-10-27 05:10:47 +02:00
|
|
|
OutputFlags flags,
|
|
|
|
Set *output_fields) = {
|
2012-09-27 23:27:10 +02:00
|
|
|
|
2012-09-06 00:25:32 +02:00
|
|
|
[OUTPUT_SHORT] = output_short,
|
2013-07-18 10:21:45 +02:00
|
|
|
[OUTPUT_SHORT_ISO] = output_short,
|
2017-05-08 02:23:49 +02:00
|
|
|
[OUTPUT_SHORT_ISO_PRECISE] = output_short,
|
2013-08-04 15:04:20 +02:00
|
|
|
[OUTPUT_SHORT_PRECISE] = output_short,
|
|
|
|
[OUTPUT_SHORT_MONOTONIC] = output_short,
|
2016-04-20 19:46:37 +02:00
|
|
|
[OUTPUT_SHORT_UNIX] = output_short,
|
2016-08-04 01:45:07 +02:00
|
|
|
[OUTPUT_SHORT_FULL] = output_short,
|
2012-01-03 21:08:28 +01:00
|
|
|
[OUTPUT_VERBOSE] = output_verbose,
|
|
|
|
[OUTPUT_EXPORT] = output_export,
|
2012-01-13 02:58:45 +01:00
|
|
|
[OUTPUT_JSON] = output_json,
|
2012-09-06 00:25:32 +02:00
|
|
|
[OUTPUT_JSON_PRETTY] = output_json,
|
2012-10-11 02:37:10 +02:00
|
|
|
[OUTPUT_JSON_SSE] = output_json,
|
2012-01-13 02:58:45 +01:00
|
|
|
[OUTPUT_CAT] = output_cat
|
2012-01-03 21:08:28 +01:00
|
|
|
};
|
|
|
|
|
2012-09-27 23:27:10 +02:00
|
|
|
int output_journal(
|
|
|
|
FILE *f,
|
|
|
|
sd_journal *j,
|
|
|
|
OutputMode mode,
|
|
|
|
unsigned n_columns,
|
2013-08-04 01:38:13 +02:00
|
|
|
OutputFlags flags,
|
2017-10-27 05:10:47 +02:00
|
|
|
char **output_fields,
|
2013-08-04 01:38:13 +02:00
|
|
|
bool *ellipsized) {
|
2012-09-27 23:27:10 +02:00
|
|
|
|
2012-09-04 03:37:46 +02:00
|
|
|
int ret;
|
2017-10-27 05:10:47 +02:00
|
|
|
_cleanup_set_free_free_ Set *fields = NULL;
|
2012-01-04 18:33:36 +01:00
|
|
|
assert(mode >= 0);
|
2012-01-03 21:08:28 +01:00
|
|
|
assert(mode < _OUTPUT_MODE_MAX);
|
|
|
|
|
2012-03-15 20:49:25 +01:00
|
|
|
if (n_columns <= 0)
|
|
|
|
n_columns = columns();
|
|
|
|
|
2017-10-27 05:10:47 +02:00
|
|
|
if (output_fields) {
|
|
|
|
fields = set_new(&string_hash_ops);
|
|
|
|
if (!fields)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
ret = set_put_strdupv(fields, output_fields);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = output_funcs[mode](f, j, mode, n_columns, flags, fields);
|
2013-08-04 01:38:13 +02:00
|
|
|
|
|
|
|
if (ellipsized && ret > 0)
|
|
|
|
*ellipsized = true;
|
|
|
|
|
2012-09-04 03:37:46 +02:00
|
|
|
return ret;
|
2012-01-03 21:08:28 +01:00
|
|
|
}
|
|
|
|
|
2014-03-13 03:58:03 +01:00
|
|
|
static int maybe_print_begin_newline(FILE *f, OutputFlags *flags) {
|
|
|
|
assert(f);
|
|
|
|
assert(flags);
|
|
|
|
|
|
|
|
if (!(*flags & OUTPUT_BEGIN_NEWLINE))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Print a beginning new line if that's request, but only once
|
|
|
|
* on the first line we print. */
|
|
|
|
|
|
|
|
fputc('\n', f);
|
|
|
|
*flags &= ~OUTPUT_BEGIN_NEWLINE;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-17 18:55:07 +01:00
|
|
|
static int show_journal(FILE *f,
|
|
|
|
sd_journal *j,
|
|
|
|
OutputMode mode,
|
|
|
|
unsigned n_columns,
|
|
|
|
usec_t not_before,
|
|
|
|
unsigned how_many,
|
2013-08-04 01:38:13 +02:00
|
|
|
OutputFlags flags,
|
|
|
|
bool *ellipsized) {
|
2012-01-03 21:08:28 +01:00
|
|
|
|
|
|
|
int r;
|
2012-01-04 18:33:36 +01:00
|
|
|
unsigned line = 0;
|
|
|
|
bool need_seek = false;
|
2012-07-17 07:35:07 +02:00
|
|
|
int warn_cutoff = flags & OUTPUT_WARN_CUTOFF;
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2013-01-17 18:55:07 +01:00
|
|
|
assert(j);
|
2012-01-04 18:33:36 +01:00
|
|
|
assert(mode >= 0);
|
|
|
|
assert(mode < _OUTPUT_MODE_MAX);
|
2012-08-25 00:55:22 +02:00
|
|
|
|
|
|
|
/* Seek to end */
|
2012-01-03 21:08:28 +01:00
|
|
|
r = sd_journal_seek_tail(j);
|
|
|
|
if (r < 0)
|
2015-05-19 00:25:45 +02:00
|
|
|
return log_error_errno(r, "Failed to seek to tail: %m");
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2012-01-04 18:33:36 +01:00
|
|
|
r = sd_journal_previous_skip(j, how_many);
|
|
|
|
if (r < 0)
|
2015-05-19 00:25:45 +02:00
|
|
|
return log_error_errno(r, "Failed to skip previous: %m");
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2012-01-04 18:33:36 +01:00
|
|
|
for (;;) {
|
|
|
|
for (;;) {
|
|
|
|
usec_t usec;
|
|
|
|
|
|
|
|
if (need_seek) {
|
|
|
|
r = sd_journal_next(j);
|
|
|
|
if (r < 0)
|
2015-05-19 00:25:45 +02:00
|
|
|
return log_error_errno(r, "Failed to iterate through journal: %m");
|
2012-01-04 18:33:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (r == 0)
|
|
|
|
break;
|
2012-01-03 21:08:28 +01:00
|
|
|
|
2012-01-04 18:33:36 +01:00
|
|
|
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)
|
2015-05-19 00:25:45 +02:00
|
|
|
return log_error_errno(r, "Failed to get journal time: %m");
|
2012-01-04 18:33:36 +01:00
|
|
|
|
|
|
|
if (usec < not_before)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-02-23 05:32:04 +01:00
|
|
|
line++;
|
2014-03-13 03:58:03 +01:00
|
|
|
maybe_print_begin_newline(f, &flags);
|
2012-01-04 18:33:36 +01:00
|
|
|
|
2017-10-27 05:10:47 +02:00
|
|
|
r = output_journal(f, j, mode, n_columns, flags, NULL, ellipsized);
|
2012-01-04 18:33:36 +01:00
|
|
|
if (r < 0)
|
2015-05-19 00:25:45 +02:00
|
|
|
return r;
|
2012-01-04 18:33:36 +01:00
|
|
|
}
|
|
|
|
|
2012-06-09 10:32:38 +02:00
|
|
|
if (warn_cutoff && line < how_many && not_before > 0) {
|
|
|
|
sd_id128_t boot_id;
|
2015-03-27 12:02:49 +01:00
|
|
|
usec_t cutoff = 0;
|
2012-06-09 10:32:38 +02:00
|
|
|
|
|
|
|
/* Check whether the cutoff line is too early */
|
|
|
|
|
|
|
|
r = sd_id128_get_boot(&boot_id);
|
|
|
|
if (r < 0)
|
2015-05-19 00:25:45 +02:00
|
|
|
return log_error_errno(r, "Failed to get boot id: %m");
|
2012-06-09 10:32:38 +02:00
|
|
|
|
|
|
|
r = sd_journal_get_cutoff_monotonic_usec(j, boot_id, &cutoff, NULL);
|
|
|
|
if (r < 0)
|
2015-05-19 00:25:45 +02:00
|
|
|
return log_error_errno(r, "Failed to get journal cutoff time: %m");
|
2012-06-09 10:32:38 +02:00
|
|
|
|
2014-03-13 03:58:03 +01:00
|
|
|
if (r > 0 && not_before < cutoff) {
|
|
|
|
maybe_print_begin_newline(f, &flags);
|
2012-09-27 23:27:10 +02:00
|
|
|
fprintf(f, "Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.\n");
|
2014-03-13 03:58:03 +01:00
|
|
|
}
|
2012-06-09 10:32:38 +02:00
|
|
|
|
|
|
|
warn_cutoff = false;
|
|
|
|
}
|
|
|
|
|
2012-07-17 07:35:07 +02:00
|
|
|
if (!(flags & OUTPUT_FOLLOW))
|
2012-01-03 21:08:28 +01:00
|
|
|
break;
|
|
|
|
|
2014-07-29 12:23:31 +02:00
|
|
|
r = sd_journal_wait(j, USEC_INFINITY);
|
2012-01-04 18:33:36 +01:00
|
|
|
if (r < 0)
|
2015-05-19 00:25:45 +02:00
|
|
|
return log_error_errno(r, "Failed to wait for journal: %m");
|
2012-01-04 18:33:36 +01:00
|
|
|
|
2012-01-03 21:08:28 +01:00
|
|
|
}
|
|
|
|
|
2015-05-19 00:25:45 +02:00
|
|
|
return 0;
|
2013-01-17 18:55:07 +01:00
|
|
|
}
|
|
|
|
|
2013-03-14 00:30:05 +01:00
|
|
|
int add_matches_for_unit(sd_journal *j, const char *unit) {
|
2016-04-20 15:22:46 +02:00
|
|
|
const char *m1, *m2, *m3, *m4;
|
2013-01-17 18:55:07 +01:00
|
|
|
int r;
|
|
|
|
|
2013-03-14 00:30:05 +01:00
|
|
|
assert(j);
|
2013-01-17 18:55:07 +01:00
|
|
|
assert(unit);
|
|
|
|
|
2015-02-03 02:05:59 +01:00
|
|
|
m1 = strjoina("_SYSTEMD_UNIT=", unit);
|
|
|
|
m2 = strjoina("COREDUMP_UNIT=", unit);
|
|
|
|
m3 = strjoina("UNIT=", unit);
|
|
|
|
m4 = strjoina("OBJECT_SYSTEMD_UNIT=", unit);
|
2013-01-17 18:55:07 +01:00
|
|
|
|
2013-03-14 00:30:05 +01:00
|
|
|
(void)(
|
|
|
|
/* Look for messages from the service itself */
|
|
|
|
(r = sd_journal_add_match(j, m1, 0)) ||
|
|
|
|
|
|
|
|
/* Look for coredumps of the service */
|
|
|
|
(r = sd_journal_add_disjunction(j)) ||
|
2013-06-21 03:48:26 +02:00
|
|
|
(r = sd_journal_add_match(j, "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1", 0)) ||
|
|
|
|
(r = sd_journal_add_match(j, "_UID=0", 0)) ||
|
2013-03-14 00:30:05 +01:00
|
|
|
(r = sd_journal_add_match(j, m2, 0)) ||
|
|
|
|
|
|
|
|
/* Look for messages from PID 1 about this service */
|
|
|
|
(r = sd_journal_add_disjunction(j)) ||
|
|
|
|
(r = sd_journal_add_match(j, "_PID=1", 0)) ||
|
2013-06-21 04:25:49 +02:00
|
|
|
(r = sd_journal_add_match(j, m3, 0)) ||
|
|
|
|
|
|
|
|
/* Look for messages from authorized daemons about this service */
|
|
|
|
(r = sd_journal_add_disjunction(j)) ||
|
|
|
|
(r = sd_journal_add_match(j, "_UID=0", 0)) ||
|
|
|
|
(r = sd_journal_add_match(j, m4, 0))
|
2013-03-14 00:30:05 +01:00
|
|
|
);
|
2013-06-21 04:25:49 +02:00
|
|
|
|
2013-09-30 23:58:44 +02:00
|
|
|
if (r == 0 && endswith(unit, ".slice")) {
|
2016-04-20 15:22:46 +02:00
|
|
|
const char *m5;
|
|
|
|
|
|
|
|
m5 = strjoina("_SYSTEMD_SLICE=", unit);
|
2013-09-30 23:58:44 +02:00
|
|
|
|
|
|
|
/* Show all messages belonging to a slice */
|
|
|
|
(void)(
|
|
|
|
(r = sd_journal_add_disjunction(j)) ||
|
|
|
|
(r = sd_journal_add_match(j, m5, 0))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2013-03-14 00:30:05 +01:00
|
|
|
return r;
|
|
|
|
}
|
2013-01-17 18:55:07 +01:00
|
|
|
|
2013-03-14 00:30:05 +01:00
|
|
|
int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {
|
|
|
|
int r;
|
2013-06-21 04:25:49 +02:00
|
|
|
char *m1, *m2, *m3, *m4;
|
|
|
|
char muid[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t)];
|
2013-01-17 18:55:07 +01:00
|
|
|
|
2013-03-14 00:30:05 +01:00
|
|
|
assert(j);
|
|
|
|
assert(unit);
|
2013-01-17 18:55:07 +01:00
|
|
|
|
2015-02-03 02:05:59 +01:00
|
|
|
m1 = strjoina("_SYSTEMD_USER_UNIT=", unit);
|
|
|
|
m2 = strjoina("USER_UNIT=", unit);
|
|
|
|
m3 = strjoina("COREDUMP_USER_UNIT=", unit);
|
|
|
|
m4 = strjoina("OBJECT_SYSTEMD_USER_UNIT=", unit);
|
2014-04-25 13:45:15 +02:00
|
|
|
sprintf(muid, "_UID="UID_FMT, uid);
|
2013-01-17 18:55:07 +01:00
|
|
|
|
2013-03-14 00:30:05 +01:00
|
|
|
(void) (
|
|
|
|
/* Look for messages from the user service itself */
|
|
|
|
(r = sd_journal_add_match(j, m1, 0)) ||
|
2013-06-21 04:25:49 +02:00
|
|
|
(r = sd_journal_add_match(j, muid, 0)) ||
|
2013-03-14 00:30:05 +01:00
|
|
|
|
|
|
|
/* Look for messages from systemd about this service */
|
|
|
|
(r = sd_journal_add_disjunction(j)) ||
|
|
|
|
(r = sd_journal_add_match(j, m2, 0)) ||
|
2013-06-21 04:25:49 +02:00
|
|
|
(r = sd_journal_add_match(j, muid, 0)) ||
|
2013-03-14 00:30:05 +01:00
|
|
|
|
|
|
|
/* Look for coredumps of the service */
|
|
|
|
(r = sd_journal_add_disjunction(j)) ||
|
|
|
|
(r = sd_journal_add_match(j, m3, 0)) ||
|
2013-06-21 04:25:49 +02:00
|
|
|
(r = sd_journal_add_match(j, muid, 0)) ||
|
|
|
|
(r = sd_journal_add_match(j, "_UID=0", 0)) ||
|
|
|
|
|
|
|
|
/* Look for messages from authorized daemons about this service */
|
|
|
|
(r = sd_journal_add_disjunction(j)) ||
|
2013-06-21 03:48:26 +02:00
|
|
|
(r = sd_journal_add_match(j, m4, 0)) ||
|
2013-06-21 04:25:49 +02:00
|
|
|
(r = sd_journal_add_match(j, muid, 0)) ||
|
2013-06-21 03:48:26 +02:00
|
|
|
(r = sd_journal_add_match(j, "_UID=0", 0))
|
2013-03-14 00:30:05 +01:00
|
|
|
);
|
2013-09-30 23:58:44 +02:00
|
|
|
|
|
|
|
if (r == 0 && endswith(unit, ".slice")) {
|
2016-04-20 15:22:46 +02:00
|
|
|
const char *m5;
|
|
|
|
|
|
|
|
m5 = strjoina("_SYSTEMD_SLICE=", unit);
|
2013-09-30 23:58:44 +02:00
|
|
|
|
|
|
|
/* Show all messages belonging to a slice */
|
|
|
|
(void)(
|
|
|
|
(r = sd_journal_add_disjunction(j)) ||
|
|
|
|
(r = sd_journal_add_match(j, m5, 0)) ||
|
|
|
|
(r = sd_journal_add_match(j, muid, 0))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2013-01-17 18:55:07 +01:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2013-12-11 22:04:03 +01:00
|
|
|
static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) {
|
2014-03-24 03:22:44 +01:00
|
|
|
_cleanup_close_pair_ int pair[2] = { -1, -1 };
|
2013-12-17 01:03:09 +01:00
|
|
|
_cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
|
2013-12-11 22:04:03 +01:00
|
|
|
pid_t pid, child;
|
|
|
|
siginfo_t si;
|
|
|
|
char buf[37];
|
|
|
|
ssize_t k;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(machine);
|
|
|
|
assert(boot_id);
|
|
|
|
|
2014-10-22 23:20:59 +02:00
|
|
|
if (!machine_name_is_valid(machine))
|
2013-12-11 22:04:03 +01:00
|
|
|
return -EINVAL;
|
|
|
|
|
2013-12-14 05:06:40 +01:00
|
|
|
r = container_get_leader(machine, &pid);
|
2013-12-11 22:04:03 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2013-12-14 05:06:40 +01:00
|
|
|
|
namespace helpers: Allow entering a UID namespace
To be able to use `systemd-run` or `machinectl login` on a container
that is in a private user namespace, the sub-process must have entered
the user namespace before connecting to the container's D-Bus, otherwise
the UID and GID in the peer credentials are garbage.
So we extend namespace_open and namespace_enter to support UID namespaces,
and we enter the UID namespace in bus_container_connect_{socket,kernel}.
namespace_open will degrade to a no-op if user namespaces are not enabled
in the kernel.
Special handling is required for the setns call in namespace_enter with
a user namespace, since transitioning to your own namespace is forbidden,
as it would result in re-entering your user namespace as root.
Arguably it may be valid to check this at the call site, rather than
inside namespace_enter, but it is less code to do it inside, and if the
intention of calling namespace_enter is to *be* in the target namespace,
rather than to transition to the target namespace, it is a reasonable
approach.
The check for whether the user namespace is the same must happen before
entering namespaces, as we may not be able to access /proc during the
intermediate transition stage.
We can't instead attempt to enter the user namespace and then ignore
the failure from it being the same namespace, since the error code is
not distinct, and we can't compare namespaces while mid-transition.
2015-08-17 10:52:13 +02:00
|
|
|
r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, NULL, &rootfd);
|
2013-12-11 22:04:03 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2013-12-14 05:06:40 +01:00
|
|
|
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
|
2013-12-11 22:04:03 +01:00
|
|
|
return -errno;
|
|
|
|
|
|
|
|
child = fork();
|
|
|
|
if (child < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
if (child == 0) {
|
|
|
|
int fd;
|
|
|
|
|
2014-03-18 19:22:43 +01:00
|
|
|
pair[0] = safe_close(pair[0]);
|
2013-12-11 22:04:03 +01:00
|
|
|
|
namespace helpers: Allow entering a UID namespace
To be able to use `systemd-run` or `machinectl login` on a container
that is in a private user namespace, the sub-process must have entered
the user namespace before connecting to the container's D-Bus, otherwise
the UID and GID in the peer credentials are garbage.
So we extend namespace_open and namespace_enter to support UID namespaces,
and we enter the UID namespace in bus_container_connect_{socket,kernel}.
namespace_open will degrade to a no-op if user namespaces are not enabled
in the kernel.
Special handling is required for the setns call in namespace_enter with
a user namespace, since transitioning to your own namespace is forbidden,
as it would result in re-entering your user namespace as root.
Arguably it may be valid to check this at the call site, rather than
inside namespace_enter, but it is less code to do it inside, and if the
intention of calling namespace_enter is to *be* in the target namespace,
rather than to transition to the target namespace, it is a reasonable
approach.
The check for whether the user namespace is the same must happen before
entering namespaces, as we may not be able to access /proc during the
intermediate transition stage.
We can't instead attempt to enter the user namespace and then ignore
the failure from it being the same namespace, since the error code is
not distinct, and we can't compare namespaces while mid-transition.
2015-08-17 10:52:13 +02:00
|
|
|
r = namespace_enter(pidnsfd, mntnsfd, -1, -1, rootfd);
|
2013-12-11 22:04:03 +01:00
|
|
|
if (r < 0)
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
|
|
|
fd = open("/proc/sys/kernel/random/boot_id", O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
|
|
|
if (fd < 0)
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
2015-03-10 02:23:53 +01:00
|
|
|
r = loop_read_exact(fd, buf, 36, false);
|
2014-03-18 19:22:43 +01:00
|
|
|
safe_close(fd);
|
2015-03-10 05:18:26 +01:00
|
|
|
if (r < 0)
|
2013-12-11 22:04:03 +01:00
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
2013-12-14 05:06:40 +01:00
|
|
|
k = send(pair[1], buf, 36, MSG_NOSIGNAL);
|
2013-12-11 22:04:03 +01:00
|
|
|
if (k != 36)
|
|
|
|
_exit(EXIT_FAILURE);
|
|
|
|
|
|
|
|
_exit(EXIT_SUCCESS);
|
|
|
|
}
|
|
|
|
|
2014-03-18 19:22:43 +01:00
|
|
|
pair[1] = safe_close(pair[1]);
|
2013-12-11 22:04:03 +01:00
|
|
|
|
|
|
|
r = wait_for_terminate(child, &si);
|
|
|
|
if (r < 0 || si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
|
|
|
|
return r < 0 ? r : -EIO;
|
|
|
|
|
2013-12-23 19:10:11 +01:00
|
|
|
k = recv(pair[0], buf, 36, 0);
|
|
|
|
if (k != 36)
|
|
|
|
return -EIO;
|
|
|
|
|
2013-12-11 22:04:03 +01:00
|
|
|
buf[36] = 0;
|
|
|
|
r = sd_id128_from_string(buf, boot_id);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int add_match_this_boot(sd_journal *j, const char *machine) {
|
2013-06-04 00:28:12 +02:00
|
|
|
char match[9+32+1] = "_BOOT_ID=";
|
|
|
|
sd_id128_t boot_id;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(j);
|
|
|
|
|
2013-12-11 22:04:03 +01:00
|
|
|
if (machine) {
|
|
|
|
r = get_boot_id_for_machine(machine, &boot_id);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to get boot id of container %s: %m", machine);
|
2013-12-11 22:04:03 +01:00
|
|
|
} else {
|
|
|
|
r = sd_id128_get_boot(&boot_id);
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to get boot id: %m");
|
2013-06-04 00:28:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
sd_id128_to_string(boot_id, match + 9);
|
|
|
|
r = sd_journal_add_match(j, match, strlen(match));
|
2014-11-28 18:50:43 +01:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to add match: %m");
|
2013-06-04 00:28:12 +02:00
|
|
|
|
|
|
|
r = sd_journal_add_conjunction(j);
|
|
|
|
if (r < 0)
|
2015-05-19 00:25:45 +02:00
|
|
|
return log_error_errno(r, "Failed to add conjunction: %m");
|
2013-06-04 00:28:12 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-03-14 00:30:05 +01:00
|
|
|
int show_journal_by_unit(
|
2013-01-17 18:55:07 +01:00
|
|
|
FILE *f,
|
|
|
|
const char *unit,
|
|
|
|
OutputMode mode,
|
|
|
|
unsigned n_columns,
|
|
|
|
usec_t not_before,
|
|
|
|
unsigned how_many,
|
|
|
|
uid_t uid,
|
2013-03-14 00:30:05 +01:00
|
|
|
OutputFlags flags,
|
2015-01-08 14:38:52 +01:00
|
|
|
int journal_open_flags,
|
|
|
|
bool system_unit,
|
2013-08-04 01:38:13 +02:00
|
|
|
bool *ellipsized) {
|
2013-01-17 18:55:07 +01:00
|
|
|
|
tree-wide: expose "p"-suffix unref calls in public APIs to make gcc cleanup easy
GLIB has recently started to officially support the gcc cleanup
attribute in its public API, hence let's do the same for our APIs.
With this patch we'll define an xyz_unrefp() call for each public
xyz_unref() call, to make it easy to use inside a
__attribute__((cleanup())) expression. Then, all code is ported over to
make use of this.
The new calls are also documented in the man pages, with examples how to
use them (well, I only added docs where the _unref() call itself already
had docs, and the examples, only cover sd_bus_unrefp() and
sd_event_unrefp()).
This also renames sd_lldp_free() to sd_lldp_unref(), since that's how we
tend to call our destructors these days.
Note that this defines no public macro that wraps gcc's attribute and
makes it easier to use. While I think it's our duty in the library to
make our stuff easy to use, I figure it's not our duty to make gcc's own
features easy to use on its own. Most likely, client code which wants to
make use of this should define its own:
#define _cleanup_(function) __attribute__((cleanup(function)))
Or similar, to make the gcc feature easier to use.
Making this logic public has the benefit that we can remove three header
files whose only purpose was to define these functions internally.
See #2008.
2015-11-27 19:13:45 +01:00
|
|
|
_cleanup_(sd_journal_closep) sd_journal *j = NULL;
|
2013-01-17 18:55:07 +01:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(mode >= 0);
|
|
|
|
assert(mode < _OUTPUT_MODE_MAX);
|
|
|
|
assert(unit);
|
|
|
|
|
|
|
|
if (how_many <= 0)
|
|
|
|
return 0;
|
|
|
|
|
2015-01-08 14:38:52 +01:00
|
|
|
r = sd_journal_open(&j, journal_open_flags);
|
2013-02-04 15:13:23 +01:00
|
|
|
if (r < 0)
|
2015-05-19 00:25:45 +02:00
|
|
|
return log_error_errno(r, "Failed to open journal: %m");
|
2013-02-04 15:13:23 +01:00
|
|
|
|
2013-12-11 22:04:03 +01:00
|
|
|
r = add_match_this_boot(j, NULL);
|
2013-06-04 00:28:12 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2015-01-08 14:38:52 +01:00
|
|
|
if (system_unit)
|
2013-03-14 00:30:05 +01:00
|
|
|
r = add_matches_for_unit(j, unit);
|
|
|
|
else
|
|
|
|
r = add_matches_for_user_unit(j, unit, uid);
|
2013-01-17 18:55:07 +01:00
|
|
|
if (r < 0)
|
2015-05-19 00:25:45 +02:00
|
|
|
return log_error_errno(r, "Failed to add unit matches: %m");
|
2013-01-17 18:55:07 +01:00
|
|
|
|
2015-01-06 06:29:40 +01:00
|
|
|
if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
|
2013-07-16 20:45:28 +02:00
|
|
|
_cleanup_free_ char *filter;
|
|
|
|
|
|
|
|
filter = journal_make_match_string(j);
|
2015-05-19 00:25:45 +02:00
|
|
|
if (!filter)
|
|
|
|
return log_oom();
|
|
|
|
|
2013-07-16 20:45:28 +02:00
|
|
|
log_debug("Journal filter: %s", filter);
|
|
|
|
}
|
2013-06-04 00:28:12 +02:00
|
|
|
|
2013-08-04 01:38:13 +02:00
|
|
|
return show_journal(f, j, mode, n_columns, not_before, how_many, flags, ellipsized);
|
2012-01-03 21:08:28 +01:00
|
|
|
}
|