summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2016-05-04 10:46:13 +0200
committerLennart Poettering <lennart@poettering.net>2016-05-04 10:46:13 +0200
commit1ed7ebcfcaa952441565c1c8c43084add1f58a89 (patch)
tree9c2a3a4e826c8a0ebab05562d66b7070a3274dc6
parentd428dd6ac9a56e7b3421fb8ef3aac9937a4a2e62 (diff)
parentdbb0578edc5ab8e11641c8b2d29904d4f5f8e1e6 (diff)
Merge pull request #3170 from poettering/v230-preparation-fixes
make virtualization detection quieter, rework unit start limit logic, detect unit file drop-in changes correctly, fix autofs state propagation
-rw-r--r--man/systemd.unit.xml4
-rw-r--r--src/basic/virt.c2
-rw-r--r--src/core/automount.c117
-rw-r--r--src/core/automount.h4
-rw-r--r--src/core/busname.c8
-rw-r--r--src/core/busname.h1
-rw-r--r--src/core/mount.c29
-rw-r--r--src/core/mount.h1
-rw-r--r--src/core/path.c8
-rw-r--r--src/core/path.h1
-rw-r--r--src/core/service.c9
-rw-r--r--src/core/service.h1
-rw-r--r--src/core/socket.c25
-rw-r--r--src/core/socket.h1
-rw-r--r--src/core/swap.c10
-rw-r--r--src/core/swap.h1
-rw-r--r--src/core/timer.c10
-rw-r--r--src/core/timer.h1
-rw-r--r--src/core/unit.c40
-rw-r--r--src/core/unit.h2
-rw-r--r--src/fstab-generator/fstab-generator.c1
-rw-r--r--src/systemctl/systemctl.c2
22 files changed, 172 insertions, 106 deletions
diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index abd47bd237..90a1ec6b9c 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -770,7 +770,9 @@
<command>systemctl reset-failed</command> will cause the restart rate counter for a service to be flushed,
which is useful if the administrator wants to manually start a unit and the start limit interferes with
that. Note that this rate-limiting is enforced after any unit condition checks are executed, and hence unit
- activations with failing conditions are not counted by this rate limiting.</para></listitem>
+ activations with failing conditions are not counted by this rate limiting. Slice, target, device and scope
+ units do not enforce this setting, as they are unit types whose activation may either never fail, or may
+ succeed only a single time.</para></listitem>
</varlistentry>
<varlistentry>
diff --git a/src/basic/virt.c b/src/basic/virt.c
index e6c5a095a0..dace1f4328 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -280,7 +280,7 @@ static int detect_vm_uml(void) {
return VIRTUALIZATION_UML;
}
- log_debug("No virtualization found in /proc/cpuinfo (%s)", cpuinfo_contents);
+ log_debug("No virtualization found in /proc/cpuinfo.");
return VIRTUALIZATION_NONE;
}
diff --git a/src/core/automount.c b/src/core/automount.c
index 1239a0efc6..f06d837e30 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -75,6 +75,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct expire_data*, expire_data_free);
static int open_dev_autofs(Manager *m);
static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, void *userdata);
+static int automount_start_expire(Automount *a);
+static void automount_stop_expire(Automount *a);
+static int automount_send_ready(Automount *a, Set *tokens, int status);
static void automount_init(Unit *u) {
Automount *a = AUTOMOUNT(u);
@@ -87,8 +90,6 @@ static void automount_init(Unit *u) {
UNIT(a)->ignore_on_isolate = true;
}
-static int automount_send_ready(Automount *a, Set *tokens, int status);
-
static void unmount_autofs(Automount *a) {
int r;
@@ -235,6 +236,9 @@ static void automount_set_state(Automount *a, AutomountState state) {
old_state = a->state;
a->state = state;
+ if (state != AUTOMOUNT_RUNNING)
+ automount_stop_expire(a);
+
if (state != AUTOMOUNT_WAITING &&
state != AUTOMOUNT_RUNNING)
unmount_autofs(a);
@@ -408,7 +412,7 @@ static int autofs_send_ready(int dev_autofs_fd, int ioctl_fd, uint32_t token, in
init_autofs_dev_ioctl(&param);
param.ioctlfd = ioctl_fd;
- if (status) {
+ if (status != 0) {
param.fail.token = token;
param.fail.status = status;
} else
@@ -435,7 +439,7 @@ static int automount_send_ready(Automount *a, Set *tokens, int status) {
if (ioctl_fd < 0)
return ioctl_fd;
- if (status)
+ if (status != 0)
log_unit_debug_errno(UNIT(a), status, "Sending failure: %m");
else
log_unit_debug(UNIT(a), "Sending success.");
@@ -462,59 +466,54 @@ static int automount_send_ready(Automount *a, Set *tokens, int status) {
return r;
}
-static int automount_start_expire(Automount *a);
-
-int automount_update_mount(Automount *a, MountState old_state, MountState state) {
+static void automount_trigger_notify(Unit *u, Unit *other) {
+ Automount *a = AUTOMOUNT(u);
int r;
assert(a);
+ assert(other);
+
+ /* Filter out invocations with bogus state */
+ if (other->load_state != UNIT_LOADED || other->type != UNIT_MOUNT)
+ return;
+
+ /* Don't propagate state changes from the mount if we are already down */
+ if (!IN_SET(a->state, AUTOMOUNT_WAITING, AUTOMOUNT_RUNNING))
+ return;
+
+ /* Propagate start limit hit state */
+ if (other->start_limit_hit) {
+ automount_enter_dead(a, AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT);
+ return;
+ }
+
+ /* Don't propagate anything if there's still a job queued */
+ if (other->job)
+ return;
+
+ /* The mount is successfully established */
+ if (IN_SET(MOUNT(other)->state, MOUNT_MOUNTED, MOUNT_REMOUNTING)) {
+ (void) automount_send_ready(a, a->tokens, 0);
- switch (state) {
- case MOUNT_MOUNTED:
- case MOUNT_REMOUNTING:
- automount_send_ready(a, a->tokens, 0);
r = automount_start_expire(a);
if (r < 0)
log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m");
- break;
- case MOUNT_DEAD:
- case MOUNT_UNMOUNTING:
- case MOUNT_MOUNTING_SIGTERM:
- case MOUNT_MOUNTING_SIGKILL:
- case MOUNT_REMOUNTING_SIGTERM:
- case MOUNT_REMOUNTING_SIGKILL:
- case MOUNT_UNMOUNTING_SIGTERM:
- case MOUNT_UNMOUNTING_SIGKILL:
- case MOUNT_FAILED:
- if (old_state != state)
- automount_send_ready(a, a->tokens, -ENODEV);
- (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF);
- break;
- default:
- break;
- }
- switch (state) {
- case MOUNT_DEAD:
- automount_send_ready(a, a->expire_tokens, 0);
- break;
- case MOUNT_MOUNTING:
- case MOUNT_MOUNTING_DONE:
- case MOUNT_MOUNTING_SIGTERM:
- case MOUNT_MOUNTING_SIGKILL:
- case MOUNT_REMOUNTING_SIGTERM:
- case MOUNT_REMOUNTING_SIGKILL:
- case MOUNT_UNMOUNTING_SIGTERM:
- case MOUNT_UNMOUNTING_SIGKILL:
- case MOUNT_FAILED:
- if (old_state != state)
- automount_send_ready(a, a->expire_tokens, -ENODEV);
- break;
- default:
- break;
+ automount_set_state(a, AUTOMOUNT_RUNNING);
}
- return 0;
+ /* The mount is in some unhappy state now, let's unfreeze any waiting clients */
+ if (IN_SET(MOUNT(other)->state,
+ MOUNT_DEAD, MOUNT_UNMOUNTING,
+ MOUNT_MOUNTING_SIGTERM, MOUNT_MOUNTING_SIGKILL,
+ MOUNT_REMOUNTING_SIGTERM, MOUNT_REMOUNTING_SIGKILL,
+ MOUNT_UNMOUNTING_SIGTERM, MOUNT_UNMOUNTING_SIGKILL,
+ MOUNT_FAILED)) {
+
+ (void) automount_send_ready(a, a->tokens, -ENODEV);
+
+ automount_set_state(a, AUTOMOUNT_WAITING);
+ }
}
static void automount_enter_waiting(Automount *a) {
@@ -699,6 +698,15 @@ static int automount_start_expire(Automount *a) {
return 0;
}
+static void automount_stop_expire(Automount *a) {
+ assert(a);
+
+ if (!a->expire_event_source)
+ return;
+
+ (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF);
+}
+
static void automount_enter_runnning(Automount *a) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
struct stat st;
@@ -751,6 +759,7 @@ fail:
static int automount_start(Unit *u) {
Automount *a = AUTOMOUNT(u);
Unit *trigger;
+ int r;
assert(a);
assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED);
@@ -766,6 +775,12 @@ static int automount_start(Unit *u) {
return -ENOENT;
}
+ r = unit_start_limit_test(u);
+ if (r < 0) {
+ automount_enter_dead(a, AUTOMOUNT_FAILURE_START_LIMIT_HIT);
+ return r;
+ }
+
a->result = AUTOMOUNT_SUCCESS;
automount_enter_waiting(a);
return 1;
@@ -958,7 +973,7 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo
case autofs_ptype_expire_direct:
log_unit_debug(UNIT(a), "Got direct umount request on %s", a->where);
- (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF);
+ automount_stop_expire(a);
r = set_ensure_allocated(&a->expire_tokens, NULL);
if (r < 0) {
@@ -1037,7 +1052,9 @@ static bool automount_supported(void) {
static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = {
[AUTOMOUNT_SUCCESS] = "success",
- [AUTOMOUNT_FAILURE_RESOURCES] = "resources"
+ [AUTOMOUNT_FAILURE_RESOURCES] = "resources",
+ [AUTOMOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
+ [AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT] = "mount-start-limit-hit",
};
DEFINE_STRING_TABLE_LOOKUP(automount_result, AutomountResult);
@@ -1069,6 +1086,8 @@ const UnitVTable automount_vtable = {
.check_gc = automount_check_gc,
+ .trigger_notify = automount_trigger_notify,
+
.reset_failed = automount_reset_failed,
.bus_vtable = bus_automount_vtable,
diff --git a/src/core/automount.h b/src/core/automount.h
index cf5b1cf994..76a201178e 100644
--- a/src/core/automount.h
+++ b/src/core/automount.h
@@ -26,6 +26,8 @@ typedef struct Automount Automount;
typedef enum AutomountResult {
AUTOMOUNT_SUCCESS,
AUTOMOUNT_FAILURE_RESOURCES,
+ AUTOMOUNT_FAILURE_START_LIMIT_HIT,
+ AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT,
_AUTOMOUNT_RESULT_MAX,
_AUTOMOUNT_RESULT_INVALID = -1
} AutomountResult;
@@ -53,7 +55,5 @@ struct Automount {
extern const UnitVTable automount_vtable;
-int automount_update_mount(Automount *a, MountState old_state, MountState state);
-
const char* automount_result_to_string(AutomountResult i) _const_;
AutomountResult automount_result_from_string(const char *s) _pure_;
diff --git a/src/core/busname.c b/src/core/busname.c
index e7b7b5c012..f03a95c24e 100644
--- a/src/core/busname.c
+++ b/src/core/busname.c
@@ -607,6 +607,7 @@ fail:
static int busname_start(Unit *u) {
BusName *n = BUSNAME(u);
+ int r;
assert(n);
@@ -632,6 +633,12 @@ static int busname_start(Unit *u) {
assert(IN_SET(n->state, BUSNAME_DEAD, BUSNAME_FAILED));
+ r = unit_start_limit_test(u);
+ if (r < 0) {
+ busname_enter_dead(n, BUSNAME_FAILURE_START_LIMIT_HIT);
+ return r;
+ }
+
n->result = BUSNAME_SUCCESS;
busname_enter_making(n);
@@ -1014,6 +1021,7 @@ static const char* const busname_result_table[_BUSNAME_RESULT_MAX] = {
[BUSNAME_FAILURE_EXIT_CODE] = "exit-code",
[BUSNAME_FAILURE_SIGNAL] = "signal",
[BUSNAME_FAILURE_CORE_DUMP] = "core-dump",
+ [BUSNAME_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
[BUSNAME_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit",
};
diff --git a/src/core/busname.h b/src/core/busname.h
index 52c4055dbb..a8562db458 100644
--- a/src/core/busname.h
+++ b/src/core/busname.h
@@ -32,6 +32,7 @@ typedef enum BusNameResult {
BUSNAME_FAILURE_EXIT_CODE,
BUSNAME_FAILURE_SIGNAL,
BUSNAME_FAILURE_CORE_DUMP,
+ BUSNAME_FAILURE_START_LIMIT_HIT,
BUSNAME_FAILURE_SERVICE_START_LIMIT_HIT,
_BUSNAME_RESULT_MAX,
_BUSNAME_RESULT_INVALID = -1
diff --git a/src/core/mount.c b/src/core/mount.c
index c8a898e4dc..5a8c26b9e1 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -584,23 +584,6 @@ static int mount_load(Unit *u) {
return mount_verify(m);
}
-static int mount_notify_automount(Mount *m, MountState old_state, MountState state) {
- Unit *p;
- int r;
- Iterator i;
-
- assert(m);
-
- SET_FOREACH(p, UNIT(m)->dependencies[UNIT_TRIGGERED_BY], i)
- if (p->type == UNIT_AUTOMOUNT) {
- r = automount_update_mount(AUTOMOUNT(p), old_state, state);
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
static void mount_set_state(Mount *m, MountState state) {
MountState old_state;
assert(m);
@@ -624,8 +607,6 @@ static void mount_set_state(Mount *m, MountState state) {
m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID;
}
- mount_notify_automount(m, old_state, state);
-
if (state != old_state)
log_unit_debug(UNIT(m), "Changed %s -> %s", mount_state_to_string(old_state), mount_state_to_string(state));
@@ -984,6 +965,7 @@ fail:
static int mount_start(Unit *u) {
Mount *m = MOUNT(u);
+ int r;
assert(m);
@@ -1002,6 +984,12 @@ static int mount_start(Unit *u) {
assert(m->state == MOUNT_DEAD || m->state == MOUNT_FAILED);
+ r = unit_start_limit_test(u);
+ if (r < 0) {
+ mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT);
+ return r;
+ }
+
m->result = MOUNT_SUCCESS;
m->reload_result = MOUNT_SUCCESS;
m->reset_cpu_usage = true;
@@ -1821,7 +1809,8 @@ static const char* const mount_result_table[_MOUNT_RESULT_MAX] = {
[MOUNT_FAILURE_TIMEOUT] = "timeout",
[MOUNT_FAILURE_EXIT_CODE] = "exit-code",
[MOUNT_FAILURE_SIGNAL] = "signal",
- [MOUNT_FAILURE_CORE_DUMP] = "core-dump"
+ [MOUNT_FAILURE_CORE_DUMP] = "core-dump",
+ [MOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
};
DEFINE_STRING_TABLE_LOOKUP(mount_result, MountResult);
diff --git a/src/core/mount.h b/src/core/mount.h
index 3b343c6b1f..da529c44f4 100644
--- a/src/core/mount.h
+++ b/src/core/mount.h
@@ -39,6 +39,7 @@ typedef enum MountResult {
MOUNT_FAILURE_EXIT_CODE,
MOUNT_FAILURE_SIGNAL,
MOUNT_FAILURE_CORE_DUMP,
+ MOUNT_FAILURE_START_LIMIT_HIT,
_MOUNT_RESULT_MAX,
_MOUNT_RESULT_INVALID = -1
} MountResult;
diff --git a/src/core/path.c b/src/core/path.c
index 5e7b3eb234..0dd0d375d8 100644
--- a/src/core/path.c
+++ b/src/core/path.c
@@ -560,6 +560,7 @@ static void path_mkdir(Path *p) {
static int path_start(Unit *u) {
Path *p = PATH(u);
Unit *trigger;
+ int r;
assert(p);
assert(p->state == PATH_DEAD || p->state == PATH_FAILED);
@@ -570,6 +571,12 @@ static int path_start(Unit *u) {
return -ENOENT;
}
+ r = unit_start_limit_test(u);
+ if (r < 0) {
+ path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT);
+ return r;
+ }
+
path_mkdir(p);
p->result = PATH_SUCCESS;
@@ -739,6 +746,7 @@ DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
static const char* const path_result_table[_PATH_RESULT_MAX] = {
[PATH_SUCCESS] = "success",
[PATH_FAILURE_RESOURCES] = "resources",
+ [PATH_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
};
DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult);
diff --git a/src/core/path.h b/src/core/path.h
index bbbcebd78e..4230c8fb99 100644
--- a/src/core/path.h
+++ b/src/core/path.h
@@ -62,6 +62,7 @@ static inline bool path_spec_owns_inotify_fd(PathSpec *s, int fd) {
typedef enum PathResult {
PATH_SUCCESS,
PATH_FAILURE_RESOURCES,
+ PATH_FAILURE_START_LIMIT_HIT,
_PATH_RESULT_MAX,
_PATH_RESULT_INVALID = -1
} PathResult;
diff --git a/src/core/service.c b/src/core/service.c
index f7a3fcf2b9..7ebabca5d6 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -1957,6 +1957,7 @@ fail:
static int service_start(Unit *u) {
Service *s = SERVICE(u);
+ int r;
assert(s);
@@ -1983,6 +1984,13 @@ static int service_start(Unit *u) {
assert(IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED));
+ /* Make sure we don't enter a busy loop of some kind. */
+ r = unit_start_limit_test(u);
+ if (r < 0) {
+ service_enter_dead(s, SERVICE_FAILURE_START_LIMIT_HIT, false);
+ return r;
+ }
+
s->result = SERVICE_SUCCESS;
s->reload_result = SERVICE_SUCCESS;
s->main_pid_known = false;
@@ -3266,6 +3274,7 @@ static const char* const service_result_table[_SERVICE_RESULT_MAX] = {
[SERVICE_FAILURE_SIGNAL] = "signal",
[SERVICE_FAILURE_CORE_DUMP] = "core-dump",
[SERVICE_FAILURE_WATCHDOG] = "watchdog",
+ [SERVICE_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
};
DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult);
diff --git a/src/core/service.h b/src/core/service.h
index c7f1e81bdb..4af3d40439 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -86,6 +86,7 @@ typedef enum ServiceResult {
SERVICE_FAILURE_SIGNAL,
SERVICE_FAILURE_CORE_DUMP,
SERVICE_FAILURE_WATCHDOG,
+ SERVICE_FAILURE_START_LIMIT_HIT,
_SERVICE_RESULT_MAX,
_SERVICE_RESULT_INVALID = -1
} ServiceResult;
diff --git a/src/core/socket.c b/src/core/socket.c
index 7eeed068bd..d3d4866fe6 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -2057,6 +2057,7 @@ fail:
static int socket_start(Unit *u) {
Socket *s = SOCKET(u);
+ int r;
assert(s);
@@ -2101,6 +2102,12 @@ static int socket_start(Unit *u) {
assert(s->state == SOCKET_DEAD || s->state == SOCKET_FAILED);
+ r = unit_start_limit_test(u);
+ if (r < 0) {
+ socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT);
+ return r;
+ }
+
s->result = SOCKET_SUCCESS;
s->reset_cpu_usage = true;
@@ -2735,17 +2742,26 @@ static void socket_trigger_notify(Unit *u, Unit *other) {
assert(u);
assert(other);
- /* Don't propagate state changes from the service if we are
- already down or accepting connections */
- if (!IN_SET(s->state, SOCKET_RUNNING, SOCKET_LISTENING) || s->accept)
+ /* Filter out invocations with bogus state */
+ if (other->load_state != UNIT_LOADED || other->type != UNIT_SERVICE)
+ return;
+
+ /* Don't propagate state changes from the service if we are already down */
+ if (!IN_SET(s->state, SOCKET_RUNNING, SOCKET_LISTENING))
+ return;
+
+ /* We don't care for the service state if we are in Accept=yes mode */
+ if (s->accept)
return;
+ /* Propagate start limit hit state */
if (other->start_limit_hit) {
socket_enter_stop_pre(s, SOCKET_FAILURE_SERVICE_START_LIMIT_HIT);
return;
}
- if (other->load_state != UNIT_LOADED || other->type != UNIT_SERVICE)
+ /* Don't propagate anything if there's still a job queued */
+ if (other->job)
return;
if (IN_SET(SERVICE(other)->state,
@@ -2818,6 +2834,7 @@ static const char* const socket_result_table[_SOCKET_RESULT_MAX] = {
[SOCKET_FAILURE_EXIT_CODE] = "exit-code",
[SOCKET_FAILURE_SIGNAL] = "signal",
[SOCKET_FAILURE_CORE_DUMP] = "core-dump",
+ [SOCKET_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
[SOCKET_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit",
[SOCKET_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit"
};
diff --git a/src/core/socket.h b/src/core/socket.h
index 2a4b1bb674..0f1ac69c6f 100644
--- a/src/core/socket.h
+++ b/src/core/socket.h
@@ -52,6 +52,7 @@ typedef enum SocketResult {
SOCKET_FAILURE_EXIT_CODE,
SOCKET_FAILURE_SIGNAL,
SOCKET_FAILURE_CORE_DUMP,
+ SOCKET_FAILURE_START_LIMIT_HIT,
SOCKET_FAILURE_TRIGGER_LIMIT_HIT,
SOCKET_FAILURE_SERVICE_START_LIMIT_HIT,
_SOCKET_RESULT_MAX,
diff --git a/src/core/swap.c b/src/core/swap.c
index c018648d87..a532b15be8 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -814,6 +814,7 @@ fail:
static int swap_start(Unit *u) {
Swap *s = SWAP(u), *other;
+ int r;
assert(s);
@@ -842,6 +843,12 @@ static int swap_start(Unit *u) {
if (UNIT(other)->job && UNIT(other)->job->state == JOB_RUNNING)
return -EAGAIN;
+ r = unit_start_limit_test(u);
+ if (r < 0) {
+ swap_enter_dead(s, SWAP_FAILURE_START_LIMIT_HIT);
+ return r;
+ }
+
s->result = SWAP_SUCCESS;
s->reset_cpu_usage = true;
@@ -1447,7 +1454,8 @@ static const char* const swap_result_table[_SWAP_RESULT_MAX] = {
[SWAP_FAILURE_TIMEOUT] = "timeout",
[SWAP_FAILURE_EXIT_CODE] = "exit-code",
[SWAP_FAILURE_SIGNAL] = "signal",
- [SWAP_FAILURE_CORE_DUMP] = "core-dump"
+ [SWAP_FAILURE_CORE_DUMP] = "core-dump",
+ [SWAP_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
};
DEFINE_STRING_TABLE_LOOKUP(swap_result, SwapResult);
diff --git a/src/core/swap.h b/src/core/swap.h
index ac7a63d81b..fbf66debdc 100644
--- a/src/core/swap.h
+++ b/src/core/swap.h
@@ -38,6 +38,7 @@ typedef enum SwapResult {
SWAP_FAILURE_EXIT_CODE,
SWAP_FAILURE_SIGNAL,
SWAP_FAILURE_CORE_DUMP,
+ SWAP_FAILURE_START_LIMIT_HIT,
_SWAP_RESULT_MAX,
_SWAP_RESULT_INVALID = -1
} SwapResult;
diff --git a/src/core/timer.c b/src/core/timer.c
index f8f5f4b2e4..3206296f09 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -599,6 +599,7 @@ static int timer_start(Unit *u) {
Timer *t = TIMER(u);
TimerValue *v;
Unit *trigger;
+ int r;
assert(t);
assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED);
@@ -609,6 +610,12 @@ static int timer_start(Unit *u) {
return -ENOENT;
}
+ r = unit_start_limit_test(u);
+ if (r < 0) {
+ timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT);
+ return r;
+ }
+
t->last_trigger = DUAL_TIMESTAMP_NULL;
/* Reenable all timers that depend on unit activation time */
@@ -808,7 +815,8 @@ DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
[TIMER_SUCCESS] = "success",
- [TIMER_FAILURE_RESOURCES] = "resources"
+ [TIMER_FAILURE_RESOURCES] = "resources",
+ [TIMER_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
};
DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult);
diff --git a/src/core/timer.h b/src/core/timer.h
index 698e6da2f5..9c4b64f898 100644
--- a/src/core/timer.h
+++ b/src/core/timer.h
@@ -48,6 +48,7 @@ typedef struct TimerValue {
typedef enum TimerResult {
TIMER_SUCCESS,
TIMER_FAILURE_RESOURCES,
+ TIMER_FAILURE_START_LIMIT_HIT,
_TIMER_RESULT_MAX,
_TIMER_RESULT_INVALID = -1
} TimerResult;
diff --git a/src/core/unit.c b/src/core/unit.c
index 4ace6b075b..8153515e89 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -1462,7 +1462,7 @@ void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t) {
unit_status_print_starting_stopping(u, t);
}
-static int unit_start_limit_test(Unit *u) {
+int unit_start_limit_test(Unit *u) {
assert(u);
if (ratelimit_test(&u->start_limit)) {
@@ -1488,7 +1488,6 @@ static int unit_start_limit_test(Unit *u) {
int unit_start(Unit *u) {
UnitActiveState state;
Unit *following;
- int r;
assert(u);
@@ -1541,11 +1540,6 @@ int unit_start(Unit *u) {
if (!UNIT_VTABLE(u)->start)
return -EBADR;
- /* Make sure we don't enter a busy loop of some kind. */
- r = unit_start_limit_test(u);
- if (r < 0)
- return r;
-
/* We don't suppress calls to ->start() here when we are
* already starting, to allow this request to be used as a
* "hurry up" call, for example when the unit is in some "auto
@@ -2951,7 +2945,7 @@ int unit_coldplug(Unit *u) {
return 0;
}
-static bool fragment_mtime_changed(const char *path, usec_t mtime) {
+static bool fragment_mtime_newer(const char *path, usec_t mtime) {
struct stat st;
if (!path)
@@ -2963,7 +2957,7 @@ static bool fragment_mtime_changed(const char *path, usec_t mtime) {
if (mtime > 0)
/* For non-empty files check the mtime */
- return timespec_load(&st.st_mtim) != mtime;
+ return timespec_load(&st.st_mtim) > mtime;
else if (!null_or_empty(&st))
/* For masked files check if they are still so */
return true;
@@ -2974,32 +2968,24 @@ static bool fragment_mtime_changed(const char *path, usec_t mtime) {
bool unit_need_daemon_reload(Unit *u) {
_cleanup_strv_free_ char **t = NULL;
char **path;
- unsigned loaded_cnt, current_cnt;
assert(u);
- if (fragment_mtime_changed(u->fragment_path, u->fragment_mtime) ||
- fragment_mtime_changed(u->source_path, u->source_mtime))
+ if (fragment_mtime_newer(u->fragment_path, u->fragment_mtime))
return true;
- (void) unit_find_dropin_paths(u, &t);
- loaded_cnt = strv_length(t);
- current_cnt = strv_length(u->dropin_paths);
-
- if (loaded_cnt == current_cnt) {
- if (loaded_cnt == 0)
- return false;
+ if (fragment_mtime_newer(u->source_path, u->source_mtime))
+ return true;
- if (strv_overlap(u->dropin_paths, t)) {
- STRV_FOREACH(path, u->dropin_paths)
- if (fragment_mtime_changed(*path, u->dropin_mtime))
- return true;
+ (void) unit_find_dropin_paths(u, &t);
+ if (!strv_equal(u->dropin_paths, t))
+ return true;
- return false;
- }
- }
+ STRV_FOREACH(path, u->dropin_paths)
+ if (fragment_mtime_newer(*path, u->dropin_mtime))
+ return true;
- return true;
+ return false;
}
void unit_reset_failed(Unit *u) {
diff --git a/src/core/unit.h b/src/core/unit.h
index be62e88421..f14972728e 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -611,6 +611,8 @@ static inline bool unit_supported(Unit *u) {
void unit_warn_if_dir_nonempty(Unit *u, const char* where);
int unit_fail_if_symlink(Unit *u, const char* where);
+int unit_start_limit_test(Unit *u);
+
/* Macros which append UNIT= or USER_UNIT= to the message */
#define log_unit_full(unit, level, error, ...) \
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index 6f576b5ecf..343e3b1817 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -379,6 +379,7 @@ static int add_mount(
}
fprintf(f,
+ "\n"
"[Automount]\n"
"Where=%s\n",
where);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 9af25e22a4..bec4f31b39 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -2334,6 +2334,8 @@ static int need_daemon_reload(sd_bus *bus, const char *unit) {
}
static void warn_unit_file_changed(const char *name) {
+ assert(name);
+
log_warning("%sWarning:%s %s changed on disk. Run 'systemctl%s daemon-reload' to reload units.",
ansi_highlight_red(),
ansi_normal(),