summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Nykryn <lnykryn@redhat.com>2012-08-13 13:58:01 +0200
committerLennart Poettering <lennart@poettering.net>2012-08-14 14:46:03 +0200
commit96342de68d0d6de71a062d984dafd2a0905ed9fe (patch)
treeadbeb686c6c3c2bfd5a1c8768555575acb4850a2
parentd98cc1f29fbf31ccc500d6e20c29b636b9af7e0f (diff)
service: add options RestartPreventExitStatus and SuccessExitStatus
In some cases, like wrong configuration, restarting after error does not help, so administrator can specify statuses by RestartPreventExitStatus which will not cause restart of a service. Sometimes you have non-standart exit status, so this can be specified by SuccessfulExitStatus.
-rw-r--r--man/systemd.service.xml14
-rw-r--r--src/core/load-fragment-gperf.gperf.m42
-rw-r--r--src/core/mount.c2
-rw-r--r--src/core/service.c20
-rw-r--r--src/core/service.h3
-rw-r--r--src/core/socket.c2
-rw-r--r--src/core/swap.c2
-rw-r--r--src/remount-fs/remount-fs.c2
-rw-r--r--src/shared/conf-parser.c70
-rw-r--r--src/shared/conf-parser.h1
-rw-r--r--src/shared/exit-status.c16
-rw-r--r--src/shared/exit-status.h11
-rw-r--r--src/shared/hashmap.c15
-rw-r--r--src/shared/hashmap.h1
-rw-r--r--src/shared/set.c4
-rw-r--r--src/shared/set.h1
-rw-r--r--src/shared/util.c2
-rw-r--r--src/systemctl/systemctl.c2
18 files changed, 154 insertions, 16 deletions
diff --git a/man/systemd.service.xml b/man/systemd.service.xml
index 1946d85f48..c4bd65e349 100644
--- a/man/systemd.service.xml
+++ b/man/systemd.service.xml
@@ -580,6 +580,20 @@
</varlistentry>
<varlistentry>
+ <term><varname>RestartPreventExitStatus=</varname></term>
+ <listitem><para>Specify exit status list, which
+ will prevent service from restart. Codes are
+ separated by whitespace (e.g. "1 6 SIGKILL").</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>SuccessExitStatus=</varname></term>
+ <listitem><para>Specify exit status list, which
+ will be considered as successful exit. Codes are
+ separated by whitespace (e.g. "1 6 SIGKILL").</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>PermissionsStartOnly=</varname></term>
<listitem><para>Takes a boolean
argument. If true, the permission
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index e738213e27..84eea1c465 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -158,6 +158,8 @@ Service.PermissionsStartOnly, config_parse_bool, 0,
Service.RootDirectoryStartOnly, config_parse_bool, 0, offsetof(Service, root_directory_start_only)
Service.RemainAfterExit, config_parse_bool, 0, offsetof(Service, remain_after_exit)
Service.GuessMainPID, config_parse_bool, 0, offsetof(Service, guess_main_pid)
+Service.RestartPreventExitStatus, config_parse_set_status, 0, offsetof(Service, restart_ignore_status)
+Service.SuccessExitStatus, config_parse_set_status, 0, offsetof(Service, success_status)
m4_ifdef(`HAVE_SYSV_COMPAT',
`Service.SysVStartPriority, config_parse_sysv_priority, 0, offsetof(Service, sysv_start_priority)',
`Service.SysVStartPriority, config_parse_warn_compat, 0, 0')
diff --git a/src/core/mount.c b/src/core/mount.c
index 83e51a7aba..fc981c74f4 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -1225,7 +1225,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
m->control_pid = 0;
- if (is_clean_exit(code, status))
+ if (is_clean_exit(code, status, NULL))
f = MOUNT_SUCCESS;
else if (code == CLD_EXITED)
f = MOUNT_FAILURE_EXIT_CODE;
diff --git a/src/core/service.c b/src/core/service.c
index e74da54eac..f540752b61 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -294,6 +294,16 @@ static void service_done(Unit *u) {
s->control_command = NULL;
s->main_command = NULL;
+ set_free(s->restart_ignore_status.code);
+ s->restart_ignore_status.code = NULL;
+ set_free(s->restart_ignore_status.signal);
+ s->restart_ignore_status.signal = NULL;
+
+ set_free(s->success_status.code);
+ s->success_status.code = NULL;
+ set_free(s->success_status.signal);
+ s->success_status.signal = NULL;
+
/* This will leak a process, but at least no memory or any of
* our resources */
service_unwatch_main_pid(s);
@@ -1902,7 +1912,12 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
(s->restart == SERVICE_RESTART_ON_SUCCESS && s->result == SERVICE_SUCCESS) ||
(s->restart == SERVICE_RESTART_ON_FAILURE && s->result != SERVICE_SUCCESS) ||
(s->restart == SERVICE_RESTART_ON_ABORT && (s->result == SERVICE_FAILURE_SIGNAL ||
- s->result == SERVICE_FAILURE_CORE_DUMP)))) {
+ s->result == SERVICE_FAILURE_CORE_DUMP))) &&
+ (s->result != SERVICE_FAILURE_EXIT_CODE ||
+ !set_contains(s->restart_ignore_status.code, INT_TO_PTR(s->main_exec_status.status))) &&
+ (s->result != SERVICE_FAILURE_SIGNAL ||
+ !set_contains(s->restart_ignore_status.signal, INT_TO_PTR(s->main_exec_status.status)))
+ ) {
r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch);
if (r < 0)
@@ -2874,7 +2889,8 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
assert(s);
assert(pid >= 0);
- if (UNIT(s)->fragment_path ? is_clean_exit(code, status) : is_clean_exit_lsb(code, status))
+ if (UNIT(s)->fragment_path ? is_clean_exit(code, status, &s->success_status) :
+ is_clean_exit_lsb(code, status, &s->success_status))
f = SERVICE_SUCCESS;
else if (code == CLD_EXITED)
f = SERVICE_FAILURE_EXIT_CODE;
diff --git a/src/core/service.h b/src/core/service.h
index c78de79a09..2a4dc30d0b 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -28,6 +28,7 @@ typedef struct Service Service;
#include "ratelimit.h"
#include "service.h"
#include "kill.h"
+#include "exit-status.h"
typedef enum ServiceState {
SERVICE_DEAD,
@@ -115,6 +116,8 @@ struct Service {
ServiceType type;
ServiceRestart restart;
+ ExitStatusSet restart_ignore_status;
+ ExitStatusSet success_status;
/* If set we'll read the main daemon PID from this file */
char *pid_file;
diff --git a/src/core/socket.c b/src/core/socket.c
index 837b166e3b..cbbfb0cd3a 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -1884,7 +1884,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_pid = 0;
- if (is_clean_exit(code, status))
+ if (is_clean_exit(code, status, NULL))
f = SOCKET_SUCCESS;
else if (code == CLD_EXITED)
f = SOCKET_FAILURE_EXIT_CODE;
diff --git a/src/core/swap.c b/src/core/swap.c
index 458e00efe5..cd4d9ab3d7 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -917,7 +917,7 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) {
s->control_pid = 0;
- if (is_clean_exit(code, status))
+ if (is_clean_exit(code, status, NULL))
f = SWAP_SUCCESS;
else if (code == CLD_EXITED)
f = SWAP_FAILURE_EXIT_CODE;
diff --git a/src/remount-fs/remount-fs.c b/src/remount-fs/remount-fs.c
index 636c46f0f3..b49d095cbb 100644
--- a/src/remount-fs/remount-fs.c
+++ b/src/remount-fs/remount-fs.c
@@ -145,7 +145,7 @@ int main(int argc, char *argv[]) {
s = hashmap_remove(pids, UINT_TO_PTR(si.si_pid));
if (s) {
- if (!is_clean_exit(si.si_code, si.si_status)) {
+ if (!is_clean_exit(si.si_code, si.si_status, NULL)) {
if (si.si_code == CLD_EXITED)
log_error("/bin/mount for %s exited with exit status %i.", s, si.si_status);
else
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
index 1eccec5989..595bb51a27 100644
--- a/src/shared/conf-parser.c
+++ b/src/shared/conf-parser.c
@@ -32,6 +32,8 @@
#include "log.h"
#include "utf8.h"
#include "path-util.h"
+#include "set.h"
+#include "exit-status.h"
int config_item_table_lookup(
void *table,
@@ -933,3 +935,71 @@ int config_parse_level(
*o = (*o & LOG_FACMASK) | x;
return 0;
}
+
+int config_parse_set_status(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char *w;
+ size_t l;
+ char *state;
+ int r;
+ ExitStatusSet *status_set = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ FOREACH_WORD(w, l, rvalue, state) {
+ int val;
+ char *temp = strndup(w, l);
+ if (!temp)
+ return log_oom();
+
+ r = safe_atoi(temp, &val);
+ if (r < 0) {
+ val = signal_from_string_try_harder(temp);
+ free(temp);
+ if (val > 0) {
+ if (!status_set->signal) {
+ status_set->signal = set_new(trivial_hash_func, trivial_compare_func);
+ if (!status_set->signal)
+ return log_oom();
+ }
+ r = set_put(status_set->signal, INT_TO_PTR(val));
+ if (r < 0) {
+ log_error("[%s:%u] Unable to store: %s", filename, line, w);
+ return r;
+ }
+ } else {
+ log_error("[%s:%u] Failed to parse value: %s", filename, line, w);
+ return r;
+ }
+ } else {
+ free(temp);
+ if(val < 0 || val > 255)
+ log_warning("[%s:%u] Value %d is outside range 0-255, ignoring", filename, line, val);
+ else {
+ if (!status_set->code) {
+ status_set->code = set_new(trivial_hash_func, trivial_compare_func);
+ if (!status_set->code)
+ return log_oom();
+ }
+ r = set_put(status_set->code, INT_TO_PTR(val));
+ if (r < 0) {
+ log_error("[%s:%u] Unable to store: %s", filename, line, w);
+ return r;
+ }
+ }
+ }
+
+ }
+ return 0;
+}
diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h
index 4f94b3b907..56ffc2f8a8 100644
--- a/src/shared/conf-parser.h
+++ b/src/shared/conf-parser.h
@@ -105,6 +105,7 @@ int config_parse_nsec(const char *filename, unsigned line, const char *section,
int config_parse_mode(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_facility(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_level(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_set_status(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
#define DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg) \
int function( \
diff --git a/src/shared/exit-status.c b/src/shared/exit-status.c
index 0dc82b2e13..45131f2b2a 100644
--- a/src/shared/exit-status.c
+++ b/src/shared/exit-status.c
@@ -23,6 +23,8 @@
#include <sys/wait.h>
#include "exit-status.h"
+#include "set.h"
+#include "macro.h"
const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
@@ -158,10 +160,12 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
}
-bool is_clean_exit(int code, int status) {
+bool is_clean_exit(int code, int status, ExitStatusSet *success_status) {
if (code == CLD_EXITED)
- return status == 0;
+ return status == 0 ||
+ (success_status &&
+ set_contains(success_status->code, INT_TO_PTR(status)));
/* If a daemon does not implement handlers for some of the
* signals that's not considered an unclean shutdown */
@@ -170,14 +174,16 @@ bool is_clean_exit(int code, int status) {
status == SIGHUP ||
status == SIGINT ||
status == SIGTERM ||
- status == SIGPIPE;
+ status == SIGPIPE ||
+ (success_status &&
+ set_contains(success_status->signal, INT_TO_PTR(status)));
return false;
}
-bool is_clean_exit_lsb(int code, int status) {
+bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status) {
- if (is_clean_exit(code, status))
+ if (is_clean_exit(code, status, success_status))
return true;
return
diff --git a/src/shared/exit-status.h b/src/shared/exit-status.h
index 3f42c15e3b..d3b548fc96 100644
--- a/src/shared/exit-status.h
+++ b/src/shared/exit-status.h
@@ -22,7 +22,7 @@
***/
#include <stdbool.h>
-
+#include "set.h"
typedef enum ExitStatus {
/* EXIT_SUCCESS defined by libc */
/* EXIT_FAILURE defined by libc */
@@ -77,7 +77,12 @@ typedef enum ExitStatusLevel {
EXIT_STATUS_FULL = EXIT_STATUS_LSB
} ExitStatusLevel;
+typedef struct ExitStatusSet {
+ Set *code;
+ Set *signal;
+} ExitStatusSet;
+
const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level);
-bool is_clean_exit(int code, int status);
-bool is_clean_exit_lsb(int code, int status);
+bool is_clean_exit(int code, int status, ExitStatusSet *success_status);
+bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status);
diff --git a/src/shared/hashmap.c b/src/shared/hashmap.c
index e2395d4d42..be37a3659d 100644
--- a/src/shared/hashmap.c
+++ b/src/shared/hashmap.c
@@ -378,6 +378,21 @@ void* hashmap_get(Hashmap *h, const void *key) {
return e->value;
}
+bool hashmap_contains(Hashmap *h, const void *key) {
+ unsigned hash;
+ struct hashmap_entry *e;
+
+ if (!h)
+ return false;
+
+ hash = h->hash_func(key) % NBUCKETS;
+
+ if (!(e = hash_scan(h, hash, key)))
+ return false;
+
+ return true;
+}
+
void* hashmap_remove(Hashmap *h, const void *key) {
struct hashmap_entry *e;
unsigned hash;
diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h
index ee810f5ae1..504f0b7637 100644
--- a/src/shared/hashmap.h
+++ b/src/shared/hashmap.h
@@ -53,6 +53,7 @@ int hashmap_ensure_allocated(Hashmap **h, hash_func_t hash_func, compare_func_t
int hashmap_put(Hashmap *h, const void *key, void *value);
int hashmap_replace(Hashmap *h, const void *key, void *value);
void* hashmap_get(Hashmap *h, const void *key);
+bool hashmap_contains(Hashmap *h, const void *key);
void* hashmap_remove(Hashmap *h, const void *key);
void* hashmap_remove_value(Hashmap *h, const void *key, void *value);
int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value);
diff --git a/src/shared/set.c b/src/shared/set.c
index f5c7c37cab..4d56c4f505 100644
--- a/src/shared/set.c
+++ b/src/shared/set.c
@@ -57,6 +57,10 @@ void *set_get(Set *s, void *value) {
return hashmap_get(MAKE_HASHMAP(s), value);
}
+bool set_contains(Set *s, void *value) {
+ return hashmap_contains(MAKE_HASHMAP(s), value);
+}
+
void *set_remove(Set *s, void *value) {
return hashmap_remove(MAKE_HASHMAP(s), value);
}
diff --git a/src/shared/set.h b/src/shared/set.h
index c7b6231eed..a6c1d76b0e 100644
--- a/src/shared/set.h
+++ b/src/shared/set.h
@@ -40,6 +40,7 @@ int set_ensure_allocated(Set **s, hash_func_t hash_func, compare_func_t compare_
int set_put(Set *s, void *value);
int set_replace(Set *s, void *value);
void *set_get(Set *s, void *value);
+bool set_contains(Set *s, void *value);
void *set_remove(Set *s, void *value);
int set_remove_and_put(Set *s, void *old_value, void *new_value);
diff --git a/src/shared/util.c b/src/shared/util.c
index e615195af5..cbf44ebdfd 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -4357,7 +4357,7 @@ void execute_directory(const char *directory, DIR *d, char *argv[]) {
}
if ((path = hashmap_remove(pids, UINT_TO_PTR(si.si_pid)))) {
- if (!is_clean_exit(si.si_code, si.si_status)) {
+ if (!is_clean_exit(si.si_code, si.si_status, NULL)) {
if (si.si_code == CLD_EXITED)
log_error("%s exited with exit status %i.", path, si.si_status);
else
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 13e0f91096..2481849232 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -2148,7 +2148,7 @@ static void print_status_info(UnitStatusInfo *i) {
printf("\t Process: %u %s=%s ", p->pid, p->name, strna(t));
free(t);
- good = is_clean_exit_lsb(p->code, p->status);
+ good = is_clean_exit_lsb(p->code, p->status, NULL);
if (!good) {
on = ansi_highlight_red(true);
off = ansi_highlight_red(false);