mirror of
https://github.com/systemd/systemd.git
synced 2025-01-11 09:18:07 +03:00
exec: optionally apply cgroup attributes to the cgroups we create
This commit is contained in:
parent
5ed27dbdbf
commit
ab1f063390
@ -627,7 +627,8 @@ libsystemd_core_la_SOURCES = \
|
|||||||
src/condition.c \
|
src/condition.c \
|
||||||
src/dbus-common.c \
|
src/dbus-common.c \
|
||||||
src/sd-daemon.c \
|
src/sd-daemon.c \
|
||||||
src/install.c
|
src/install.c \
|
||||||
|
src/cgroup-attr.c
|
||||||
|
|
||||||
nodist_libsystemd_core_la_SOURCES = \
|
nodist_libsystemd_core_la_SOURCES = \
|
||||||
src/load-fragment-gperf.c \
|
src/load-fragment-gperf.c \
|
||||||
|
@ -132,7 +132,7 @@
|
|||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><option>--directory=</option></term>
|
<term><option>--directory=</option></term>
|
||||||
<term><option>--D</option></term>
|
<term><option>-D</option></term>
|
||||||
|
|
||||||
<listitem><para>Directory to use as
|
<listitem><para>Directory to use as
|
||||||
file system root for the namespace
|
file system root for the namespace
|
||||||
@ -207,7 +207,7 @@
|
|||||||
<para>
|
<para>
|
||||||
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||||
<citerefentry><refentrytitle>chroot</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
<citerefentry><refentrytitle>chroot</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||||
<citerefentry><refentrytitle>debootstrap</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
<citerefentry><refentrytitle>debootstrap</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||||
<citerefentry><refentrytitle>mock</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
<citerefentry><refentrytitle>mock</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||||
</para>
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
@ -629,18 +629,6 @@
|
|||||||
for details.</para></listitem>
|
for details.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
|
||||||
<term><varname>ControlGroupModify=</varname></term>
|
|
||||||
<listitem><para>Takes a boolean
|
|
||||||
argument. If true, the control groups
|
|
||||||
created for this unit will be owned by
|
|
||||||
ther user specified with
|
|
||||||
<varname>User=</varname> (and the
|
|
||||||
configured group), and he can create
|
|
||||||
subgroups as well as add processes to
|
|
||||||
the group.</para></listitem>
|
|
||||||
</varlistentry>
|
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><varname>CapabilityBoundingSet=</varname></term>
|
<term><varname>CapabilityBoundingSet=</varname></term>
|
||||||
|
|
||||||
@ -718,9 +706,9 @@
|
|||||||
where "cpu" identifies the kernel
|
where "cpu" identifies the kernel
|
||||||
control group controller used, and
|
control group controller used, and
|
||||||
<filename>/foo/bar</filename> is the
|
<filename>/foo/bar</filename> is the
|
||||||
control group path. The controller name
|
control group path. The controller
|
||||||
and ":" may be omitted in which case
|
name and ":" may be omitted in which
|
||||||
the named systemd control group
|
case the named systemd control group
|
||||||
hierarchy is implied. Alternatively,
|
hierarchy is implied. Alternatively,
|
||||||
the path and ":" may be omitted, in
|
the path and ":" may be omitted, in
|
||||||
which case the default control group
|
which case the default control group
|
||||||
@ -728,20 +716,138 @@
|
|||||||
option may be used to place executed
|
option may be used to place executed
|
||||||
processes in arbitrary groups in
|
processes in arbitrary groups in
|
||||||
arbitrary hierarchies -- which can be
|
arbitrary hierarchies -- which can be
|
||||||
configured externally with additional execution limits. By default
|
configured externally with additional
|
||||||
systemd will place all executed
|
execution limits. By default systemd
|
||||||
processes in separate per-unit control
|
will place all executed processes in
|
||||||
groups (named after the unit) in the
|
separate per-unit control groups
|
||||||
systemd named hierarchy. Since every
|
(named after the unit) in the systemd
|
||||||
process can be in one group per
|
named hierarchy. Since every process
|
||||||
hierarchy only overriding the control group
|
can be in one group per hierarchy only
|
||||||
path in the named systemd hierarchy
|
overriding the control group path in
|
||||||
will disable automatic placement in
|
the named systemd hierarchy will
|
||||||
the default group. For details about control
|
disable automatic placement in the
|
||||||
groups see <ulink
|
default group. This option is
|
||||||
|
primarily intended to place executed
|
||||||
|
processes in specific paths in
|
||||||
|
specific kernel controller
|
||||||
|
hierarchies. It is however not
|
||||||
|
recommended to manipulate the service
|
||||||
|
control group path in the systemd
|
||||||
|
named hierarchy. For details about
|
||||||
|
control groups see <ulink
|
||||||
url="http://www.kernel.org/doc/Documentation/cgroups/cgroups.txt">cgroups.txt</ulink>.</para></listitem>
|
url="http://www.kernel.org/doc/Documentation/cgroups/cgroups.txt">cgroups.txt</ulink>.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>ControlGroupModify=</varname></term>
|
||||||
|
<listitem><para>Takes a boolean
|
||||||
|
argument. If true, the control groups
|
||||||
|
created for this unit will be owned by
|
||||||
|
the user specified with
|
||||||
|
<varname>User=</varname> (and the
|
||||||
|
appropriate group), and he/she can create
|
||||||
|
subgroups as well as add processes to
|
||||||
|
the group.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>ControlGroupAttribute=</varname></term>
|
||||||
|
|
||||||
|
<listitem><para>Set a specific control
|
||||||
|
group attribute for executed
|
||||||
|
processes, and (if needed) add the the
|
||||||
|
executed processes to a cgroup in the
|
||||||
|
hierarchy of the controller the
|
||||||
|
attribute belongs to. Takes two
|
||||||
|
space-separated arguments: the
|
||||||
|
attribute name (syntax is
|
||||||
|
<literal>cpu.shares</literal> where
|
||||||
|
<literal>cpu</literal> refers to a
|
||||||
|
specific controller and
|
||||||
|
<literal>shares</literal> to the
|
||||||
|
attribute name), and the attribute
|
||||||
|
value. Example:
|
||||||
|
<literal>ControlGroupAttribute=cpu.shares
|
||||||
|
512</literal>. If this option is used
|
||||||
|
for an attribute that belongs to a
|
||||||
|
kernel controller hierarchy the unit
|
||||||
|
is not already configured to be added
|
||||||
|
to (for example via the
|
||||||
|
<literal>ControlGroup=</literal>
|
||||||
|
option) then the unit will be added to
|
||||||
|
the controller and the default unit
|
||||||
|
cgroup path is implied. Thus, using
|
||||||
|
<varname>ControlGroupAttribute=</varname>
|
||||||
|
is in most case sufficient to make use
|
||||||
|
of control group enforcements,
|
||||||
|
explicit
|
||||||
|
<varname>ControlGroup=</varname> are
|
||||||
|
only necessary in case the implied
|
||||||
|
default control group path for a
|
||||||
|
service is not desirable. For details
|
||||||
|
about control group attributes see
|
||||||
|
<ulink
|
||||||
|
url="http://www.kernel.org/doc/Documentation/cgroups/cgroups.txt">cgroups.txt</ulink>. This
|
||||||
|
option may appear more than once, in
|
||||||
|
order to set multiple control group
|
||||||
|
attributes.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>CPUShares=</varname></term>
|
||||||
|
|
||||||
|
<listitem><para>Assign the specified
|
||||||
|
overall CPU time shares to the processes executed. Takes
|
||||||
|
an integer value. This controls the
|
||||||
|
<literal>cpu.shares</literal> control
|
||||||
|
group attribute. For details about
|
||||||
|
this control group attribute see <ulink
|
||||||
|
url="http://www.kernel.org/doc/Documentation/scheduler/sched-design-CFS.txt">sched-design-CFS.txt</ulink>.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>MemoryLimit=</varname></term>
|
||||||
|
<term><varname>MemorySoftLimit=</varname></term>
|
||||||
|
|
||||||
|
<listitem><para>Limit the overall memory usage
|
||||||
|
of the executed processes to a certain
|
||||||
|
size. 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
|
||||||
|
resp. Terabytes (to the base
|
||||||
|
1024). This controls the
|
||||||
|
<literal>memory.limit_in_bytes</literal>
|
||||||
|
and
|
||||||
|
<literal>memory.soft_limit_in_bytes</literal>
|
||||||
|
control group attributes. For details
|
||||||
|
about these control group attributes
|
||||||
|
see <ulink
|
||||||
|
url="http://www.kernel.org/doc/Documentation/cgroups/memory.txt">memory.txt</ulink>.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>DeviceAllow=</varname></term>
|
||||||
|
<term><varname>DeviceDeny=</varname></term>
|
||||||
|
|
||||||
|
<listitem><para>Control access to
|
||||||
|
specific device nodes by the executed processes. Takes two
|
||||||
|
space separated strings: a device node
|
||||||
|
path (such as
|
||||||
|
<filename>/dev/null</filename>)
|
||||||
|
followed by a combination of r, w, m
|
||||||
|
to control reading, writing resp.
|
||||||
|
creating of the specific device node
|
||||||
|
by the unit. This controls the
|
||||||
|
<literal>devices.allow</literal>
|
||||||
|
and
|
||||||
|
<literal>devices.deny</literal>
|
||||||
|
control group attributes. For details
|
||||||
|
about these control group attributes
|
||||||
|
see <ulink
|
||||||
|
url="http://www.kernel.org/doc/Documentation/cgroups/devices.txt">devices.txt</ulink>.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><varname>ReadWriteDirectories=</varname></term>
|
<term><varname>ReadWriteDirectories=</varname></term>
|
||||||
<term><varname>ReadOnlyDirectories=</varname></term>
|
<term><varname>ReadOnlyDirectories=</varname></term>
|
||||||
|
102
src/cgroup-attr.c
Normal file
102
src/cgroup-attr.c
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||||
|
|
||||||
|
/***
|
||||||
|
This file is part of systemd.
|
||||||
|
|
||||||
|
Copyright 2011 Lennart Poettering
|
||||||
|
|
||||||
|
systemd is free software; you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
systemd is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
***/
|
||||||
|
|
||||||
|
#include "cgroup-attr.h"
|
||||||
|
#include "cgroup-util.h"
|
||||||
|
#include "list.h"
|
||||||
|
|
||||||
|
int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b) {
|
||||||
|
int r;
|
||||||
|
char *path = NULL;
|
||||||
|
char *v = NULL;
|
||||||
|
|
||||||
|
assert(a);
|
||||||
|
|
||||||
|
b = cgroup_bonding_find_list(b, a->controller);
|
||||||
|
if (!b)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (a->map_callback) {
|
||||||
|
r = a->map_callback(a->controller, a->name, a->value, &v);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = cg_get_path(a->controller, b->path, a->name, &path);
|
||||||
|
if (r < 0) {
|
||||||
|
free(v);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = write_one_line_file(path, v ? v : a->value);
|
||||||
|
if (r < 0)
|
||||||
|
log_warning("Failed to write '%s' to %s: %s", v ? v : a->value, path, strerror(-r));
|
||||||
|
|
||||||
|
free(path);
|
||||||
|
free(v);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b) {
|
||||||
|
CGroupAttribute *a;
|
||||||
|
int r = 0;
|
||||||
|
|
||||||
|
LIST_FOREACH(by_unit, a, first) {
|
||||||
|
int k;
|
||||||
|
|
||||||
|
k = cgroup_attribute_apply(a, b);
|
||||||
|
if (r == 0)
|
||||||
|
r = k;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name) {
|
||||||
|
CGroupAttribute *a;
|
||||||
|
|
||||||
|
assert(controller);
|
||||||
|
assert(name);
|
||||||
|
|
||||||
|
LIST_FOREACH(by_unit, a, first)
|
||||||
|
if (streq(a->controller, controller) &&
|
||||||
|
streq(a->name, name))
|
||||||
|
return a;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cgroup_attribute_free(CGroupAttribute *a) {
|
||||||
|
assert(a);
|
||||||
|
|
||||||
|
free(a->controller);
|
||||||
|
free(a->name);
|
||||||
|
free(a->value);
|
||||||
|
free(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cgroup_attribute_free_list(CGroupAttribute *first) {
|
||||||
|
CGroupAttribute *a, *n;
|
||||||
|
|
||||||
|
LIST_FOREACH_SAFE(by_unit, a, n, first)
|
||||||
|
cgroup_attribute_free(a);
|
||||||
|
}
|
49
src/cgroup-attr.h
Normal file
49
src/cgroup-attr.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
|
||||||
|
|
||||||
|
#ifndef foocgroupattrhfoo
|
||||||
|
#define foocgroupattrhfoo
|
||||||
|
|
||||||
|
/***
|
||||||
|
This file is part of systemd.
|
||||||
|
|
||||||
|
Copyright 2011 Lennart Poettering
|
||||||
|
|
||||||
|
systemd is free software; you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
systemd is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
***/
|
||||||
|
|
||||||
|
typedef struct CGroupAttribute CGroupAttribute;
|
||||||
|
|
||||||
|
typedef int (*CGroupAttributeMapCallback)(const char *controller, const char*name, const char *value, char **ret);
|
||||||
|
|
||||||
|
#include "unit.h"
|
||||||
|
#include "cgroup.h"
|
||||||
|
|
||||||
|
struct CGroupAttribute {
|
||||||
|
char *controller;
|
||||||
|
char *name;
|
||||||
|
char *value;
|
||||||
|
|
||||||
|
CGroupAttributeMapCallback map_callback;
|
||||||
|
|
||||||
|
LIST_FIELDS(CGroupAttribute, by_unit);
|
||||||
|
};
|
||||||
|
|
||||||
|
int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b);
|
||||||
|
int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b);
|
||||||
|
|
||||||
|
CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name);
|
||||||
|
|
||||||
|
void cgroup_attribute_free_list(CGroupAttribute *first);
|
||||||
|
|
||||||
|
#endif
|
@ -41,8 +41,11 @@ int cgroup_bonding_realize(CGroupBonding *b) {
|
|||||||
if (b->realized)
|
if (b->realized)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if ((r = cg_create(b->controller, b->path)) < 0)
|
r = cg_create(b->controller, b->path);
|
||||||
|
if (r < 0) {
|
||||||
|
log_warning("Failed to create cgroup %s:%s: %s", b->controller, b->path, strerror(-r));
|
||||||
return r;
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
b->realized = true;
|
b->realized = true;
|
||||||
|
|
||||||
|
@ -930,6 +930,7 @@ int exec_spawn(ExecCommand *command,
|
|||||||
bool apply_tty_stdin,
|
bool apply_tty_stdin,
|
||||||
bool confirm_spawn,
|
bool confirm_spawn,
|
||||||
CGroupBonding *cgroup_bondings,
|
CGroupBonding *cgroup_bondings,
|
||||||
|
CGroupAttribute *cgroup_attributes,
|
||||||
pid_t *ret) {
|
pid_t *ret) {
|
||||||
|
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
@ -973,9 +974,11 @@ int exec_spawn(ExecCommand *command,
|
|||||||
log_debug("About to execute: %s", line);
|
log_debug("About to execute: %s", line);
|
||||||
free(line);
|
free(line);
|
||||||
|
|
||||||
if (cgroup_bondings)
|
r = cgroup_bonding_realize_list(cgroup_bondings);
|
||||||
if ((r = cgroup_bonding_realize_list(cgroup_bondings)))
|
if (r < 0)
|
||||||
goto fail_parent;
|
goto fail_parent;
|
||||||
|
|
||||||
|
cgroup_attribute_apply_list(cgroup_attributes, cgroup_bondings);
|
||||||
|
|
||||||
if ((pid = fork()) < 0) {
|
if ((pid = fork()) < 0) {
|
||||||
r = -errno;
|
r = -errno;
|
||||||
|
@ -35,6 +35,7 @@ typedef struct ExecContext ExecContext;
|
|||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
|
|
||||||
struct CGroupBonding;
|
struct CGroupBonding;
|
||||||
|
struct CGroupAttribute;
|
||||||
|
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
@ -187,6 +188,7 @@ int exec_spawn(ExecCommand *command,
|
|||||||
bool apply_tty_stdin,
|
bool apply_tty_stdin,
|
||||||
bool confirm_spawn,
|
bool confirm_spawn,
|
||||||
struct CGroupBonding *cgroup_bondings,
|
struct CGroupBonding *cgroup_bondings,
|
||||||
|
struct CGroupAttribute *cgroup_attributes,
|
||||||
pid_t *ret);
|
pid_t *ret);
|
||||||
|
|
||||||
void exec_command_done(ExecCommand *c);
|
void exec_command_done(ExecCommand *c);
|
||||||
|
@ -70,7 +70,7 @@ int kmod_setup(void) {
|
|||||||
command.argv = (char**) cmdline;
|
command.argv = (char**) cmdline;
|
||||||
|
|
||||||
exec_context_init(&context);
|
exec_context_init(&context);
|
||||||
r = exec_spawn(&command, NULL, &context, NULL, 0, NULL, false, false, false, false, NULL, &pid);
|
r = exec_spawn(&command, NULL, &context, NULL, 0, NULL, false, false, false, false, NULL, NULL, &pid);
|
||||||
exec_context_done(&context);
|
exec_context_done(&context);
|
||||||
|
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
|
@ -64,7 +64,13 @@ $1.LimitMSGQUEUE, config_parse_limit, RLIMIT_MSGQ
|
|||||||
$1.LimitNICE, config_parse_limit, RLIMIT_NICE, offsetof($1, exec_context.rlimit)
|
$1.LimitNICE, config_parse_limit, RLIMIT_NICE, offsetof($1, exec_context.rlimit)
|
||||||
$1.LimitRTPRIO, config_parse_limit, RLIMIT_RTPRIO, offsetof($1, exec_context.rlimit)
|
$1.LimitRTPRIO, config_parse_limit, RLIMIT_RTPRIO, offsetof($1, exec_context.rlimit)
|
||||||
$1.LimitRTTIME, config_parse_limit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit)
|
$1.LimitRTTIME, config_parse_limit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit)
|
||||||
$1.ControlGroup, config_parse_unit_cgroup, 0, offsetof($1, exec_context)
|
$1.ControlGroup, config_parse_unit_cgroup, 0, 0
|
||||||
|
$1.ControlGroupAttribute, config_parse_unit_cgroup_attr, 0, 0
|
||||||
|
$1.CPUShares, config_parse_unit_cpu_shares, 0, 0
|
||||||
|
$1.MemoryLimit, config_parse_unit_memory_limit, 0, 0
|
||||||
|
$1.MemorySoftLimit, config_parse_unit_memory_limit, 0, 0
|
||||||
|
$1.DeviceAllow, config_parse_unit_device_allow, 0, 0
|
||||||
|
$1.DeviceDeny, config_parse_unit_device_allow, 0, 0
|
||||||
$1.ReadWriteDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.read_write_dirs)
|
$1.ReadWriteDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.read_write_dirs)
|
||||||
$1.ReadOnlyDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.read_only_dirs)
|
$1.ReadOnlyDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.read_only_dirs)
|
||||||
$1.InaccessibleDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.inaccessible_dirs)
|
$1.InaccessibleDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.inaccessible_dirs)
|
||||||
|
@ -1639,6 +1639,201 @@ int config_parse_unit_condition_null(
|
|||||||
|
|
||||||
DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
|
DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier");
|
||||||
|
|
||||||
|
int config_parse_unit_cgroup_attr(
|
||||||
|
const char *filename,
|
||||||
|
unsigned line,
|
||||||
|
const char *section,
|
||||||
|
const char *lvalue,
|
||||||
|
int ltype,
|
||||||
|
const char *rvalue,
|
||||||
|
void *data,
|
||||||
|
void *userdata) {
|
||||||
|
|
||||||
|
Unit *u = data;
|
||||||
|
char **l;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(filename);
|
||||||
|
assert(lvalue);
|
||||||
|
assert(rvalue);
|
||||||
|
assert(data);
|
||||||
|
|
||||||
|
l = strv_split_quoted(rvalue);
|
||||||
|
if (!l)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (strv_length(l) != 2) {
|
||||||
|
log_error("[%s:%u] Failed to parse cgroup attribute value, ignoring: %s", filename, line, rvalue);
|
||||||
|
strv_free(l);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = unit_add_cgroup_attribute(u, NULL, l[0], l[1], NULL);
|
||||||
|
strv_free(l);
|
||||||
|
|
||||||
|
if (r < 0) {
|
||||||
|
log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
|
||||||
|
Unit *u = data;
|
||||||
|
int r;
|
||||||
|
unsigned long ul;
|
||||||
|
char *t;
|
||||||
|
|
||||||
|
assert(filename);
|
||||||
|
assert(lvalue);
|
||||||
|
assert(rvalue);
|
||||||
|
assert(data);
|
||||||
|
|
||||||
|
if (safe_atolu(rvalue, &ul) < 0 || ul < 1) {
|
||||||
|
log_error("[%s:%u] Failed to parse CPU shares value, ignoring: %s", filename, line, rvalue);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (asprintf(&t, "%lu", ul) < 0)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r = unit_add_cgroup_attribute(u, "cpu", "cpu.shares", t, NULL);
|
||||||
|
free(t);
|
||||||
|
|
||||||
|
if (r < 0) {
|
||||||
|
log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
|
||||||
|
Unit *u = data;
|
||||||
|
int r;
|
||||||
|
off_t sz;
|
||||||
|
char *t;
|
||||||
|
|
||||||
|
assert(filename);
|
||||||
|
assert(lvalue);
|
||||||
|
assert(rvalue);
|
||||||
|
assert(data);
|
||||||
|
|
||||||
|
if (parse_bytes(rvalue, &sz) < 0 || sz <= 0) {
|
||||||
|
log_error("[%s:%u] Failed to parse memory limit value, ignoring: %s", filename, line, rvalue);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (asprintf(&t, "%llu", (unsigned long long) sz) < 0)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r = unit_add_cgroup_attribute(u,
|
||||||
|
"memory",
|
||||||
|
streq(lvalue, "MemorySoftLimit") ? "memory.soft_limit_in_bytes" : "memory.limit_in_bytes",
|
||||||
|
t, NULL);
|
||||||
|
free(t);
|
||||||
|
|
||||||
|
if (r < 0) {
|
||||||
|
log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int device_map(const char *controller, const char *name, const char *value, char **ret) {
|
||||||
|
struct stat st;
|
||||||
|
char **l;
|
||||||
|
|
||||||
|
l = strv_split_quoted(value);
|
||||||
|
if (!l)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
assert(strv_length(l) >= 1);
|
||||||
|
|
||||||
|
if (streq(l[0], "*")) {
|
||||||
|
|
||||||
|
if (asprintf(ret, "a *:*%s%s",
|
||||||
|
isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) {
|
||||||
|
strv_free(l);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (lstat(l[0], &st) < 0) {
|
||||||
|
log_warning("Couldn't stat device %s", l[0]);
|
||||||
|
strv_free(l);
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
|
||||||
|
log_warning("%s is not a device.", l[0]);
|
||||||
|
strv_free(l);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (asprintf(ret, "%c %u:%u%s%s",
|
||||||
|
S_ISCHR(st.st_mode) ? 'c' : 'b',
|
||||||
|
major(st.st_rdev), minor(st.st_rdev),
|
||||||
|
isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) {
|
||||||
|
|
||||||
|
strv_free(l);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strv_free(l);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
|
||||||
|
Unit *u = data;
|
||||||
|
char **l;
|
||||||
|
int r;
|
||||||
|
unsigned k;
|
||||||
|
|
||||||
|
assert(filename);
|
||||||
|
assert(lvalue);
|
||||||
|
assert(rvalue);
|
||||||
|
assert(data);
|
||||||
|
|
||||||
|
l = strv_split_quoted(rvalue);
|
||||||
|
if (!l)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
k = strv_length(l);
|
||||||
|
if (k < 1 || k > 2) {
|
||||||
|
log_error("[%s:%u] Failed to parse device value, ignoring: %s", filename, line, rvalue);
|
||||||
|
strv_free(l);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!streq(l[0], "*") && !path_startswith(l[0], "/dev")) {
|
||||||
|
log_error("[%s:%u] Device node path not absolute, ignoring: %s", filename, line, rvalue);
|
||||||
|
strv_free(l);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isempty(l[1]) && !in_charset(l[1], "rwm")) {
|
||||||
|
log_error("[%s:%u] Device access string invalid, ignoring: %s", filename, line, rvalue);
|
||||||
|
strv_free(l);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
strv_free(l);
|
||||||
|
|
||||||
|
r = unit_add_cgroup_attribute(u, "devices",
|
||||||
|
streq(lvalue, "DeviceAllow") ? "devices.allow" : "devices.deny",
|
||||||
|
rvalue, device_map);
|
||||||
|
|
||||||
|
if (r < 0) {
|
||||||
|
log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#define FOLLOW_MAX 8
|
#define FOLLOW_MAX 8
|
||||||
|
|
||||||
static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
|
static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
|
||||||
|
@ -76,6 +76,10 @@ int config_parse_unit_condition_string(const char *filename, unsigned line, cons
|
|||||||
int config_parse_unit_condition_null(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
int config_parse_unit_condition_null(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||||
int config_parse_kill_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
int config_parse_kill_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||||
int config_parse_notify_access(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
int config_parse_notify_access(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||||
|
int config_parse_unit_cgroup_attr(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||||
|
int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||||
|
int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||||
|
int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
|
||||||
|
|
||||||
/* gperf prototypes */
|
/* gperf prototypes */
|
||||||
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length);
|
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length);
|
||||||
|
@ -791,6 +791,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
|
|||||||
true,
|
true,
|
||||||
m->meta.manager->confirm_spawn,
|
m->meta.manager->confirm_spawn,
|
||||||
m->meta.cgroup_bondings,
|
m->meta.cgroup_bondings,
|
||||||
|
m->meta.cgroup_attributes,
|
||||||
&pid)) < 0)
|
&pid)) < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
@ -1728,6 +1728,7 @@ static int service_spawn(
|
|||||||
apply_tty_stdin,
|
apply_tty_stdin,
|
||||||
s->meta.manager->confirm_spawn,
|
s->meta.manager->confirm_spawn,
|
||||||
s->meta.cgroup_bondings,
|
s->meta.cgroup_bondings,
|
||||||
|
s->meta.cgroup_attributes,
|
||||||
&pid);
|
&pid);
|
||||||
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
@ -1112,6 +1112,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
|
|||||||
true,
|
true,
|
||||||
s->meta.manager->confirm_spawn,
|
s->meta.manager->confirm_spawn,
|
||||||
s->meta.cgroup_bondings,
|
s->meta.cgroup_bondings,
|
||||||
|
s->meta.cgroup_attributes,
|
||||||
&pid);
|
&pid);
|
||||||
|
|
||||||
strv_free(argv);
|
strv_free(argv);
|
||||||
|
@ -613,6 +613,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
|
|||||||
true,
|
true,
|
||||||
s->meta.manager->confirm_spawn,
|
s->meta.manager->confirm_spawn,
|
||||||
s->meta.cgroup_bondings,
|
s->meta.cgroup_bondings,
|
||||||
|
s->meta.cgroup_attributes,
|
||||||
&pid)) < 0)
|
&pid)) < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
81
src/unit.c
81
src/unit.c
@ -42,6 +42,7 @@
|
|||||||
#include "special.h"
|
#include "special.h"
|
||||||
#include "cgroup-util.h"
|
#include "cgroup-util.h"
|
||||||
#include "missing.h"
|
#include "missing.h"
|
||||||
|
#include "cgroup-attr.h"
|
||||||
|
|
||||||
const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
|
const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
|
||||||
[UNIT_SERVICE] = &service_vtable,
|
[UNIT_SERVICE] = &service_vtable,
|
||||||
@ -372,6 +373,7 @@ void unit_free(Unit *u) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cgroup_bonding_free_list(u->meta.cgroup_bondings, u->meta.manager->n_reloading <= 0);
|
cgroup_bonding_free_list(u->meta.cgroup_bondings, u->meta.manager->n_reloading <= 0);
|
||||||
|
cgroup_attribute_free_list(u->meta.cgroup_attributes);
|
||||||
|
|
||||||
free(u->meta.description);
|
free(u->meta.description);
|
||||||
free(u->meta.fragment_path);
|
free(u->meta.fragment_path);
|
||||||
@ -592,7 +594,6 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
|
|||||||
Iterator i;
|
Iterator i;
|
||||||
char *p2;
|
char *p2;
|
||||||
const char *prefix2;
|
const char *prefix2;
|
||||||
CGroupBonding *b;
|
|
||||||
char
|
char
|
||||||
timestamp1[FORMAT_TIMESTAMP_MAX],
|
timestamp1[FORMAT_TIMESTAMP_MAX],
|
||||||
timestamp2[FORMAT_TIMESTAMP_MAX],
|
timestamp2[FORMAT_TIMESTAMP_MAX],
|
||||||
@ -662,6 +663,9 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (u->meta.load_state == UNIT_LOADED) {
|
if (u->meta.load_state == UNIT_LOADED) {
|
||||||
|
CGroupBonding *b;
|
||||||
|
CGroupAttribute *a;
|
||||||
|
|
||||||
fprintf(f,
|
fprintf(f,
|
||||||
"%s\tStopWhenUnneeded: %s\n"
|
"%s\tStopWhenUnneeded: %s\n"
|
||||||
"%s\tRefuseManualStart: %s\n"
|
"%s\tRefuseManualStart: %s\n"
|
||||||
@ -682,6 +686,10 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
|
|||||||
fprintf(f, "%s\tControlGroup: %s:%s\n",
|
fprintf(f, "%s\tControlGroup: %s:%s\n",
|
||||||
prefix, b->controller, b->path);
|
prefix, b->controller, b->path);
|
||||||
|
|
||||||
|
LIST_FOREACH(by_unit, a, u->meta.cgroup_attributes)
|
||||||
|
fprintf(f, "%s\tControlGroupAttribute: %s %s \"%s\"\n",
|
||||||
|
prefix, a->controller, a->name, a->value);
|
||||||
|
|
||||||
if (UNIT_VTABLE(u)->dump)
|
if (UNIT_VTABLE(u)->dump)
|
||||||
UNIT_VTABLE(u)->dump(u, f, prefix2);
|
UNIT_VTABLE(u)->dump(u, f, prefix2);
|
||||||
|
|
||||||
@ -1885,8 +1893,10 @@ fail:
|
|||||||
}
|
}
|
||||||
|
|
||||||
int unit_add_default_cgroups(Unit *u) {
|
int unit_add_default_cgroups(Unit *u) {
|
||||||
|
CGroupAttribute *a;
|
||||||
char **c;
|
char **c;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(u);
|
assert(u);
|
||||||
|
|
||||||
/* Adds in the default cgroups, if they weren't specified
|
/* Adds in the default cgroups, if they weren't specified
|
||||||
@ -1899,8 +1909,10 @@ int unit_add_default_cgroups(Unit *u) {
|
|||||||
return r;
|
return r;
|
||||||
|
|
||||||
STRV_FOREACH(c, u->meta.manager->default_controllers)
|
STRV_FOREACH(c, u->meta.manager->default_controllers)
|
||||||
if ((r = unit_add_one_default_cgroup(u, *c)) < 0)
|
unit_add_one_default_cgroup(u, *c);
|
||||||
return r;
|
|
||||||
|
LIST_FOREACH(by_unit, a, u->meta.cgroup_attributes)
|
||||||
|
unit_add_one_default_cgroup(u, a->controller);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1911,6 +1923,69 @@ CGroupBonding* unit_get_default_cgroup(Unit *u) {
|
|||||||
return cgroup_bonding_find_list(u->meta.cgroup_bondings, SYSTEMD_CGROUP_CONTROLLER);
|
return cgroup_bonding_find_list(u->meta.cgroup_bondings, SYSTEMD_CGROUP_CONTROLLER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback) {
|
||||||
|
int r;
|
||||||
|
char *c = NULL;
|
||||||
|
CGroupAttribute *a;
|
||||||
|
|
||||||
|
assert(u);
|
||||||
|
assert(name);
|
||||||
|
assert(value);
|
||||||
|
|
||||||
|
if (!controller) {
|
||||||
|
const char *dot;
|
||||||
|
|
||||||
|
dot = strchr(name, '.');
|
||||||
|
if (!dot)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
c = strndup(name, dot - name);
|
||||||
|
if (!c)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
controller = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
|
||||||
|
r = -EINVAL;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
a = new0(CGroupAttribute, 1);
|
||||||
|
if (!a) {
|
||||||
|
r = -ENOMEM;
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c) {
|
||||||
|
a->controller = c;
|
||||||
|
c = NULL;
|
||||||
|
} else
|
||||||
|
a->controller = strdup(controller);
|
||||||
|
|
||||||
|
a->name = strdup(name);
|
||||||
|
a->value = strdup(value);
|
||||||
|
|
||||||
|
if (!a->controller || !a->name || !a->value) {
|
||||||
|
free(a->controller);
|
||||||
|
free(a->name);
|
||||||
|
free(a->value);
|
||||||
|
free(a);
|
||||||
|
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
a->map_callback = map_callback;
|
||||||
|
|
||||||
|
LIST_PREPEND(CGroupAttribute, by_unit, u->meta.cgroup_attributes, a);
|
||||||
|
|
||||||
|
r = 0;
|
||||||
|
|
||||||
|
finish:
|
||||||
|
free(c);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
int unit_load_related_unit(Unit *u, const char *type, Unit **_found) {
|
int unit_load_related_unit(Unit *u, const char *type, Unit **_found) {
|
||||||
char *t;
|
char *t;
|
||||||
int r;
|
int r;
|
||||||
|
@ -130,6 +130,7 @@ enum UnitDependency {
|
|||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
#include "job.h"
|
#include "job.h"
|
||||||
#include "cgroup.h"
|
#include "cgroup.h"
|
||||||
|
#include "cgroup-attr.h"
|
||||||
|
|
||||||
struct Meta {
|
struct Meta {
|
||||||
Manager *manager;
|
Manager *manager;
|
||||||
@ -167,6 +168,7 @@ struct Meta {
|
|||||||
|
|
||||||
/* Counterparts in the cgroup filesystem */
|
/* Counterparts in the cgroup filesystem */
|
||||||
CGroupBonding *cgroup_bondings;
|
CGroupBonding *cgroup_bondings;
|
||||||
|
CGroupAttribute *cgroup_attributes;
|
||||||
|
|
||||||
/* Per type list */
|
/* Per type list */
|
||||||
LIST_FIELDS(Meta, units_by_type);
|
LIST_FIELDS(Meta, units_by_type);
|
||||||
@ -432,6 +434,7 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b);
|
|||||||
int unit_add_cgroup_from_text(Unit *u, const char *name);
|
int unit_add_cgroup_from_text(Unit *u, const char *name);
|
||||||
int unit_add_default_cgroups(Unit *u);
|
int unit_add_default_cgroups(Unit *u);
|
||||||
CGroupBonding* unit_get_default_cgroup(Unit *u);
|
CGroupBonding* unit_get_default_cgroup(Unit *u);
|
||||||
|
int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback);
|
||||||
|
|
||||||
int unit_choose_id(Unit *u, const char *name);
|
int unit_choose_id(Unit *u, const char *name);
|
||||||
int unit_set_description(Unit *u, const char *description);
|
int unit_set_description(Unit *u, const char *description);
|
||||||
|
69
src/util.c
69
src/util.c
@ -1439,6 +1439,19 @@ char *delete_chars(char *s, const char *bad) {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool in_charset(const char *s, const char* charset) {
|
||||||
|
const char *i;
|
||||||
|
|
||||||
|
assert(s);
|
||||||
|
assert(charset);
|
||||||
|
|
||||||
|
for (i = s; *i; i++)
|
||||||
|
if (!strchr(charset, *i))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
char *file_in_same_dir(const char *path, const char *filename) {
|
char *file_in_same_dir(const char *path, const char *filename) {
|
||||||
char *e, *r;
|
char *e, *r;
|
||||||
size_t k;
|
size_t k;
|
||||||
@ -2969,6 +2982,62 @@ int parse_usec(const char *t, usec_t *usec) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int parse_bytes(const char *t, off_t *bytes) {
|
||||||
|
static const struct {
|
||||||
|
const char *suffix;
|
||||||
|
off_t factor;
|
||||||
|
} table[] = {
|
||||||
|
{ "B", 1 },
|
||||||
|
{ "K", 1024ULL },
|
||||||
|
{ "M", 1024ULL*1024ULL },
|
||||||
|
{ "G", 1024ULL*1024ULL*1024ULL },
|
||||||
|
{ "T", 1024ULL*1024ULL*1024ULL*1024ULL },
|
||||||
|
{ "", 1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *p;
|
||||||
|
off_t r = 0;
|
||||||
|
|
||||||
|
assert(t);
|
||||||
|
assert(bytes);
|
||||||
|
|
||||||
|
p = t;
|
||||||
|
do {
|
||||||
|
long long l;
|
||||||
|
char *e;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
l = strtoll(p, &e, 10);
|
||||||
|
|
||||||
|
if (errno != 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
if (l < 0)
|
||||||
|
return -ERANGE;
|
||||||
|
|
||||||
|
if (e == p)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
e += strspn(e, WHITESPACE);
|
||||||
|
|
||||||
|
for (i = 0; i < ELEMENTSOF(table); i++)
|
||||||
|
if (startswith(e, table[i].suffix)) {
|
||||||
|
r += (off_t) l * table[i].factor;
|
||||||
|
p = e + strlen(table[i].suffix);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >= ELEMENTSOF(table))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
} while (*p != 0);
|
||||||
|
|
||||||
|
*bytes = r;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int make_stdio(int fd) {
|
int make_stdio(int fd) {
|
||||||
int r, s, t;
|
int r, s, t;
|
||||||
|
|
||||||
|
@ -135,6 +135,7 @@ void close_many(const int fds[], unsigned n_fd);
|
|||||||
|
|
||||||
int parse_boolean(const char *v);
|
int parse_boolean(const char *v);
|
||||||
int parse_usec(const char *t, usec_t *usec);
|
int parse_usec(const char *t, usec_t *usec);
|
||||||
|
int parse_bytes(const char *t, off_t *bytes);
|
||||||
int parse_pid(const char *s, pid_t* ret_pid);
|
int parse_pid(const char *s, pid_t* ret_pid);
|
||||||
int parse_uid(const char *s, uid_t* ret_uid);
|
int parse_uid(const char *s, uid_t* ret_uid);
|
||||||
#define parse_gid(s, ret_uid) parse_uid(s, ret_uid)
|
#define parse_gid(s, ret_uid) parse_uid(s, ret_uid)
|
||||||
@ -462,6 +463,8 @@ char *join(const char *x, ...) _sentinel_;
|
|||||||
|
|
||||||
bool is_main_thread(void);
|
bool is_main_thread(void);
|
||||||
|
|
||||||
|
bool in_charset(const char *s, const char* charset);
|
||||||
|
|
||||||
#define NULSTR_FOREACH(i, l) \
|
#define NULSTR_FOREACH(i, l) \
|
||||||
for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
|
for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user