Merge pull request #1944 from poettering/randoms-ec

add RandomSec= setting to timer units, and more
This commit is contained in:
Tom Gundersen
2015-11-19 02:22:12 +01:00
13 changed files with 287 additions and 165 deletions
+8
View File
@@ -88,6 +88,14 @@ CHANGES WITH 228:
from PID1's environment block into the environment block of
the service.
* Timer units gained support for a new RemainAfterElapse=
setting which takes a boolean argument. It defaults on on,
exposing behaviour unchanged to previous releases. If set to
off, timer units are unloaded after they elapsed if they
cannot elapse again. This is particularly useful for
transient timer units, which shall not stay around longer
than until they first elapse.
* systemd will now bump the net.unix.max_dgram_qlen to 512 by
default now (the kernel default is 16). This is beneficial
for avoiding blocking on AF_UNIX/SOCK_DGRAM sockets since it
+1 -8
View File
@@ -71,8 +71,6 @@ Features:
* install: include generator dirs in unit file search paths
* invent a better systemd-run scheme for naming scopes, that works with remoting
* rework C11 utf8.[ch] to use char32_t instead of uint32_t when referring
to unicode chars, to make things more expressive.
@@ -103,7 +101,7 @@ Features:
* Rework systemctl's GetAll property parsing to use the generic bus_map_all_properties() API
* core/cgroup: support net_cls modules, and support automatically allocating class ids, then add support for making firewall changes depending on it, to implement a per-service firewall
* implement a per-service firewall based on net_cls
* Port various tools to make use of verbs.[ch], where applicable
@@ -155,8 +153,6 @@ Features:
* maybe provide an API to allow migration of foreign PIDs into existing scopes.
* maybe support a new very "soft" reboot mode, that simply kills all processes, disassembles everything, flushes /run and sysvipc, and then reexecs systemd again
* man: maybe use the word "inspect" rather than "introspect"?
* systemctl: if some operation fails, show log output?
@@ -213,8 +209,6 @@ Features:
* generator that automatically discovers btrfs subvolumes, identifies their purpose based on some xattr on them.
* timer units: actually add extra delays to timer units with high AccuracySec values, don't start them already when we are awake...
* a way for container managers to turn off getty starting via $container_headless= or so...
* figure out a nice way how we can let the admin know what child/sibling unit causes cgroup membership for a specific unit
@@ -736,7 +730,6 @@ Features:
- Support --test based on current system state
- If we show an error about a unit (such as not showing up) and it has no Description string, then show a description string generated form the reverse of unit_name_mangle().
- after deserializing sockets in socket.c we should reapply sockopts and things
- make timer units go away after they elapsed
- drop PID 1 reloading, only do reexecing (difficult: Reload()
currently is properly synchronous, Reexec() is weird, because we
cannot delay the response properly until we are back, so instead of
+49 -9
View File
@@ -190,13 +190,12 @@
<varname>OnUnitInactiveSec=</varname> and ending the time
configured with <varname>AccuracySec=</varname> later. Within
this time window, the expiry time will be placed at a
host-specific, randomized but stable position that is
host-specific, randomized, but stable position that is
synchronized between all local timer units. This is done in
order to distribute the wake-up time in networked
installations, as well as optimizing power consumption to
suppress unnecessary CPU wake-ups. To get best accuracy, set
this option to 1us. Note that the timer is still subject to
the timer slack configured via
order to optimize power consumption to suppress unnecessary
CPU wake-ups. To get best accuracy, set this option to
1us. Note that the timer is still subject to the timer slack
configured via
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>'s
<varname>TimerSlackNSec=</varname> setting. See
<citerefentry><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry>
@@ -204,6 +203,38 @@
this value as high as possible and as low as
necessary.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>RandomSec=</varname></term>
<listitem><para>Delay the timer by a randomly selected, evenly
distributed amount of time between 0 and the specified time
value. Defaults to 0, indicating that no randomized delay
shall be applied. Each timer unit will determine this delay
randomly each time it is started, and the delay will simply be
added on top of the next determined elapsing time. This is
useful to stretch dispatching of similarly configured timer
events over a certain amount time, to avoid that they all fire
at the same time, possibly resulting in resource
congestion. Note the relation to
<varname>AccuracySec=</varname> above: the latter allows the
service manager to coalesce timer events within a specified
time range in order to minimize wakeups, the former does the
opposite: it stretches timer events over a time range, to make
it unlikely that they fire simultaneously. If
<varname>RandomSec=</varname> and
<varname>AccuracySec=</varname> are used in conjunction, first
the a randomized time is added, and the result is then
possibly shifted further to coalesce it with other timer
events possibly happening on the system. As mentioned above
<varname>AccuracySec=</varname> defaults to 1min and
<varname>RandomSec=</varname> to 0, thus encouraging
coalescing of timer events. In order to optimally stretch
timer events over a certain range of time, make sure to set
<varname>RandomSec=</varname> to a higher value, and
<varname>AccuracySec=1us</varname>.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>Unit=</varname></term>
@@ -245,11 +276,20 @@
</varlistentry>
<varlistentry>
<term><varname>RemainAfterExit=</varname></term>
<term><varname>RemainAfterElapse=</varname></term>
<listitem><para>Takes a boolean argument. If true, an elapsed
timer will stay loaded, and its state remains
queriable. Defaults to
timer will stay loaded, and its state remains queriable. If
false, an elapsed timer unit that cannot elapse anymore is
unloaded. Turning this off is particularly useful for
transient timer units that shall disappear after they first
elapse. Note that this setting has an effect on repeatedly
starting the a timer unit that only elapses once: if
<varname>RemainAfterElapse=</varname> is on, it will not be
started again, and is guaranteed to elapse only once. However,
if <varname>RemainAfterLeapse=</varname> is off, it might be
started again if it is already elapsed, and thus be triggered
multiple times. Defaults to
<varname>yes</varname>.</para></listitem>
</varlistentry>
</variablelist>
+17
View File
@@ -180,6 +180,7 @@ const sd_bus_vtable bus_timer_vtable[] = {
BUS_PROPERTY_DUAL_TIMESTAMP("LastTriggerUSec", offsetof(Timer, last_trigger), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Timer, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("AccuracyUSec", "t", bus_property_get_usec, offsetof(Timer, accuracy_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RandomUSec", "t", bus_property_get_usec, offsetof(Timer, random_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Persistent", "b", bus_property_get_bool, offsetof(Timer, persistent), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("WakeSystem", "b", bus_property_get_bool, offsetof(Timer, wake_system), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RemainAfterElapse", "b", bus_property_get_bool, offsetof(Timer, remain_after_elapse), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -283,6 +284,22 @@ static int bus_timer_set_transient_property(
return 1;
} else if (streq(name, "RandomUSec")) {
usec_t u = 0;
r = sd_bus_message_read(message, "t", &u);
if (r < 0)
return r;
if (mode != UNIT_CHECK) {
char time[FORMAT_TIMESPAN_MAX];
t->random_usec = u;
unit_write_drop_in_private_format(UNIT(t), mode, name, "RandomSec=%s\n", format_timespan(time, sizeof(time), u, USEC_PER_MSEC));
}
return 1;
} else if (streq(name, "WakeSystem")) {
int b;
+1
View File
@@ -347,6 +347,7 @@ Timer.Persistent, config_parse_bool, 0,
Timer.WakeSystem, config_parse_bool, 0, offsetof(Timer, wake_system)
Timer.RemainAfterElapse, config_parse_bool, 0, offsetof(Timer, remain_after_elapse)
Timer.AccuracySec, config_parse_sec, 0, offsetof(Timer, accuracy_usec)
Timer.RandomSec, config_parse_sec, 0, offsetof(Timer, random_usec)
Timer.Unit, config_parse_trigger_unit, 0, 0
m4_dnl
Path.PathExists, config_parse_path_spec, 0, 0
+28
View File
@@ -27,6 +27,7 @@
#include "dbus-timer.h"
#include "fs-util.h"
#include "parse-util.h"
#include "random-util.h"
#include "special.h"
#include "string-table.h"
#include "string-util.h"
@@ -330,6 +331,28 @@ static usec_t monotonic_to_boottime(usec_t t) {
return 0;
}
static void add_random(Timer *t, usec_t *v) {
char s[FORMAT_TIMESPAN_MAX];
usec_t add;
assert(t);
assert(*v);
if (t->random_usec == 0)
return;
if (*v == USEC_INFINITY)
return;
add = random_u64() % t->random_usec;
if (*v + add < *v) /* overflow */
*v = (usec_t) -2; /* Highest possible value, that is not USEC_INFINITY */
else
*v += add;
log_unit_info(UNIT(t), "Adding %s random time.", format_timespan(s, sizeof(s), add, 0));
}
static void timer_enter_waiting(Timer *t, bool initial) {
bool found_monotonic = false, found_realtime = false;
usec_t ts_realtime, ts_monotonic;
@@ -452,6 +475,8 @@ static void timer_enter_waiting(Timer *t, bool initial) {
char buf[FORMAT_TIMESPAN_MAX];
usec_t left;
add_random(t, &t->next_elapse_monotonic_or_boottime);
left = t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0;
log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), left, 0));
@@ -486,6 +511,9 @@ static void timer_enter_waiting(Timer *t, bool initial) {
if (found_realtime) {
char buf[FORMAT_TIMESTAMP_MAX];
add_random(t, &t->next_elapse_realtime);
log_unit_debug(UNIT(t), "Realtime timer elapses at %s.", format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
if (t->realtime_event_source) {
+1
View File
@@ -58,6 +58,7 @@ struct Timer {
Unit meta;
usec_t accuracy_usec;
usec_t random_usec;
LIST_HEAD(TimerValue, values);
usec_t next_elapse_realtime;
+5 -1
View File
@@ -543,6 +543,7 @@ static void dns_scope_verify_conflicts(DnsScope *s, DnsPacket *p) {
void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
_cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL;
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
DnsResourceKey *key = NULL;
bool tentative = false;
int r, fd;
@@ -576,7 +577,10 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) {
return;
}
r = dns_zone_lookup(&s->zone, p->question, &answer, &soa, &tentative);
assert(p->question->n_keys == 1);
key = p->question->keys[0];
r = dns_zone_lookup(&s->zone, key, &answer, &soa, &tentative);
if (r < 0) {
log_debug_errno(r, "Failed to lookup key: %m");
return;
+14
View File
@@ -626,6 +626,20 @@ int dns_transaction_go(DnsTransaction *t) {
t->cached = dns_answer_unref(t->cached);
t->cached_rcode = 0;
/* Check the zone, but obly if this transaction is not used
* for probing or verifying a zone item. */
if (set_isempty(t->zone_items)) {
r = dns_zone_lookup(&t->scope->zone, t->key, &t->cached, NULL, NULL);
if (r < 0)
return r;
if (r > 0) {
t->cached_rcode = DNS_RCODE_SUCCESS;
dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS);
return 0;
}
}
/* Check the cache, but only if this transaction is not used
* for probing or verifying a zone item. */
if (set_isempty(t->zone_items)) {
+129 -136
View File
@@ -283,97 +283,76 @@ int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) {
return 0;
}
int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) {
int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) {
_cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL;
unsigned i, n_answer = 0, n_soa = 0;
bool tentative = true;
unsigned n_answer = 0;
DnsZoneItem *j, *first;
bool tentative = true, need_soa = false;
int r;
assert(z);
assert(q);
assert(key);
assert(ret_answer);
assert(ret_soa);
if (q->n_keys <= 0) {
*ret_answer = NULL;
*ret_soa = NULL;
if (ret_tentative)
*ret_tentative = false;
return 0;
}
/* First iteration, count what we have */
for (i = 0; i < q->n_keys; i++) {
DnsZoneItem *j, *first;
if (q->keys[i]->type == DNS_TYPE_ANY ||
q->keys[i]->class == DNS_CLASS_ANY) {
bool found = false, added = false;
int k;
if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
bool found = false, added = false;
int k;
/* If this is a generic match, then we have to
* go through the list by the name and look
* for everything manually */
/* If this is a generic match, then we have to
* go through the list by the name and look
* for everything manually */
first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]));
first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key));
LIST_FOREACH(by_name, j, first) {
if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
continue;
found = true;
k = dns_resource_key_match_rr(key, j->rr);
if (k < 0)
return k;
if (k > 0) {
n_answer++;
added = true;
}
}
if (found && !added)
need_soa = true;
} else {
bool found = false;
/* If this is a specific match, then look for
* the right key immediately */
first = hashmap_get(z->by_key, key);
LIST_FOREACH(by_key, j, first) {
if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
continue;
found = true;
n_answer++;
}
if (!found) {
first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key));
LIST_FOREACH(by_name, j, first) {
if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
continue;
found = true;
k = dns_resource_key_match_rr(q->keys[i], j->rr);
if (k < 0)
return k;
if (k > 0) {
n_answer++;
added = true;
}
}
if (found && !added)
n_soa++;
} else {
bool found = false;
/* If this is a specific match, then look for
* the right key immediately */
first = hashmap_get(z->by_key, q->keys[i]);
LIST_FOREACH(by_key, j, first) {
if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
continue;
found = true;
n_answer++;
}
if (!found) {
first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]));
LIST_FOREACH(by_name, j, first) {
if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
continue;
n_soa++;
break;
}
need_soa = true;
break;
}
}
}
if (n_answer <= 0 && n_soa <= 0) {
*ret_answer = NULL;
*ret_soa = NULL;
if (ret_tentative)
*ret_tentative = false;
return 0;
}
if (n_answer <= 0 && !need_soa)
goto return_empty;
if (n_answer > 0) {
answer = dns_answer_new(n_answer);
@@ -381,99 +360,113 @@ int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswe
return -ENOMEM;
}
if (n_soa > 0) {
soa = dns_answer_new(n_soa);
if (need_soa) {
soa = dns_answer_new(1);
if (!soa)
return -ENOMEM;
}
/* Second iteration, actually add the RRs to the answers */
for (i = 0; i < q->n_keys; i++) {
DnsZoneItem *j, *first;
if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) {
bool found = false, added = false;
int k;
if (q->keys[i]->type == DNS_TYPE_ANY ||
q->keys[i]->class == DNS_CLASS_ANY) {
bool found = false, added = false;
int k;
first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key));
LIST_FOREACH(by_name, j, first) {
if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
continue;
first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]));
found = true;
if (j->state != DNS_ZONE_ITEM_PROBING)
tentative = false;
k = dns_resource_key_match_rr(key, j->rr);
if (k < 0)
return k;
if (k > 0) {
r = dns_answer_add(answer, j->rr, 0);
if (r < 0)
return r;
added = true;
}
}
if (found && !added) {
r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(key), LLMNR_DEFAULT_TTL);
if (r < 0)
return r;
}
} else {
bool found = false;
first = hashmap_get(z->by_key, key);
LIST_FOREACH(by_key, j, first) {
if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
continue;
found = true;
if (j->state != DNS_ZONE_ITEM_PROBING)
tentative = false;
r = dns_answer_add(answer, j->rr, 0);
if (r < 0)
return r;
}
if (!found) {
bool add_soa = false;
first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key));
LIST_FOREACH(by_name, j, first) {
if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
continue;
found = true;
if (j->state != DNS_ZONE_ITEM_PROBING)
tentative = false;
k = dns_resource_key_match_rr(q->keys[i], j->rr);
if (k < 0)
return k;
if (k > 0) {
r = dns_answer_add(answer, j->rr, 0);
if (r < 0)
return r;
added = true;
}
add_soa = true;
}
if (found && !added) {
r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]), LLMNR_DEFAULT_TTL);
if (add_soa) {
r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(key), LLMNR_DEFAULT_TTL);
if (r < 0)
return r;
}
} else {
bool found = false;
first = hashmap_get(z->by_key, q->keys[i]);
LIST_FOREACH(by_key, j, first) {
if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
continue;
found = true;
if (j->state != DNS_ZONE_ITEM_PROBING)
tentative = false;
r = dns_answer_add(answer, j->rr, 0);
if (r < 0)
return r;
}
if (!found) {
bool add_soa = false;
first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i]));
LIST_FOREACH(by_name, j, first) {
if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING))
continue;
if (j->state != DNS_ZONE_ITEM_PROBING)
tentative = false;
add_soa = true;
}
if (add_soa) {
r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]), LLMNR_DEFAULT_TTL);
if (r < 0)
return r;
}
}
}
}
/* If the caller sets ret_tentative to NULL, then use this as
* indication to not return tentative entries */
if (!ret_tentative && tentative)
goto return_empty;
*ret_answer = answer;
answer = NULL;
*ret_soa = soa;
soa = NULL;
if (ret_soa) {
*ret_soa = soa;
soa = NULL;
}
if (ret_tentative)
*ret_tentative = tentative;
return 1;
return_empty:
*ret_answer = NULL;
if (ret_soa)
*ret_soa = NULL;
if (ret_tentative)
*ret_tentative = false;
return 0;
}
void dns_zone_item_conflict(DnsZoneItem *i) {
+1 -1
View File
@@ -67,7 +67,7 @@ void dns_zone_flush(DnsZone *z);
int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe);
void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr);
int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **answer, DnsAnswer **soa, bool *tentative);
int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **answer, DnsAnswer **soa, bool *tentative);
void dns_zone_item_conflict(DnsZoneItem *i);
void dns_zone_item_ready(DnsZoneItem *i);
+25 -7
View File
@@ -1428,16 +1428,36 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
return bus_log_create_error(r);
return 0;
} else if (streq(field, "EnvironmentFile")) {
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "EnvironmentFiles");
if (r < 0)
return r;
return bus_log_create_error(r);
r = sd_bus_message_append(m, "v", "a(sb)", 1,
eq[0] == '-' ? eq + 1 : eq,
eq[0] == '-');
if (r < 0)
return r;
return bus_log_create_error(r);
return 0;
} else if (streq(field, "RandomSec")) {
usec_t t;
r = parse_sec(eq, &t);
if (r < 0)
return log_error_errno(r, "Failed to parse RandomSec= parameter: %s", eq);
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "RandomUSec");
if (r < 0)
return bus_log_create_error(r);
r = sd_bus_message_append(m, "v", "t", t);
if (r < 0)
return bus_log_create_error(r);
return 0;
}
@@ -1450,13 +1470,11 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
"SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies",
"IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
"PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges",
"SyslogLevelPrefix", "Delegate")) {
"SyslogLevelPrefix", "Delegate", "RemainAfterElapse")) {
r = parse_boolean(eq);
if (r < 0) {
log_error("Failed to parse boolean assignment %s.", assignment);
return -EINVAL;
}
if (r < 0)
return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment);
r = sd_bus_message_append(m, "v", "b", r);
+8 -3
View File
@@ -3360,6 +3360,7 @@ typedef struct UnitStatusInfo {
usec_t inactive_enter_timestamp;
bool need_daemon_reload;
bool transient;
/* Service */
pid_t main_pid;
@@ -3459,7 +3460,7 @@ static void print_status_info(
path = i->source_path ? i->source_path : i->fragment_path;
if (i->load_error)
if (i->load_error != 0)
printf(" Loaded: %s%s%s (Reason: %s)\n",
on, strna(i->load_state), off, i->load_error);
else if (path && !isempty(i->unit_file_state) && !isempty(i->unit_file_preset))
@@ -3475,6 +3476,9 @@ static void print_status_info(
printf(" Loaded: %s%s%s\n",
on, strna(i->load_state), off);
if (i->transient)
printf("Transient: yes\n");
if (!strv_isempty(i->dropin_paths)) {
_cleanup_free_ char *dir = NULL;
bool last = false;
@@ -3839,6 +3843,8 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *
i->condition_result = b;
else if (streq(name, "AssertResult"))
i->assert_result = b;
else if (streq(name, "Transient"))
i->transient = b;
break;
}
@@ -4646,8 +4652,7 @@ static int show(int argc, char *argv[], void *userdata) {
return -EINVAL;
}
if (show_properties)
pager_open_if_enabled();
pager_open_if_enabled();
if (show_status)
/* Increase max number of open files to 16K if we can, we