There are many cases where scripts want to make sure that a daemon
released their resources properly before continuing.  These are e.g.
init scripts that want to restart a daemon to apply changes in the
configuration.

start-stop-daemon when FEATURE_START_STOP_DAEMON_FANCY is enabled
already accepts -R and ignores it.  This means that they are already
scripts in the wild that uses -R with schedules, even in buildroot.
It is thus not possible to only support a subset of -R, like only "-R
secs" because that would cause regressions.

There are two remaining options: accept -R, ignore its argument and use
a hardcoded schedule, or accept all common forms of -R (everything
but 'forever' which does not appear to be used anywhere).

Parsing -R schedules takes a lot of bytes so this implement both
options: _FANCY alone gives -R with a hardcoded schedule of
signal/30/KILL/30, while _RETRY_SCHEDULE enables the parser.

Like debianutils's start-stop-daemon, this one uses kill(0) to detect
gone processes.  But it sleeps in 125ms increments instead of using a
complicated algorithm that sleeps at least 20ms or using Linux-specific
pidfd.

With parsing disabled (-R is hardcoded to signal/30/KILL/30):

function                                             old     new   delta
start_stop_daemon_main                              1327    1353     +26
static.do_kill                                         -      95     +95
packed_usage                                       34718   34738     +20
check                                                460     464      +4
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 3/0 up/down: 145/0)             Total: 145 bytes

With parsing enabled:

function                                             old     new   delta
start_stop_daemon_main                              1327    1493    +166
static.do_kill                                         -      95     +95
packed_usage                                       34718   34730     +12
check                                                460     464      +4
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 3/0 up/down: 277/0)             Total: 277 bytes

Signed-off-by: Nicolas Cavallari <[email protected]>
---
 debianutils/start_stop_daemon.c | 152 ++++++++++++++++++++++++++------
 1 file changed, 127 insertions(+), 25 deletions(-)

diff --git a/debianutils/start_stop_daemon.c b/debianutils/start_stop_daemon.c
index 271bc4edf..c86a82b3e 100644
--- a/debianutils/start_stop_daemon.c
+++ b/debianutils/start_stop_daemon.c
@@ -111,6 +111,14 @@ Misc options:
 //config:      -o|--oknodo ignored since we exit with 0 anyway
 //config:      -v|--verbose
 //config:      -N|--nicelevel N
+//config:      -R|--retry R
+//config:
+//config:config FEATURE_START_STOP_DAEMON_RETRY_SCHEDULE
+//config:      bool "Support -R schedules"
+//config:      depends on FEATURE_START_STOP_DAEMON_FANCY
+//config:      help
+//config:      support complex --retry schedules instead of a
+//config:      hardcoded signal/30/KILL/30
 
 //applet:IF_START_STOP_DAEMON(APPLET_ODDNAME(start-stop-daemon, 
start_stop_daemon, BB_DIR_SBIN, BB_SUID_DROP, start_stop_daemon))
 /* not NOEXEC: uses bb_common_bufsiz1 */
@@ -145,6 +153,14 @@ Misc options:
 //usage:     "\n-K only:"
 //usage:     "\n       -s SIG          Signal to send"
 //usage:     "\n       -t              Match only, exit with 0 if found"
+//usage:       IF_FEATURE_START_STOP_DAEMON_FANCY(
+//usage:       IF_FEATURE_START_STOP_DAEMON_RETRY_SCHEDULE(
+//usage:     "\n       -R SEC[/SIG/..] Wait until stopped"
+//usage:       )
+//usage:       IF_NOT_FEATURE_START_STOP_DAEMON_RETRY_SCHEDULE(
+//usage:     "\n       -R IGNORED      Wait until stopped"
+//usage:       )
+//usage:       )
 //usage:     "\nOther:"
 //usage:       IF_FEATURE_START_STOP_DAEMON_FANCY(
 //usage:     "\n       -o              Exit with status 0 if nothing is done"
