summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO6
-rw-r--r--configure.ac1
-rw-r--r--man/systemd.exec.xml20
-rw-r--r--man/systemd.resource-control.xml48
-rw-r--r--src/basic/formats-util.h16
-rw-r--r--src/basic/parse-util.c19
-rw-r--r--src/basic/parse-util.h2
-rw-r--r--src/basic/util.c62
-rw-r--r--src/basic/util.h1
-rw-r--r--src/core/dbus-cgroup.c60
-rw-r--r--src/core/execute.c72
-rw-r--r--src/core/load-fragment.c31
-rw-r--r--src/core/unit.c2
-rw-r--r--src/libsystemd-network/sd-ndisc.c2
-rw-r--r--src/login/logind-user.c29
-rw-r--r--src/login/logind.c2
-rw-r--r--src/shared/bus-unit-util.c58
-rw-r--r--src/test/test-parse-util.c19
-rw-r--r--src/test/test-util.c50
19 files changed, 391 insertions, 109 deletions
diff --git a/TODO b/TODO
index 929fb96491..5de4901133 100644
--- a/TODO
+++ b/TODO
@@ -33,7 +33,10 @@ Janitorial Clean-ups:
Features:
-* use phyical_memory() to allow MemoryLimit= configuration based on available system memory
+* resolved: make sure when we get an ip address with ifindex suffix, we handle
+ it nicely
+
+* resolved: maybe add a switch to disable any local caching
* ProtectKernelLogs= (drops CAP_SYSLOG, add seccomp for syslog() syscall, and DeviceAllow to /dev/kmsg) in service files
@@ -227,7 +230,6 @@ Features:
- resolved should optionally register additional per-interface LLMNR
names, so that for the container case we can establish the same name
(maybe "host") for referencing the server, everywhere.
- - enable DNSSEC by default
- allow clients to request DNSSEC for a single lookup even if DNSSEC is off (?)
* refcounting in sd-resolve is borked
diff --git a/configure.ac b/configure.ac
index f4fcc4f113..072b17f01a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -249,6 +249,7 @@ AC_CHECK_SIZEOF(uid_t)
AC_CHECK_SIZEOF(gid_t)
AC_CHECK_SIZEOF(time_t)
AC_CHECK_SIZEOF(dev_t)
+AC_CHECK_SIZEOF(ino_t)
AC_CHECK_SIZEOF(rlim_t,,[
#include <sys/time.h>
#include <sys/resource.h>
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index a39e800854..dbfc7692f7 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -1539,6 +1539,26 @@
<citerefentry project='man-pages'><refentrytitle>termcap</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
</para></listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><varname>$JOURNAL_STREAM</varname></term>
+
+ <listitem><para>If the standard output or standard error output of the executed processes are connected to the
+ journal (for example, by setting <varname>StandardError=journal</varname>) <varname>$JOURNAL_STREAM</varname>
+ contains the device and inode numbers of the connection file descriptor, formatted in decimal, separated by a
+ colon (<literal>:</literal>). This permits invoked processes to safely detect whether their standard output or
+ standard error output are connected to the journal. The device and inode numbers of the file descriptors should
+ be compared with the values set in the environment variable to determine whether the process output is still
+ connected to the journal. Note that it is generally not sufficient to only check whether
+ <varname>$JOURNAL_STREAM</varname> is set at all as services might invoke external processes replacing their
+ standard output or standard error output, without unsetting the environment variable.</para>
+
+ <para>This environment variable is primarily useful to allow services to optionally upgrade their used log
+ protocol to the native journal protocol (using
+ <citerefentry><refentrytitle>sd_journal_print</refentrytitle><manvolnum>3</manvolnum></citerefentry> and other
+ functions) if their standard output or standard error output is connected to the journal anyway, thus enabling
+ delivery of structured metadata along with logged messages.</para></listitem>
+ </varlistentry>
</variablelist>
<para>Additional variables may be configured by the following
diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml
index d4c8fa7091..7263c0b329 100644
--- a/man/systemd.resource-control.xml
+++ b/man/systemd.resource-control.xml
@@ -92,16 +92,14 @@
<refsect1>
<title>Automatic Dependencies</title>
- <para>Units with the <varname>Slice=</varname> setting set get
- automatic <varname>Requires=</varname> and
- <varname>After=</varname> dependencies on the specified slice
- unit.</para>
+ <para>Units with the <varname>Slice=</varname> setting set automatically acquire <varname>Requires=</varname> and
+ <varname>After=</varname> dependencies on the specified slice unit.</para>
</refsect1>
<refsect1>
<title>Unified and Legacy Control Group Hierarchies</title>
- <para>Unified control group hierarchy is the new version of kernel control group interface. Depending on the
+ <para>The unified control group hierarchy is the new version of kernel control group interface. Depending on the
resource type, there are differences in resource control capabilities. Also, because of interface changes, some
resource types have a separate set of options on the unified hierarchy.</para>
@@ -117,8 +115,8 @@
<varlistentry>
<term><option>Memory</option></term>
<listitem>
- <para><varname>MemoryMax</varname> replaces <varname>MemoryLimit</varname>. <varname>MemoryLow</varname>
- and <varname>MemoryHigh</varname> are effective only on unified hierarchy.</para>
+ <para><varname>MemoryMax=</varname> replaces <varname>MemoryLimit=</varname>. <varname>MemoryLow=</varname>
+ and <varname>MemoryHigh=</varname> are effective only on unified hierarchy.</para>
</listitem>
</varlistentry>
</variablelist>
@@ -228,9 +226,11 @@
reclaimed as long as memory can be reclaimed from unprotected units.</para>
<para>Takes a memory size in bytes. If the value is suffixed with K, M, G or T, the specified memory size is
- parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. This controls the
- <literal>memory.low</literal> control group attribute. For details about this control group attribute, see
- <ulink url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
+ parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. Alternatively, a
+ percentage value may be specified, which is taken relative to the installed physical memory on the
+ system. This controls the <literal>memory.low</literal> control group attribute. For details about this
+ control group attribute, see <ulink
+ url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
<para>Implies <literal>MemoryAccounting=true</literal>.</para>
@@ -247,7 +247,9 @@
aggressively in such cases. This is the main mechanism to control memory usage of a unit.</para>
<para>Takes a memory size in bytes. If the value is suffixed with K, M, G or T, the specified memory size is
- parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. If assigned the
+ parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. Alternatively, a
+ percentage value may be specified, which is taken relative to the installed physical memory on the
+ system. If assigned the
special value <literal>infinity</literal>, no memory limit is applied. This controls the
<literal>memory.high</literal> control group attribute. For details about this control group attribute, see
<ulink url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
@@ -268,8 +270,9 @@
last line of defense.</para>
<para>Takes a memory size in bytes. If the value is suffixed with K, M, G or T, the specified memory size is
- parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. If assigned the
- special value <literal>infinity</literal>, no memory limit is applied. This controls the
+ parsed as Kilobytes, Megabytes, Gigabytes, or Terabytes (with the base 1024), respectively. Alternatively, a
+ percentage value may be specified, which is taken relative to the installed physical memory on the system. If
+ assigned the special value <literal>infinity</literal>, no memory limit is applied. This controls the
<literal>memory.max</literal> control group attribute. For details about this control group attribute, see
<ulink url="https://www.kernel.org/doc/Documentation/cgroup-v2.txt">cgroup-v2.txt</ulink>.</para>
@@ -284,17 +287,14 @@
<term><varname>MemoryLimit=<replaceable>bytes</replaceable></varname></term>
<listitem>
- <para>Specify the limit on maximum memory usage of the
- executed processes. The limit specifies how much process and
- kernel memory can be used by tasks in this unit. Takes a
- memory size in bytes. If the value is suffixed with K, M, G
- or T, the specified memory size is parsed as Kilobytes,
- Megabytes, Gigabytes, or Terabytes (with the base 1024),
- respectively. If assigned the special value
- <literal>infinity</literal>, no memory limit is applied. This
- controls the <literal>memory.limit_in_bytes</literal>
- control group attribute. For details about this control
- group attribute, see <ulink
+ <para>Specify the limit on maximum memory usage of the executed processes. The limit specifies how much
+ process and kernel memory can be used by tasks in this unit. Takes a memory size in bytes. If the value is
+ suffixed with K, M, G or T, the specified memory size is parsed as Kilobytes, Megabytes, Gigabytes, or
+ Terabytes (with the base 1024), respectively. Alternatively, a percentage value may be specified, which is
+ taken relative to the installed physical memory on the system. If assigned the special value
+ <literal>infinity</literal>, no memory limit is applied. This controls the
+ <literal>memory.limit_in_bytes</literal> control group attribute. For details about this control group
+ attribute, see <ulink
url="https://www.kernel.org/doc/Documentation/cgroup-v1/memory.txt">memory.txt</ulink>.</para>
<para>Implies <literal>MemoryAccounting=true</literal>.</para>
diff --git a/src/basic/formats-util.h b/src/basic/formats-util.h
index 9b4e8e98fa..39a185f59b 100644
--- a/src/basic/formats-util.h
+++ b/src/basic/formats-util.h
@@ -61,3 +61,19 @@
#else
# error Unknown rlim_t size
#endif
+
+#if SIZEOF_DEV_T == 8
+# define DEV_FMT "%" PRIu64
+#elif SIZEOF_DEV_T == 4
+# define DEV_FMT "%" PRIu32
+#else
+# error Unknown dev_t size
+#endif
+
+#if SIZEOF_INO_T == 8
+# define INO_FMT "%" PRIu64
+#elif SIZEOF_INO_T == 4
+# define INO_FMT "%" PRIu32
+#else
+# error Unknown ino_t size
+#endif
diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c
index 6c11b605a9..503a895731 100644
--- a/src/basic/parse-util.c
+++ b/src/basic/parse-util.c
@@ -532,3 +532,22 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
return 0;
}
+
+int parse_percent(const char *p) {
+ const char *pc, *n;
+ unsigned v;
+ int r;
+
+ pc = endswith(p, "%");
+ if (!pc)
+ return -EINVAL;
+
+ n = strndupa(p, pc - p);
+ r = safe_atou(n, &v);
+ if (r < 0)
+ return r;
+ if (v > 100)
+ return -ERANGE;
+
+ return (int) v;
+}
diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h
index 7dc579a159..73441bb6fd 100644
--- a/src/basic/parse-util.h
+++ b/src/basic/parse-util.h
@@ -105,3 +105,5 @@ static inline int safe_atozu(const char *s, size_t *ret_u) {
int safe_atod(const char *s, double *ret_d);
int parse_fractional_part_u(const char **s, size_t digits, unsigned *res);
+
+int parse_percent(const char *p);
diff --git a/src/basic/util.c b/src/basic/util.c
index f2f92fb3b7..09d16697b7 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -36,6 +36,7 @@
#include "alloc-util.h"
#include "build.h"
+#include "cgroup-util.h"
#include "def.h"
#include "dirent-util.h"
#include "fd-util.h"
@@ -771,15 +772,64 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int
}
uint64_t physical_memory(void) {
- long mem;
+ _cleanup_free_ char *root = NULL, *value = NULL;
+ uint64_t mem, lim;
+ size_t ps;
+ long sc;
- /* We return this as uint64_t in case we are running as 32bit
- * process on a 64bit kernel with huge amounts of memory */
+ /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of
+ * memory.
+ *
+ * In order to support containers nicely that have a configured memory limit we'll take the minimum of the
+ * physically reported amount of memory and the limit configured for the root cgroup, if there is any. */
+
+ sc = sysconf(_SC_PHYS_PAGES);
+ assert(sc > 0);
+
+ ps = page_size();
+ mem = (uint64_t) sc * (uint64_t) ps;
+
+ if (cg_get_root_path(&root) < 0)
+ return mem;
+
+ if (cg_get_attribute("memory", root, "memory.limit_in_bytes", &value))
+ return mem;
+
+ if (safe_atou64(value, &lim) < 0)
+ return mem;
+
+ /* Make sure the limit is a multiple of our own page size */
+ lim /= ps;
+ lim *= ps;
+
+ return MIN(mem, lim);
+}
+
+uint64_t physical_memory_scale(uint64_t v, uint64_t max) {
+ uint64_t p, m, ps, r;
+
+ assert(max > 0);
+
+ /* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success
+ * the result is a multiple of the page size (rounds down). */
+
+ ps = page_size();
+ assert(ps > 0);
+
+ p = physical_memory() / ps;
+ assert(p > 0);
+
+ m = p * v;
+ if (m / p != v)
+ return UINT64_MAX;
+
+ m /= max;
- mem = sysconf(_SC_PHYS_PAGES);
- assert(mem > 0);
+ r = m * ps;
+ if (r / ps != m)
+ return UINT64_MAX;
- return (uint64_t) mem * (uint64_t) page_size();
+ return r;
}
int update_reboot_parameter_and_warn(const char *param) {
diff --git a/src/basic/util.h b/src/basic/util.h
index 9e6df19ef1..db105197e8 100644
--- a/src/basic/util.h
+++ b/src/basic/util.h
@@ -184,6 +184,7 @@ int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *
int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd);
uint64_t physical_memory(void);
+uint64_t physical_memory_scale(uint64_t v, uint64_t max);
int update_reboot_parameter_and_warn(const char *param);
diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c
index 8525fa1bf1..27bbe2d26d 100644
--- a/src/core/dbus-cgroup.c
+++ b/src/core/dbus-cgroup.c
@@ -641,7 +641,7 @@ int bus_cgroup_set_property(
return 1;
- } else if (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth")) {
+ } else if (STR_IN_SET(name, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) {
const char *path;
bool read = true;
unsigned n = 0;
@@ -835,6 +835,8 @@ int bus_cgroup_set_property(
r = sd_bus_message_read(message, "t", &v);
if (r < 0)
return r;
+ if (v <= 0)
+ return sd_bus_error_set_errnof(error, EINVAL, "%s= is too small", name);
if (mode != UNIT_CHECK) {
if (streq(name, "MemoryLow"))
@@ -847,19 +849,53 @@ int bus_cgroup_set_property(
unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY);
if (v == CGROUP_LIMIT_MAX)
- unit_write_drop_in_private_format(u, mode, name, "%s=max", name);
+ unit_write_drop_in_private_format(u, mode, name, "%s=infinity", name);
else
unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, v);
}
return 1;
+ } else if (STR_IN_SET(name, "MemoryLowByPhysicalMemory", "MemoryHighByPhysicalMemory", "MemoryMaxByPhysicalMemory")) {
+ uint32_t raw;
+ uint64_t v;
+
+ r = sd_bus_message_read(message, "u", &raw);
+ if (r < 0)
+ return r;
+
+ v = physical_memory_scale(raw, UINT32_MAX);
+ if (v <= 0 || v == UINT64_MAX)
+ return sd_bus_error_set_errnof(error, EINVAL, "%s= is out of range", name);
+
+ if (mode != UNIT_CHECK) {
+ const char *e;
+
+ /* Chop off suffix */
+ assert_se(e = endswith(name, "ByPhysicalMemory"));
+ name = strndupa(name, e - name);
+
+ if (streq(name, "MemoryLow"))
+ c->memory_low = v;
+ else if (streq(name, "MemoryHigh"))
+ c->memory_high = v;
+ else
+ c->memory_max = v;
+
+ unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY);
+ unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu32 "%%", name, (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100, (uint64_t) UINT32_MAX)));
+ }
+
+ return 1;
+
} else if (streq(name, "MemoryLimit")) {
uint64_t limit;
r = sd_bus_message_read(message, "t", &limit);
if (r < 0)
return r;
+ if (limit <= 0)
+ return sd_bus_error_set_errnof(error, EINVAL, "%s= is too small", name);
if (mode != UNIT_CHECK) {
c->memory_limit = limit;
@@ -873,6 +909,26 @@ int bus_cgroup_set_property(
return 1;
+ } else if (streq(name, "MemoryLimitByPhysicalMemory")) {
+ uint64_t limit;
+ uint32_t raw;
+
+ r = sd_bus_message_read(message, "u", &raw);
+ if (r < 0)
+ return r;
+
+ limit = physical_memory_scale(raw, UINT32_MAX);
+ if (limit <= 0 || limit == UINT64_MAX)
+ return sd_bus_error_set_errnof(error, EINVAL, "%s= is out of range", name);
+
+ if (mode != UNIT_CHECK) {
+ c->memory_limit = limit;
+ unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY);
+ unit_write_drop_in_private_format(u, mode, "MemoryLimit", "MemoryLimit=%" PRIu32 "%%", (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100, (uint64_t) UINT32_MAX)));
+ }
+
+ return 1;
+
} else if (streq(name, "DevicePolicy")) {
const char *policy;
CGroupDevicePolicy p;
diff --git a/src/core/execute.c b/src/core/execute.c
index c20650626c..3c3369373f 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -289,7 +289,15 @@ static int connect_journal_socket(int fd, uid_t uid, gid_t gid) {
return r;
}
-static int connect_logger_as(const ExecContext *context, ExecOutput output, const char *ident, const char *unit_id, int nfd, uid_t uid, gid_t gid) {
+static int connect_logger_as(
+ const ExecContext *context,
+ ExecOutput output,
+ const char *ident,
+ const char *unit_id,
+ int nfd,
+ uid_t uid,
+ gid_t gid) {
+
int fd, r;
assert(context);
@@ -310,7 +318,7 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons
return -errno;
}
- fd_inc_sndbuf(fd, SNDBUF_SIZE);
+ (void) fd_inc_sndbuf(fd, SNDBUF_SIZE);
dprintf(fd,
"%s\n"
@@ -328,11 +336,11 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons
output == EXEC_OUTPUT_KMSG || output == EXEC_OUTPUT_KMSG_AND_CONSOLE,
is_terminal_output(output));
- if (fd != nfd) {
- r = dup2(fd, nfd) < 0 ? -errno : nfd;
- safe_close(fd);
- } else
- r = nfd;
+ if (fd == nfd)
+ return nfd;
+
+ r = dup2(fd, nfd) < 0 ? -errno : nfd;
+ safe_close(fd);
return r;
}
@@ -446,7 +454,10 @@ static int setup_output(
int fileno,
int socket_fd,
const char *ident,
- uid_t uid, gid_t gid) {
+ uid_t uid,
+ gid_t gid,
+ dev_t *journal_stream_dev,
+ ino_t *journal_stream_ino) {
ExecOutput o;
ExecInput i;
@@ -456,6 +467,8 @@ static int setup_output(
assert(context);
assert(params);
assert(ident);
+ assert(journal_stream_dev);
+ assert(journal_stream_ino);
if (fileno == STDOUT_FILENO && params->stdout_fd >= 0) {
@@ -535,6 +548,17 @@ static int setup_output(
if (r < 0) {
log_unit_error_errno(unit, r, "Failed to connect %s to the journal socket, ignoring: %m", fileno == STDOUT_FILENO ? "stdout" : "stderr");
r = open_null_as(O_WRONLY, fileno);
+ } else {
+ struct stat st;
+
+ /* If we connected this fd to the journal via a stream, patch the device/inode into the passed
+ * parameters, but only then. This is useful so that we can set $JOURNAL_STREAM that permits
+ * services to detect whether they are connected to the journal or not. */
+
+ if (fstat(fileno, &st) >= 0) {
+ *journal_stream_dev = st.st_dev;
+ *journal_stream_ino = st.st_ino;
+ }
}
return r;
@@ -1278,6 +1302,8 @@ static int build_environment(
const char *home,
const char *username,
const char *shell,
+ dev_t journal_stream_dev,
+ ino_t journal_stream_ino,
char ***ret) {
_cleanup_strv_free_ char **our_env = NULL;
@@ -1287,7 +1313,7 @@ static int build_environment(
assert(c);
assert(ret);
- our_env = new0(char*, 11);
+ our_env = new0(char*, 12);
if (!our_env)
return -ENOMEM;
@@ -1359,8 +1385,15 @@ static int build_environment(
our_env[n_env++] = x;
}
+ if (journal_stream_dev != 0 && journal_stream_ino != 0) {
+ if (asprintf(&x, "JOURNAL_STREAM=" DEV_FMT ":" INO_FMT, journal_stream_dev, journal_stream_ino) < 0)
+ return -ENOMEM;
+
+ our_env[n_env++] = x;
+ }
+
our_env[n_env++] = NULL;
- assert(n_env <= 11);
+ assert(n_env <= 12);
*ret = our_env;
our_env = NULL;
@@ -1473,10 +1506,12 @@ static int exec_child(
_cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **final_argv = NULL;
_cleanup_free_ char *mac_selinux_context_net = NULL;
const char *username = NULL, *home = NULL, *shell = NULL, *wd;
+ dev_t journal_stream_dev = 0;
+ ino_t journal_stream_ino = 0;
+ bool needs_mount_namespace;
uid_t uid = UID_INVALID;
gid_t gid = GID_INVALID;
int i, r;
- bool needs_mount_namespace;
assert(unit);
assert(command);
@@ -1576,13 +1611,13 @@ static int exec_child(
return r;
}
- r = setup_output(unit, context, params, STDOUT_FILENO, socket_fd, basename(command->path), uid, gid);
+ r = setup_output(unit, context, params, STDOUT_FILENO, socket_fd, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino);
if (r < 0) {
*exit_status = EXIT_STDOUT;
return r;
}
- r = setup_output(unit, context, params, STDERR_FILENO, socket_fd, basename(command->path), uid, gid);
+ r = setup_output(unit, context, params, STDERR_FILENO, socket_fd, basename(command->path), uid, gid, &journal_stream_dev, &journal_stream_ino);
if (r < 0) {
*exit_status = EXIT_STDERR;
return r;
@@ -1721,7 +1756,16 @@ static int exec_child(
}
}
- r = build_environment(context, params, n_fds, home, username, shell, &our_env);
+ r = build_environment(
+ context,
+ params,
+ n_fds,
+ home,
+ username,
+ shell,
+ journal_stream_dev,
+ journal_stream_ino,
+ &our_env);
if (r < 0) {
*exit_status = EXIT_MEMORY;
return r;
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index d42f517354..8295cf45a6 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -2774,7 +2774,7 @@ int config_parse_cpu_quota(
void *userdata) {
CGroupContext *c = data;
- double percent;
+ int r;
assert(filename);
assert(lvalue);
@@ -2785,18 +2785,13 @@ int config_parse_cpu_quota(
return 0;
}
- if (!endswith(rvalue, "%")) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "CPU quota '%s' not ending in '%%'. Ignoring.", rvalue);
- return 0;
- }
-
- if (sscanf(rvalue, "%lf%%", &percent) != 1 || percent <= 0) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "CPU quota '%s' invalid. Ignoring.", rvalue);
+ r = parse_percent(rvalue);
+ if (r <= 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "CPU quota '%s' invalid. Ignoring.", rvalue);
return 0;
}
- c->cpu_quota_per_sec_usec = (usec_t) (percent * USEC_PER_SEC / 100);
-
+ c->cpu_quota_per_sec_usec = ((usec_t) r * USEC_PER_SEC) / 100U;
return 0;
}
@@ -2817,9 +2812,19 @@ int config_parse_memory_limit(
int r;
if (!isempty(rvalue) && !streq(rvalue, "infinity")) {
- r = parse_size(rvalue, 1024, &bytes);
- if (r < 0 || bytes < 1) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Memory limit '%s' invalid. Ignoring.", rvalue);
+
+ r = parse_percent(rvalue);
+ if (r < 0) {
+ r = parse_size(rvalue, 1024, &bytes);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Memory limit '%s' invalid. Ignoring.", rvalue);
+ return 0;
+ }
+ } else
+ bytes = physical_memory_scale(r, 100U);
+
+ if (bytes < 1) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Memory limit '%s' too small. Ignoring.", rvalue);
return 0;
}
}
diff --git a/src/core/unit.c b/src/core/unit.c
index e98086a3f6..581962eba6 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -3375,7 +3375,7 @@ int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, co
return -EINVAL;
wrapped = strjoina("# This is a drop-in unit file extension, created via \"systemctl set-property\"\n"
- "or an equivalent operation. Do not edit.\n",
+ "# or an equivalent operation. Do not edit.\n",
data,
"\n");
diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c
index ea3fe369ce..07b0d7f704 100644
--- a/src/libsystemd-network/sd-ndisc.c
+++ b/src/libsystemd-network/sd-ndisc.c
@@ -307,7 +307,7 @@ static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userda
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SO_TIMESTAMP &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
- triple_timestamp_from_realtime(&rt->timestamp, timeval_load(CMSG_DATA(cmsg)));
+ triple_timestamp_from_realtime(&rt->timestamp, timeval_load((struct timeval*) CMSG_DATA(cmsg)));
}
if (!triple_timestamp_is_set(&rt->timestamp))
diff --git a/src/login/logind-user.c b/src/login/logind-user.c
index a826321bf0..de44d369cf 100644
--- a/src/login/logind-user.c
+++ b/src/login/logind-user.c
@@ -843,7 +843,6 @@ int config_parse_tmpfs_size(
void *userdata) {
size_t *sz = data;
- const char *e;
int r;
assert(filename);
@@ -851,29 +850,17 @@ int config_parse_tmpfs_size(
assert(rvalue);
assert(data);
- e = endswith(rvalue, "%");
- if (e) {
- unsigned long ul;
- char *f;
-
- errno = 0;
- ul = strtoul(rvalue, &f, 10);
- if (errno > 0 || f != e) {
- log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse percentage value, ignoring: %s", rvalue);
- return 0;
- }
-
- if (ul <= 0 || ul >= 100) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Percentage value out of range, ignoring: %s", rvalue);
- return 0;
- }
-
- *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
- } else {
+ /* First, try to parse as percentage */
+ r = parse_percent(rvalue);
+ if (r > 0 && r < 100)
+ *sz = physical_memory_scale(r, 100U);
+ else {
uint64_t k;
+ /* If the passed argument was not a percentage, or out of range, parse as byte size */
+
r = parse_size(rvalue, 1024, &k);
- if (r < 0 || (uint64_t) (size_t) k != k) {
+ if (r < 0 || k <= 0 || (uint64_t) (size_t) k != k) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
return 0;
}
diff --git a/src/login/logind.c b/src/login/logind.c
index caf149cfb7..d01dd110ea 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -61,7 +61,7 @@ static void manager_reset_config(Manager *m) {
m->idle_action_usec = 30 * USEC_PER_MINUTE;
m->idle_action = HANDLE_IGNORE;
- m->runtime_dir_size = PAGE_ALIGN((size_t) (physical_memory() / 10)); /* 10% */
+ m->runtime_dir_size = physical_memory_scale(10U, 100U); /* 10% */
m->user_tasks_max = 12288;
m->sessions_max = 8192;
m->inhibitors_max = 8192;
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index 8f4f93ee0c..6fc201b885 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -83,18 +83,14 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
if (isempty(eq))
r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY);
- else if (endswith(eq, "%")) {
- double percent;
-
- if (sscanf(eq, "%lf%%", &percent) != 1 || percent <= 0) {
- log_error("CPU quota '%s' invalid.", eq);
+ else {
+ r = parse_percent(eq);
+ if (r <= 0) {
+ log_error_errno(r, "CPU quota '%s' invalid.", eq);
return -EINVAL;
}
- r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) percent * USEC_PER_SEC / 100);
- } else {
- log_error("CPU quota needs to be in percent.");
- return -EINVAL;
+ r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) r * USEC_PER_SEC / 100U);
}
goto finish;
@@ -110,6 +106,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
char *n;
usec_t t;
size_t l;
+
r = parse_sec(eq, &t);
if (r < 0)
return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq);
@@ -123,6 +120,34 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
strcpy(mempcpy(n, field, l - 3), "USec");
r = sd_bus_message_append(m, "sv", n, "t", t);
goto finish;
+
+ } else if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemoryLimit")) {
+ uint64_t bytes;
+
+ if (isempty(eq) || streq(eq, "infinity"))
+ bytes = CGROUP_LIMIT_MAX;
+ else {
+ r = parse_percent(eq);
+ if (r >= 0) {
+ char *n;
+
+ /* When this is a percentage we'll convert this into a relative value in the range
+ * 0…UINT32_MAX and pass it in the MemoryLowByPhysicalMemory property (and related
+ * ones). This way the physical memory size can be determined server-side */
+
+ n = strjoina(field, "ByPhysicalMemory");
+ r = sd_bus_message_append(m, "sv", n, "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U));
+ goto finish;
+
+ } else {
+ r = parse_size(eq, 1024, &bytes);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse bytes specification %s", assignment);
+ }
+ }
+
+ r = sd_bus_message_append(m, "sv", field, "t", bytes);
+ goto finish;
}
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
@@ -166,21 +191,6 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
r = sd_bus_message_append(m, "v", "b", r);
- } else if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemoryLimit")) {
- uint64_t bytes;
-
- if (isempty(eq) || streq(eq, "infinity"))
- bytes = CGROUP_LIMIT_MAX;
- else {
- r = parse_size(eq, 1024, &bytes);
- if (r < 0) {
- log_error("Failed to parse bytes specification %s", assignment);
- return -EINVAL;
- }
- }
-
- r = sd_bus_message_append(m, "v", "t", bytes);
-
} else if (streq(field, "TasksMax")) {
uint64_t n;
diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c
index 7d8677e17c..0a76308f72 100644
--- a/src/test/test-parse-util.c
+++ b/src/test/test-parse-util.c
@@ -475,6 +475,24 @@ static void test_safe_atod(void) {
assert_se(*e == ',');
}
+static void test_parse_percent(void) {
+ assert_se(parse_percent("") == -EINVAL);
+ assert_se(parse_percent("foo") == -EINVAL);
+ assert_se(parse_percent("0") == -EINVAL);
+ assert_se(parse_percent("50") == -EINVAL);
+ assert_se(parse_percent("100") == -EINVAL);
+ assert_se(parse_percent("-1") == -EINVAL);
+ assert_se(parse_percent("0%") == 0);
+ assert_se(parse_percent("55%") == 55);
+ assert_se(parse_percent("100%") == 100);
+ assert_se(parse_percent("-7%") == -ERANGE);
+ assert_se(parse_percent("107%") == -ERANGE);
+ assert_se(parse_percent("%") == -EINVAL);
+ assert_se(parse_percent("%%") == -EINVAL);
+ assert_se(parse_percent("%1") == -EINVAL);
+ assert_se(parse_percent("1%%") == -EINVAL);
+}
+
int main(int argc, char *argv[]) {
log_parse_environment();
log_open();
@@ -488,6 +506,7 @@ int main(int argc, char *argv[]) {
test_safe_atou16();
test_safe_atoi16();
test_safe_atod();
+ test_parse_percent();
return 0;
}
diff --git a/src/test/test-util.c b/src/test/test-util.c
index 9b6d2a7968..e177612a9f 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -26,6 +26,7 @@
#include "def.h"
#include "fileio.h"
#include "fs-util.h"
+#include "parse-util.h"
#include "raw-clone.h"
#include "rm-rf.h"
#include "string-util.h"
@@ -263,6 +264,53 @@ static void test_raw_clone(void) {
}
}
+static void test_physical_memory(void) {
+ uint64_t p;
+ char buf[FORMAT_BYTES_MAX];
+
+ p = physical_memory();
+ assert_se(p > 0);
+ assert_se(p < UINT64_MAX);
+ assert_se(p % page_size() == 0);
+
+ log_info("Memory: %s (%" PRIu64 ")", format_bytes(buf, sizeof(buf), p), p);
+}
+
+static void test_physical_memory_scale(void) {
+ uint64_t p;
+
+ p = physical_memory();
+
+ assert_se(physical_memory_scale(0, 100) == 0);
+ assert_se(physical_memory_scale(100, 100) == p);
+
+ log_info("Memory original: %" PRIu64, physical_memory());
+ log_info("Memory scaled by 50%%: %" PRIu64, physical_memory_scale(50, 100));
+ log_info("Memory divided by 2: %" PRIu64, physical_memory() / 2);
+ log_info("Page size: %zu", page_size());
+
+ /* There might be an uneven number of pages, hence permit these calculations to be half a page off... */
+ assert_se(page_size()/2 + physical_memory_scale(50, 100) - p/2 <= page_size());
+ assert_se(physical_memory_scale(200, 100) == p*2);
+
+ assert_se(physical_memory_scale(0, 1) == 0);
+ assert_se(physical_memory_scale(1, 1) == p);
+ assert_se(physical_memory_scale(2, 1) == p*2);
+
+ assert_se(physical_memory_scale(0, 2) == 0);
+
+ assert_se(page_size()/2 + physical_memory_scale(1, 2) - p/2 <= page_size());
+ assert_se(physical_memory_scale(2, 2) == p);
+ assert_se(physical_memory_scale(4, 2) == p*2);
+
+ assert_se(physical_memory_scale(0, UINT32_MAX) == 0);
+ assert_se(physical_memory_scale(UINT32_MAX, UINT32_MAX) == p);
+
+ /* overflow */
+ assert_se(physical_memory_scale(UINT64_MAX/4, UINT64_MAX) == UINT64_MAX);
+
+}
+
int main(int argc, char *argv[]) {
log_parse_environment();
log_open();
@@ -277,6 +325,8 @@ int main(int argc, char *argv[]) {
test_log2i();
test_execute_directory();
test_raw_clone();
+ test_physical_memory();
+ test_physical_memory_scale();
return 0;
}