mirror of
https://github.com/systemd/systemd.git
synced 2025-01-11 09:18:07 +03:00
core: hook up service unit type with the new clean operation
The implementation is pretty straight-foward: when we get a request to clean some type of resources we fork off a process doing that, and while it is running we are in the "cleaning" state.
This commit is contained in:
parent
380dc8b0a2
commit
4c2f584230
@ -178,6 +178,7 @@ static const char* const service_state_table[_SERVICE_STATE_MAX] = {
|
||||
[SERVICE_FINAL_SIGKILL] = "final-sigkill",
|
||||
[SERVICE_FAILED] = "failed",
|
||||
[SERVICE_AUTO_RESTART] = "auto-restart",
|
||||
[SERVICE_CLEANING] = "cleaning",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
|
||||
|
@ -117,6 +117,7 @@ typedef enum ServiceState {
|
||||
SERVICE_FINAL_SIGKILL,
|
||||
SERVICE_FAILED,
|
||||
SERVICE_AUTO_RESTART,
|
||||
SERVICE_CLEANING,
|
||||
_SERVICE_STATE_MAX,
|
||||
_SERVICE_STATE_INVALID = -1
|
||||
} ServiceState;
|
||||
|
@ -4774,6 +4774,60 @@ void exec_context_revert_tty(ExecContext *c) {
|
||||
}
|
||||
}
|
||||
|
||||
int exec_context_get_clean_directories(
|
||||
ExecContext *c,
|
||||
char **prefix,
|
||||
ExecCleanMask mask,
|
||||
char ***ret) {
|
||||
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
ExecDirectoryType t;
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
assert(prefix);
|
||||
assert(ret);
|
||||
|
||||
for (t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
|
||||
char **i;
|
||||
|
||||
if (!FLAGS_SET(mask, 1U << t))
|
||||
continue;
|
||||
|
||||
if (!prefix[t])
|
||||
continue;
|
||||
|
||||
STRV_FOREACH(i, c->directories[t].paths) {
|
||||
char *j;
|
||||
|
||||
j = path_join(prefix[t], *i);
|
||||
if (!j)
|
||||
return -ENOMEM;
|
||||
|
||||
r = strv_consume(&l, j);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
|
||||
ExecCleanMask mask = 0;
|
||||
|
||||
assert(c);
|
||||
assert(ret);
|
||||
|
||||
for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
|
||||
if (!strv_isempty(c->directories[t].paths))
|
||||
mask |= 1U << t;
|
||||
|
||||
*ret = mask;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void exec_status_start(ExecStatus *s, pid_t pid) {
|
||||
assert(s);
|
||||
|
||||
|
@ -382,6 +382,9 @@ void exec_context_free_log_extra_fields(ExecContext *c);
|
||||
|
||||
void exec_context_revert_tty(ExecContext *c);
|
||||
|
||||
int exec_context_get_clean_directories(ExecContext *c, char **prefix, ExecCleanMask mask, char ***ret);
|
||||
int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret);
|
||||
|
||||
void exec_status_start(ExecStatus *s, pid_t pid);
|
||||
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status);
|
||||
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix);
|
||||
|
@ -315,6 +315,7 @@ Service.TimeoutSec, config_parse_service_timeout, 0,
|
||||
Service.TimeoutStartSec, config_parse_service_timeout, 0, 0
|
||||
Service.TimeoutStopSec, config_parse_sec_fix_0, 0, offsetof(Service, timeout_stop_usec)
|
||||
Service.TimeoutAbortSec, config_parse_service_timeout_abort, 0, 0
|
||||
Service.TimeoutCleanSec, config_parse_sec, 0, offsetof(Service, timeout_clean_usec)
|
||||
Service.RuntimeMaxSec, config_parse_sec, 0, offsetof(Service, runtime_max_usec)
|
||||
Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec)
|
||||
m4_dnl The following five only exist for compatibility, they moved into Unit, see above
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "rm-rf.h"
|
||||
#include "serialize.h"
|
||||
#include "service.h"
|
||||
#include "signal-util.h"
|
||||
@ -59,7 +60,8 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
|
||||
[SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
|
||||
[SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
|
||||
[SERVICE_FAILED] = UNIT_FAILED,
|
||||
[SERVICE_AUTO_RESTART] = UNIT_ACTIVATING
|
||||
[SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
|
||||
[SERVICE_CLEANING] = UNIT_MAINTENANCE,
|
||||
};
|
||||
|
||||
/* For Type=idle we never want to delay any other jobs, hence we
|
||||
@ -80,7 +82,8 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] =
|
||||
[SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
|
||||
[SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
|
||||
[SERVICE_FAILED] = UNIT_FAILED,
|
||||
[SERVICE_AUTO_RESTART] = UNIT_ACTIVATING
|
||||
[SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
|
||||
[SERVICE_CLEANING] = UNIT_MAINTENANCE,
|
||||
};
|
||||
|
||||
static int service_dispatch_inotify_io(sd_event_source *source, int fd, uint32_t events, void *userdata);
|
||||
@ -103,6 +106,7 @@ static void service_init(Unit *u) {
|
||||
s->timeout_abort_set = u->manager->default_timeout_abort_set;
|
||||
s->restart_usec = u->manager->default_restart_usec;
|
||||
s->runtime_max_usec = USEC_INFINITY;
|
||||
s->timeout_clean_usec = USEC_INFINITY;
|
||||
s->type = _SERVICE_TYPE_INVALID;
|
||||
s->socket_fd = -1;
|
||||
s->stdin_fd = s->stdout_fd = s->stderr_fd = -1;
|
||||
@ -787,8 +791,9 @@ static int service_load(Unit *u) {
|
||||
}
|
||||
|
||||
static void service_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
char buf_restart[FORMAT_TIMESPAN_MAX], buf_start[FORMAT_TIMESPAN_MAX], buf_stop[FORMAT_TIMESPAN_MAX];
|
||||
char buf_runtime[FORMAT_TIMESPAN_MAX], buf_watchdog[FORMAT_TIMESPAN_MAX], buf_abort[FORMAT_TIMESPAN_MAX];
|
||||
char buf_restart[FORMAT_TIMESPAN_MAX], buf_start[FORMAT_TIMESPAN_MAX], buf_stop[FORMAT_TIMESPAN_MAX],
|
||||
buf_runtime[FORMAT_TIMESPAN_MAX], buf_watchdog[FORMAT_TIMESPAN_MAX], buf_abort[FORMAT_TIMESPAN_MAX],
|
||||
buf_clean[FORMAT_TIMESPAN_MAX];
|
||||
ServiceExecCommand c;
|
||||
Service *s = SERVICE(u);
|
||||
const char *prefix2;
|
||||
@ -802,6 +807,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
"%sService State: %s\n"
|
||||
"%sResult: %s\n"
|
||||
"%sReload Result: %s\n"
|
||||
"%sClean Result: %s\n"
|
||||
"%sPermissionsStartOnly: %s\n"
|
||||
"%sRootDirectoryStartOnly: %s\n"
|
||||
"%sRemainAfterExit: %s\n"
|
||||
@ -814,6 +820,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
prefix, service_state_to_string(s->state),
|
||||
prefix, service_result_to_string(s->result),
|
||||
prefix, service_result_to_string(s->reload_result),
|
||||
prefix, service_result_to_string(s->clean_result),
|
||||
prefix, yes_no(s->permissions_start_only),
|
||||
prefix, yes_no(s->root_directory_start_only),
|
||||
prefix, yes_no(s->remain_after_exit),
|
||||
@ -869,8 +876,10 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
prefix, format_timespan(buf_abort, sizeof(buf_abort), s->timeout_abort_usec, USEC_PER_SEC));
|
||||
|
||||
fprintf(f,
|
||||
"%sTimeoutCleanSec: %s\n"
|
||||
"%sRuntimeMaxSec: %s\n"
|
||||
"%sWatchdogSec: %s\n",
|
||||
prefix, format_timespan(buf_clean, sizeof(buf_clean), s->timeout_clean_usec, USEC_PER_SEC),
|
||||
prefix, format_timespan(buf_runtime, sizeof(buf_runtime), s->runtime_max_usec, USEC_PER_SEC),
|
||||
prefix, format_timespan(buf_watchdog, sizeof(buf_watchdog), s->watchdog_usec, USEC_PER_SEC));
|
||||
|
||||
@ -1069,7 +1078,8 @@ static void service_set_state(Service *s, ServiceState state) {
|
||||
SERVICE_RELOAD,
|
||||
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
|
||||
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
|
||||
SERVICE_AUTO_RESTART))
|
||||
SERVICE_AUTO_RESTART,
|
||||
SERVICE_CLEANING))
|
||||
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
|
||||
|
||||
if (!IN_SET(state,
|
||||
@ -1085,7 +1095,8 @@ static void service_set_state(Service *s, ServiceState state) {
|
||||
SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
|
||||
SERVICE_RELOAD,
|
||||
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
|
||||
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
|
||||
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
|
||||
SERVICE_CLEANING)) {
|
||||
service_unwatch_control_pid(s);
|
||||
s->control_command = NULL;
|
||||
s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
|
||||
@ -1151,6 +1162,9 @@ static usec_t service_coldplug_timeout(Service *s) {
|
||||
case SERVICE_AUTO_RESTART:
|
||||
return usec_add(UNIT(s)->inactive_enter_timestamp.monotonic, s->restart_usec);
|
||||
|
||||
case SERVICE_CLEANING:
|
||||
return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_clean_usec);
|
||||
|
||||
default:
|
||||
return USEC_INFINITY;
|
||||
}
|
||||
@ -1188,13 +1202,14 @@ static int service_coldplug(Unit *u) {
|
||||
SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
|
||||
SERVICE_RELOAD,
|
||||
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
|
||||
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
|
||||
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
|
||||
SERVICE_CLEANING)) {
|
||||
r = unit_watch_pid(UNIT(s), s->control_pid, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART)) {
|
||||
if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART, SERVICE_CLEANING)) {
|
||||
(void) unit_enqueue_rewatch_pids(u);
|
||||
(void) unit_setup_dynamic_creds(u);
|
||||
(void) unit_setup_exec_runtime(u);
|
||||
@ -2368,7 +2383,7 @@ static int service_start(Unit *u) {
|
||||
* please! */
|
||||
if (IN_SET(s->state,
|
||||
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
|
||||
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))
|
||||
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, SERVICE_CLEANING))
|
||||
return -EAGAIN;
|
||||
|
||||
/* Already on it! */
|
||||
@ -2455,6 +2470,12 @@ static int service_stop(Unit *u) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If we are currently cleaning, then abort it, brutally. */
|
||||
if (s->state == SERVICE_CLEANING) {
|
||||
service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_SUCCESS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(IN_SET(s->state, SERVICE_RUNNING, SERVICE_EXITED));
|
||||
|
||||
service_enter_stop(s, SERVICE_SUCCESS);
|
||||
@ -3563,6 +3584,14 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||
service_enter_dead(s, f, true);
|
||||
break;
|
||||
|
||||
case SERVICE_CLEANING:
|
||||
|
||||
if (s->clean_result == SERVICE_SUCCESS)
|
||||
s->clean_result = f;
|
||||
|
||||
service_enter_dead(s, SERVICE_SUCCESS, false);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Uh, control process died at wrong time.");
|
||||
}
|
||||
@ -3679,6 +3708,15 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
|
||||
service_enter_restart(s);
|
||||
break;
|
||||
|
||||
case SERVICE_CLEANING:
|
||||
log_unit_warning(UNIT(s), "Cleaning timed out. killing.");
|
||||
|
||||
if (s->clean_result == SERVICE_SUCCESS)
|
||||
s->clean_result = SERVICE_FAILURE_TIMEOUT;
|
||||
|
||||
service_enter_signal(s, SERVICE_FINAL_SIGKILL, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached("Timeout at wrong time.");
|
||||
}
|
||||
@ -4086,6 +4124,7 @@ static void service_reset_failed(Unit *u) {
|
||||
|
||||
s->result = SERVICE_SUCCESS;
|
||||
s->reload_result = SERVICE_SUCCESS;
|
||||
s->clean_result = SERVICE_SUCCESS;
|
||||
s->n_restarts = 0;
|
||||
s->flush_n_restarts = false;
|
||||
}
|
||||
@ -4155,6 +4194,77 @@ static int service_exit_status(Unit *u) {
|
||||
return s->main_exec_status.status;
|
||||
}
|
||||
|
||||
static int service_clean(Unit *u, ExecCleanMask mask) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
Service *s = SERVICE(u);
|
||||
pid_t pid;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(mask != 0);
|
||||
|
||||
if (s->state != SERVICE_DEAD)
|
||||
return -EBUSY;
|
||||
|
||||
r = exec_context_get_clean_directories(&s->exec_context, u->manager->prefix, mask, &l);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (strv_isempty(l))
|
||||
return -EUNATCH;
|
||||
|
||||
service_unwatch_control_pid(s);
|
||||
s->clean_result = SERVICE_SUCCESS;
|
||||
s->control_command = NULL;
|
||||
s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
|
||||
|
||||
r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_clean_usec));
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
r = unit_fork_helper_process(UNIT(s), "(sd-rmrf)", &pid);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
if (r == 0) {
|
||||
int ret = EXIT_SUCCESS;
|
||||
char **i;
|
||||
|
||||
STRV_FOREACH(i, l) {
|
||||
r = rm_rf(*i, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_MISSING_OK);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to remove '%s': %m", *i);
|
||||
ret = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
_exit(ret);
|
||||
}
|
||||
|
||||
r = unit_watch_pid(u, pid, true);
|
||||
if (r < 0)
|
||||
goto fail;
|
||||
|
||||
s->control_pid = pid;
|
||||
|
||||
service_set_state(s, SERVICE_CLEANING);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
log_unit_warning_errno(UNIT(s), r, "Failed to initiate cleaning: %m");
|
||||
s->clean_result = SERVICE_FAILURE_RESOURCES;
|
||||
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int service_can_clean(Unit *u, ExecCleanMask *ret) {
|
||||
Service *s = SERVICE(u);
|
||||
|
||||
assert(s);
|
||||
|
||||
return exec_context_get_clean_mask(&s->exec_context, ret);
|
||||
}
|
||||
|
||||
static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
|
||||
[SERVICE_RESTART_NO] = "no",
|
||||
[SERVICE_RESTART_ON_SUCCESS] = "on-success",
|
||||
@ -4255,6 +4365,8 @@ const UnitVTable service_vtable = {
|
||||
.can_reload = service_can_reload,
|
||||
|
||||
.kill = service_kill,
|
||||
.clean = service_clean,
|
||||
.can_clean = service_can_clean,
|
||||
|
||||
.serialize = service_serialize,
|
||||
.deserialize_item = service_deserialize_item,
|
||||
|
@ -99,6 +99,7 @@ struct Service {
|
||||
usec_t timeout_stop_usec;
|
||||
usec_t timeout_abort_usec;
|
||||
bool timeout_abort_set;
|
||||
usec_t timeout_clean_usec;
|
||||
usec_t runtime_max_usec;
|
||||
|
||||
dual_timestamp watchdog_timestamp;
|
||||
@ -147,6 +148,7 @@ struct Service {
|
||||
/* If we shut down, remember why */
|
||||
ServiceResult result;
|
||||
ServiceResult reload_result;
|
||||
ServiceResult clean_result;
|
||||
|
||||
bool main_pid_known:1;
|
||||
bool main_pid_alien:1;
|
||||
|
Loading…
Reference in New Issue
Block a user