@@ -160,6 +176,9 @@ Misc options:
 struct pid_list {
        struct pid_list *next;
        pid_t pid;
+# if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
+       smallint gone;
+#endif
 };
 
 enum {
@@ -194,7 +213,12 @@ struct globals {
        char *execname_cmpbuf;
        unsigned execname_sizeof;
        int user_id;
+#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
+#define RETRIES_COUNT 16
+       int retries[RETRIES_COUNT];
+#else
        smallint signal_nr;
+#endif
 #ifdef OLDER_VERSION_OF_X
        struct stat execstat;
 #endif
@@ -205,7 +229,14 @@ struct globals {
 #define execname          (G.execname            )
 #define pidfile           (G.pidfile             )
 #define user_id           (G.user_id             )
+
+#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
+#define retries           (G.retries             )
+#define signal_nr         (retries[0]            )
+#else
 #define signal_nr         (G.signal_nr           )
+#endif
+
 #define INIT_G() do { \
        setup_common_bufsiz(); \
        user_id = -1; \
@@ -308,6 +339,9 @@ static void check(int pid)
        p = xmalloc(sizeof(*p));
        p->next = G.found_procs;
        p->pid = pid;
+#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
+       p->gone = -1;
+#endif
        G.found_procs = p;
 }
 
@@ -358,6 +392,54 @@ static void do_procinit(void)
                bb_simple_error_msg_and_die("nothing in /proc - not mounted?");
 }
 
+static int do_kill(int sig, int test)
+{
+       struct pid_list *p;
+       int killed = 0;
+
+       for (p = G.found_procs; p; p = p->next) {
+#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
+               if (p->gone > 0)
+                       continue;
+#endif
+               if (kill(p->pid, sig) == 0) {
+                       killed++;
+               } else {
+                       if (sig)
+                               bb_perror_msg("warning: killing process %u", 
(unsigned)p->pid);
+#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
+                       p->gone = !sig;
+#else
+                       p->pid = 0;
+#endif
+                       if (test) {
+                               /* Example: -K --test --pidfile PIDFILE detected
+                                * that PIDFILE's pid doesn't exist */
+                               return -1;
+                       }
+               }
+       }
+       return killed;
+}
+
+#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
+static int do_stop_schedule(void) {
+       int killed = 0;
+       int *action = &retries[0];
+       while (*action) {
+               if (*action > 0) {
+                       killed += do_kill(*action, 0);
+               } else {
+                       msleep(125);
+                       if (do_kill(0, 0) && ++(*action))
+                               continue;
+               }
+               action++;
+       }
+       return killed;
+}
+#endif
+
 static int do_stop(void)
 {
        const char *what;
@@ -384,24 +466,19 @@ static int do_stop(void)
                killed = -1;
                goto ret;
        }
-       for (p = G.found_procs; p; p = p->next) {
-               if (kill(p->pid, TEST ? 0 : signal_nr) == 0) {
-                       killed++;
-               } else {
-                       bb_perror_msg("warning: killing process %u", 
(unsigned)p->pid);
-                       p->pid = 0;
-                       if (TEST) {
-                               /* Example: -K --test --pidfile PIDFILE detected
-                                * that PIDFILE's pid doesn't exist */
-                               killed = -1;
-                               goto ret;
-                       }
-               }
-       }
-       if (!QUIET && killed) {
+#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
+       killed = do_stop_schedule();
+#else
+       killed = do_kill(TEST ? 0 : signal_nr, TEST);
+#endif
+       if (!QUIET && killed > 0) {
                printf("stopped %s (pid", what);
                for (p = G.found_procs; p; p = p->next)
+#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
+                       if (p->gone)
+#else
                        if (p->pid)
+#endif
                                printf(" %u", (unsigned)p->pid);
                puts(")");
        }
@@ -453,9 +530,9 @@ int start_stop_daemon_main(int argc UNUSED_PARAM, char 
**argv)
        char *chuid;
        const char *chdir;
        const char *output = NULL;
+       int i = 0;
 #if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
-//     const char *retry_arg = NULL;
-//     int retries = -1;
+       char *retry_arg = NULL;
        const char *opt_N;
 #endif
 
@@ -479,9 +556,7 @@ int start_stop_daemon_main(int argc UNUSED_PARAM, char 
**argv)
                        ,
                LONGOPTS
                &startas, &cmdname, &signame, &userspec, &chuid, &chdir, 
&execname, &pidfile, &output
-               IF_FEATURE_START_STOP_DAEMON_FANCY(,&opt_N)
-               /* We accept and ignore -R <param> / --retry <param> */
-               IF_FEATURE_START_STOP_DAEMON_FANCY(,NULL)
+               IF_FEATURE_START_STOP_DAEMON_FANCY(,&opt_N, &retry_arg)
        );
 
 //-O requires --background and absolute pathname (tested with 1.21.22).
@@ -523,10 +598,37 @@ int start_stop_daemon_main(int argc UNUSED_PARAM, char 
**argv)
                G.execname_sizeof = strlen(execname) + 1;
                G.execname_cmpbuf = xmalloc(G.execname_sizeof + 1);
        }
-//     IF_FEATURE_START_STOP_DAEMON_FANCY(
-//             if (retry_arg)
-//                     retries = xatoi_positive(retry_arg);
-//     )
+#if ENABLE_FEATURE_START_STOP_DAEMON_FANCY
+       if (retry_arg) {
+#if ENABLE_FEATURE_START_STOP_DAEMON_RETRY_SCHEDULE
+               char *first_strtok_arg = retry_arg;
+               int signal = signal_nr;
+               int act_value;
+               const char *action;
+
+               while ((action = strtok_r(first_strtok_arg, "/", &retry_arg))
+                      && i < RETRIES_COUNT - 1) {
+                       if (isdigit(action[0])) {
+                               act_value = -8 * xatoi_positive(action);
+                       } else {
+                               act_value = get_signum(action + (action[0] == 
'-'));
+                               if (act_value < 0)
+                                       bb_error_msg_and_die("unknown signal 
'%s'", action);
+                       }
+                       retries[i++] = act_value;
+                       first_strtok_arg = NULL;
+               }
+               if (i == 1) {
+                       retries[1] = retries[3] = retries[0];
+                       retries[0] = signal;
+                       retries[2] = SIGKILL;
+               }
+#else
+               retries[1] = retries[3] = -30*8;
+               retries[2] = SIGKILL;
+#endif
+       }
+#endif
        if (userspec) {
                user_id = bb_strtou(userspec, NULL, 10);
                if (errno)
@@ -537,7 +639,7 @@ int start_stop_daemon_main(int argc UNUSED_PARAM, char 
**argv)
        do_procinit();
 
        if (opt & CTX_STOP) {
-               int i = do_stop();
+               i = do_stop();
                return (opt & OPT_OKNODO) ? 0 : (i <= 0);
        }
 
-- 
2.51.0

_______________________________________________
busybox mailing list
[email protected]
https://lists.busybox.net/mailman/listinfo/busybox

Reply via email to