link->dnssec_negative_trust_anchors = set_free_free(link->dnssec_negative_trust_anchors);
}
-static void link_free_engines(Link *link) {
+void link_free_engines(Link *link) {
if (!link)
return;
bool keep_dhcp = may_keep_dhcp &&
link->network &&
!link->network->dhcp_send_decline && /* IPv4 ACD for the DHCPv4 address is running. */
- (link->manager->restarting ||
+ (link->manager->state == MANAGER_RESTARTING ||
FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP_ON_STOP));
if (!keep_dhcp) {
int r;
assert(link);
+ assert(link->manager);
link_assign_netdev(link);
+ if (link->manager->state != MANAGER_RUNNING)
+ return 0;
+
if (IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_LINGER))
return 0;
static int manager_post_handler(sd_event_source *s, void *userdata) {
Manager *manager = ASSERT_PTR(userdata);
+ /* To release dynamic leases, we need to process queued remove requests before stopping networkd.
+ * This is especially important when KeepConfiguration=no. See issue #34837. */
(void) manager_process_remove_requests(manager);
- (void) manager_process_requests(manager);
- (void) manager_clean_all(manager);
+
+ switch (manager->state) {
+ case MANAGER_RUNNING:
+ (void) manager_process_requests(manager);
+ (void) manager_clean_all(manager);
+ return 0;
+
+ case MANAGER_TERMINATING:
+ case MANAGER_RESTARTING:
+ if (!ordered_set_isempty(manager->remove_request_queue))
+ return 0; /* There are some unissued remove requests. */
+
+ if (netlink_get_reply_callback_count(manager->rtnl) > 0 ||
+ netlink_get_reply_callback_count(manager->genl) > 0 ||
+ fw_ctx_get_reply_callback_count(manager->fw_ctx) > 0)
+ return 0; /* There are some message calls waiting for their replies. */
+
+ manager->state = MANAGER_STOPPED;
+ return sd_event_exit(sd_event_source_get_event(s), 0);
+
+ default:
+ assert_not_reached();
+ }
+
return 0;
}
-static int signal_terminate_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
- Manager *m = ASSERT_PTR(userdata);
+static int manager_stop(Manager *manager, ManagerState state) {
+ assert(manager);
+ assert(IN_SET(state, MANAGER_TERMINATING, MANAGER_RESTARTING));
- m->restarting = false;
+ if (manager->state != MANAGER_RUNNING) {
+ log_debug("Already terminating or restarting systemd-networkd, refusing further operation request.");
+ return 0;
+ }
- log_debug("Terminate operation initiated.");
+ switch (state) {
+ case MANAGER_TERMINATING:
+ log_debug("Terminate operation initiated.");
+ break;
+ case MANAGER_RESTARTING:
+ log_debug("Restart operation initiated.");
+ break;
+ default:
+ assert_not_reached();
+ }
- return sd_event_exit(sd_event_source_get_event(s), 0);
-}
+ manager->state = state;
-static int signal_restart_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
- Manager *m = ASSERT_PTR(userdata);
+ Link *link;
+ HASHMAP_FOREACH(link, manager->links_by_index) {
+ (void) link_stop_engines(link, /* may_keep_dhcp = */ true);
+ link_free_engines(link);
+ }
- m->restarting = true;
+ return 0;
+}
- log_debug("Restart operation initiated.");
+static int signal_terminate_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ return manager_stop(userdata, MANAGER_TERMINATING);
+}
- return sd_event_exit(sd_event_source_get_event(s), 0);
+static int signal_restart_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
+ return manager_stop(userdata, MANAGER_RESTARTING);
}
static int signal_reload_callback(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
}
Manager* manager_free(Manager *m) {
- Link *link;
-
if (!m)
return NULL;
free(m->state_file);
- HASHMAP_FOREACH(link, m->links_by_index)
- (void) link_stop_engines(link, true);
-
m->request_queue = ordered_set_free(m->request_queue);
m->remove_request_queue = ordered_set_free(m->remove_request_queue);