1
0
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:
Lennart Poettering 2019-04-01 18:48:20 +02:00
parent 380dc8b0a2
commit 4c2f584230
7 changed files with 183 additions and 9 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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,

View File

@ -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;