journald-server.c revision d378991747d67fff1d4dc39e7fb2bc8f49f1b561
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering/***
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering This file is part of systemd.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Copyright 2011 Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering systemd is free software; you can redistribute it and/or modify it
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering under the terms of the GNU Lesser General Public License as published by
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering (at your option) any later version.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering systemd is distributed in the hope that it will be useful, but
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Lesser General Public License for more details.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering You should have received a copy of the GNU Lesser General Public License
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering***/
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
a0166609f782da91710dea9183d1bf138538db37Tom Gundersen#include <sys/signalfd.h>
71d35b6b5563817dfbe757ab9e3b9f018b2db491Thomas Hindoe Paaboel Andersen#include <sys/ioctl.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <linux/sockios.h>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include <sys/statvfs.h>
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering#include <sys/mman.h>
71d35b6b5563817dfbe757ab9e3b9f018b2db491Thomas Hindoe Paaboel Andersen#include <sys/timerfd.h>
71d35b6b5563817dfbe757ab9e3b9f018b2db491Thomas Hindoe Paaboel Andersen
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering#include <libudev.h>
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering#include <systemd/sd-journal.h>
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering#include <systemd/sd-messages.h>
7e8e0422aeb16f2a09a40546c61df753d10029b6Lennart Poettering#include <systemd/sd-daemon.h>
51323288fc628a5cac50914df915545d685b793eLennart Poettering
71d35b6b5563817dfbe757ab9e3b9f018b2db491Thomas Hindoe Paaboel Andersen#ifdef HAVE_LOGIND
71d35b6b5563817dfbe757ab9e3b9f018b2db491Thomas Hindoe Paaboel Andersen#include <systemd/sd-login.h>
71d35b6b5563817dfbe757ab9e3b9f018b2db491Thomas Hindoe Paaboel Andersen#endif
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering#include "fileio.h"
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering#include "mkdir.h"
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering#include "hashmap.h"
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering#include "journal-file.h"
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering#include "socket-util.h"
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering#include "cgroup-util.h"
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering#include "list.h"
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering#include "virt.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "missing.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "conf-parser.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "journal-internal.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "journal-vacuum.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "journal-authenticate.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "journald-server.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "journald-rate-limit.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "journald-kmsg.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "journald-syslog.h"
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#include "journald-stream.h"
a0166609f782da91710dea9183d1bf138538db37Tom Gundersen#include "journald-console.h"
c73ce96b569e2f10dff64b7dc0bd271972674c2aLennart Poettering#include "journald-native.h"
c73ce96b569e2f10dff64b7dc0bd271972674c2aLennart Poettering
c73ce96b569e2f10dff64b7dc0bd271972674c2aLennart Poettering#ifdef HAVE_ACL
e1c959948c0e31d6997bcdfbabfbd077784b2baeLennart Poettering#include <sys/acl.h>
c73ce96b569e2f10dff64b7dc0bd271972674c2aLennart Poettering#include <acl/libacl.h>
e1c959948c0e31d6997bcdfbabfbd077784b2baeLennart Poettering#include "acl-util.h"
e1c959948c0e31d6997bcdfbabfbd077784b2baeLennart Poettering#endif
e1c959948c0e31d6997bcdfbabfbd077784b2baeLennart Poettering
e1c959948c0e31d6997bcdfbabfbd077784b2baeLennart Poettering#ifdef HAVE_SELINUX
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen#include <selinux/selinux.h>
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen#endif
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#define USER_JOURNALS_MAX 1024
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#define DEFAULT_SYNC_INTERVAL_USEC (5*USEC_PER_MINUTE)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#define DEFAULT_RATE_LIMIT_INTERVAL (10*USEC_PER_SEC)
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering#define DEFAULT_RATE_LIMIT_BURST 200
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering#define RECHECK_AVAILABLE_SPACE_USEC (30*USEC_PER_SEC)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poetteringstatic const char* const storage_table[] = {
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering [STORAGE_AUTO] = "auto",
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering [STORAGE_VOLATILE] = "volatile",
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering [STORAGE_PERSISTENT] = "persistent",
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering [STORAGE_NONE] = "none"
d75acfb059ece4512278b8820a9103664996f1e5Lennart Poettering};
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering
dc61b7e45d89a69f0469ab7b3289cdde7fcc55abTorstein HusebøDEFINE_STRING_TABLE_LOOKUP(storage, Storage);
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart PoetteringDEFINE_CONFIG_PARSE_ENUM(config_parse_storage, storage, Storage, "Failed to parse storage setting");
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poettering
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poetteringstatic const char* const split_mode_table[] = {
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poettering [SPLIT_NONE] = "none",
0dd25fb9f005d8ab7ac4bc10a609d00569f8c56aLennart Poettering [SPLIT_UID] = "uid",
a407657425a3e47fd2b559cd3bc800f791303f63Lennart Poettering [SPLIT_LOGIN] = "login"
9c491563837983385bf9fa244590e76e142f4fa3Daniel Mack};
9c491563837983385bf9fa244590e76e142f4fa3Daniel Mack
9c491563837983385bf9fa244590e76e142f4fa3Daniel MackDEFINE_STRING_TABLE_LOOKUP(split_mode, SplitMode);
a8812dd7f161a3e459c1730ac92ff2bbc9986ff1Lennart PoetteringDEFINE_CONFIG_PARSE_ENUM(config_parse_split_mode, split_mode, SplitMode, "Failed to parse split mode setting");
a8812dd7f161a3e459c1730ac92ff2bbc9986ff1Lennart Poettering
a8812dd7f161a3e459c1730ac92ff2bbc9986ff1Lennart Poetteringstatic uint64_t available_space(Server *s) {
a8812dd7f161a3e459c1730ac92ff2bbc9986ff1Lennart Poettering char ids[33];
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering char _cleanup_free_ *p = NULL;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering const char *f;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering sd_id128_t machine;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering struct statvfs ss;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering uint64_t sum = 0, avail = 0, ss_avail = 0;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering int r;
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering DIR _cleanup_closedir_ *d = NULL;
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering usec_t ts;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering JournalMetrics *m;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering ts = now(CLOCK_MONOTONIC);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (s->cached_available_space_timestamp + RECHECK_AVAILABLE_SPACE_USEC > ts)
3cb10d3a0b1b6a7c44f307f2abb5215104e16941Lennart Poettering return s->cached_available_space;
3cb10d3a0b1b6a7c44f307f2abb5215104e16941Lennart Poettering
3cb10d3a0b1b6a7c44f307f2abb5215104e16941Lennart Poettering r = sd_id128_get_machine(&machine);
8b757a38611006a751c90933d1810cccaa47e1afDaniel Mack if (r < 0)
ad867662936a4c7ab2c7116d804c272338801231Lennart Poettering return 0;
8b757a38611006a751c90933d1810cccaa47e1afDaniel Mack
8b757a38611006a751c90933d1810cccaa47e1afDaniel Mack if (s->system_journal) {
8b757a38611006a751c90933d1810cccaa47e1afDaniel Mack f = "/var/log/journal/";
8b757a38611006a751c90933d1810cccaa47e1afDaniel Mack m = &s->system_metrics;
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering } else {
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering f = "/run/log/journal/";
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering m = &s->runtime_metrics;
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering }
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering assert(m);
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering p = strappend(f, sd_id128_to_string(machine, ids));
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering if (!p)
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering return 0;
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering
8b757a38611006a751c90933d1810cccaa47e1afDaniel Mack d = opendir(p);
8b757a38611006a751c90933d1810cccaa47e1afDaniel Mack if (!d)
8b757a38611006a751c90933d1810cccaa47e1afDaniel Mack return 0;
8b757a38611006a751c90933d1810cccaa47e1afDaniel Mack
8b757a38611006a751c90933d1810cccaa47e1afDaniel Mack if (fstatvfs(dirfd(d), &ss) < 0)
3cb10d3a0b1b6a7c44f307f2abb5215104e16941Lennart Poettering return 0;
3cb10d3a0b1b6a7c44f307f2abb5215104e16941Lennart Poettering
3cb10d3a0b1b6a7c44f307f2abb5215104e16941Lennart Poettering for (;;) {
3cb10d3a0b1b6a7c44f307f2abb5215104e16941Lennart Poettering struct stat st;
3cb10d3a0b1b6a7c44f307f2abb5215104e16941Lennart Poettering struct dirent *de;
3cb10d3a0b1b6a7c44f307f2abb5215104e16941Lennart Poettering union dirent_storage buf;
f0258e473667f44f4656dde49597b2badb9f598aLennart Poettering
f0258e473667f44f4656dde49597b2badb9f598aLennart Poettering r = readdir_r(d, &buf.de, &de);
f0258e473667f44f4656dde49597b2badb9f598aLennart Poettering if (r != 0)
f0258e473667f44f4656dde49597b2badb9f598aLennart Poettering break;
f0258e473667f44f4656dde49597b2badb9f598aLennart Poettering
f0258e473667f44f4656dde49597b2badb9f598aLennart Poettering if (!de)
f0258e473667f44f4656dde49597b2badb9f598aLennart Poettering break;
f0258e473667f44f4656dde49597b2badb9f598aLennart Poettering
f0258e473667f44f4656dde49597b2badb9f598aLennart Poettering if (!endswith(de->d_name, ".journal") &&
3cb10d3a0b1b6a7c44f307f2abb5215104e16941Lennart Poettering !endswith(de->d_name, ".journal~"))
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering continue;
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering continue;
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering
322345fdb9865ef2477fba8e4bdde0e1183ef505Lennart Poettering if (!S_ISREG(st.st_mode))
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering continue;
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering
24710c48ed16be5fa461fbb303a744a907541dafLennart Poettering sum += (uint64_t) st.st_blocks * 512UL;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering }
dbfbb6e776d613cb9be76d13de076d08450c9d29Daniel Mack
dbfbb6e776d613cb9be76d13de076d08450c9d29Daniel Mack avail = sum >= m->max_use ? 0 : m->max_use - sum;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering ss_avail = ss.f_bsize * ss.f_bavail;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering ss_avail = ss_avail < m->keep_free ? 0 : ss_avail - m->keep_free;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (ss_avail < avail)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering avail = ss_avail;
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering s->cached_available_space = avail;
8af5b883227ac8dfa796742b9edcc1647a5d4d6cLennart Poettering s->cached_available_space_timestamp = ts;
8af5b883227ac8dfa796742b9edcc1647a5d4d6cLennart Poettering
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poettering return avail;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering}
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poetteringstatic void server_read_file_gid(Server *s) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering const char *g = "systemd-journal";
2001c80560e3dae69e14fd994d3978c187af48b8Lennart Poettering int r;
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering
a3db237b8f1b97867395e1419f39b8ba5749b777Lennart Poettering assert(s);
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poettering
a8812dd7f161a3e459c1730ac92ff2bbc9986ff1Lennart Poettering if (s->file_gid_valid)
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering return;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
9c5e12a4314e7192e834e1b855e5e80111e636a6Tom Gundersen r = get_group_creds(&g, &s->file_gid);
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering if (r < 0)
9c5e12a4314e7192e834e1b855e5e80111e636a6Tom Gundersen log_warning("Failed to resolve '%s' group: %s", g, strerror(-r));
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
623a4c97b9175f95c4b1c6fc34e36c56f1e4ddbfLennart Poettering /* if we couldn't read the gid, then it will be 0, but that's
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering * fine and we shouldn't try to resolve the group again, so
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering * let's just pretend it worked right-away. */
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering s->file_gid_valid = true;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering}
2001c80560e3dae69e14fd994d3978c187af48b8Lennart Poettering
2001c80560e3dae69e14fd994d3978c187af48b8Lennart Poetteringvoid server_fix_perms(Server *s, JournalFile *f, uid_t uid) {
d2579eec5e1b845b2cf29caddc951dc22f2abb91Lennart Poettering int r;
d2579eec5e1b845b2cf29caddc951dc22f2abb91Lennart Poettering#ifdef HAVE_ACL
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering acl_t acl;
8ba9fd9cee0eef572f7b3ed7a8c3ed31160e93d3Lennart Poettering acl_entry_t entry;
8ba9fd9cee0eef572f7b3ed7a8c3ed31160e93d3Lennart Poettering acl_permset_t permset;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering#endif
faa133f3aa7a18f26563dc5d6b95898cb315c37aLennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering assert(f);
d830ebbdf67d8cb32d33d8fdd47cf467fd6d3815Lennart Poettering
d830ebbdf67d8cb32d33d8fdd47cf467fd6d3815Lennart Poettering server_read_file_gid(s);
d830ebbdf67d8cb32d33d8fdd47cf467fd6d3815Lennart Poettering
d830ebbdf67d8cb32d33d8fdd47cf467fd6d3815Lennart Poettering r = fchmod_and_fchown(f->fd, 0640, 0, s->file_gid);
d830ebbdf67d8cb32d33d8fdd47cf467fd6d3815Lennart Poettering if (r < 0)
d830ebbdf67d8cb32d33d8fdd47cf467fd6d3815Lennart Poettering log_warning("Failed to fix access mode/rights on %s, ignoring: %s", f->path, strerror(-r));
d830ebbdf67d8cb32d33d8fdd47cf467fd6d3815Lennart Poettering
d830ebbdf67d8cb32d33d8fdd47cf467fd6d3815Lennart Poettering#ifdef HAVE_ACL
f3abbe25403444688e1a1a23b9dbcc9aeefc0507Lennart Poettering if (uid <= 0)
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering return;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering acl = acl_get_fd(f->fd);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (!acl) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering log_warning("Failed to read ACL on %s, ignoring: %m", f->path);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering return;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering }
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering r = acl_find_uid(acl, uid, &entry);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (r <= 0) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (acl_create_entry(&acl, &entry) < 0 ||
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering acl_set_tag_type(entry, ACL_USER) < 0 ||
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering acl_set_qualifier(entry, &uid) < 0) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering log_warning("Failed to patch ACL on %s, ignoring: %m", f->path);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering goto finish;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering }
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering }
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering if (acl_get_permset(entry, &permset) < 0 ||
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering acl_add_perm(permset, ACL_READ) < 0 ||
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering acl_calc_mask(&acl) < 0) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering log_warning("Failed to patch ACL on %s, ignoring: %m", f->path);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering goto finish;
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering }
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering if (acl_set_fd(f->fd, acl) < 0)
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering log_warning("Failed to set ACL on %s, ignoring: %m", f->path);
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poetteringfinish:
1716f6dcf54d4c181c2e2558e3d5414f54c8d9caLennart Poettering acl_free(acl);
8db0d2f5c37e7e8f5bfce016cfdad7947a3ea939Zbigniew Jędrzejewski-Szmek#endif
0db4c90afd7d9c7c8884bf8b3ec459edc74a03daDaniel Mack}
b6c5d46b23a28b5b03601ee1e8162b1bc7c7be25Daniel Mack
0db4c90afd7d9c7c8884bf8b3ec459edc74a03daDaniel Mackstatic JournalFile* find_journal(Server *s, uid_t uid) {
931851e8e492a4d2715e22dcde50a5e7ccef4b49Lennart Poettering char *p;
931851e8e492a4d2715e22dcde50a5e7ccef4b49Lennart Poettering int r;
51323288fc628a5cac50914df915545d685b793eLennart Poettering JournalFile *f;
931851e8e492a4d2715e22dcde50a5e7ccef4b49Lennart Poettering sd_id128_t machine;
931851e8e492a4d2715e22dcde50a5e7ccef4b49Lennart Poettering
931851e8e492a4d2715e22dcde50a5e7ccef4b49Lennart Poettering assert(s);
51323288fc628a5cac50914df915545d685b793eLennart Poettering
106784ebb7b303ae471851100a773ad2aebf5b80Daniel Mack /* We split up user logs only on /var, not on /run. If the
106784ebb7b303ae471851100a773ad2aebf5b80Daniel Mack * runtime file is open, we write to it exclusively, in order
931851e8e492a4d2715e22dcde50a5e7ccef4b49Lennart Poettering * to guarantee proper order as soon as we flush /run to
51323288fc628a5cac50914df915545d685b793eLennart Poettering * /var and close the runtime file. */
106784ebb7b303ae471851100a773ad2aebf5b80Daniel Mack
931851e8e492a4d2715e22dcde50a5e7ccef4b49Lennart Poettering if (s->runtime_journal)
51323288fc628a5cac50914df915545d685b793eLennart Poettering return s->runtime_journal;
4e5bf5e15899de3f9d11c2ddfe9721d9f8b07a37Daniel Mack
4e5bf5e15899de3f9d11c2ddfe9721d9f8b07a37Daniel Mack if (uid <= 0)
4e5bf5e15899de3f9d11c2ddfe9721d9f8b07a37Daniel Mack return s->system_journal;
106784ebb7b303ae471851100a773ad2aebf5b80Daniel Mack
106784ebb7b303ae471851100a773ad2aebf5b80Daniel Mack r = sd_id128_get_machine(&machine);
106784ebb7b303ae471851100a773ad2aebf5b80Daniel Mack if (r < 0)
106784ebb7b303ae471851100a773ad2aebf5b80Daniel Mack return s->system_journal;
51323288fc628a5cac50914df915545d685b793eLennart Poettering
51323288fc628a5cac50914df915545d685b793eLennart Poettering f = hashmap_get(s->user_journals, UINT32_TO_PTR(uid));
if (f)
return f;
if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/user-%lu.journal",
SD_ID128_FORMAT_VAL(machine), (unsigned long) uid) < 0)
return s->system_journal;
while (hashmap_size(s->user_journals) >= USER_JOURNALS_MAX) {
/* Too many open? Then let's close one */
f = hashmap_steal_first(s->user_journals);
assert(f);
journal_file_close(f);
}
r = journal_file_open_reliably(p, O_RDWR|O_CREAT, 0640, s->compress, s->seal, &s->system_metrics, s->mmap, s->system_journal, &f);
free(p);
if (r < 0)
return s->system_journal;
server_fix_perms(s, f, uid);
r = hashmap_put(s->user_journals, UINT32_TO_PTR(uid), f);
if (r < 0) {
journal_file_close(f);
return s->system_journal;
}
return f;
}
void server_rotate(Server *s) {
JournalFile *f;
void *k;
Iterator i;
int r;
log_debug("Rotating...");
if (s->runtime_journal) {
r = journal_file_rotate(&s->runtime_journal, s->compress, false);
if (r < 0)
if (s->runtime_journal)
log_error("Failed to rotate %s: %s", s->runtime_journal->path, strerror(-r));
else
log_error("Failed to create new runtime journal: %s", strerror(-r));
else
server_fix_perms(s, s->runtime_journal, 0);
}
if (s->system_journal) {
r = journal_file_rotate(&s->system_journal, s->compress, s->seal);
if (r < 0)
if (s->system_journal)
log_error("Failed to rotate %s: %s", s->system_journal->path, strerror(-r));
else
log_error("Failed to create new system journal: %s", strerror(-r));
else
server_fix_perms(s, s->system_journal, 0);
}
HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) {
r = journal_file_rotate(&f, s->compress, s->seal);
if (r < 0)
if (f)
log_error("Failed to rotate %s: %s", f->path, strerror(-r));
else
log_error("Failed to create user journal: %s", strerror(-r));
else {
hashmap_replace(s->user_journals, k, f);
server_fix_perms(s, f, PTR_TO_UINT32(k));
}
}
}
void server_sync(Server *s) {
JournalFile *f;
void *k;
Iterator i;
int r;
static const struct itimerspec sync_timer_disable = {};
if (s->system_journal) {
r = journal_file_set_offline(s->system_journal);
if (r < 0)
log_error("Failed to sync system journal: %s", strerror(-r));
}
HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) {
r = journal_file_set_offline(f);
if (r < 0)
log_error("Failed to sync user journal: %s", strerror(-r));
}
r = timerfd_settime(s->sync_timer_fd, 0, &sync_timer_disable, NULL);
if (r < 0)
log_error("Failed to disable max timer: %m");
s->sync_scheduled = false;
}
void server_vacuum(Server *s) {
char *p;
char ids[33];
sd_id128_t machine;
int r;
log_debug("Vacuuming...");
s->oldest_file_usec = 0;
r = sd_id128_get_machine(&machine);
if (r < 0) {
log_error("Failed to get machine ID: %s", strerror(-r));
return;
}
sd_id128_to_string(machine, ids);
if (s->system_journal) {
p = strappend("/var/log/journal/", ids);
if (!p) {
log_oom();
return;
}
r = journal_directory_vacuum(p, s->system_metrics.max_use, s->system_metrics.keep_free, s->max_retention_usec, &s->oldest_file_usec);
if (r < 0 && r != -ENOENT)
log_error("Failed to vacuum %s: %s", p, strerror(-r));
free(p);
}
if (s->runtime_journal) {
p = strappend("/run/log/journal/", ids);
if (!p) {
log_oom();
return;
}
r = journal_directory_vacuum(p, s->runtime_metrics.max_use, s->runtime_metrics.keep_free, s->max_retention_usec, &s->oldest_file_usec);
if (r < 0 && r != -ENOENT)
log_error("Failed to vacuum %s: %s", p, strerror(-r));
free(p);
}
s->cached_available_space_timestamp = 0;
}
static char *shortened_cgroup_path(pid_t pid) {
int r;
char _cleanup_free_ *process_path = NULL, *init_path = NULL;
char *path;
assert(pid > 0);
r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &process_path);
if (r < 0)
return NULL;
r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &init_path);
if (r < 0)
return NULL;
if (endswith(init_path, "/system"))
init_path[strlen(init_path) - 7] = 0;
else if (streq(init_path, "/"))
init_path[0] = 0;
if (startswith(process_path, init_path)) {
path = strdup(process_path + strlen(init_path));
} else {
path = process_path;
process_path = NULL;
}
return path;
}
bool shall_try_append_again(JournalFile *f, int r) {
/* -E2BIG Hit configured limit
-EFBIG Hit fs limit
-EDQUOT Quota limit hit
-ENOSPC Disk full
-EHOSTDOWN Other machine
-EBUSY Unclean shutdown
-EPROTONOSUPPORT Unsupported feature
-EBADMSG Corrupted
-ENODATA Truncated
-ESHUTDOWN Already archived */
if (r == -E2BIG || r == -EFBIG || r == -EDQUOT || r == -ENOSPC)
log_debug("%s: Allocation limit reached, rotating.", f->path);
else if (r == -EHOSTDOWN)
log_info("%s: Journal file from other machine, rotating.", f->path);
else if (r == -EBUSY)
log_info("%s: Unclean shutdown, rotating.", f->path);
else if (r == -EPROTONOSUPPORT)
log_info("%s: Unsupported feature, rotating.", f->path);
else if (r == -EBADMSG || r == -ENODATA || r == ESHUTDOWN)
log_warning("%s: Journal file corrupted, rotating.", f->path);
else
return false;
return true;
}
static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned n) {
JournalFile *f;
bool vacuumed = false;
int r;
assert(s);
assert(iovec);
assert(n > 0);
f = find_journal(s, uid);
if (!f)
return;
if (journal_file_rotate_suggested(f, s->max_file_usec)) {
log_debug("%s: Journal header limits reached or header out-of-date, rotating.", f->path);
server_rotate(s);
server_vacuum(s);
vacuumed = true;
f = find_journal(s, uid);
if (!f)
return;
}
r = journal_file_append_entry(f, NULL, iovec, n, &s->seqnum, NULL, NULL);
if (r >= 0) {
server_schedule_sync(s);
return;
}
if (vacuumed || !shall_try_append_again(f, r)) {
log_error("Failed to write entry, ignoring: %s", strerror(-r));
return;
}
server_rotate(s);
server_vacuum(s);
f = find_journal(s, uid);
if (!f)
return;
log_debug("Retrying write.");
r = journal_file_append_entry(f, NULL, iovec, n, &s->seqnum, NULL, NULL);
if (r < 0)
log_error("Failed to write entry, ignoring: %s", strerror(-r));
}
static void dispatch_message_real(
Server *s,
struct iovec *iovec, unsigned n, unsigned m,
struct ucred *ucred,
struct timeval *tv,
const char *label, size_t label_len,
const char *unit_id) {
char pid[sizeof("_PID=") + DECIMAL_STR_MAX(ucred->pid)],
uid[sizeof("_UID=") + DECIMAL_STR_MAX(ucred->uid)],
gid[sizeof("_GID=") + DECIMAL_STR_MAX(ucred->gid)],
source_time[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t)],
boot_id[sizeof("_BOOT_ID=") + 32] = "_BOOT_ID=",
machine_id[sizeof("_MACHINE_ID=") + 32] = "_MACHINE_ID=";
char _cleanup_free_ *comm = NULL, *cmdline = NULL, *hostname = NULL,
*exe = NULL, *cgroup = NULL, *session = NULL,
*owner_uid = NULL, *unit = NULL, *selinux_context = NULL;
#ifdef HAVE_AUDIT
char _cleanup_free_ *audit_session = NULL, *audit_loginuid = NULL;
#endif
sd_id128_t id;
int r;
char *t;
uid_t realuid = 0, owner = 0, journal_uid;
bool owner_valid = false;
assert(s);
assert(iovec);
assert(n > 0);
assert(n + N_IOVEC_META_FIELDS <= m);
if (ucred) {
#ifdef HAVE_AUDIT
uint32_t audit;
uid_t loginuid;
#endif
realuid = ucred->uid;
snprintf(pid, sizeof(pid) - 1, "_PID=%lu", (unsigned long) ucred->pid);
char_array_0(pid);
IOVEC_SET_STRING(iovec[n++], pid);
snprintf(uid, sizeof(uid) - 1, "_UID=%lu", (unsigned long) ucred->uid);
char_array_0(uid);
IOVEC_SET_STRING(iovec[n++], uid);
snprintf(gid, sizeof(gid) - 1, "_GID=%lu", (unsigned long) ucred->gid);
char_array_0(gid);
IOVEC_SET_STRING(iovec[n++], gid);
r = get_process_comm(ucred->pid, &t);
if (r >= 0) {
comm = strappend("_COMM=", t);
free(t);
if (comm)
IOVEC_SET_STRING(iovec[n++], comm);
}
r = get_process_exe(ucred->pid, &t);
if (r >= 0) {
exe = strappend("_EXE=", t);
free(t);
if (exe)
IOVEC_SET_STRING(iovec[n++], exe);
}
r = get_process_cmdline(ucred->pid, 0, false, &t);
if (r >= 0) {
cmdline = strappend("_CMDLINE=", t);
free(t);
if (cmdline)
IOVEC_SET_STRING(iovec[n++], cmdline);
}
#ifdef HAVE_AUDIT
r = audit_session_from_pid(ucred->pid, &audit);
if (r >= 0)
if (asprintf(&audit_session, "_AUDIT_SESSION=%lu", (unsigned long) audit) >= 0)
IOVEC_SET_STRING(iovec[n++], audit_session);
r = audit_loginuid_from_pid(ucred->pid, &loginuid);
if (r >= 0)
if (asprintf(&audit_loginuid, "_AUDIT_LOGINUID=%lu", (unsigned long) loginuid) >= 0)
IOVEC_SET_STRING(iovec[n++], audit_loginuid);
#endif
t = shortened_cgroup_path(ucred->pid);
if (t) {
cgroup = strappend("_SYSTEMD_CGROUP=", t);
free(t);
if (cgroup)
IOVEC_SET_STRING(iovec[n++], cgroup);
}
#ifdef HAVE_LOGIND
if (sd_pid_get_session(ucred->pid, &t) >= 0) {
session = strappend("_SYSTEMD_SESSION=", t);
free(t);
if (session)
IOVEC_SET_STRING(iovec[n++], session);
}
if (sd_pid_get_owner_uid(ucred->pid, &owner) >= 0) {
owner_valid = true;
if (asprintf(&owner_uid, "_SYSTEMD_OWNER_UID=%lu", (unsigned long) owner) >= 0)
IOVEC_SET_STRING(iovec[n++], owner_uid);
}
#endif
if (cg_pid_get_unit(ucred->pid, &t) >= 0) {
unit = strappend("_SYSTEMD_UNIT=", t);
free(t);
} else if (cg_pid_get_user_unit(ucred->pid, &t) >= 0) {
unit = strappend("_SYSTEMD_USER_UNIT=", t);
free(t);
} else if (unit_id) {
if (session)
unit = strappend("_SYSTEMD_USER_UNIT=", unit_id);
else
unit = strappend("_SYSTEMD_UNIT=", unit_id);
}
if (unit)
IOVEC_SET_STRING(iovec[n++], unit);
#ifdef HAVE_SELINUX
if (label) {
selinux_context = malloc(sizeof("_SELINUX_CONTEXT=") + label_len);
if (selinux_context) {
*((char*) mempcpy(stpcpy(selinux_context, "_SELINUX_CONTEXT="), label, label_len)) = 0;
IOVEC_SET_STRING(iovec[n++], selinux_context);
}
} else {
security_context_t con;
if (getpidcon(ucred->pid, &con) >= 0) {
selinux_context = strappend("_SELINUX_CONTEXT=", con);
if (selinux_context)
IOVEC_SET_STRING(iovec[n++], selinux_context);
freecon(con);
}
}
#endif
}
if (tv) {
snprintf(source_time, sizeof(source_time) - 1, "_SOURCE_REALTIME_TIMESTAMP=%llu",
(unsigned long long) timeval_load(tv));
char_array_0(source_time);
IOVEC_SET_STRING(iovec[n++], source_time);
}
/* 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. */
r = sd_id128_get_boot(&id);
if (r >= 0) {
sd_id128_to_string(id, boot_id + sizeof("_BOOT_ID=") - 1);
IOVEC_SET_STRING(iovec[n++], boot_id);
}
r = sd_id128_get_machine(&id);
if (r >= 0) {
sd_id128_to_string(id, machine_id + sizeof("_MACHINE_ID=") - 1);
IOVEC_SET_STRING(iovec[n++], machine_id);
}
t = gethostname_malloc();
if (t) {
hostname = strappend("_HOSTNAME=", t);
free(t);
if (hostname)
IOVEC_SET_STRING(iovec[n++], hostname);
}
assert(n <= m);
if (s->split_mode == SPLIT_UID && realuid > 0)
/* Split up strictly by any UID */
journal_uid = realuid;
else if (s->split_mode == SPLIT_LOGIN && realuid > 0 && owner_valid && owner > 0)
/* Split up by login UIDs, this avoids creation of
* individual journals for system 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;
write_to_journal(s, journal_uid, iovec, n);
}
void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...) {
char mid[11 + 32 + 1];
char buffer[16 + LINE_MAX + 1];
struct iovec iovec[N_IOVEC_META_FIELDS + 4];
int n = 0;
va_list ap;
struct ucred ucred = {};
assert(s);
assert(format);
IOVEC_SET_STRING(iovec[n++], "PRIORITY=6");
IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=driver");
memcpy(buffer, "MESSAGE=", 8);
va_start(ap, format);
vsnprintf(buffer + 8, sizeof(buffer) - 8, format, ap);
va_end(ap);
char_array_0(buffer);
IOVEC_SET_STRING(iovec[n++], buffer);
if (!sd_id128_equal(message_id, SD_ID128_NULL)) {
snprintf(mid, sizeof(mid), MESSAGE_ID(message_id));
char_array_0(mid);
IOVEC_SET_STRING(iovec[n++], mid);
}
ucred.pid = getpid();
ucred.uid = getuid();
ucred.gid = getgid();
dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL, NULL, 0, NULL);
}
void server_dispatch_message(
Server *s,
struct iovec *iovec, unsigned n, unsigned m,
struct ucred *ucred,
struct timeval *tv,
const char *label, size_t label_len,
const char *unit_id,
int priority) {
int rl;
char _cleanup_free_ *path = NULL;
char *c;
assert(s);
assert(iovec || n == 0);
if (n == 0)
return;
if (LOG_PRI(priority) > s->max_level_store)
return;
if (!ucred)
goto finish;
path = shortened_cgroup_path(ucred->pid);
if (!path)
goto finish;
/* example: /user/lennart/3/foobar
* /system/dbus.service/foobar
*
* So let's cut of everything past the third /, since that is
* where user directories start */
c = strchr(path, '/');
if (c) {
c = strchr(c+1, '/');
if (c) {
c = strchr(c+1, '/');
if (c)
*c = 0;
}
}
rl = journal_rate_limit_test(s->rate_limit, path,
priority & LOG_PRIMASK, available_space(s));
if (rl == 0)
return;
/* Write a suppression message if we suppressed something */
if (rl > 1)
server_driver_message(s, SD_MESSAGE_JOURNAL_DROPPED,
"Suppressed %u messages from %s", rl - 1, path);
finish:
dispatch_message_real(s, iovec, n, m, ucred, tv, label, label_len, unit_id);
}
static int system_journal_open(Server *s) {
int r;
char *fn;
sd_id128_t machine;
char ids[33];
r = sd_id128_get_machine(&machine);
if (r < 0)
return r;
sd_id128_to_string(machine, ids);
if (!s->system_journal &&
(s->storage == STORAGE_PERSISTENT || s->storage == STORAGE_AUTO) &&
access("/run/systemd/journal/flushed", F_OK) >= 0) {
/* If in auto mode: first try to create the machine
* path, but not the prefix.
*
* If in persistent mode: create /var/log/journal and
* the machine path */
if (s->storage == STORAGE_PERSISTENT)
(void) mkdir("/var/log/journal/", 0755);
fn = strappend("/var/log/journal/", ids);
if (!fn)
return -ENOMEM;
(void) mkdir(fn, 0755);
free(fn);
fn = strjoin("/var/log/journal/", ids, "/system.journal", NULL);
if (!fn)
return -ENOMEM;
r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, s->compress, s->seal, &s->system_metrics, s->mmap, NULL, &s->system_journal);
free(fn);
if (r >= 0) {
char fb[FORMAT_BYTES_MAX];
server_fix_perms(s, s->system_journal, 0);
server_driver_message(s, SD_ID128_NULL, "Allowing system journal files to grow to %s.",
format_bytes(fb, sizeof(fb), s->system_metrics.max_use));
} else if (r < 0) {
if (r != -ENOENT && r != -EROFS)
log_warning("Failed to open system journal: %s", strerror(-r));
r = 0;
}
}
if (!s->runtime_journal &&
(s->storage != STORAGE_NONE)) {
fn = strjoin("/run/log/journal/", ids, "/system.journal", NULL);
if (!fn)
return -ENOMEM;
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);
free(fn);
if (r < 0) {
if (r != -ENOENT)
log_warning("Failed to open runtime journal: %s", strerror(-r));
r = 0;
}
} else {
/* OK, we really need the runtime journal, so create
* it if necessary. */
(void) mkdir_parents(fn, 0755);
r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, s->compress, false, &s->runtime_metrics, s->mmap, NULL, &s->runtime_journal);
free(fn);
if (r < 0) {
log_error("Failed to open runtime journal: %s", strerror(-r));
return r;
}
}
if (s->runtime_journal) {
char fb[FORMAT_BYTES_MAX];
server_fix_perms(s, s->runtime_journal, 0);
server_driver_message(s, SD_ID128_NULL, "Allowing runtime journal files to grow to %s.",
format_bytes(fb, sizeof(fb), s->runtime_metrics.max_use));
}
}
return r;
}
int server_flush_to_var(Server *s) {
int r;
sd_id128_t machine;
sd_journal *j = NULL;
assert(s);
if (s->storage != STORAGE_AUTO &&
s->storage != STORAGE_PERSISTENT)
return 0;
if (!s->runtime_journal)
return 0;
system_journal_open(s);
if (!s->system_journal)
return 0;
log_debug("Flushing to /var...");
r = sd_id128_get_machine(&machine);
if (r < 0) {
log_error("Failed to get machine id: %s", strerror(-r));
return r;
}
r = sd_journal_open(&j, SD_JOURNAL_RUNTIME_ONLY);
if (r < 0) {
log_error("Failed to read runtime journal: %s", strerror(-r));
return r;
}
sd_journal_set_data_threshold(j, 0);
SD_JOURNAL_FOREACH(j) {
Object *o = NULL;
JournalFile *f;
f = j->current_file;
assert(f && f->current_offset > 0);
r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o);
if (r < 0) {
log_error("Can't read entry: %s", strerror(-r));
goto finish;
}
r = journal_file_copy_entry(f, s->system_journal, o, f->current_offset, NULL, NULL, NULL);
if (r >= 0)
continue;
if (!shall_try_append_again(s->system_journal, r)) {
log_error("Can't write entry: %s", strerror(-r));
goto finish;
}
server_rotate(s);
server_vacuum(s);
log_debug("Retrying write.");
r = journal_file_copy_entry(f, s->system_journal, o, f->current_offset, NULL, NULL, NULL);
if (r < 0) {
log_error("Can't write entry: %s", strerror(-r));
goto finish;
}
}
finish:
journal_file_post_change(s->system_journal);
journal_file_close(s->runtime_journal);
s->runtime_journal = NULL;
if (r >= 0)
rm_rf("/run/log/journal", false, true, false);
sd_journal_close(j);
return r;
}
int process_event(Server *s, struct epoll_event *ev) {
assert(s);
assert(ev);
if (ev->data.fd == s->signal_fd) {
struct signalfd_siginfo sfsi;
ssize_t n;
if (ev->events != EPOLLIN) {
log_error("Got invalid event from epoll.");
return -EIO;
}
n = read(s->signal_fd, &sfsi, sizeof(sfsi));
if (n != sizeof(sfsi)) {
if (n >= 0)
return -EIO;
if (errno == EINTR || errno == EAGAIN)
return 1;
return -errno;
}
if (sfsi.ssi_signo == SIGUSR1) {
touch("/run/systemd/journal/flushed");
server_flush_to_var(s);
server_sync(s);
return 1;
}
if (sfsi.ssi_signo == SIGUSR2) {
server_rotate(s);
server_vacuum(s);
return 1;
}
log_info("Received SIG%s", signal_to_string(sfsi.ssi_signo));
return 0;
} else if (ev->data.fd == s->sync_timer_fd) {
int r;
uint64_t t;
log_debug("Got sync request from epoll.");
r = read(ev->data.fd, (void *)&t, sizeof(t));
if (r < 0)
return 0;
server_sync(s);
return 1;
} else if (ev->data.fd == s->dev_kmsg_fd) {
int r;
if (ev->events != EPOLLIN) {
log_error("Got invalid event from epoll.");
return -EIO;
}
r = server_read_dev_kmsg(s);
if (r < 0)
return r;
return 1;
} else if (ev->data.fd == s->native_fd ||
ev->data.fd == s->syslog_fd) {
if (ev->events != EPOLLIN) {
log_error("Got invalid event from epoll.");
return -EIO;
}
for (;;) {
struct msghdr msghdr;
struct iovec iovec;
struct ucred *ucred = NULL;
struct timeval *tv = NULL;
struct cmsghdr *cmsg;
char *label = NULL;
size_t label_len = 0;
union {
struct cmsghdr cmsghdr;
/* 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.*/
uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
CMSG_SPACE(sizeof(struct timeval)) +
CMSG_SPACE(sizeof(int)) + /* fd */
CMSG_SPACE(NAME_MAX)]; /* selinux label */
} control;
ssize_t n;
int v;
int *fds = NULL;
unsigned n_fds = 0;
if (ioctl(ev->data.fd, SIOCINQ, &v) < 0) {
log_error("SIOCINQ failed: %m");
return -errno;
}
if (s->buffer_size < (size_t) v) {
void *b;
size_t l;
l = MAX(LINE_MAX + (size_t) v, s->buffer_size * 2);
b = realloc(s->buffer, l+1);
if (!b) {
log_error("Couldn't increase buffer.");
return -ENOMEM;
}
s->buffer_size = l;
s->buffer = b;
}
zero(iovec);
iovec.iov_base = s->buffer;
iovec.iov_len = s->buffer_size;
zero(control);
zero(msghdr);
msghdr.msg_iov = &iovec;
msghdr.msg_iovlen = 1;
msghdr.msg_control = &control;
msghdr.msg_controllen = sizeof(control);
n = recvmsg(ev->data.fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
if (n < 0) {
if (errno == EINTR || errno == EAGAIN)
return 1;
log_error("recvmsg() failed: %m");
return -errno;
}
for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_CREDENTIALS &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)))
ucred = (struct ucred*) CMSG_DATA(cmsg);
else if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_SECURITY) {
label = (char*) CMSG_DATA(cmsg);
label_len = cmsg->cmsg_len - CMSG_LEN(0);
} else if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SO_TIMESTAMP &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
tv = (struct timeval*) CMSG_DATA(cmsg);
else if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_RIGHTS) {
fds = (int*) CMSG_DATA(cmsg);
n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
}
}
if (ev->data.fd == s->syslog_fd) {
char *e;
if (n > 0 && n_fds == 0) {
e = memchr(s->buffer, '\n', n);
if (e)
*e = 0;
else
s->buffer[n] = 0;
server_process_syslog_message(s, strstrip(s->buffer), ucred, tv, label, label_len);
} else if (n_fds > 0)
log_warning("Got file descriptors via syslog socket. Ignoring.");
} else {
if (n > 0 && n_fds == 0)
server_process_native_message(s, s->buffer, n, ucred, tv, label, label_len);
else if (n == 0 && n_fds == 1)
server_process_native_file(s, fds[0], ucred, tv, label, label_len);
else if (n_fds > 0)
log_warning("Got too many file descriptors via native socket. Ignoring.");
}
close_many(fds, n_fds);
}
return 1;
} else if (ev->data.fd == s->stdout_fd) {
if (ev->events != EPOLLIN) {
log_error("Got invalid event from epoll.");
return -EIO;
}
stdout_stream_new(s);
return 1;
} else {
StdoutStream *stream;
if ((ev->events|EPOLLIN|EPOLLHUP) != (EPOLLIN|EPOLLHUP)) {
log_error("Got invalid event from epoll.");
return -EIO;
}
/* If it is none of the well-known fds, it must be an
* stdout stream fd. Note that this is a bit ugly here
* (since we rely that none of the well-known fds
* could be interpreted as pointer), but nonetheless
* safe, since the well-known fds would never get an
* fd > 4096, i.e. beyond the first memory page */
stream = ev->data.ptr;
if (stdout_stream_process(stream) <= 0)
stdout_stream_free(stream);
return 1;
}
log_error("Unknown event.");
return 0;
}
static int open_signalfd(Server *s) {
sigset_t mask;
struct epoll_event ev;
assert(s);
assert_se(sigemptyset(&mask) == 0);
sigset_add_many(&mask, SIGINT, SIGTERM, SIGUSR1, SIGUSR2, -1);
assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
s->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
if (s->signal_fd < 0) {
log_error("signalfd(): %m");
return -errno;
}
zero(ev);
ev.events = EPOLLIN;
ev.data.fd = s->signal_fd;
if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->signal_fd, &ev) < 0) {
log_error("epoll_ctl(): %m");
return -errno;
}
return 0;
}
static int server_parse_proc_cmdline(Server *s) {
char _cleanup_free_ *line = NULL;
char *w, *state;
int r;
size_t l;
if (detect_container(NULL) > 0)
return 0;
r = read_one_line_file("/proc/cmdline", &line);
if (r < 0) {
log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
return 0;
}
FOREACH_WORD_QUOTED(w, l, line, state) {
char _cleanup_free_ *word;
word = strndup(w, l);
if (!word)
return -ENOMEM;
if (startswith(word, "systemd.journald.forward_to_syslog=")) {
r = parse_boolean(word + 35);
if (r < 0)
log_warning("Failed to parse forward to syslog switch %s. Ignoring.", word + 35);
else
s->forward_to_syslog = r;
} else if (startswith(word, "systemd.journald.forward_to_kmsg=")) {
r = parse_boolean(word + 33);
if (r < 0)
log_warning("Failed to parse forward to kmsg switch %s. Ignoring.", word + 33);
else
s->forward_to_kmsg = r;
} else if (startswith(word, "systemd.journald.forward_to_console=")) {
r = parse_boolean(word + 36);
if (r < 0)
log_warning("Failed to parse forward to console switch %s. Ignoring.", word + 36);
else
s->forward_to_console = r;
} else if (startswith(word, "systemd.journald"))
log_warning("Invalid systemd.journald parameter. Ignoring.");
}
return 0;
}
static int server_parse_config_file(Server *s) {
static const char *fn = "/etc/systemd/journald.conf";
FILE _cleanup_fclose_ *f = NULL;
int r;
assert(s);
f = fopen(fn, "re");
if (!f) {
if (errno == ENOENT)
return 0;
log_warning("Failed to open configuration file %s: %m", fn);
return -errno;
}
r = config_parse(fn, f, "Journal\0", config_item_perf_lookup,
(void*) journald_gperf_lookup, false, s);
if (r < 0)
log_warning("Failed to parse configuration file: %s", strerror(-r));
return r;
}
static int server_open_sync_timer(Server *s) {
int r;
struct epoll_event ev;
assert(s);
s->sync_timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
if (s->sync_timer_fd < 0)
return -errno;
zero(ev);
ev.events = EPOLLIN;
ev.data.fd = s->sync_timer_fd;
r = epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->sync_timer_fd, &ev);
if (r < 0) {
log_error("Failed to add idle timer fd to epoll object: %m");
return -errno;
}
return 0;
}
int server_schedule_sync(Server *s) {
int r;
assert(s);
if (s->sync_scheduled)
return 0;
if (s->sync_interval_usec) {
struct itimerspec sync_timer_enable = {
.it_value.tv_sec = s->sync_interval_usec / USEC_PER_SEC,
.it_value.tv_nsec = s->sync_interval_usec % MSEC_PER_SEC,
};
r = timerfd_settime(s->sync_timer_fd, 0, &sync_timer_enable, NULL);
if (r < 0)
return -errno;
}
s->sync_scheduled = true;
return 0;
}
int server_init(Server *s) {
int n, r, fd;
assert(s);
zero(*s);
s->sync_timer_fd = s->syslog_fd = s->native_fd = s->stdout_fd =
s->signal_fd = s->epoll_fd = s->dev_kmsg_fd = -1;
s->compress = true;
s->seal = true;
s->sync_interval_usec = DEFAULT_SYNC_INTERVAL_USEC;
s->sync_scheduled = false;
s->rate_limit_interval = DEFAULT_RATE_LIMIT_INTERVAL;
s->rate_limit_burst = DEFAULT_RATE_LIMIT_BURST;
s->forward_to_syslog = 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;
memset(&s->system_metrics, 0xFF, sizeof(s->system_metrics));
memset(&s->runtime_metrics, 0xFF, sizeof(s->runtime_metrics));
server_parse_config_file(s);
server_parse_proc_cmdline(s);
if (!!s->rate_limit_interval ^ !!s->rate_limit_burst) {
log_debug("Setting both rate limit interval and burst from %llu,%u to 0,0",
(long long unsigned) s->rate_limit_interval,
s->rate_limit_burst);
s->rate_limit_interval = s->rate_limit_burst = 0;
}
mkdir_p("/run/systemd/journal", 0755);
s->user_journals = hashmap_new(trivial_hash_func, trivial_compare_func);
if (!s->user_journals)
return log_oom();
s->mmap = mmap_cache_new();
if (!s->mmap)
return log_oom();
s->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (s->epoll_fd < 0) {
log_error("Failed to create epoll object: %m");
return -errno;
}
n = sd_listen_fds(true);
if (n < 0) {
log_error("Failed to read listening file descriptors from environment: %s", strerror(-n));
return n;
}
for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/run/systemd/journal/socket", 0) > 0) {
if (s->native_fd >= 0) {
log_error("Too many native sockets passed.");
return -EINVAL;
}
s->native_fd = fd;
} else if (sd_is_socket_unix(fd, SOCK_STREAM, 1, "/run/systemd/journal/stdout", 0) > 0) {
if (s->stdout_fd >= 0) {
log_error("Too many stdout sockets passed.");
return -EINVAL;
}
s->stdout_fd = fd;
} else if (sd_is_socket_unix(fd, SOCK_DGRAM, -1, "/dev/log", 0) > 0) {
if (s->syslog_fd >= 0) {
log_error("Too many /dev/log sockets passed.");
return -EINVAL;
}
s->syslog_fd = fd;
} else {
log_error("Unknown socket passed.");
return -EINVAL;
}
}
r = server_open_syslog_socket(s);
if (r < 0)
return r;
r = server_open_native_socket(s);
if (r < 0)
return r;
r = server_open_stdout_socket(s);
if (r < 0)
return r;
r = server_open_dev_kmsg(s);
if (r < 0)
return r;
r = server_open_kernel_seqnum(s);
if (r < 0)
return r;
r = server_open_sync_timer(s);
if (r < 0)
return r;
r = open_signalfd(s);
if (r < 0)
return r;
s->udev = udev_new();
if (!s->udev)
return -ENOMEM;
s->rate_limit = journal_rate_limit_new(s->rate_limit_interval,
s->rate_limit_burst);
if (!s->rate_limit)
return -ENOMEM;
r = system_journal_open(s);
if (r < 0)
return r;
return 0;
}
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)
journal_file_maybe_append_tag(s->system_journal, n);
HASHMAP_FOREACH(f, s->user_journals, i)
journal_file_maybe_append_tag(f, n);
#endif
}
void server_done(Server *s) {
JournalFile *f;
assert(s);
while (s->stdout_streams)
stdout_stream_free(s->stdout_streams);
if (s->system_journal)
journal_file_close(s->system_journal);
if (s->runtime_journal)
journal_file_close(s->runtime_journal);
while ((f = hashmap_steal_first(s->user_journals)))
journal_file_close(f);
hashmap_free(s->user_journals);
if (s->epoll_fd >= 0)
close_nointr_nofail(s->epoll_fd);
if (s->signal_fd >= 0)
close_nointr_nofail(s->signal_fd);
if (s->syslog_fd >= 0)
close_nointr_nofail(s->syslog_fd);
if (s->native_fd >= 0)
close_nointr_nofail(s->native_fd);
if (s->stdout_fd >= 0)
close_nointr_nofail(s->stdout_fd);
if (s->dev_kmsg_fd >= 0)
close_nointr_nofail(s->dev_kmsg_fd);
if (s->sync_timer_fd >= 0)
close_nointr_nofail(s->sync_timer_fd);
if (s->rate_limit)
journal_rate_limit_free(s->rate_limit);
if (s->kernel_seqnum)
munmap(s->kernel_seqnum, sizeof(uint64_t));
free(s->buffer);
free(s->tty_path);
if (s->mmap)
mmap_cache_unref(s->mmap);
if (s->udev)
udev_unref(s->udev);
}