#include "pager.h"
#include "parse-util.h"
#include "path-util.h"
+#include "pcre2-dlopen.h"
#include "pretty-print.h"
#include "rlimit-util.h"
#include "set.h"
#include "sigbus.h"
#include "string-table.h"
#include "strv.h"
+#include "stdio-util.h"
#include "syslog-util.h"
#include "terminal-util.h"
#include "tmpfile-util.h"
static char **arg_file = NULL;
static bool arg_file_stdin = false;
static int arg_priorities = 0xFF;
+static Set *arg_facilities = NULL;
static char *arg_verify_key = NULL;
#if HAVE_GCRYPT
static usec_t arg_interval = DEFAULT_FSS_INTERVAL_USEC;
} BootId;
#if HAVE_PCRE2
-DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_match_data*, pcre2_match_data_free);
-DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_code*, pcre2_code_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_match_data*, sym_pcre2_match_data_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_code*, sym_pcre2_code_free);
static int pattern_compile(const char *pattern, unsigned flags, pcre2_code **out) {
int errorcode, r;
PCRE2_SIZE erroroffset;
pcre2_code *p;
- p = pcre2_compile((PCRE2_SPTR8) pattern,
- PCRE2_ZERO_TERMINATED, flags, &errorcode, &erroroffset, NULL);
+ p = sym_pcre2_compile((PCRE2_SPTR8) pattern,
+ PCRE2_ZERO_TERMINATED, flags, &errorcode, &erroroffset, NULL);
if (!p) {
unsigned char buf[LINE_MAX];
- r = pcre2_get_error_message(errorcode, buf, sizeof buf);
+ r = sym_pcre2_get_error_message(errorcode, buf, sizeof buf);
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Bad pattern \"%s\": %s", pattern,
*out = p;
return 0;
}
-
#endif
static int add_matches_for_device(sd_journal *j, const char *devpath) {
return 1;
}
+static int help_facilities(void) {
+ if (!arg_quiet)
+ puts("Available facilities:");
+
+ for (int i = 0; i < LOG_NFACILITIES; i++) {
+ _cleanup_free_ char *t = NULL;
+
+ if (log_facility_unshifted_to_string_alloc(i, &t))
+ return log_oom();
+ puts(t);
+ }
+
+ return 0;
+}
+
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
" --user-unit=UNIT Show logs from the specified user unit\n"
" -t --identifier=STRING Show entries with the specified syslog identifier\n"
" -p --priority=RANGE Show entries with the specified priority\n"
+ " --facility=FACILITY... Show entries with the specified facilities\n"
" -g --grep=PATTERN Show entries with MESSAGE matching PATTERN\n"
- " --case-sensitive[=BOOL] Force case sensitive or insenstive matching\n"
+ " --case-sensitive[=BOOL] Force case sensitive or insensitive matching\n"
" -e --pager-end Immediately jump to the end in the pager\n"
" -f --follow Follow the journal\n"
" -n --lines[=INTEGER] Number of journal entries to show\n"
ARG_SYSTEM,
ARG_ROOT,
ARG_HEADER,
+ ARG_FACILITY,
ARG_SETUP_KEYS,
ARG_FILE,
ARG_INTERVAL,
{ "header", no_argument, NULL, ARG_HEADER },
{ "identifier", required_argument, NULL, 't' },
{ "priority", required_argument, NULL, 'p' },
+ { "facility", required_argument, NULL, ARG_FACILITY },
{ "grep", required_argument, NULL, 'g' },
{ "case-sensitive", optional_argument, NULL, ARG_CASE_SENSITIVE },
{ "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
* STDIN. To avoid confusion we hence don't document this feature. */
arg_file_stdin = true;
else {
- r = glob_extend(&arg_file, optarg);
+ r = glob_extend(&arg_file, optarg, GLOB_NOCHECK);
if (r < 0)
return log_error_errno(r, "Failed to add paths: %m");
}
break;
}
+ case ARG_FACILITY: {
+ const char *p;
+
+ for (p = optarg;;) {
+ _cleanup_free_ char *fac = NULL;
+ int num;
+
+ r = extract_first_word(&p, &fac, ",", 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse facilities: %s", optarg);
+ if (r == 0)
+ break;
+
+ if (streq(fac, "help")) {
+ help_facilities();
+ return 0;
+ }
+
+ num = log_facility_unshifted_from_string(fac);
+ if (num < 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Bad --facility= argument \"%s\".", fac);
+
+ if (set_ensure_put(&arg_facilities, NULL, INT_TO_PTR(num)) < 0)
+ return log_oom();
+ }
+
+ break;
+ }
+
#if HAVE_PCRE2
case 'g':
arg_pattern = optarg;
if (arg_pattern) {
unsigned flags;
+ r = dlopen_pcre2();
+ if (r < 0)
+ return r;
+
if (arg_case_sensitive >= 0)
flags = !arg_case_sensitive * PCRE2_CASELESS;
else {
- _cleanup_(pcre2_match_data_freep) pcre2_match_data *md = NULL;
+ _cleanup_(sym_pcre2_match_data_freep) pcre2_match_data *md = NULL;
bool has_case;
- _cleanup_(pcre2_code_freep) pcre2_code *cs = NULL;
+ _cleanup_(sym_pcre2_code_freep) pcre2_code *cs = NULL;
- md = pcre2_match_data_create(1, NULL);
+ md = sym_pcre2_match_data_create(1, NULL);
if (!md)
return log_oom();
if (r < 0)
return r;
- r = pcre2_match(cs, (PCRE2_SPTR8) arg_pattern, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL);
+ r = sym_pcre2_match(cs, (PCRE2_SPTR8) arg_pattern, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL);
has_case = r >= 0;
flags = !has_case * PCRE2_CASELESS;
return 0;
}
+static int add_facilities(sd_journal *j) {
+ void *p;
+ Iterator it;
+ int r;
+
+ SET_FOREACH(p, arg_facilities, it) {
+ char match[STRLEN("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)];
+
+ xsprintf(match, "SYSLOG_FACILITY=%d", PTR_TO_INT(p));
+
+ r = sd_journal_add_match(j, match, strlen(match));
+ if (r < 0)
+ return log_error_errno(r, "Failed to add match: %m");
+ }
+
+ return 0;
+}
+
static int add_syslog_identifier(sd_journal *j) {
int r;
char **i;
int fd = -1, r;
sd_id128_t machine, boot;
char *p = NULL, *k = NULL;
- struct FSSHeader h;
uint64_t n;
struct stat st;
if (r < 0)
log_warning_errno(r, "Failed to set file attributes: %m");
- zero(h);
+ struct FSSHeader h = {
+ .machine_id = machine,
+ .boot_id = boot,
+ .header_size = htole64(sizeof(h)),
+ .start_usec = htole64(n * arg_interval),
+ .interval_usec = htole64(arg_interval),
+ .fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR),
+ .fsprg_state_size = htole64(state_size),
+ };
+
memcpy(h.signature, "KSHHRHLP", 8);
- h.machine_id = machine;
- h.boot_id = boot;
- h.header_size = htole64(sizeof(h));
- h.start_usec = htole64(n * arg_interval);
- h.interval_usec = htole64(arg_interval);
- h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR);
- h.fsprg_state_size = htole64(state_size);
r = loop_write(fd, &h, sizeof(h), false);
if (r < 0) {
return log_error_errno(errno, "Couldn't wait for journal event: %m");
}
- if (pollfds[1].revents & (POLLHUP|POLLERR)) /* STDOUT has been closed? */
+ if (pollfds[1].revents & (POLLHUP|POLLERR|POLLNVAL)) /* STDOUT has been closed? */
return log_debug_errno(SYNTHETIC_ERRNO(ECANCELED),
"Standard output has been closed.");
+ if (pollfds[0].revents & POLLNVAL)
+ return log_debug_errno(SYNTHETIC_ERRNO(EBADF), "Change fd closed?");
+
r = sd_journal_process(j);
if (r < 0)
return log_error_errno(r, "Failed to process journal events: %m");
int n_shown = 0, r, poll_fd = -1;
setlocale(LC_ALL, "");
- log_show_color(true);
- log_parse_environment();
- log_open();
+ log_setup_cli();
/* Increase max number of open files if we can, we might needs this when browsing journal files, which might be
* split up into many files. */
if (r < 0)
goto finish;
+ r = add_facilities(j);
+ if (r < 0)
+ goto finish;
+
r = add_matches(j, argv + optind);
if (r < 0)
goto finish;
#if HAVE_PCRE2
if (arg_compiled_pattern) {
- _cleanup_(pcre2_match_data_freep) pcre2_match_data *md = NULL;
+ _cleanup_(sym_pcre2_match_data_freep) pcre2_match_data *md = NULL;
const void *message;
size_t len;
PCRE2_SIZE *ovec;
- md = pcre2_match_data_create(1, NULL);
+ md = sym_pcre2_match_data_create(1, NULL);
if (!md)
return log_oom();
assert_se(message = startswith(message, "MESSAGE="));
- r = pcre2_match(arg_compiled_pattern,
- message,
- len - strlen("MESSAGE="),
- 0, /* start at offset 0 in the subject */
- 0, /* default options */
- md,
- NULL);
+ r = sym_pcre2_match(arg_compiled_pattern,
+ message,
+ len - strlen("MESSAGE="),
+ 0, /* start at offset 0 in the subject */
+ 0, /* default options */
+ md,
+ NULL);
if (r == PCRE2_ERROR_NOMATCH) {
need_seek = true;
continue;
unsigned char buf[LINE_MAX];
int r2;
- r2 = pcre2_get_error_message(r, buf, sizeof buf);
+ r2 = sym_pcre2_get_error_message(r, buf, sizeof buf);
log_error("Pattern matching failed: %s",
r2 < 0 ? "unknown error" : (char*) buf);
r = -EINVAL;
goto finish;
}
- ovec = pcre2_get_ovector_pointer(md);
+ ovec = sym_pcre2_get_ovector_pointer(md);
highlight[0] = ovec[0];
highlight[1] = ovec[1];
}
strv_free(arg_file);
+ set_free(arg_facilities);
strv_free(arg_syslog_identifier);
strv_free(arg_system_units);
strv_free(arg_user_units);
#if HAVE_PCRE2
if (arg_compiled_pattern) {
- pcre2_code_free(arg_compiled_pattern);
+ sym_pcre2_code_free(arg_compiled_pattern);
/* --grep was used, no error was thrown, but the pattern didn't
* match anything. Let's mimic grep's behavior here and return