of group names or IDs. This option may
be specified more than once in which
case all listed groups are set as
- supplementary groups. This option does
- not override but extends the list of
- supplementary groups configured in the
- system group database for the
+ supplementary groups. When the empty
+ string is assigned the list of
+ supplementary groups is reset, and all
+ assignments prior to this one will
+ have no effect. In any way, this
+ option does not override, but extends
+ the list of supplementary groups
+ configured in the system group
+ database for the
user.</para></listitem>
</varlistentry>
<listitem><para>Controls the CPU
affinity of the executed
processes. Takes a space-separated
- list of CPU indexes. See
+ list of CPU indexes. This option may
+ be specified more than once in which
+ case the specificed CPU affinity masks
+ are merged. If the empty string is
+ assigned the mask is reset, all
+ assignments prior to this will have no
+ effect. See
<citerefentry><refentrytitle>sched_setaffinity</refentrytitle><manvolnum>2</manvolnum></citerefentry>
for details.</para></listitem>
</varlistentry>
in which case all listed variables
will be set. If the same variable is
set twice the later setting will
- override the earlier setting. See
+ override the earlier setting. If the
+ empty string is assigned to this
+ option the list of environment
+ variables is reset, all prior
+ assignments have no effect. See
<citerefentry><refentrytitle>environ</refentrytitle><manvolnum>7</manvolnum></citerefentry>
for details.</para></listitem>
</varlistentry>
parser strips leading and
trailing whitespace from the values
of assignments, unless you use
- double quotes (").
- The
- argument passed should be an absolute
- file name or wildcard expression, optionally prefixed with
+ double quotes (").</para>
+
+ <para>The argument passed should be an
+ absolute file name or wildcard
+ expression, optionally prefixed with
"-", which indicates that if the file
does not exist it won't be read and no
- error or warning message is
- logged. The files listed with this
+ error or warning message is logged.
+ This option may be specified more than
+ once in which case all specified files
+ are read. If the empty string is
+ assigned to this option the list of
+ file to read is reset, all prior
+ assignments have no effect.</para>
+
+ <para>The files listed with this
directive will be read shortly before
the process is executed. Settings from
these files override settings made
these files the files will be read in
the order they are specified and the
later setting will override the
- earlier setting. </para></listitem>
+ earlier setting.</para></listitem>
</varlistentry>
<varlistentry>
capability bounding set is not
modified on process execution, hence
no limits on the capabilities of the
- process are
- enforced.</para></listitem>
+ process are enforced. This option may
+ appear more than once in which case
+ the bounding sets are merged. If the empty
+ string is assigned to this option the
+ bounding set is reset, and all prior
+ settings have no
+ effect.</para></listitem>
</varlistentry>
<varlistentry>
<option>no-setuid-fixup</option>,
<option>no-setuid-fixup-locked</option>,
<option>noroot</option> and/or
- <option>noroot-locked</option>.
- </para></listitem>
+ <option>noroot-locked</option>. This
+ option may appear more than once in
+ which case the secure bits are
+ ORed. If the empty string is assigned
+ to this option the bits are reset to
+ 0.</para></listitem>
</varlistentry>
<varlistentry>
groups the executed processes shall be
made members of. Takes a
space-separated list of cgroup
- identifiers. A cgroup identifier has a
- format like
+ identifiers. A cgroup identifier is
+ formatted like
<filename>cpu:/foo/bar</filename>,
- where "cpu" identifies the kernel
+ where "cpu" indicates the kernel
control group controller used, and
<filename>/foo/bar</filename> is the
control group path. The controller
hierarchy is implied. Alternatively,
the path and ":" may be omitted, in
which case the default control group
- path for this unit is implied. This
- option may be used to place executed
- processes in arbitrary groups in
- arbitrary hierarchies -- which can be
- configured externally with additional
- execution limits. By default systemd
- will place all executed processes in
- separate per-unit control groups
- (named after the unit) in the systemd
- named hierarchy. Since every process
- can be in one group per hierarchy only
- overriding the control group path in
- the named systemd hierarchy will
- disable automatic placement in the
- default group. This option is
- primarily intended to place executed
- processes in specific paths in
- specific kernel controller
- hierarchies. It is however not
+ path for this unit is implied.</para>
+
+ <para>This option may be used to place
+ executed processes in arbitrary groups
+ in arbitrary hierarchies -- which may
+ then be externally configured with
+ additional execution limits. By
+ default systemd will place all
+ executed processes in separate
+ per-unit control groups (named after
+ the unit) in the systemd named
+ hierarchy. This option is primarily
+ intended to place executed processes
+ in specific paths in specific kernel
+ controller hierarchies. It is 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>
+
+ <para>This option may appear more than
+ once, in which case the list of
+ control group assignments is
+ merged. If the same hierarchy gets two
+ different paths assigned only the
+ later setting will take effect. If the
+ empty string is assigned to this
+ option the list of control group
+ assignments is reset, all previous
+ assignments will have no
+ effect.</para>
+
+ <para>Note that the list of control
+ group assignments of a unit is
+ extended implicitly based on the
+ settings of
+ <varname>DefaultControllers=</varname>
+ of
+ <citerefentry><refentrytitle>systemd.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ but a unit's
+ <varname>ControlGroup=</varname>
+ setting for a specific controller
+ takes precedence.</para></listitem>
</varlistentry>
<varlistentry>
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,
+ is in most cases sufficient to make
+ use of control group enforcements,
explicit
<varname>ControlGroup=</varname> are
only necessary in case the implied
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>
+ attributes. If this option is used
+ multiple times for the same cgroup
+ attribute only the later setting takes
+ effect. If the empty string is
+ assigned to this option the list of
+ attributes is reset, all previous
+ cgroup attribute settings have no
+ effect, including those done with
+ <varname>CPUShares=</varname>,
+ <varname>MemoryLimit=</varname>,
+ <varname>MemorySoftLimit</varname>,
+ <varname>DeviceAllow=</varname>,
+ <varname>DeviceDeny=</varname>,
+ <varname>BlockIOWeight=</varname>,
+ <varname>BlockIOReadBandwidth=</varname>,
+ <varname>BlockIOWriteBandwidth=</varname>.
+ </para></listitem>
</varlistentry>
<varlistentry>
usual file access controls would
permit this. Directories listed in
<varname>InaccessibleDirectories=</varname>
- will be made inaccessible for processes
- inside the namespace. Note that
- restricting access with these options
- does not extend to submounts of a
- directory. You must list submounts
- separately in these settings to
- ensure the same limited access. These
- options may be specified more than
- once in which case all directories
- listed will have limited access from
- within the
- namespace.</para></listitem>
+ will be made inaccessible for
+ processes inside the namespace. Note
+ that restricting access with these
+ options does not extend to submounts
+ of a directory. You must list
+ submounts separately in these settings
+ to ensure the same limited
+ access. These options may be specified
+ more than once in which case all
+ directories listed will have limited
+ access from within the namespace. If
+ the empty string is assigned to this
+ option the specific list is reset, and
+ all prior assignments have no
+ effect.</para></listitem>
</varlistentry>
<varlistentry>
<function>exit_group</function>,
<function>exit</function> system calls
are implicitly whitelisted and don't
- need to be listed
- explicitly.</para></listitem>
+ need to be listed explicitly. This
+ option may be specified more than once
+ in which case the filter masks are
+ merged. If the empty string is
+ assigned the filter is reset, all
+ prior assignments will have no
+ effect.</para></listitem>
</varlistentry>
</variablelist>
specified. <varname>PathChanged=</varname>
may be used to watch a file or
directory and activate the configured
- unit whenever it changes. It is not activated
- on every write to the watched file but it is
- activated if the file which was open for writing
- gets closed. <varname>PathModified=</varname>
- is similar, but additionally it is activated
- also on simple writes to the watched file.
-
+ unit whenever it changes. It is not
+ activated on every write to the
+ watched file but it is activated if
+ the file which was open for writing
+ gets
+ closed. <varname>PathModified=</varname>
+ is similar, but additionally it is
+ activated also on simple writes to the
+ watched file.
<varname>DirectoryNotEmpty=</varname>
may be used to watch a directory and
activate the configured unit whenever
<para>Multiple directives may be
combined, of the same and of different
- types, to watch multiple paths.</para>
+ types, to watch multiple paths. If the
+ empty string is assigned to any of
+ these options the list of paths to
+ watch is reset, and any prior
+ assignments of these options will not
+ have any effect.</para>
<para>If a path is already existing
(in case of
for compatibility with parsers
suitable for XDG
<filename>.desktop</filename> files.
- The commands are invoked one by
- one sequentially in the order they
- appear in the unit file.
- When <varname>Type</varname> is
- not <option>oneshot</option>, only one
+ The commands are invoked one by one
+ sequentially in the order they appear
+ in the unit file. When
+ <varname>Type</varname> is not
+ <option>oneshot</option>, only one
command may be given. Lone semicolons
may be escaped as
- '<literal>\;</literal>'.</para>
+ '<literal>\;</literal>'. If the empty
+ string is assigned to this option the
+ list of commands to start is reset,
+ prior assignments of this option will
+ have no effect.</para>
<para>Unless
<varname>Type=forking</varname> is
line (i.e. the program to execute) may
not include specifiers.</para>
- <para>Optionally, if the absolute file
- name is prefixed with
- '<literal>@</literal>', the second token
- will be passed as
- <literal>argv[0]</literal> to the
- executed process, followed by the
- further arguments specified. If the
- absolute file name is prefixed with
- '<literal>-</literal>' an exit code of
- the command normally considered a
- failure (i.e. non-zero exit status or
- abnormal exit due to signal) is ignored
- and considered success. If both
- '<literal>-</literal>' and
- '<literal>@</literal>' are used they
- can appear in either order.</para>
-
<para>On top of that basic environment
variable substitution is
supported. Use
literal and absolute path
name.</para>
+ <para>Optionally, if the absolute file
+ name is prefixed with
+ '<literal>@</literal>', the second token
+ will be passed as
+ <literal>argv[0]</literal> to the
+ executed process, followed by the
+ further arguments specified. If the
+ absolute file name is prefixed with
+ '<literal>-</literal>' an exit code of
+ the command normally considered a
+ failure (i.e. non-zero exit status or
+ abnormal exit due to signal) is ignored
+ and considered success. If both
+ '<literal>-</literal>' and
+ '<literal>@</literal>' are used they
+ can appear in either order.</para>
+
<para>Note that this setting does not
directly support shell command
lines. If shell command lines are to
SIGKILL</literal>", ensures that exit
codes 1, 2, 8 and the termination
signal SIGKILL are considered clean
- service
- terminations.</para></listitem>
+ service terminations. This option may
+ appear more than once in which case
+ the list of successful exit statuses
+ is merged. If the empty string is
+ assigned to this option the list is
+ reset, all prior assignments of this
+ option will have no
+ effect.</para></listitem>
</varlistentry>
<varlistentry>
logic. Example:
"<literal>RestartPreventExitStatus=1 6
SIGABRT</literal>", ensures that exit
- codes 1 and 6 and the termination signal
- SIGABRT will not result in automatic
- service restarting.</para></listitem>
+ codes 1 and 6 and the termination
+ signal SIGABRT will not result in
+ automatic service restarting. This
+ option may appear more than once in
+ which case the list of restart preventing
+ statuses is merged. If the empty
+ string is assigned to this option the
+ list is reset, all prior assignments
+ of this option will have no
+ effect.</para></listitem>
</varlistentry>
<varlistentry>
same time. Also note that a different
service may be activated on incoming
traffic than inherits the sockets. Or
- in other words: The
+ in other words: the
<varname>Service=</varname> setting of
<filename>.socket</filename> units
- doesn't have to match the inverse of the
- <varname>Sockets=</varname> setting of
- the <filename>.service</filename> it
- refers to.</para></listitem>
+ doesn't have to match the inverse of
+ the <varname>Sockets=</varname>
+ setting of the
+ <filename>.service</filename> it
+ refers to.</para>
+
+ <para>This option may appear more than
+ once, in which case the list of socket
+ units is merged. If the empty string
+ is assigned to this option the list of
+ sockets is reset, all prior uses of
+ this setting will have no
+ effect.</para></listitem>
</varlistentry>
<varlistentry>
<para>These options may be specified
more than once in which case incoming
- traffic on any of the sockets will trigger
- service activation, and all listed
- sockets will be passed to the service,
- regardless whether there is incoming
- traffic on them or not.</para>
-
- <para>If an IP address is used here, it
- is often desirable to listen on it
+ traffic on any of the sockets will
+ trigger service activation, and all
+ listed sockets will be passed to the
+ service, regardless whether there is
+ incoming traffic on them or not. If
+ the empty string is assigned to any of
+ these options, the list of addresses
+ to listen on is reset, all prior uses
+ of any of these options will have no
+ effect.</para>
+
+ <para>If an IP address is used here,
+ it is often desirable to listen on it
before the interface it is configured
on is up and running, and even
regardless whether it will be up and
- running ever at all. To deal with this it is
- recommended to set the
+ running ever at all. To deal with this
+ it is recommended to set the
<varname>FreeBind=</varname> option
described below.</para></listitem>
</varlistentry>
machine was booted
up. <varname>OnStartupSec=</varname>
defines a timer relative to when
- systemd was
+ systemd was first
started. <varname>OnUnitActiveSec=</varname>
defines a timer relative to when the
unit the timer is activating was last
<para>These are monotonic timers,
independent of wall-clock time and timezones. If the
computer is temporarily suspended, the
- monotonic clock stops too.</para></listitem>
+ monotonic clock stops too.</para>
+
+ <para>If the empty string is assigned
+ to any of these options the list of
+ timers is reset, and all prior
+ assignments will have no
+ effect.</para></listitem>
</varlistentry>
event expressions. See
<citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>
for more information on the syntax of
- calendar event
- expressions.</para></listitem>
+ calendar event expressions. Otherwise
+ the semantics are similar to
+ <varname>OnActiveSec=</varname> and
+ related settings.</para></listitem>
</varlistentry>
<varlistentry>
reference documentation that explains
what the unit's purpose is, followed
by how it is configured, followed by
- any other related
- documentation.</para></listitem>
+ any other related documentation. This
+ option may be specified more than once
+ in which case the specified list of
+ URIs is merged. If the empty string is
+ assigned to this option the list is
+ reset and all prior assignments will
+ have no effect.</para></listitem>
</varlistentry>
<varlistentry>
pipe symbol must be passed first, the
exclamation second. Except for
<varname>ConditionPathIsSymbolicLink=</varname>,
- all path checks follow
- symlinks.</para></listitem>
+ all path checks follow symlinks. If
+ any of these options is assigned the
+ empty string the list of conditions is
+ reset completely, all previous
+ condition settings (of any kind) will
+ have no effect.</para></listitem>
</varlistentry>
<varlistentry>
EXEC_CONTEXT_CONFIG_ITEMS(Service)m4_dnl
KILL_CONTEXT_CONFIG_ITEMS(Service)m4_dnl
m4_dnl
-Socket.ListenStream, config_parse_socket_listen, 0, 0
-Socket.ListenDatagram, config_parse_socket_listen, 0, 0
-Socket.ListenSequentialPacket, config_parse_socket_listen, 0, 0
-Socket.ListenFIFO, config_parse_socket_listen, 0, 0
-Socket.ListenNetlink, config_parse_socket_listen, 0, 0
-Socket.ListenSpecial, config_parse_socket_listen, 0, 0
-Socket.ListenMessageQueue, config_parse_socket_listen, 0, 0
+Socket.ListenStream, config_parse_socket_listen, SOCKET_SOCKET, 0
+Socket.ListenDatagram, config_parse_socket_listen, SOCKET_SOCKET, 0
+Socket.ListenSequentialPacket, config_parse_socket_listen, SOCKET_SOCKET, 0
+Socket.ListenFIFO, config_parse_socket_listen, SOCKET_FIFO, 0
+Socket.ListenNetlink, config_parse_socket_listen, SOCKET_SOCKET, 0
+Socket.ListenSpecial, config_parse_socket_listen, SOCKET_SPECIAL, 0
+Socket.ListenMessageQueue, config_parse_socket_listen, SOCKET_MQUEUE, 0
Socket.BindIPv6Only, config_parse_socket_bind, 0, 0,
Socket.Backlog, config_parse_unsigned, 0, offsetof(Socket, backlog)
Socket.BindToDevice, config_parse_socket_bindtodevice, 0, 0
t = strndup(w, l);
if (!t)
- return -ENOMEM;
+ return log_oom();
k = unit_name_printf(u, t);
if (!k)
- return -ENOMEM;
+ return log_oom();
r = unit_add_dependency_by_name(u, d, k, NULL, true);
if (r < 0)
void *userdata) {
Unit *u = userdata;
- char *k;
- int r;
+ _cleanup_free_ char *k = NULL;
assert(filename);
assert(lvalue);
k = unit_full_printf(u, rvalue);
if (!k)
- return -ENOMEM;
-
- r = config_parse_string(filename, line, section, lvalue, ltype, k, data, userdata);
- free (k);
+ return log_oom();
- return r;
+ return config_parse_string(filename, line, section, lvalue, ltype, k, data, userdata);
}
int config_parse_unit_strv_printf(
void *userdata) {
Unit *u = userdata;
- char *k;
- int r;
+ _cleanup_free_ char *k = NULL;
assert(filename);
assert(lvalue);
k = unit_full_printf(u, rvalue);
if (!k)
- return -ENOMEM;
-
- r = config_parse_strv(filename, line, section, lvalue, ltype, k, data, userdata);
- free(k);
+ return log_oom();
- return r;
+ return config_parse_strv(filename, line, section, lvalue, ltype, k, data, userdata);
}
int config_parse_unit_path_printf(
void *userdata) {
Unit *u = userdata;
- char *k;
- int r;
+ _cleanup_free_ char *k = NULL;
assert(filename);
assert(lvalue);
if (!k)
return log_oom();
- r = config_parse_path(filename, line, section, lvalue, ltype, k, data, userdata);
- free(k);
-
- return r;
+ return config_parse_path(filename, line, section, lvalue, ltype, k, data, userdata);
}
int config_parse_socket_listen(
s = SOCKET(data);
+ if (isempty(rvalue)) {
+ /* An empty assignment removes all ports */
+ socket_free_ports(s);
+ return 0;
+ }
+
p = new0(SocketPort, 1);
if (!p)
- return -ENOMEM;
-
- if (streq(lvalue, "ListenFIFO")) {
- p->type = SOCKET_FIFO;
-
- if (!(p->path = unit_full_printf(UNIT(s), rvalue))) {
- free(p);
- return -ENOMEM;
- }
-
- path_kill_slashes(p->path);
-
- } else if (streq(lvalue, "ListenSpecial")) {
- p->type = SOCKET_SPECIAL;
-
- if (!(p->path = unit_full_printf(UNIT(s), rvalue))) {
- free(p);
- return -ENOMEM;
- }
-
- path_kill_slashes(p->path);
-
- } else if (streq(lvalue, "ListenMessageQueue")) {
+ return log_oom();
- p->type = SOCKET_MQUEUE;
+ if (ltype != SOCKET_SOCKET) {
- if (!(p->path = unit_full_printf(UNIT(s), rvalue))) {
+ p->type = ltype;
+ p->path = unit_full_printf(UNIT(s), rvalue);
+ if (!p->path) {
free(p);
- return -ENOMEM;
+ return log_oom();
}
path_kill_slashes(p->path);
} else if (streq(lvalue, "ListenNetlink")) {
- char *k;
+ _cleanup_free_ char *k = NULL;
int r;
p->type = SOCKET_SOCKET;
k = unit_full_printf(UNIT(s), rvalue);
- r = socket_address_parse_netlink(&p->address, k);
- free(k);
+ if (!k) {
+ free(p);
+ return log_oom();
+ }
+ r = socket_address_parse_netlink(&p->address, k);
if (r < 0) {
log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue);
free(p);
}
} else {
- char *k;
+ _cleanup_free_ char *k = NULL;
int r;
p->type = SOCKET_SOCKET;
k = unit_full_printf(UNIT(s), rvalue);
- r = socket_address_parse(&p->address, k);
- free(k);
+ if (!k) {
+ free(p);
+ return log_oom();
+ }
+ r = socket_address_parse(&p->address, k);
if (r < 0) {
log_error("[%s:%u] Failed to parse address value, ignoring: %s", filename, line, rvalue);
free(p);
assert(rvalue);
assert(e);
+ e += ltype;
+
+ if (isempty(rvalue)) {
+ /* An empty assignment resets the list */
+ exec_command_free_list(*e);
+ *e = NULL;
+ return 0;
+ }
+
/* We accept an absolute path as first argument, or
* alternatively an absolute prefixed with @ to allow
* overriding of argv[0]. */
-
- e += ltype;
-
for (;;) {
int i;
char *w;
n = new(char*, k + !honour_argv0);
if (!n)
- return -ENOMEM;
+ return log_oom();
k = 0;
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
path = strndup(w, l);
if (!path) {
- r = -ENOMEM;
+ r = log_oom();
goto fail;
}
c = n[k++] = cunescape_length(w, l);
if (!c) {
- r = -ENOMEM;
+ r = log_oom();
goto fail;
}
if (!path) {
path = strdup(n[0]);
if (!path) {
- r = -ENOMEM;
+ r = log_oom();
goto fail;
}
}
nce = new0(ExecCommand, 1);
if (!nce) {
- r = -ENOMEM;
+ r = log_oom();
goto fail;
}
assert(data);
if (rvalue[0] && !streq(rvalue, "*")) {
- if (!(n = strdup(rvalue)))
- return -ENOMEM;
+ n = strdup(rvalue);
+ if (!n)
+ return log_oom();
} else
n = NULL;
return 0;
}
-
/* On Linux RR/FIFO range from 1 to 99 and OTHER/BATCH may only be 0 */
min = sched_get_priority_min(c->cpu_sched_policy);
max = sched_get_priority_max(c->cpu_sched_policy);
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* An empty assignment resets the CPU list */
+ if (c->cpuset)
+ CPU_FREE(c->cpuset);
+ c->cpuset = NULL;
+ return 0;
+ }
+
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
char _cleanup_free_ *t = NULL;
int r;
t = strndup(w, l);
if (!t)
- return -ENOMEM;
+ return log_oom();
r = safe_atou(t, &cpu);
if (!c->cpuset) {
c->cpuset = cpu_set_malloc(&c->cpuset_ncpus);
if (!c->cpuset)
- return -ENOMEM;
+ return log_oom();
}
if (r < 0 || cpu >= c->cpuset_ncpus) {
assert(rvalue);
assert(data);
- if (!(cap = cap_from_text(rvalue))) {
+ cap = cap_from_text(rvalue);
+ if (!cap) {
if (errno == ENOMEM)
- return -ENOMEM;
+ return log_oom();
log_error("[%s:%u] Failed to parse capabilities, ignoring: %s", filename, line, rvalue);
return 0;
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* An empty assignment resets the field */
+ c->secure_bits = 0;
+ return 0;
+ }
+
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
if (first_word(w, "keep-caps"))
c->secure_bits |= SECURE_KEEP_CAPS;
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* An empty assignment resets */
+ *capability_bounding_set_drop = 0;
+ return 0;
+ }
+
if (rvalue[0] == '~') {
invert = true;
rvalue++;
t = strndup(w, l);
if (!t)
- return -ENOMEM;
+ return log_oom();
r = cap_from_name(t, &cap);
if (r < 0) {
return 0;
}
- if (!*rl)
- if (!(*rl = new(struct rlimit, 1)))
- return -ENOMEM;
+ if (!*rl) {
+ *rl = new(struct rlimit, 1);
+ if (!*rl)
+ return log_oom();
+ }
(*rl)->rlim_cur = (*rl)->rlim_max = (rlim_t) u;
return 0;
size_t l;
char *state;
+ if (isempty(rvalue)) {
+ /* An empty assignment resets the list */
+ cgroup_bonding_free_list(u->cgroup_bondings, false);
+ u->cgroup_bondings = NULL;
+ return 0;
+ }
+
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
char _cleanup_free_ *t = NULL, *k = NULL, *ku = NULL;
int r;
t = strndup(w, l);
if (!t)
- return -ENOMEM;
+ return log_oom();
k = unit_full_printf(u, t);
if (!k)
- return -ENOMEM;
+ return log_oom();
ku = cunescape(k);
if (!ku)
- return -ENOMEM;
+ return log_oom();
r = unit_add_cgroup_from_text(u, ku, true, NULL);
if (r < 0) {
assert(rvalue);
assert(sig);
- if ((r = signal_from_string_try_harder(rvalue)) <= 0) {
+ r = signal_from_string_try_harder(rvalue);
+ if (r <= 0) {
log_error("[%s:%u] Failed to parse kill signal, ignoring: %s", filename, line, rvalue);
return 0;
}
t = strndup(w, l);
if (!t)
- return -ENOMEM;
+ return log_oom();
if (streq(t, "shared"))
flags |= MS_SHARED;
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* Empty assignment resets list */
+ timer_free_values(t);
+ return 0;
+ }
+
b = timer_base_from_string(lvalue);
if (b < 0) {
log_error("[%s:%u] Failed to parse timer base, ignoring: %s", filename, line, lvalue);
v = new0(TimerValue, 1);
if (!v)
- return -ENOMEM;
+ return log_oom();
v->base = b;
v->clock_id = id;
int r;
DBusError error;
Unit *u;
+ _cleanup_free_ char *p = NULL;
assert(filename);
assert(lvalue);
dbus_error_init(&error);
- if (endswith(rvalue, ".timer")) {
+ p = unit_name_printf(UNIT(t), rvalue);
+ if (!p)
+ return log_oom();
+
+ if (endswith(p, ".timer")) {
log_error("[%s:%u] Unit cannot be of type timer, ignoring: %s", filename, line, rvalue);
return 0;
}
- r = manager_load_unit(UNIT(t)->manager, rvalue, NULL, NULL, &u);
+ r = manager_load_unit(UNIT(t)->manager, p, NULL, NULL, &u);
if (r < 0) {
log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
dbus_error_free(&error);
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* Empty assignment clears list */
+ path_free_specs(p);
+ return 0;
+ }
+
b = path_type_from_string(lvalue);
if (b < 0) {
log_error("[%s:%u] Failed to parse path type, ignoring: %s", filename, line, lvalue);
int r;
DBusError error;
Unit *x;
+ _cleanup_free_ char *p = NULL;
assert(filename);
assert(lvalue);
dbus_error_init(&error);
- if (!endswith(rvalue, ".service")) {
+ p = unit_name_printf(UNIT(s), rvalue);
+ if (!p)
+ return log_oom();
+
+ if (!endswith(p, ".service")) {
log_error("[%s:%u] Unit must be of type service, ignoring: %s", filename, line, rvalue);
return 0;
}
- r = manager_load_unit(UNIT(s)->manager, rvalue, NULL, &error, &x);
+ r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
if (r < 0) {
log_error("[%s:%u] Failed to load unit %s, ignoring: %s", filename, line, rvalue, bus_error(&error, r));
dbus_error_free(&error);
t = strndup(w, l);
if (!t)
- return -ENOMEM;
+ return log_oom();
k = unit_name_printf(UNIT(s), t);
if (!k)
- return -ENOMEM;
+ return log_oom();
if (!endswith(k, ".socket")) {
log_error("[%s:%u] Unit must be of type socket, ignoring: %s",
assert(s);
r = config_parse_usec(filename, line, section, lvalue, ltype, rvalue, data, userdata);
-
- if (r)
+ if (r < 0)
return r;
if (streq(lvalue, "TimeoutSec")) {
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* Empty assignment frees the list */
+
+ strv_free(*env);
+ *env = NULL;
+ return 0;
+ }
+
s = unit_full_printf(u, rvalue);
if (!s)
- return -ENOMEM;
+ return log_oom();
if (!path_is_absolute(s[0] == '-' ? s + 1 : s)) {
log_error("[%s:%u] Path '%s' is not absolute, ignoring.", filename, line, s);
k = strv_append(*env, s);
free(s);
if (!k)
- return -ENOMEM;
+ return log_oom();
strv_free(*env);
*env = k;
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ condition_free_list(u->conditions);
+ u->conditions = NULL;
+ return 0;
+ }
+
trigger = rvalue[0] == '|';
if (trigger)
rvalue++;
p = unit_full_printf(u, rvalue);
if (!p)
- return -ENOMEM;
+ return log_oom();
if (!path_is_absolute(p)) {
log_error("[%s:%u] Path in condition not absolute, ignoring: %s", filename, line, p);
c = condition_new(cond, p, trigger, negate);
if (!c)
- return -ENOMEM;
+ return log_oom();
LIST_PREPEND(Condition, conditions, u->conditions, c);
return 0;
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ condition_free_list(u->conditions);
+ u->conditions = NULL;
+ return 0;
+ }
+
trigger = rvalue[0] == '|';
if (trigger)
rvalue++;
s = unit_full_printf(u, rvalue);
if (!s)
- return -ENOMEM;
+ return log_oom();
c = condition_new(cond, s, trigger, negate);
if (!c)
assert(rvalue);
assert(data);
- if ((trigger = rvalue[0] == '|'))
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ condition_free_list(u->conditions);
+ u->conditions = NULL;
+ return 0;
+ }
+
+ trigger = rvalue[0] == '|';
+ if (trigger)
rvalue++;
- if ((negate = rvalue[0] == '!'))
+ negate = rvalue[0] == '!';
+ if (negate)
rvalue++;
- if ((b = parse_boolean(rvalue)) < 0) {
+ b = parse_boolean(rvalue);
+ if (b < 0) {
log_error("[%s:%u] Failed to parse boolean value in condition, ignoring: %s", filename, line, rvalue);
return 0;
}
if (!b)
negate = !negate;
- if (!(c = condition_new(CONDITION_NULL, NULL, trigger, negate)))
- return -ENOMEM;
+ c = condition_new(CONDITION_NULL, NULL, trigger, negate);
+ if (!c)
+ return log_oom();
LIST_PREPEND(Condition, conditions, u->conditions, c);
return 0;
void *userdata) {
Unit *u = data;
- char **l;
+ _cleanup_strv_free_ char **l = NULL;
int r;
assert(filename);
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* Empty assignment clears the list */
+ cgroup_attribute_free_list(u->cgroup_attributes);
+ u->cgroup_attributes = NULL;
+ return 0;
+ }
+
l = strv_split_quoted(rvalue);
if (!l)
- return -ENOMEM;
+ return log_oom();
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, NULL);
- strv_free(l);
-
if (r < 0) {
log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
return 0;
Unit *u = data;
int r;
unsigned long ul;
- char *t;
+ _cleanup_free_ char *t = NULL;
assert(filename);
assert(lvalue);
}
if (asprintf(&t, "%lu", ul) < 0)
- return -ENOMEM;
+ return log_oom();
r = unit_add_cgroup_attribute(u, "cpu", "cpu.shares", t, NULL, NULL);
- free(t);
-
if (r < 0) {
log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
return 0;
Unit *u = data;
int r;
off_t sz;
- char *t;
+ _cleanup_free_ char *t = NULL;
assert(filename);
assert(lvalue);
}
if (asprintf(&t, "%llu", (unsigned long long) sz) < 0)
- return -ENOMEM;
+ return log_oom();
r = unit_add_cgroup_attribute(u,
"memory",
streq(lvalue, "MemorySoftLimit") ? "memory.soft_limit_in_bytes" : "memory.limit_in_bytes",
t, NULL, NULL);
- free(t);
-
if (r < 0) {
log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
return 0;
}
static int device_map(const char *controller, const char *name, const char *value, char **ret) {
- char **l;
+ _cleanup_strv_free_ char **l = NULL;
assert(controller);
assert(name);
if (streq(l[0], "*")) {
if (asprintf(ret, "a *:*%s%s",
- isempty(l[1]) ? "" : " ", strempty(l[1])) < 0) {
- strv_free(l);
+ isempty(l[1]) ? "" : " ", strempty(l[1])) < 0)
return -ENOMEM;
- }
-
} else {
struct stat st;
if (stat(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);
+ isempty(l[1]) ? "" : " ", strempty(l[1])) < 0)
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;
+ _cleanup_strv_free_ char **l = NULL;
int r;
unsigned k;
l = strv_split_quoted(rvalue);
if (!l)
- return -ENOMEM;
+ return log_oom();
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",
static int blkio_map(const char *controller, const char *name, const char *value, char **ret) {
struct stat st;
- char **l;
+ _cleanup_strv_free_ char **l = NULL;
dev_t d;
assert(controller);
l = strv_split_quoted(value);
if (!l)
- return -ENOMEM;
+ return log_oom();
assert(strv_length(l) == 2);
if (stat(l[0], &st) < 0) {
log_warning("Couldn't stat device %s", l[0]);
- strv_free(l);
return -errno;
}
block_get_whole_disk(d, &d);
} else {
log_warning("%s is not a block device and file system block device cannot be determined or is not local.", l[0]);
- strv_free(l);
return -ENODEV;
}
- if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0) {
- strv_free(l);
+ if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0)
return -ENOMEM;
- }
- strv_free(l);
return 0;
}
unsigned long ul;
const char *device = NULL, *weight;
unsigned k;
- char *t, **l;
+ _cleanup_free_ char *t = NULL;
+ _cleanup_strv_free_ char **l = NULL;
assert(filename);
assert(lvalue);
l = strv_split_quoted(rvalue);
if (!l)
- return -ENOMEM;
+ return log_oom();
k = strv_length(l);
if (k < 1 || k > 2) {
log_error("[%s:%u] Failed to parse weight value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
return 0;
}
if (device && !path_is_absolute(device)) {
log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
return 0;
}
if (safe_atolu(weight, &ul) < 0 || ul < 10 || ul > 1000) {
log_error("[%s:%u] Failed to parse block IO weight value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
return 0;
}
r = asprintf(&t, "%s %lu", device, ul);
else
r = asprintf(&t, "%lu", ul);
- strv_free(l);
-
if (r < 0)
- return -ENOMEM;
+ return log_oom();
if (device)
r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight_device", t, blkio_map, NULL);
else
r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight", t, NULL, NULL);
- free(t);
-
if (r < 0) {
log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
return 0;
int r;
off_t bytes;
unsigned k;
- char *t, **l;
+ _cleanup_free_ char *t = NULL;
+ _cleanup_strv_free_ char **l = NULL;
assert(filename);
assert(lvalue);
l = strv_split_quoted(rvalue);
if (!l)
- return -ENOMEM;
+ return log_oom();
k = strv_length(l);
if (k != 2) {
log_error("[%s:%u] Failed to parse bandwidth value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
return 0;
}
if (!path_is_absolute(l[0])) {
log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
return 0;
}
if (parse_bytes(l[1], &bytes) < 0 || bytes <= 0) {
log_error("[%s:%u] Failed to parse block IO bandwidth value, ignoring: %s", filename, line, rvalue);
- strv_free(l);
return 0;
}
r = asprintf(&t, "%s %llu", l[0], (unsigned long long) bytes);
- strv_free(l);
-
if (r < 0)
- return -ENOMEM;
+ return log_oom();
r = unit_add_cgroup_attribute(u, "blkio",
streq(lvalue, "BlockIOReadBandwidth") ? "blkio.read_bps_device" : "blkio.write_bps_device",
t, blkio_map, NULL);
- free(t);
-
if (r < 0) {
log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
return 0;
assert(rvalue);
assert(u);
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ strv_free(u->documentation);
+ u->documentation = NULL;
+ return 0;
+ }
+
r = config_parse_unit_strv_printf(filename, line, section, lvalue, ltype, rvalue, data, userdata);
if (r < 0)
return r;
assert(rvalue);
assert(u);
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ free(c->syscall_filter);
+ c->syscall_filter = NULL;
+ return 0;
+ }
+
if (rvalue[0] == '~') {
invert = true;
rvalue++;
n = (syscall_max() + 31) >> 4;
c->syscall_filter = new(uint32_t, n);
if (!c->syscall_filter)
- return -ENOMEM;
+ return log_oom();
memset(c->syscall_filter, invert ? 0xFF : 0, n * sizeof(uint32_t));
t = strndup(w, l);
if (!t)
- return -ENOMEM;
+ return log_oom();
id = syscall_from_name(t);
-
if (id < 0) {
log_error("[%s:%u] Failed to parse syscall, ignoring: %s",
filename, line, t);
p->directory_mode = 0755;
}
-static void path_done(Unit *u) {
- Path *p = PATH(u);
+void path_free_specs(Path *p) {
PathSpec *s;
assert(p);
- unit_ref_unset(&p->unit);
-
while ((s = p->specs)) {
- path_spec_unwatch(s, u);
+ path_spec_unwatch(s, UNIT(p));
LIST_REMOVE(PathSpec, spec, p->specs, s);
path_spec_done(s);
free(s);
}
}
+static void path_done(Unit *u) {
+ Path *p = PATH(u);
+
+ assert(p);
+
+ unit_ref_unset(&p->unit);
+ path_free_specs(p);
+}
+
int path_add_one_mount_link(Path *p, Mount *m) {
PathSpec *s;
int r;
* any of the paths of this path object */
int path_add_one_mount_link(Path *p, Mount *m);
+void path_free_specs(Path *p);
+
extern const UnitVTable path_vtable;
const char* path_state_to_string(PathState i);
s->control_pid = 0;
}
-static void socket_done(Unit *u) {
- Socket *s = SOCKET(u);
+void socket_free_ports(Socket *s) {
SocketPort *p;
assert(s);
free(p->path);
free(p);
}
+}
+
+static void socket_done(Unit *u) {
+ Socket *s = SOCKET(u);
+
+ assert(s);
+
+ socket_free_ports(s);
exec_context_done(&s->exec_context);
exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX);
/* Called from the service code when a per-connection service ended */
void socket_connection_unref(Socket *s);
+void socket_free_ports(Socket *s);
+
extern const UnitVTable socket_vtable;
const char* socket_state_to_string(SocketState i);
watch_init(&t->realtime_watch);
}
-static void timer_done(Unit *u) {
- Timer *t = TIMER(u);
+void timer_free_values(Timer *t) {
TimerValue *v;
assert(t);
free(v);
}
+}
+
+static void timer_done(Unit *u) {
+ Timer *t = TIMER(u);
+
+ assert(t);
+
+ timer_free_values(t);
unit_unwatch_timer(u, &t->monotonic_watch);
unit_unwatch_timer(u, &t->realtime_watch);
void timer_unit_notify(Unit *u, UnitActiveState new_state);
+void timer_free_values(Timer *t);
+
extern const UnitVTable timer_vtable;
const char *timer_state_to_string(TimerState i);
assert(rvalue);
assert(data);
- if ((r = safe_atoi(rvalue, i)) < 0) {
+ r = safe_atoi(rvalue, i);
+ if (r < 0) {
log_error("[%s:%u] Failed to parse numeric value, ingoring: %s", filename, line, rvalue);
return 0;
}
assert(rvalue);
assert(data);
- if ((r = safe_atoli(rvalue, i)) < 0) {
+ r = safe_atoli(rvalue, i);
+ if (r < 0) {
log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
return 0;
}
assert(rvalue);
assert(data);
- if ((r = safe_atou64(rvalue, u)) < 0) {
+ r = safe_atou64(rvalue, u);
+ if (r < 0) {
log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
return 0;
}
assert(rvalue);
assert(data);
- if ((r = safe_atou(rvalue, u)) < 0) {
+ r = safe_atou(rvalue, u);
+ if (r < 0) {
log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
return r;
}
n = strdup(rvalue);
if (!n)
- return -ENOMEM;
+ return log_oom();
if (!utf8_is_valid(n)) {
log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
n = strdup(rvalue);
if (!n)
- return -ENOMEM;
+ return log_oom();
path_kill_slashes(n);
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ strv_free(*sv);
+ *sv = NULL;
+ }
+
k = strv_length(*sv);
FOREACH_WORD_QUOTED(w, l, rvalue, state)
k++;
n = new(char*, k+1);
if (!n)
- return -ENOMEM;
+ return log_oom();
if (*sv)
for (k = 0; (*sv)[k]; k++)
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
n[k] = cunescape_length(w, l);
if (!n[k]) {
- r = -ENOMEM;
+ r = log_oom();
goto fail;
}
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ strv_free(*sv);
+ *sv = NULL;
+ }
+
k = strv_length(*sv);
FOREACH_WORD_QUOTED(w, l, rvalue, state)
k++;
n = new(char*, k+1);
if (!n)
- return -ENOMEM;
+ return log_oom();
k = 0;
if (*sv)
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
n[k] = strndup(w, l);
if (!n[k]) {
- r = -ENOMEM;
+ r = log_oom();
goto fail;
}
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+
+ set_free(status_set->signal);
+ set_free(status_set->code);
+
+ status_set->signal = status_set->code = NULL;
+ return 0;
+ }
+
FOREACH_WORD(w, l, rvalue, state) {
int val;
char *temp;