SD_BUS_PROPERTY("LogRateLimitIntervalUSec", "t", bus_property_get_usec, offsetof(ExecContext, log_ratelimit_interval_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LogRateLimitBurst", "u", bus_property_get_unsigned, offsetof(ExecContext, log_ratelimit_burst), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LogExtraFields", "aay", property_get_log_extra_fields, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("LogNamespace", "s", NULL, offsetof(ExecContext, log_namespace), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SecureBits", "i", bus_property_get_int, offsetof(ExecContext, secure_bits), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CapabilityBoundingSet", "t", NULL, offsetof(ExecContext, capability_bounding_set), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("AmbientCapabilities", "t", NULL, offsetof(ExecContext, capability_ambient_set), SD_BUS_VTABLE_PROPERTY_CONST),
return 1;
+ } else if (streq(name, "LogNamespace")) {
+ const char *n;
+
+ r = sd_bus_message_read(message, "s", &n);
+ if (r < 0)
+ return r;
+
+ if (!isempty(n) && !log_namespace_name_valid(n))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Log namespace name not valid");
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+
+ if (isempty(n)) {
+ c->log_namespace = mfree(c->log_namespace);
+ unit_write_settingf(u, flags, name, "%s=", name);
+ } else {
+ r = free_and_strdup(&c->log_namespace, n);
+ if (r < 0)
+ return r;
+
+ unit_write_settingf(u, flags, name, "%s=%s", name, n);
+ }
+ }
+
+ return 1;
+
} else if (streq(name, "LogExtraFields")) {
size_t n = 0;
return move_fd(fd, nfd, false);
}
-static int connect_journal_socket(int fd, uid_t uid, gid_t gid) {
- static const union sockaddr_union sa = {
+static int connect_journal_socket(
+ int fd,
+ const char *log_namespace,
+ uid_t uid,
+ gid_t gid) {
+
+ union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
- .un.sun_path = "/run/systemd/journal/stdout",
};
uid_t olduid = UID_INVALID;
gid_t oldgid = GID_INVALID;
+ const char *j;
int r;
+ j = log_namespace ?
+ strjoina("/run/systemd/journal.", log_namespace, "/stdout") :
+ "/run/systemd/journal/stdout";
+ r = sockaddr_un_set_path(&sa.un, j);
+ if (r < 0)
+ return r;
+
if (gid_is_valid(gid)) {
oldgid = getgid();
if (fd < 0)
return -errno;
- r = connect_journal_socket(fd, uid, gid);
+ r = connect_journal_socket(fd, context->log_namespace, uid, gid);
if (r < 0)
return r;
assert(p);
assert(ret);
- our_env = new0(char*, 14 + _EXEC_DIRECTORY_TYPE_MAX);
+ our_env = new0(char*, 15 + _EXEC_DIRECTORY_TYPE_MAX);
if (!our_env)
return -ENOMEM;
our_env[n_env++] = x;
}
+ if (c->log_namespace) {
+ x = strjoin("LOG_NAMESPACE=", c->log_namespace);
+ if (!x)
+ return -ENOMEM;
+
+ our_env[n_env++] = x;
+ }
+
for (t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
_cleanup_free_ char *pre = NULL, *joined = NULL;
const char *n;
!strv_isempty(context->directories[EXEC_DIRECTORY_LOGS].paths)))
return true;
+ if (context->log_namespace)
+ return true;
+
return false;
}
if (!path_equal(bind_mounts[i].source, bind_mounts[i].destination))
return true;
+ if (context->log_namespace)
+ return true;
+
return false;
}
context->n_temporary_filesystems,
tmp,
var,
+ context->log_namespace,
needs_sandboxing ? context->protect_home : PROTECT_HOME_NO,
needs_sandboxing ? context->protect_system : PROTECT_SYSTEM_NO,
context->mount_flags,
c->stdin_data_size = 0;
c->network_namespace_path = mfree(c->network_namespace_path);
+
+ c->log_namespace = mfree(c->log_namespace);
}
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) {
}
}
+ if (c->log_namespace)
+ fprintf(f, "%sLogNamespace: %s\n", prefix, c->log_namespace);
+
if (c->secure_bits) {
_cleanup_free_ char *str = NULL;
int log_level_max;
+ char *log_namespace;
+
bool private_tmp;
bool private_network;
bool private_devices;
$1.ProtectClock, config_parse_bool, 0, offsetof($1, exec_context.protect_clock)
$1.ProtectControlGroups, config_parse_bool, 0, offsetof($1, exec_context.protect_control_groups)
$1.NetworkNamespacePath, config_parse_unit_path_printf, 0, offsetof($1, exec_context.network_namespace_path)
+$1.LogNamespace, config_parse_log_namespace, 0, offsetof($1, exec_context)
$1.PrivateNetwork, config_parse_bool, 0, offsetof($1, exec_context.private_network)
$1.PrivateUsers, config_parse_bool, 0, offsetof($1, exec_context.private_users)
$1.PrivateMounts, config_parse_bool, 0, offsetof($1, exec_context.private_mounts)
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
+#include "syslog-util.h"
+#include "time-util.h"
#include "unit-name.h"
#include "unit-printf.h"
#include "user-util.h"
-#include "time-util.h"
#include "web-util.h"
static int parse_socket_protocol(const char *s) {
}
}
+int config_parse_log_namespace(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_free_ char *k = NULL;
+ ExecContext *c = data;
+ const Unit *u = userdata;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(c);
+
+ if (isempty(rvalue)) {
+ c->log_namespace = mfree(c->log_namespace);
+ return 0;
+ }
+
+ r = unit_full_printf(u, rvalue, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue);
+ return 0;
+ }
+
+ if (!log_namespace_name_valid(k)) {
+ log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), "Specified log namespace name is not valid: %s", k);
+ return 0;
+ }
+
+ free_and_replace(c->log_namespace, k);
+ return 0;
+}
+
int config_parse_unit_condition_path(
const char *unit,
const char *filename,
CONFIG_PARSER_PROTOTYPE(config_parse_job_timeout_sec);
CONFIG_PARSER_PROTOTYPE(config_parse_job_running_timeout_sec);
CONFIG_PARSER_PROTOTYPE(config_parse_log_extra_fields);
+CONFIG_PARSER_PROTOTYPE(config_parse_log_namespace);
CONFIG_PARSER_PROTOTYPE(config_parse_collect_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_pid_file);
CONFIG_PARSER_PROTOTYPE(config_parse_exit_status);
size_t n_temporary_filesystems,
const char* tmp_dir,
const char* var_tmp_dir,
+ const char* log_namespace,
ProtectHome protect_home,
ProtectSystem protect_system) {
(ns_info->protect_control_groups ? 1 : 0) +
protect_home_cnt + protect_system_cnt +
(ns_info->protect_hostname ? 2 : 0) +
- (namespace_info_mount_apivfs(ns_info) ? ELEMENTSOF(apivfs_table) : 0);
+ (namespace_info_mount_apivfs(ns_info) ? ELEMENTSOF(apivfs_table) : 0) +
+ !!log_namespace;
}
static void normalize_mounts(const char *root_directory, MountEntry *mounts, size_t *n_mounts) {
size_t n_temporary_filesystems,
const char* tmp_dir,
const char* var_tmp_dir,
+ const char *log_namespace,
ProtectHome protect_home,
ProtectSystem protect_system,
unsigned long mount_flags,
n_bind_mounts,
n_temporary_filesystems,
tmp_dir, var_tmp_dir,
+ log_namespace,
protect_home, protect_system);
if (n_mounts > 0) {
};
}
+ if (log_namespace) {
+ _cleanup_free_ char *q;
+
+ q = strjoin("/run/systemd/journal.", log_namespace);
+ if (!q) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ *(m++) = (MountEntry) {
+ .path_const = "/run/systemd/journal",
+ .mode = BIND_MOUNT_RECURSIVE,
+ .read_only = true,
+ .source_malloc = TAKE_PTR(q),
+ };
+ }
+
assert(mounts + n_mounts == m);
/* Prepend the root directory where that's necessary */
size_t n_temporary_filesystems,
const char *tmp_dir,
const char *var_tmp_dir,
+ const char *log_namespace,
ProtectHome protect_home,
ProtectSystem protect_system,
unsigned long mount_flags,
!IN_SET(c->std_error,
EXEC_OUTPUT_JOURNAL, EXEC_OUTPUT_JOURNAL_AND_CONSOLE,
EXEC_OUTPUT_KMSG, EXEC_OUTPUT_KMSG_AND_CONSOLE,
- EXEC_OUTPUT_SYSLOG, EXEC_OUTPUT_SYSLOG_AND_CONSOLE))
+ EXEC_OUTPUT_SYSLOG, EXEC_OUTPUT_SYSLOG_AND_CONSOLE) &&
+ !c->log_namespace)
return 0;
- /* If syslog or kernel logging is requested, make sure our own
- * logging daemon is run first. */
+ /* If syslog or kernel logging is requested (or log namespacing is), make sure our own logging daemon
+ * is run first. */
+
+ if (c->log_namespace) {
+ _cleanup_free_ char *socket_unit = NULL;
+
+ r = unit_name_build_from_type("systemd-journald", c->log_namespace, UNIT_SOCKET, &socket_unit);
+ if (r < 0)
+ return r;
- r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, true, UNIT_DEPENDENCY_FILE);
+ r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, socket_unit, true, UNIT_DEPENDENCY_FILE);
+ if (r < 0)
+ return r;
+ } else
+ r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_JOURNALD_SOCKET, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
"RuntimeDirectoryPreserve",
"Personality",
"KeyringMode",
- "NetworkNamespacePath"))
+ "NetworkNamespacePath",
+ "LogNamespace"))
return bus_append_string(m, field, eq);
if (STR_IN_SET(field, "IgnoreSIGPIPE",
NULL, 0,
NULL,
NULL,
+ NULL,
PROTECT_HOME_NO,
PROTECT_SYSTEM_NO,
0,
&(TemporaryFileSystem) { .path = (char*) "/var", .options = (char*) "ro" }, 1,
tmp_dir,
var_tmp_dir,
+ NULL,
PROTECT_HOME_NO,
PROTECT_SYSTEM_NO,
0,