Lennart Poettering píše v St 08. 08. 2012 v 19:26 +0200:
> On Mon, 06.08.12 17:16, Lukáš Nykrýn (lnyk...@redhat.com) wrote:
> 
> > Again thanks for review. Here is modified patch.
> > If you think that it would be better to add signal stuff before
> > accepting this patch I will not disagree.
> 
> > +                r = set_put(*set, INT_TO_PTR(val));
> 
> Hmm, so I was about to merge this, but there is a problem here: exit
> code 0 is added to the set as NULL, and that's what we return if
> something is *not* in the set. So we'll always mishandle exit code 0
> like this.
> 
> We probably need some code here that just adds one to all exit codes, so
> that we can safely distuingish exit code 0 from "not in this set". And
> that probably means the parsing function needs to be renamed a bit, to
> include "plus_one" or so...
> 
> Lennart
> 

Sorry for delay. I have rewrote the patch and added posibility to set
"successful return statuses" and now you can also specify signals.

Lukas 
>From 65bc722b758d2906be8e811979eee9a5e0640e28 Mon Sep 17 00:00:00 2001
From: Lukas Nykryn <lnyk...@redhat.com>
Date: Mon, 13 Aug 2012 13:58:01 +0200
Subject: [PATCH] service: add options RestartPreventExitStatus and
 SuccessfulExitStatus

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.
---
 man/systemd.service.xml               |   14 +++++++
 src/core/load-fragment-gperf.gperf.m4 |    2 +
 src/core/mount.c                      |    2 +-
 src/core/service.c                    |   20 +++++++++-
 src/core/service.h                    |    3 +
 src/core/socket.c                     |    2 +-
 src/core/swap.c                       |    2 +-
 src/remount-fs/remount-fs.c           |    2 +-
 src/shared/conf-parser.c              |   69 +++++++++++++++++++++++++++++++++
 src/shared/conf-parser.h              |    1 +
 src/shared/exit-status.c              |   16 +++++--
 src/shared/exit-status.h              |   11 ++++-
 src/shared/hashmap.c                  |   15 +++++++
 src/shared/hashmap.h                  |    1 +
 src/shared/set.c                      |    4 ++
 src/shared/set.h                      |    1 +
 src/shared/util.c                     |    2 +-
 src/systemctl/systemctl.c             |    2 +-
 18 files changed, 153 insertions(+), 16 deletions(-)

diff --git a/man/systemd.service.xml b/man/systemd.service.xml
index 1946d85..c587847 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>SuccessfulExitStatus=</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 e738213..0674fe6 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.SuccessfulExitStatus,    config_parse_set_status,            0,                             offsetof(Service, successful_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 83e51a7..fc981c7 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 e74da54..0e0a4e9 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->successful_status.code);
+        s->successful_status.code = NULL;
+        set_free(s->successful_status.signal);
+        s->successful_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->successful_status) :
+                                     is_clean_exit_lsb(code, status, &s->successful_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 c78de79..98b9935 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;
+        ExitStatuses restart_ignore_status;
+        ExitStatuses successful_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 837b166..cbbfb0c 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 458e00e..cd4d9ab 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 636c46f..b49d095 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 1eccec5..671c921 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,70 @@ 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;
+        ExitStatuses *statuses = data;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(data);
+
+        if(!statuses->code){
+                statuses->code = set_new(trivial_hash_func, trivial_compare_func);
+                if (!statuses->code)
+                        return log_oom();
+        }
+
+        if(!statuses->signal){
+                statuses->signal = set_new(trivial_hash_func, trivial_compare_func);
+                if (!statuses->signal)
+                        return log_oom();
+        }
+
+        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) {
+                                r = set_put(statuses->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);
+                        r = set_put(statuses->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 4f94b3b..56ffc2f 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 0dc82b2..b5eacbf 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, ExitStatuses *successful_status) {
 
         if (code == CLD_EXITED)
-                return status == 0;
+                return status == 0 ||
+                       (successful_status &&
+                       set_contains(successful_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 ||
+                        (successful_status &&
+                        set_contains(successful_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, ExitStatuses *successful_status) {
 
-        if (is_clean_exit(code, status))
+        if (is_clean_exit(code, status, successful_status))
                 return true;
 
         return
diff --git a/src/shared/exit-status.h b/src/shared/exit-status.h
index 3f42c15..498b26e 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 ExitStatuses {
+        Set *code;
+        Set *signal;
+} ExitStatuses;
+
 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, ExitStatuses *successful_status);
+bool is_clean_exit_lsb(int code, int status, ExitStatuses *successful_status);
diff --git a/src/shared/hashmap.c b/src/shared/hashmap.c
index e2395d4..be37a36 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 ee810f5..504f0b7 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 f5c7c37..4d56c4f 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 c7b6231..a6c1d76 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 946b7d5..922c100 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -4356,7 +4356,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 13e0f91..2481849 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);
-- 
1.7.6.5

_______________________________________________
systemd-devel mailing list
systemd-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/systemd-devel

Reply via email to