#define RECHECK_VAR_AVAILABLE_USEC (30*USEC_PER_SEC)
+#define SYSLOG_TIMEOUT_USEC (5*USEC_PER_SEC)
+
typedef struct StdoutStream StdoutStream;
typedef struct Server {
JournalRateLimit *rate_limit;
- JournalMetrics metrics;
- uint64_t max_use;
+ JournalMetrics runtime_metrics;
+ JournalMetrics system_metrics;
+
bool compress;
uint64_t cached_available_space;
static int server_flush_to_var(Server *s);
static uint64_t available_space(Server *s) {
- char ids[33];
- sd_id128_t machine;
- char *p;
+ char ids[33], *p;
const char *f;
+ sd_id128_t machine;
struct statvfs ss;
uint64_t sum = 0, avail = 0, ss_avail = 0;
int r;
DIR *d;
- usec_t ts = now(CLOCK_MONOTONIC);
+ usec_t ts;
+ JournalMetrics *m;
+
+ ts = now(CLOCK_MONOTONIC);
if (s->cached_available_space_timestamp + RECHECK_AVAILABLE_SPACE_USEC > ts)
return s->cached_available_space;
if (r < 0)
return 0;
- if (s->system_journal)
+ if (s->system_journal) {
f = "/var/log/journal/";
- else
+ m = &s->system_metrics;
+ } else {
f = "/run/log/journal/";
+ m = &s->runtime_metrics;
+ }
+
+ assert(m);
p = strappend(f, sd_id128_to_string(machine, ids));
if (!p)
sum += (uint64_t) st.st_blocks * (uint64_t) st.st_blksize;
}
- avail = sum >= s->max_use ? 0 : s->max_use - sum;
+ avail = sum >= m->max_use ? 0 : m->max_use - sum;
ss_avail = ss.f_bsize * ss.f_bavail;
- ss_avail = ss_avail < s->metrics.keep_free ? 0 : ss_avail - s->metrics.keep_free;
+ ss_avail = ss_avail < m->keep_free ? 0 : ss_avail - m->keep_free;
if (ss_avail < avail)
avail = ss_avail;
return s->system_journal;
fix_perms(f, uid);
- f->metrics = s->metrics;
+ f->metrics = s->system_metrics;
f->compress = s->compress;
r = hashmap_put(s->user_journals, UINT32_TO_PTR(uid), f);
return;
}
- if (asprintf(&p, "/var/log/journal/%s", sd_id128_to_string(machine, ids)) < 0) {
- log_error("Out of memory.");
- return;
- }
+ sd_id128_to_string(machine, ids);
- r = journal_directory_vacuum(p, s->max_use, s->metrics.keep_free);
- if (r < 0 && r != -ENOENT)
- log_error("Failed to vacuum %s: %s", p, strerror(-r));
- free(p);
+ if (s->system_journal) {
+ if (asprintf(&p, "/var/log/journal/%s", ids) < 0) {
+ log_error("Out of memory.");
+ return;
+ }
- if (asprintf(&p, "/run/log/journal/%s", ids) < 0) {
- log_error("Out of memory.");
- return;
+ r = journal_directory_vacuum(p, s->system_metrics.max_use, s->system_metrics.keep_free);
+ if (r < 0 && r != -ENOENT)
+ log_error("Failed to vacuum %s: %s", p, strerror(-r));
+ free(p);
}
- r = journal_directory_vacuum(p, s->max_use, s->metrics.keep_free);
- if (r < 0 && r != -ENOENT)
- log_error("Failed to vacuum %s: %s", p, strerror(-r));
- free(p);
+
+ if (s->runtime_journal) {
+ if (asprintf(&p, "/run/log/journal/%s", ids) < 0) {
+ log_error("Out of memory.");
+ return;
+ }
+
+ r = journal_directory_vacuum(p, s->runtime_metrics.max_use, s->runtime_metrics.keep_free);
+ if (r < 0 && r != -ENOENT)
+ log_error("Failed to vacuum %s: %s", p, strerror(-r));
+ free(p);
+ }
s->cached_available_space_timestamp = 0;
}
if (streq(init_path, "/"))
init_path[0] = 0;
- if (startswith(process_path, init_path))
- path = process_path + strlen(init_path);
- else
+ if (startswith(process_path, init_path)) {
+ char *p;
+
+ p = strdup(process_path + strlen(init_path));
+ if (!p) {
+ free(process_path);
+ free(init_path);
+ return NULL;
+ }
+ path = p;
+ } else {
path = process_path;
+ process_path = NULL;
+ }
+ free(process_path);
free(init_path);
return path;
struct timeval *tv,
int priority) {
int rl;
- char *path, *c;
+ char *path = NULL, *c;
assert(s);
assert(iovec || n == 0);
free(fn);
if (r >= 0) {
- s->system_journal->metrics = s->metrics;
+ journal_default_metrics(&s->system_metrics, s->system_journal->fd);
+
+ s->system_journal->metrics = s->system_metrics;
s->system_journal->compress = s->compress;
fix_perms(s->system_journal, 0);
}
if (s->runtime_journal) {
- s->runtime_journal->metrics = s->metrics;
+ journal_default_metrics(&s->runtime_metrics, s->runtime_journal->fd);
+
+ s->runtime_journal->metrics = s->runtime_metrics;
s->runtime_journal->compress = s->compress;
fix_perms(s->runtime_journal, 0);
return r;
}
+static void forward_syslog(Server *s, const void *buffer, size_t length, struct ucred *ucred, struct timeval *tv) {
+ struct msghdr msghdr;
+ struct iovec iovec;
+ struct cmsghdr *cmsg;
+ union {
+ struct cmsghdr cmsghdr;
+ uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
+ CMSG_SPACE(sizeof(struct timeval))];
+ } control;
+ union sockaddr_union sa;
+
+ assert(s);
+
+ zero(msghdr);
+
+ zero(iovec);
+ iovec.iov_base = (void*) buffer;
+ iovec.iov_len = length;
+ msghdr.msg_iov = &iovec;
+ msghdr.msg_iovlen = 1;
+
+ zero(sa);
+ sa.un.sun_family = AF_UNIX;
+ strncpy(sa.un.sun_path, "/run/systemd/syslog", sizeof(sa.un.sun_path));
+ msghdr.msg_name = &sa;
+ msghdr.msg_namelen = offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path);
+
+ zero(control);
+ msghdr.msg_control = &control;
+ msghdr.msg_controllen = sizeof(control);
+
+ cmsg = CMSG_FIRSTHDR(&msghdr);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_CREDENTIALS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
+ memcpy(CMSG_DATA(cmsg), ucred, sizeof(struct ucred));
+ msghdr.msg_controllen = cmsg->cmsg_len;
+
+ /* Forward the syslog message we received via /dev/log to
+ * /run/systemd/syslog. Unfortunately we currently can't set
+ * the SO_TIMESTAMP auxiliary data, and hence we don't. */
+
+ if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
+ return;
+
+ if (errno == ESRCH) {
+ struct ucred u;
+
+ /* Hmm, presumably the sender process vanished
+ * by now, so let's fix it as good as we
+ * can, and retry */
+
+ u = *ucred;
+ u.pid = getpid();
+ memcpy(CMSG_DATA(cmsg), &u, sizeof(struct ucred));
+
+ if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
+ return;
+ }
+
+ log_debug("Failed to forward syslog message: %m");
+}
+
static int process_event(Server *s, struct epoll_event *ev) {
assert(s);
else
s->buffer[n] = 0;
+ forward_syslog(s, s->buffer, n, ucred, tv);
process_syslog_message(s, strstrip(s->buffer), ucred, tv);
} else
process_native_message(s, s->buffer, n, ucred, tv);
union sockaddr_union sa;
int one, r;
struct epoll_event ev;
+ struct timeval tv;
assert(s);
zero(sa);
sa.un.sun_family = AF_UNIX;
- strncpy(sa.un.sun_path, "/run/systemd/syslog", sizeof(sa.un.sun_path));
+ strncpy(sa.un.sun_path, "/dev/log", sizeof(sa.un.sun_path));
unlink(sa.un.sun_path);
return -errno;
}
+ /* Since we use the same socket for forwarding this to some
+ * other syslog implementation, make sure we don't hang
+ * forever */
+ timeval_store(&tv, SYSLOG_TIMEOUT_USEC);
+ if (setsockopt(s->syslog_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
+ log_error("SO_SNDTIMEO failed: %m");
+ return -errno;
+ }
+
zero(ev);
ev.events = EPOLLIN;
ev.data.fd = s->syslog_fd;
zero(*s);
s->syslog_fd = s->native_fd = s->stdout_fd = s->signal_fd = s->epoll_fd = -1;
- s->metrics.max_size = DEFAULT_MAX_SIZE;
- s->metrics.min_size = DEFAULT_MIN_SIZE;
- s->metrics.keep_free = DEFAULT_KEEP_FREE;
- s->max_use = DEFAULT_MAX_USE;
s->compress = true;
+ memset(&s->system_metrics, 0xFF, sizeof(s->system_metrics));
+ memset(&s->runtime_metrics, 0xFF, sizeof(s->runtime_metrics));
+
s->user_journals = hashmap_new(trivial_hash_func, trivial_compare_func);
if (!s->user_journals) {
log_error("Out of memory.");
if (s->rate_limit)
journal_rate_limit_free(s->rate_limit);
+
+ free(s->buffer);
}
int main(int argc, char *argv[]) {
}
log_set_target(LOG_TARGET_CONSOLE);
- log_set_max_level(LOG_DEBUG);
log_parse_environment();
log_open();