journald-server.c revision 75eb615480afd787fa412f0a529523f568f79b26
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2011 Lennart Poettering
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
(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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_SELINUX
#endif
#include <sys/signalfd.h>
#include "libudev.h"
#include "sd-daemon.h"
#include "sd-journal.h"
#include "sd-messages.h"
#include "acl-util.h"
#include "alloc-util.h"
#include "audit-util.h"
#include "cgroup-util.h"
#include "conf-parser.h"
#include "dirent-util.h"
#include "extract-word.h"
#include "fd-util.h"
#include "formats-util.h"
#include "fs-util.h"
#include "hashmap.h"
#include "hostname-util.h"
#include "io-util.h"
#include "journal-authenticate.h"
#include "journal-file.h"
#include "journal-internal.h"
#include "journal-vacuum.h"
#include "journald-audit.h"
#include "journald-kmsg.h"
#include "journald-native.h"
#include "journald-rate-limit.h"
#include "journald-server.h"
#include "journald-stream.h"
#include "journald-syslog.h"
#include "missing.h"
#include "mkdir.h"
#include "parse-util.h"
#include "proc-cmdline.h"
#include "process-util.h"
#include "rm-rf.h"
#include "selinux-util.h"
#include "signal-util.h"
#include "socket-util.h"
#include "string-table.h"
#include "string-util.h"
#define USER_JOURNALS_MAX 1024
#define DEFAULT_RATE_LIMIT_BURST 1000
#define DEFAULT_MAX_FILE_USEC USEC_PER_MONTH
static int determine_space_for(
Server *s,
const char *path,
const char *name,
bool verbose,
bool patch_min_use,
const char *p;
assert(s);
if (available)
*available = s->cached_space_available;
if (limit)
*limit = s->cached_space_limit;
return 0;
}
d = opendir(p);
if (!d)
FOREACH_DIRENT_ALL(de, d, break) {
continue;
continue;
}
continue;
}
/* If request, then let's bump the min_use limit to the
* current usage on disk. We do this when starting up and
* first opening the journal files. This way sudden spikes in
* disk usage will not cause journald to vacuum files without
* bounds. Note that this means that only a restart of
* journald will make it reset this value. */
if (patch_min_use)
s->cached_space_timestamp = ts;
if (verbose) {
"%s (%s) is currently using %s.\n"
"Maximum allowed usage is set to %s.\n"
"Leaving at least %s free (of currently available %s of space).\n"
"Enforced usage limit is thus %s, of which %s are still available.",
}
if (available)
*available = s->cached_space_available;
if (limit)
*limit = s->cached_space_limit;
return 1;
}
static int determine_space(Server *s, bool verbose, bool patch_min_use, uint64_t *available, uint64_t *limit) {
assert(s);
if (s->system_journal) {
metrics = &s->system_metrics;
name = "System journal";
} else {
metrics = &s->runtime_metrics;
name = "Runtime journal";
}
}
int r;
#ifdef HAVE_ACL
#endif
assert(f);
if (r < 0)
#ifdef HAVE_ACL
if (uid <= SYSTEM_UID_MAX)
return;
if (!acl) {
return;
}
if (r <= 0) {
return;
}
}
/* We do not recalculate the mask unconditionally here,
* so that the fchmod() mask above stays intact. */
return;
}
r = calc_acl_mask_if_needed(&acl);
if (r < 0) {
return;
}
#endif
}
_cleanup_free_ char *p = NULL;
int r;
JournalFile *f;
assert(s);
/* We split up user logs only on /var, not on /run. If the
* runtime file is open, we write to it exclusively, in order
* to guarantee proper order as soon as we flush /run to
* /var and close the runtime file. */
if (s->runtime_journal)
return s->runtime_journal;
if (uid <= SYSTEM_UID_MAX)
return s->system_journal;
r = sd_id128_get_machine(&machine);
if (r < 0)
return s->system_journal;
if (f)
return f;
return s->system_journal;
/* Too many open? Then let's close one */
f = ordered_hashmap_steal_first(s->user_journals);
assert(f);
}
r = journal_file_open_reliably(p, O_RDWR|O_CREAT, 0640, s->compress, s->seal, &s->system_metrics, s->mmap, NULL, &f);
if (r < 0)
return s->system_journal;
server_fix_perms(s, f, uid);
if (r < 0) {
return s->system_journal;
}
return f;
}
static int do_rotate(
Server *s,
JournalFile **f,
const char* name,
bool seal,
int r;
assert(s);
if (!*f)
return -EINVAL;
if (r < 0)
if (*f)
else
else
server_fix_perms(s, *f, uid);
return r;
}
void server_rotate(Server *s) {
JournalFile *f;
void *k;
Iterator i;
int r;
log_debug("Rotating...");
ORDERED_HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) {
if (r >= 0)
ordered_hashmap_replace(s->user_journals, k, f);
else if (!f)
/* Old file has been closed and deallocated */
ordered_hashmap_remove(s->user_journals, k);
}
}
void server_sync(Server *s) {
JournalFile *f;
void *k;
Iterator i;
int r;
if (s->system_journal) {
r = journal_file_set_offline(s->system_journal);
if (r < 0)
log_warning_errno(r, "Failed to sync system journal, ignoring: %m");
}
ORDERED_HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) {
r = journal_file_set_offline(f);
if (r < 0)
log_warning_errno(r, "Failed to sync user journal, ignoring: %m");
}
if (s->sync_event_source) {
if (r < 0)
log_error_errno(r, "Failed to disable sync timer source: %m");
}
s->sync_scheduled = false;
}
static void do_vacuum(
Server *s,
JournalFile *f,
const char *path,
const char *name,
bool verbose,
bool patch_min_use) {
const char *p;
int r;
assert(s);
if (!f)
return;
r = journal_directory_vacuum(p, limit, metrics->n_max_files, s->max_retention_usec, &s->oldest_file_usec, verbose);
if (r < 0 && r != -ENOENT)
log_warning_errno(r, "Failed to vacuum %s, ignoring: %m", p);
}
assert(s);
log_debug("Vacuuming...");
s->oldest_file_usec = 0;
do_vacuum(s, s->system_journal, &s->system_metrics, "/var/log/journal/", "System journal", verbose, patch_min_use);
do_vacuum(s, s->runtime_journal, &s->runtime_metrics, "/run/log/journal/", "Runtime journal", verbose, patch_min_use);
s->cached_space_limit = 0;
s->cached_space_available = 0;
s->cached_space_timestamp = 0;
return 0;
}
static void server_cache_machine_id(Server *s) {
int r;
assert(s);
r = sd_id128_get_machine(&id);
if (r < 0)
return;
}
static void server_cache_boot_id(Server *s) {
int r;
assert(s);
r = sd_id128_get_boot(&id);
if (r < 0)
return;
}
static void server_cache_hostname(Server *s) {
_cleanup_free_ char *t = NULL;
char *x;
assert(s);
t = gethostname_malloc();
if (!t)
return;
x = strappend("_HOSTNAME=", t);
if (!x)
return;
free(s->hostname_field);
s->hostname_field = x;
}
static bool shall_try_append_again(JournalFile *f, int r) {
/* -E2BIG Hit configured limit
-EFBIG Hit fs limit
-EDQUOT Quota limit hit
-ENOSPC Disk full
-EIO I/O error of some kind (mmap)
-EHOSTDOWN Other machine
-EBUSY Unclean shutdown
-EPROTONOSUPPORT Unsupported feature
-EBADMSG Corrupted
-ENODATA Truncated
-ESHUTDOWN Already archived
-EIDRM Journal file has been deleted */
else if (r == -EHOSTDOWN)
else if (r == -EBUSY)
else if (r == -EPROTONOSUPPORT)
else if (r == -EIO)
else if (r == -EIDRM)
else
return false;
return true;
}
JournalFile *f;
bool vacuumed = false;
int r;
assert(s);
assert(n > 0);
f = find_journal(s, uid);
if (!f)
return;
if (journal_file_rotate_suggested(f, s->max_file_usec)) {
server_rotate(s);
server_vacuum(s, false, false);
vacuumed = true;
f = find_journal(s, uid);
if (!f)
return;
}
if (r >= 0) {
return;
}
if (vacuumed || !shall_try_append_again(f, r)) {
log_error_errno(r, "Failed to write entry (%d items, %zu bytes), ignoring: %m", n, IOVEC_TOTAL_SIZE(iovec, n));
return;
}
server_rotate(s);
server_vacuum(s, false, false);
f = find_journal(s, uid);
if (!f)
return;
log_debug("Retrying write.");
if (r < 0)
log_error_errno(r, "Failed to write entry (%d items, %zu bytes) despite vacuuming, ignoring: %m", n, IOVEC_TOTAL_SIZE(iovec, n));
else
}
static void dispatch_message_real(
Server *s,
const char *unit_id,
int priority,
pid_t object_pid) {
char *x;
int r;
char *t, *c;
bool owner_valid = false;
#ifdef HAVE_AUDIT
#endif
assert(s);
assert(n > 0);
if (ucred) {
if (r >= 0) {
x = strjoina("_COMM=", t);
free(t);
IOVEC_SET_STRING(iovec[n++], x);
}
if (r >= 0) {
x = strjoina("_EXE=", t);
free(t);
IOVEC_SET_STRING(iovec[n++], x);
}
if (r >= 0) {
x = strjoina("_CMDLINE=", t);
free(t);
IOVEC_SET_STRING(iovec[n++], x);
}
if (r >= 0) {
x = strjoina("_CAP_EFFECTIVE=", t);
free(t);
IOVEC_SET_STRING(iovec[n++], x);
}
#ifdef HAVE_AUDIT
if (r >= 0) {
}
if (r >= 0) {
}
#endif
if (r >= 0) {
x = strjoina("_SYSTEMD_CGROUP=", c);
IOVEC_SET_STRING(iovec[n++], x);
r = cg_path_get_session(c, &t);
if (r >= 0) {
free(t);
}
if (cg_path_get_owner_uid(c, &owner) >= 0) {
owner_valid = true;
}
if (cg_path_get_unit(c, &t) >= 0) {
x = strjoina("_SYSTEMD_UNIT=", t);
free(t);
IOVEC_SET_STRING(iovec[n++], x);
IOVEC_SET_STRING(iovec[n++], x);
}
if (cg_path_get_user_unit(c, &t) >= 0) {
x = strjoina("_SYSTEMD_USER_UNIT=", t);
free(t);
IOVEC_SET_STRING(iovec[n++], x);
IOVEC_SET_STRING(iovec[n++], x);
}
if (cg_path_get_slice(c, &t) >= 0) {
x = strjoina("_SYSTEMD_SLICE=", t);
free(t);
IOVEC_SET_STRING(iovec[n++], x);
}
free(c);
} else if (unit_id) {
IOVEC_SET_STRING(iovec[n++], x);
}
#ifdef HAVE_SELINUX
if (mac_selinux_use()) {
if (label) {
IOVEC_SET_STRING(iovec[n++], x);
} else {
IOVEC_SET_STRING(iovec[n++], x);
}
}
}
#endif
}
assert(n <= m);
if (object_pid) {
if (r >= 0) {
}
if (r >= 0) {
}
r = get_process_comm(object_pid, &t);
if (r >= 0) {
x = strjoina("OBJECT_COMM=", t);
free(t);
IOVEC_SET_STRING(iovec[n++], x);
}
r = get_process_exe(object_pid, &t);
if (r >= 0) {
x = strjoina("OBJECT_EXE=", t);
free(t);
IOVEC_SET_STRING(iovec[n++], x);
}
r = get_process_cmdline(object_pid, 0, false, &t);
if (r >= 0) {
x = strjoina("OBJECT_CMDLINE=", t);
free(t);
IOVEC_SET_STRING(iovec[n++], x);
}
#ifdef HAVE_AUDIT
if (r >= 0) {
}
if (r >= 0) {
}
#endif
if (r >= 0) {
x = strjoina("OBJECT_SYSTEMD_CGROUP=", c);
IOVEC_SET_STRING(iovec[n++], x);
r = cg_path_get_session(c, &t);
if (r >= 0) {
x = strjoina("OBJECT_SYSTEMD_SESSION=", t);
free(t);
IOVEC_SET_STRING(iovec[n++], x);
}
if (cg_path_get_owner_uid(c, &owner) >= 0) {
}
if (cg_path_get_unit(c, &t) >= 0) {
x = strjoina("OBJECT_SYSTEMD_UNIT=", t);
free(t);
IOVEC_SET_STRING(iovec[n++], x);
}
if (cg_path_get_user_unit(c, &t) >= 0) {
x = strjoina("OBJECT_SYSTEMD_USER_UNIT=", t);
free(t);
IOVEC_SET_STRING(iovec[n++], x);
}
free(c);
}
}
assert(n <= m);
if (tv) {
}
/* Note that strictly speaking storing the boot id here is
* redundant since the entry includes this in-line
* anyway. However, we need this indexed, too. */
if (!isempty(s->boot_id_field))
if (!isempty(s->machine_id_field))
if (!isempty(s->hostname_field))
assert(n <= m);
/* Split up strictly by any UID */
/* Split up by login UIDs. We do this only if the
* realuid is not root, in order not to accidentally
* leak privileged information to the user that is
* logged by a privileged process that is part of an
* unprivileged session. */
journal_uid = owner;
else
journal_uid = 0;
}
int n = 0;
assert(s);
}
}
void server_dispatch_message(
Server *s,
const char *unit_id,
int priority,
pid_t object_pid) {
int rl, r;
char *c;
assert(s);
if (n == 0)
return;
return;
/* Stop early in case the information will not be stored
* in a journal. */
if (s->storage == STORAGE_NONE)
return;
if (!ucred)
goto finish;
if (r < 0)
goto finish;
*
* So let's cut of everything past the third /, since that is
* where user directories start */
if (c) {
if (c) {
if (c)
*c = 0;
}
}
if (rl == 0)
return;
/* Write a suppression message if we suppressed something */
if (rl > 1)
}
const char *fn;
int r = 0;
if (!s->system_journal &&
/* If in auto mode: first try to create the machine
* path, but not the prefix.
*
* the machine path */
if (s->storage == STORAGE_PERSISTENT)
r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, s->compress, s->seal, &s->system_metrics, s->mmap, NULL, &s->system_journal);
if (r >= 0) {
server_fix_perms(s, s->system_journal, 0);
(void) determine_space_for(s, &s->system_metrics, "/var/log/journal/", "System journal", true, true, NULL, NULL);
} else if (r < 0) {
log_warning_errno(r, "Failed to open system journal: %m");
r = 0;
}
}
if (!s->runtime_journal &&
(s->storage != STORAGE_NONE)) {
if (s->system_journal) {
/* Try to open the runtime journal, but only
* if it already exists, so that we can flush
* it into the system journal */
r = journal_file_open(fn, O_RDWR, 0640, s->compress, false, &s->runtime_metrics, s->mmap, NULL, &s->runtime_journal);
if (r < 0) {
if (r != -ENOENT)
log_warning_errno(r, "Failed to open runtime journal: %m");
r = 0;
}
} else {
/* OK, we really need the runtime journal, so create
* it if necessary. */
r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, s->compress, false, &s->runtime_metrics, s->mmap, NULL, &s->runtime_journal);
if (r < 0)
return log_error_errno(r, "Failed to open runtime journal: %m");
}
if (s->runtime_journal) {
server_fix_perms(s, s->runtime_journal, 0);
(void) determine_space_for(s, &s->runtime_metrics, "/run/log/journal/", "Runtime journal", true, true, NULL, NULL);
}
}
return r;
}
int server_flush_to_var(Server *s) {
sd_journal *j = NULL;
char ts[FORMAT_TIMESPAN_MAX];
unsigned n = 0;
int r;
assert(s);
if (s->storage != STORAGE_AUTO &&
s->storage != STORAGE_PERSISTENT)
return 0;
if (!s->runtime_journal)
return 0;
(void) system_journal_open(s, true);
if (!s->system_journal)
return 0;
log_debug("Flushing to /var...");
r = sd_id128_get_machine(&machine);
if (r < 0)
return r;
r = sd_journal_open(&j, SD_JOURNAL_RUNTIME_ONLY);
if (r < 0)
return log_error_errno(r, "Failed to read runtime journal: %m");
SD_JOURNAL_FOREACH(j) {
JournalFile *f;
f = j->current_file;
assert(f && f->current_offset > 0);
n++;
if (r < 0) {
log_error_errno(r, "Can't read entry: %m");
goto finish;
}
if (r >= 0)
continue;
if (!shall_try_append_again(s->system_journal, r)) {
log_error_errno(r, "Can't write entry: %m");
goto finish;
}
server_rotate(s);
server_vacuum(s, false, false);
if (!s->system_journal) {
log_notice("Didn't flush runtime journal since rotation of system journal wasn't successful.");
r = -EIO;
goto finish;
}
log_debug("Retrying write.");
if (r < 0) {
log_error_errno(r, "Can't write entry: %m");
goto finish;
}
}
r = 0;
if (r >= 0)
sd_journal_close(j);
server_driver_message(s, SD_ID128_NULL, "Time spent on flushing to /var is %s for %u entries.", format_timespan(ts, sizeof(ts), now(CLOCK_MONOTONIC) - start, 0), n);
return r;
}
ssize_t n;
unsigned n_fds = 0;
union {
/* We use NAME_MAX space for the SELinux label
* here. The kernel currently enforces no
* limit, but according to suggestions from
* the SELinux people this will change and it
* will probably be identical to NAME_MAX. For
* now we use that, but this should be updated
* one day when the final limit is known. */
CMSG_SPACE(sizeof(struct timeval)) +
CMSG_SPACE(sizeof(int)) + /* fd */
} control = {};
union sockaddr_union sa = {};
.msg_iovlen = 1,
.msg_control = &control,
.msg_controllen = sizeof(control),
.msg_namelen = sizeof(sa),
};
assert(s);
return -EIO;
}
/* Try to get the right size, if we can. (Not all
* sockets support SIOCINQ, hence we just try, but
* don't rely on it. */
/* Fix it up, if it is too small. We use the same fixed value as auditd here. Awful! */
return log_oom();
if (n < 0) {
return 0;
}
}
}
/* And a trailing NUL, just in case */
s->buffer[n] = 0;
if (n > 0 && n_fds == 0)
else if (n_fds > 0)
log_warning("Got file descriptors via syslog socket. Ignoring.");
if (n > 0 && n_fds == 0)
else if (n == 0 && n_fds == 1)
else if (n_fds > 0)
log_warning("Got too many file descriptors via native socket. Ignoring.");
} else {
if (n > 0 && n_fds == 0)
else if (n_fds > 0)
log_warning("Got file descriptors via audit socket. Ignoring.");
}
return 0;
}
static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) {
assert(s);
server_sync(s);
server_vacuum(s, false, false);
return 0;
}
static int dispatch_sigusr2(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) {
assert(s);
server_rotate(s);
server_vacuum(s, true, true);
return 0;
}
static int dispatch_sigterm(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) {
assert(s);
sd_event_exit(s->event, 0);
return 0;
}
static int setup_signals(Server *s) {
int r;
assert(s);
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
return 0;
}
static int server_parse_proc_cmdline(Server *s) {
const char *p;
int r;
r = proc_cmdline(&line);
if (r < 0) {
log_warning_errno(r, "Failed to read /proc/cmdline, ignoring: %m");
return 0;
}
p = line;
for(;;) {
_cleanup_free_ char *word;
if (r < 0)
if (r == 0)
break;
if (r < 0)
else
s->forward_to_syslog = r;
if (r < 0)
else
s->forward_to_kmsg = r;
if (r < 0)
else
s->forward_to_console = r;
if (r < 0)
else
s->forward_to_wall = r;
log_warning("Invalid systemd.journald parameter. Ignoring.");
}
/* do not warn about state here, since probably systemd already did */
return 0;
}
static int server_parse_config_file(Server *s) {
assert(s);
"Journal\0",
false, s);
}
assert(s);
server_sync(s);
return 0;
}
int r;
assert(s);
/* Immediately sync to disk when this is of priority CRIT, ALERT, EMERG */
server_sync(s);
return 0;
}
if (s->sync_scheduled)
return 0;
if (s->sync_interval_usec > 0) {
if (r < 0)
return r;
when += s->sync_interval_usec;
if (!s->sync_event_source) {
r = sd_event_add_time(
s->event,
&s->sync_event_source,
when, 0,
server_dispatch_sync, s);
if (r < 0)
return r;
} else {
if (r < 0)
return r;
}
if (r < 0)
return r;
s->sync_scheduled = true;
}
return 0;
}
static int dispatch_hostname_change(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
assert(s);
return 0;
}
static int server_open_hostname(Server *s) {
int r;
assert(s);
if (s->hostname_fd < 0)
r = sd_event_add_io(s->event, &s->hostname_event_source, s->hostname_fd, 0, dispatch_hostname_change, s);
if (r < 0) {
/* kernels prior to 3.2 don't support polling this file. Ignore
* the failure. */
if (r == -EPERM) {
log_warning_errno(r, "Failed to register hostname fd in event loop, ignoring: %m");
return 0;
}
return log_error_errno(r, "Failed to register hostname fd in event loop: %m");
}
if (r < 0)
return log_error_errno(r, "Failed to adjust priority of host name event source: %m");
return 0;
}
int r;
assert(s);
log_error("Invalid events on notify file descriptor.");
return -EINVAL;
}
/* The $NOTIFY_SOCKET is writable again, now send exactly one
* message on it. Either it's the wtachdog event, the initial
* READY=1 event or an stdout stream event. If there's nothing
* to write anymore, turn our event source off. The next time
* there's something to send it will be turned on again. */
if (!s->sent_notify_ready) {
static const char p[] =
"READY=1\n"
"STATUS=Processing requests...";
ssize_t l;
if (l < 0) {
return 0;
}
s->sent_notify_ready = true;
log_debug("Sent READY=1 notification.");
} else if (s->send_watchdog) {
static const char p[] =
"WATCHDOG=1";
ssize_t l;
if (l < 0) {
return 0;
}
s->send_watchdog = false;
log_debug("Sent WATCHDOG=1 notification.");
} else if (s->stdout_streams_notify_queue)
/* Dispatch one stream notification event */
/* Leave us enabled if there's still more to to do. */
if (s->send_watchdog || s->stdout_streams_notify_queue)
return 0;
/* There was nothing to do anymore, let's turn ourselves off. */
if (r < 0)
return log_error_errno(r, "Failed to turn off notify event source: %m");
return 0;
}
int r;
assert(s);
s->send_watchdog = true;
if (r < 0)
log_warning_errno(r, "Failed to turn on notify event source: %m");
if (r < 0)
return log_error_errno(r, "Failed to restart watchdog event source: %m");
if (r < 0)
return log_error_errno(r, "Failed to enable watchdog event source: %m");
return 0;
}
static int server_connect_notify(Server *s) {
union sockaddr_union sa = {
};
const char *e;
int r;
assert(s);
assert(!s->notify_event_source);
/*
So here's the problem: we'd like to send notification
messages to PID 1, but we cannot do that via sd_notify(),
since that's synchronous, and we might end up blocking on
it. Specifically: given that PID 1 might block on
dbus-daemon during IPC, and dbus-daemon is logging to us,
and might hence block on us, we might end up in a deadlock
if we block on sending PID 1 notification messages -- by
generating a full blocking circle. To avoid this, let's
create a non-blocking socket, and connect it to the
notification socket, and then wait for POLLOUT before we
send anything. This should efficiently avoid any deadlocks,
as we'll never block on PID 1, hence PID 1 can safely block
on dbus-daemon which can safely block on us again.
Don't think that this issue is real? It is, see:
*/
e = getenv("NOTIFY_SOCKET");
if (!e)
return 0;
if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
log_error("NOTIFY_SOCKET set to an invalid value: %s", e);
return -EINVAL;
}
log_error("NOTIFY_SOCKET path too long: %s", e);
return -EINVAL;
}
if (s->notify_fd < 0)
if (r < 0)
r = sd_event_add_io(s->event, &s->notify_event_source, s->notify_fd, EPOLLOUT, dispatch_notify_event, s);
if (r < 0)
return log_error_errno(r, "Failed to watch notification socket: %m");
if (sd_watchdog_enabled(false, &s->watchdog_usec) > 0) {
s->send_watchdog = true;
r = sd_event_add_time(s->event, &s->watchdog_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + s->watchdog_usec/2, s->watchdog_usec*3/4, dispatch_watchdog, s);
if (r < 0)
return log_error_errno(r, "Failed to add watchdog time event: %m");
}
/* This should fire pretty soon, which we'll use to send the
* READY=1 event. */
return 0;
}
int server_init(Server *s) {
int n, r, fd;
bool no_sockets;
assert(s);
zero(*s);
s->syslog_fd = s->native_fd = s->stdout_fd = s->dev_kmsg_fd = s->audit_fd = s->hostname_fd = s->notify_fd = -1;
s->compress = true;
s->seal = true;
s->watchdog_usec = USEC_INFINITY;
s->sync_scheduled = false;
s->forward_to_wall = true;
s->max_level_store = LOG_DEBUG;
s->max_level_syslog = LOG_DEBUG;
s->max_level_kmsg = LOG_NOTICE;
s->max_level_console = LOG_INFO;
s->max_level_wall = LOG_EMERG;
if (!!s->rate_limit_interval ^ !!s->rate_limit_burst) {
s->rate_limit_interval, s->rate_limit_burst);
s->rate_limit_interval = s->rate_limit_burst = 0;
}
if (!s->user_journals)
return log_oom();
s->mmap = mmap_cache_new();
if (!s->mmap)
return log_oom();
r = sd_event_default(&s->event);
if (r < 0)
return log_error_errno(r, "Failed to create event loop: %m");
n = sd_listen_fds(true);
if (n < 0)
return log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
if (s->native_fd >= 0) {
log_error("Too many native sockets passed.");
return -EINVAL;
}
if (s->stdout_fd >= 0) {
log_error("Too many stdout sockets passed.");
return -EINVAL;
}
if (s->syslog_fd >= 0) {
return -EINVAL;
}
if (s->audit_fd >= 0) {
log_error("Too many audit sockets passed.");
return -EINVAL;
}
} else {
if (!fds) {
if (!fds)
return log_oom();
}
if (r < 0)
return log_oom();
}
}
/* Try to restore streams, but don't bother if this fails */
(void) server_restore_streams(s, fds);
if (fdset_size(fds) > 0) {
}
/* always open stdout, syslog, native, and kmsg sockets */
/* systemd-journald.socket: /run/systemd/journal/stdout */
r = server_open_stdout_socket(s);
if (r < 0)
return r;
/* systemd-journald-dev-log.socket: /run/systemd/journal/dev-log */
r = server_open_syslog_socket(s);
if (r < 0)
return r;
/* systemd-journald.socket: /run/systemd/journal/socket */
r = server_open_native_socket(s);
if (r < 0)
return r;
r = server_open_dev_kmsg(s);
if (r < 0)
return r;
/* Unless we got *some* sockets and not audit, open audit socket */
if (s->audit_fd >= 0 || no_sockets) {
r = server_open_audit(s);
if (r < 0)
return r;
}
r = server_open_kernel_seqnum(s);
if (r < 0)
return r;
r = server_open_hostname(s);
if (r < 0)
return r;
r = setup_signals(s);
if (r < 0)
return r;
if (!s->udev)
return -ENOMEM;
if (!s->rate_limit)
return -ENOMEM;
r = cg_get_root_path(&s->cgroup_root);
if (r < 0)
return r;
(void) server_connect_notify(s);
return system_journal_open(s, false);
}
void server_maybe_append_tags(Server *s) {
#ifdef HAVE_GCRYPT
JournalFile *f;
Iterator i;
usec_t n;
n = now(CLOCK_REALTIME);
if (s->system_journal)
ORDERED_HASHMAP_FOREACH(f, s->user_journals, i)
#endif
}
void server_done(Server *s) {
JournalFile *f;
assert(s);
while (s->stdout_streams)
if (s->system_journal)
if (s->runtime_journal)
while ((f = ordered_hashmap_steal_first(s->user_journals)))
sd_event_unref(s->event);
safe_close(s->syslog_fd);
safe_close(s->native_fd);
safe_close(s->stdout_fd);
safe_close(s->dev_kmsg_fd);
safe_close(s->audit_fd);
safe_close(s->hostname_fd);
safe_close(s->notify_fd);
if (s->rate_limit)
if (s->kernel_seqnum)
free(s->cgroup_root);
free(s->hostname_field);
if (s->mmap)
mmap_cache_unref(s->mmap);
udev_unref(s->udev);
}
static const char* const storage_table[_STORAGE_MAX] = {
[STORAGE_AUTO] = "auto",
[STORAGE_VOLATILE] = "volatile",
[STORAGE_PERSISTENT] = "persistent",
[STORAGE_NONE] = "none"
};
DEFINE_CONFIG_PARSE_ENUM(config_parse_storage, storage, Storage, "Failed to parse storage setting");
static const char* const split_mode_table[_SPLIT_MAX] = {
[SPLIT_LOGIN] = "login",
[SPLIT_UID] = "uid",
[SPLIT_NONE] = "none",
};
DEFINE_CONFIG_PARSE_ENUM(config_parse_split_mode, split_mode, SplitMode, "Failed to parse split mode setting");