Welcome to little lamb

Code » anopa » commit eadd56c

Add (better) support of loggers

author Olivier Brunel
2015-10-16 14:02:10 UTC
committer Olivier Brunel
2015-11-12 17:04:19 UTC
parent 73dc448268120587efc65b2adb6430b9b8104091

Add (better) support of loggers

Now */log servicedirs are processed as regular servicedirs, supporting
dependencies and such. Additionally, any longrun with a logger will have
a dependency ("needs") on said logger auto-added.

Before, they were autostarted by s6-svscan (no down file was set) and
expected to always be up. They're now processed as usual, so starting
foobar will first need to start foobar/log as expected.

aa-status has more filters to deal with loggers properly.

doc/aa-enable.pod +6 -0
doc/aa-status.pod +19 -1
doc/aa-stop.pod +7 -11
doc/anopa.pod +17 -0
src/anopa/aa-status.c +85 -15
src/anopa/aa-stop.c +29 -2
src/include/anopa/enable_service.h +2 -1
src/libanopa/enable_service.c +90 -53
src/libanopa/exec_longrun.c +2 -14
src/libanopa/service.c +26 -0
src/libanopa/service_internal.h +2 -0
src/libanopa/service_name.c +4 -1
src/libanopa/service_start.c +9 -5
src/libanopa/service_stop.c +8 -3

diff --git a/doc/aa-enable.pod b/doc/aa-enable.pod
index c9e2525..9630277 100644
--- a/doc/aa-enable.pod
+++ b/doc/aa-enable.pod
@@ -194,6 +194,12 @@ Note that you can also still provide files from the configuration directory in
 listdir, since it is copied last (and that this always includes providing a new
 I<run> file).
 
+The servicedir of the logger is treated as such, meaning dependencies ("needs"
+and "wants") will be auto-enabled as needed, you can remove/append to files
+from the configuration directory as described below, and a file I<down> will be
+created, as well as a folder I<supervise> (unless B<--no-supervise> was
+specified).
+
 Lastly, B<aa-enable>(1)'s last step when creating a servicedir is to check for a
 regular file I<log/run-args> in the newly created servicedir, and if found to
 append its content to I<log/run>
diff --git a/doc/aa-status.pod b/doc/aa-status.pod
index 457463f..aba0268 100644
--- a/doc/aa-status.pod
+++ b/doc/aa-status.pod
@@ -111,6 +111,20 @@ To only process oneshot services.
 
 To only process longrun services.
 
+=item I<all>; I<os+lr+log>
+
+To process all services: oneshots, longruns, and loggers. By default, only
+oneshots and longruns are shown.
+
+=item I<log>
+
+To only process loggers (i.e. I<*/log> services)
+
+=item I<logged>; I<lr+log>
+
+To only process longruns and loggers. Note that this also include longruns
+without loggers.
+
 =back
 
 =head2 Filter by status
@@ -150,7 +164,7 @@ The same could be done using e.g:
 
     aa-status -Laf fail -f os
 
-To show statues of all longrun process currently up:
+To show statues of all longrun services currently up:
 
     aa-status -a -f longrun -f started
 
@@ -159,6 +173,10 @@ both of the same kind) :
 
     aa-status -Laf longrun --filter os
 
+To list all services, including loggers, that are up:
+
+    aa-status -Laf all -f up
+
 
 =head1 POSSIBLE STATUSES
 
diff --git a/doc/aa-stop.pod b/doc/aa-stop.pod
index 17fdafe..90f726f 100644
--- a/doc/aa-stop.pod
+++ b/doc/aa-stop.pod
@@ -92,19 +92,15 @@ following differences :
 B<aa-stop>(1) will check if the service is running, and if not simply announce
 it as not up.
 
-When B<--all> is used, if the service has a logger then command 'x' will be sent
-to said logger's B<s6-supervise> first, so that when the logger exits it isn't
-restarted and B<s6-supervise> exits as well instead.
-
-Similarly, instead of 'd' the commands 'dx' are sent to the service's
-B<s6-supervise>, so that after bringing the service down it exits as well. This
-is obviously all intended to bring the supervised tree all down, as is expected
-when using B<--all> (usually from stage 3).
+When B<--all> is used, instead of 'd' the commands 'dx' are sent to the
+service's B<s6-supervise>, so that after bringing the service down it exits as
+well. This is obviously all intended to bring the supervised tree all down, as
+is expected when using B<--all> (usually from stage 3).
 
 Note that if a service was not running, no 'x' command is sent so the
-B<s6-supervise> process of the service - and of its logger, if any - are kept
-running. This isn't a problem, since they'll simply exit when sending SIGTERM to
-all process further down in stage 3.
+B<s6-supervise> process of the service are kept running. This isn't a problem,
+since they'll simply exit when sending SIGTERM to all process further down in
+stage 3.
 
 =head2 Service not up
 
diff --git a/doc/anopa.pod b/doc/anopa.pod
index dc88b5a..28ef7a2 100644
--- a/doc/anopa.pod
+++ b/doc/anopa.pod
@@ -110,6 +110,9 @@ be logged; the I<log> service is called the service's logger.
 See B<aa-enable>(1) for how you can use a file instead, to automatically use the
 same logger for all services.
 
+Note that by default, B<aa-status>(1) only list oneshots & longruns, excluding
+such loggers. Refer to its option B<--filter> to include/list them.
+
 =item An optional regular file named I<notification-fd>
 
 If such a file exists, it means that the service supports readiness
@@ -280,6 +283,20 @@ It should also be noted that a long-run service will be considered started once
 the event 'u' is received; Should the service actually fail right after will
 have no consequences on the rest of the B<aa-start>(1) process.
 
+=head3 Dependencies and Loggers
+
+Loggers (I<*/log> services) are special in that they're not in the
+repodir/scandir directly, and also not listed by B<aa-status>(1) by default (See
+its B<--filter> for more).
+
+However, they're not ignored by anopa, and every longrun service with a logger
+will automatically have a dependency ("needs") on its logger. This means that
+e.g. to start I<foobar>, I<foobar/log> will first be started, as expected.
+
+They can also contain user-defined dependencies, exactly as with any other
+services. It is not, however, possible to add a dependency onto such a logger
+(due to the slash in its name).
+
 =head1 SERVICE REPOSITORY (AND SCANDIR)
 
 B<s6-svscan> will supervise all services found in its scandir. Obviously,
diff --git a/src/anopa/aa-status.c b/src/anopa/aa-status.c
index 403c9cf..b2749ee 100644
--- a/src/anopa/aa-status.c
+++ b/src/anopa/aa-status.c
@@ -36,6 +36,7 @@
 #include <skalibs/djbunix.h>
 #include <skalibs/genalloc.h>
 #include <skalibs/stralloc.h>
+#include <skalibs/skamisc.h>
 #include <skalibs/uint.h>
 #include <skalibs/djbtime.h>
 #include <skalibs/tai.h>
@@ -77,9 +78,16 @@ struct serv
 enum
 {
     FILTER_NONE = 0,
+    /* status */
     FILTER_UP,
     FILTER_DOWN,
-    FILTER_ERROR
+    FILTER_ERROR,
+    /* type */
+    FILTER_ONESHOT,
+    FILTER_LONGRUN,
+    FILTER_LOGGED,
+    FILTER_LOG,
+    FILTER_ALL
 };
 
 enum
@@ -90,7 +98,7 @@ enum
 
 static genalloc ga_serv = GENALLOC_ZERO;
 
-static unsigned int filter_type = AA_TYPE_UNKNOWN;
+static unsigned int filter_type = FILTER_NONE;
 static unsigned int filter_status = FILTER_NONE;
 static unsigned int sort_order = SORT_ASC;
 
@@ -525,14 +533,35 @@ match_status (struct serv *serv, unsigned int filter)
             case AA_EVT_STOP_FAILED:
                 return filter == FILTER_ERROR || filter == FILTER_UP;
 
-            case _AA_NB_EVT: /* silence warning */
+            /* silence warnings */
+            case _AA_NB_EVT:
+            default:
+                current = FILTER_NONE;
                 break;
         }
 
     return current == filter;
 }
 
-static void
+static inline unsigned int
+filter_to_type (unsigned int filter)
+{
+    switch (filter)
+    {
+        case FILTER_ONESHOT:
+            return AA_TYPE_ONESHOT;
+
+        case FILTER_LONGRUN:
+        case FILTER_LOGGED:
+        case FILTER_LOG:
+            return AA_TYPE_LONGRUN;
+
+        default:
+            return AA_TYPE_UNKNOWN;
+    }
+}
+
+static int
 load_service (const char *name, struct config *cfg)
 {
     aa_service *s;
@@ -543,14 +572,14 @@ load_service (const char *name, struct config *cfg)
     if (r < 0)
     {
         aa_put_err (name, errmsg[-r], 1);
-        return;
+        return -1;
     }
 
     r = aa_preload_service (serv.si);
     if (r < 0)
     {
         aa_put_err (name, errmsg[-r], 1);
-        return;
+        return -1;
     }
 
     s = aa_service (serv.si);
@@ -561,11 +590,14 @@ load_service (const char *name, struct config *cfg)
         aa_put_err (name, "Failed to read service status file: ", 0);
         aa_bs_noflush (AA_ERR, error_str (e));
         aa_end_err ();
-        return;
+        return -1;
     }
 
-    if (filter_type != AA_TYPE_UNKNOWN && s->st.type != filter_type)
-        return;
+    if (filter_type != FILTER_NONE && filter_type != FILTER_ALL
+            && s->st.type != filter_to_type (filter_type))
+        return -1;
+    if (filter_type == FILTER_LOG && aa_service_name (s)[strlen (aa_service_name (s)) - 4] != '/')
+        return -1;
 
     if (s->st.type == AA_TYPE_LONGRUN)
     {
@@ -578,7 +610,7 @@ load_service (const char *name, struct config *cfg)
                 aa_put_err (name, "Unable to read s6 status: ", 0);
                 aa_bs_noflush (AA_ERR, error_str (e));
                 aa_end_err ();
-                return;
+                return -1;
             }
         }
         else if (tain_less (&s->st.stamp, &serv.st6.stamp))
@@ -594,7 +626,7 @@ load_service (const char *name, struct config *cfg)
         serv.stamp = s->st.stamp;
 
     if (filter_status != FILTER_NONE && !match_status (&serv, filter_status))
-        return;
+        return -1;
 
     if (cfg->mode == MODE_LIST)
     {
@@ -605,14 +637,44 @@ load_service (const char *name, struct config *cfg)
     }
 
     genalloc_append (struct serv, &ga_serv, &serv);
+    return serv.si;
 }
 
 static int
 it_all (direntry *d, void *data)
 {
+    int si;
+
     if (*d->d_name == '.' || d->d_type != DT_DIR)
         return 0;
-    load_service (d->d_name, data);
+
+    si = load_service (d->d_name, data);
+    if ((filter_type == FILTER_ALL || filter_type == FILTER_LOG
+                || filter_type == FILTER_LOGGED)
+            /* si < 0 could be a error, or just that it was filtered out */
+            && (si < 0 || aa_service (si)->st.type == AA_TYPE_LONGRUN))
+    {
+        int l = satmp.len;
+        int ln = strlen (d->d_name);
+        int r;
+
+        /* is this not a logger already? */
+        if (ln < 5 || d->d_name[ln - 4] != '/')
+        {
+            stralloc_cats (&satmp, d->d_name);
+            stralloc_catb (&satmp, "/log/run", strlen ("/log/run") + 1);
+            r = access (satmp.s + l, F_OK);
+            if (r < 0 && (errno != ENOTDIR && errno != ENOENT))
+                aa_strerr_diefu3sys (ERR_IO, "load service: access(", satmp.s + l, ")");
+            else if (r == 0)
+            {
+                satmp.s[satmp.len - 5] = '\0';
+                load_service (satmp.s + l, data);
+            }
+        }
+        satmp.len = l;
+    }
+
     return 0;
 }
 
@@ -628,10 +690,18 @@ it_listdir (direntry *d, void *data)
 static int
 set_filter (const char *filter)
 {
+    /* type */
     if (str_equal (filter, "os") || str_equal (filter, "oneshot"))
-        filter_type = AA_TYPE_ONESHOT;
+        filter_type = FILTER_ONESHOT;
     else if (str_equal (filter, "lr") || str_equal (filter, "longrun"))
-        filter_type = AA_TYPE_LONGRUN;
+        filter_type = FILTER_LONGRUN;
+    else if (str_equal (filter, "all") || str_equal (filter, "os+lr+log"))
+        filter_type = FILTER_ALL;
+    else if (str_equal (filter, "log"))
+        filter_type = FILTER_LOG;
+    else if (str_equal (filter, "logged") || str_equal (filter, "lr+log"))
+        filter_type = FILTER_LOGGED;
+    /* status */
     else if (str_equal (filter, "up") || str_equal (filter, "start")
             || str_equal (filter, "started"))
         filter_status = FILTER_UP;
@@ -688,7 +758,7 @@ dieusage (int rc)
             " -l, --listdir DIR             Use DIR to list services to get status of\n"
             " -a, --all                     Show status of all services\n"
             " -f, --filter FILTER           Only process services matching FILTER, one of:\n"
-            "   oneshot, longrun, up, down, error   (see aa-status(1) for more)\n"
+            "   oneshot, longrun, log, logged, all, up, down, error   (see aa-status(1) for more)\n"
             " -s, --sort SORT               Sort by SORT, one of: none, name, time (default)\n"
             " -R, --reverse                 Reverse sort order\n"
             " -N, --name                    Sort by name\n"
diff --git a/src/anopa/aa-stop.c b/src/anopa/aa-stop.c
index 5a1a02f..330fef9 100644
--- a/src/anopa/aa-stop.c
+++ b/src/anopa/aa-stop.c
@@ -31,6 +31,7 @@
 #include <skalibs/bytestr.h>
 #include <skalibs/direntry.h>
 #include <skalibs/genalloc.h>
+#include <skalibs/skamisc.h>
 #include <skalibs/error.h>
 #include <skalibs/uint.h>
 #include <skalibs/tai.h>
@@ -80,8 +81,8 @@ preload_service (const char *name)
         r = aa_ensure_service_loaded (si, AA_MODE_STOP, 0, NULL);
     if (r < 0)
     {
-        /* there should be much errors possible here... basically only ERR_IO or
-         * ERR_NOT_UP should be possible, and the later should be silently
+        /* there shouldn't be much errors possible here... basically only ERR_IO
+         * or ERR_NOT_UP should be possible, and the later should be silently
          * ignored... so: */
         if (r == -ERR_IO)
         {
@@ -102,6 +103,32 @@ preload_service (const char *name)
         }
     }
 
+    /* r < 0 can mean different things (e.g. ERR_NOT_UP), including that we
+     * don't have a type set. Hence we only rely on it when possible, but we
+     * need to always check for a logger */
+    if (r < 0 || aa_service (si)->st.type == AA_TYPE_LONGRUN)
+    {
+        int l = satmp.len;
+
+        /* for longruns, even though the dependency of the logger is auto-added,
+         * we still need to ensure the service is loaded */
+        stralloc_cats (&satmp, name);
+        /* is this not a logger already? */
+        if (satmp.len - l < 5 || satmp.s[satmp.len - 4] != '/')
+        {
+            stralloc_catb (&satmp, "/log/run", strlen ("/log/run") + 1);
+            r = access (satmp.s + l, F_OK);
+            if (r < 0 && (errno != ENOTDIR && errno != ENOENT))
+                aa_strerr_diefu3sys (ERR_IO, "preload services: access(", satmp.s + l, ")");
+            else if (r == 0)
+            {
+                satmp.s[satmp.len - 5] = '\0';
+                preload_service (satmp.s + l);
+            }
+        }
+        satmp.len = l;
+    }
+
     return 0;
 }
 
diff --git a/src/include/anopa/enable_service.h b/src/include/anopa/enable_service.h
index 62c5d9a..322c106 100644
--- a/src/include/anopa/enable_service.h
+++ b/src/include/anopa/enable_service.h
@@ -36,7 +36,8 @@ typedef enum
     /* private */
     _AA_FLAG_IS_SERVICEDIR      = (1 << 5),
     _AA_FLAG_IS_CONFIGDIR       = (1 << 6),
-    _AA_FLAG_IS_1OF4            = (1 << 7)
+    _AA_FLAG_IS_1OF4            = (1 << 7),
+    _AA_FLAG_IS_LOGGER          = (1 << 8)
 } aa_enable_flags;
 
 extern stralloc aa_sa_sources;
diff --git a/src/libanopa/enable_service.c b/src/libanopa/enable_service.c
index ff4e5c0..762138a 100644
--- a/src/libanopa/enable_service.c
+++ b/src/libanopa/enable_service.c
@@ -56,9 +56,17 @@ copy_dir (const char        *src,
           aa_auto_enable_cb  ae_cb,
           const char        *instance);
 
+static int
+do_auto_needs_wants (const char *name, aa_enable_flags flags, aa_auto_enable_cb ae_cb);
+
 
 static int
-copy_log (const char *name, const char *cfg, mode_t mode, aa_warn_fn warn_fn)
+copy_log (const char        *name,
+          const char        *cfg,
+          mode_t             mode,
+          aa_warn_fn         warn_fn,
+          aa_enable_flags    flags,
+          aa_auto_enable_cb  ae_cb)
 {
     int fd;
     int r;
@@ -79,13 +87,18 @@ copy_log (const char *name, const char *cfg, mode_t mode, aa_warn_fn warn_fn)
         return r;
     }
 
+    flags |= _AA_FLAG_IS_LOGGER;
+
     /* this is a logger, so there's no autoenable of any kind; hence we can use
      * 0 for flags (don't process it as a servicedir either, since it doesn't
      * apply) and not bother with a callback */
-    r = copy_from_source ("log", 3, warn_fn, 0, NULL);
+    r = copy_from_source ("log", 3, warn_fn, flags | _AA_FLAG_IS_SERVICEDIR, NULL);
 
     if (r >= 0 && cfg)
-        r = copy_dir (cfg, "log", mode, 0, warn_fn, 0, NULL, NULL);
+        r = copy_dir (cfg, "log", mode, 0, warn_fn, flags | _AA_FLAG_IS_CONFIGDIR, NULL, NULL);
+
+    if (r >= 0 && ae_cb && flags & (AA_FLAG_AUTO_ENABLE_NEEDS | AA_FLAG_AUTO_ENABLE_WANTS))
+        r = do_auto_needs_wants ("log", flags, ae_cb);
 
     e = errno;
     fd_chdir (fd);
@@ -251,13 +264,23 @@ copy_dir (const char        *src,
             == (_AA_FLAG_IS_SERVICEDIR | AA_FLAG_UPGRADE_SERVICEDIR))
     {
         if (stat (dst, &st) < 0)
-            goto err;
+        {
+            /* logger might be new */
+            if ((flags & _AA_FLAG_IS_LOGGER) && errno == ENOENT)
+            {
+                if (mkdir (dst, S_IRWXU) < 0)
+                    goto err;
+            }
+            else
+                goto err;
+        }
         else if (!S_ISDIR (st.st_mode))
         {
             errno = ENOTDIR;
             goto err;
         }
-        else if (clear_dir (dst, 1, warn_fn) < 0)
+        /* logger: was already cleared when processing main servicedir */
+        else if (!(flags & _AA_FLAG_IS_LOGGER) && clear_dir (dst, 1, warn_fn) < 0)
             goto err;
     }
     else
@@ -315,9 +338,10 @@ copy_dir (const char        *src,
 
             if (S_ISREG (st.st_mode))
             {
-                if (has.began && depth == 0 && str_equal (satmp.s + i, "log"))
+                if (has.began && depth == 0 && !(flags & _AA_FLAG_IS_LOGGER)
+                        && str_equal (satmp.s + i, "log"))
                 {
-                    r = copy_log (dst, NULL, 0, warn_fn);
+                    r = copy_log (dst, NULL, 0, warn_fn, flags, ae_cb);
                     st.st_mode = 0755;
                 }
                 else if ((flags & _AA_FLAG_IS_CONFIGDIR) && len > 1
@@ -356,8 +380,9 @@ copy_dir (const char        *src,
             }
             else if (S_ISDIR (st.st_mode))
             {
-                if (has.began && depth == 0 && str_equal (satmp.s + i, "log"))
-                    r = copy_log (dst, buf_src, st.st_mode, warn_fn);
+                if (has.began && depth == 0 && !(flags & _AA_FLAG_IS_LOGGER)
+                        && str_equal (satmp.s + i, "log"))
+                    r = copy_log (dst, buf_src, st.st_mode, warn_fn, flags, ae_cb);
                 else
                 {
                     /* use depth because this is also enabled for the config part */
@@ -433,6 +458,7 @@ next:
                     fd_close (fd);
             }
 
+            if (!(flags & _AA_FLAG_IS_LOGGER))
             {
                 char buf_lnk[3 + l_dst + 1];
                 char buf_dst[sizeof (AA_SCANDIR_DIRNAME) + l_dst + 1];
@@ -558,6 +584,60 @@ it_cb (direntry *d, void *_data)
     return 0;
 }
 
+static int
+do_auto_needs_wants (const char *name, aa_enable_flags flags, aa_auto_enable_cb ae_cb)
+{
+    stralloc sa = STRALLOC_ZERO;
+    struct {
+        aa_auto_enable_cb cb;
+        unsigned int flag;
+    } data = { .cb = ae_cb };
+    int l_name = strlen (name);
+    int r = 0;
+
+    if (!stralloc_catb (&sa, name, l_name))
+    {
+        errno = ENOMEM;
+        return -1;
+    }
+
+    if (flags & AA_FLAG_AUTO_ENABLE_NEEDS)
+    {
+        if (!stralloc_cats (&sa, "/needs") || !stralloc_0 (&sa))
+        {
+            stralloc_free (&sa);
+            errno = ENOMEM;
+            return -1;
+        }
+        data.flag = AA_FLAG_AUTO_ENABLE_NEEDS;
+        r = aa_scan_dir (&sa, 1, it_cb, &data);
+        if (r == -ERR_IO && errno == ENOENT)
+            r = 0;
+        sa.len = l_name;
+    }
+
+    if (r == 0 && flags & AA_FLAG_AUTO_ENABLE_WANTS)
+    {
+        if (!stralloc_cats (&sa, "/wants") || !stralloc_0 (&sa))
+        {
+            stralloc_free (&sa);
+            errno = ENOMEM;
+            return -1;
+        }
+        data.flag = AA_FLAG_AUTO_ENABLE_WANTS;
+        r = aa_scan_dir (&sa, 1, it_cb, &data);
+        if (r == -ERR_IO && errno == ENOENT)
+            r = 0;
+    }
+
+    {
+        int e = errno;
+        stralloc_free (&sa);
+        errno = e;
+    }
+    return r;
+}
+
 int
 aa_enable_service (const char       *_name,
                    aa_warn_fn        warn_fn,
@@ -645,50 +725,7 @@ aa_enable_service (const char       *_name,
     }
 
     if (ae_cb && flags & (AA_FLAG_AUTO_ENABLE_NEEDS | AA_FLAG_AUTO_ENABLE_WANTS))
-    {
-        stralloc sa = STRALLOC_ZERO;
-        struct {
-            aa_auto_enable_cb cb;
-            unsigned int flag;
-        } data = { .cb = ae_cb };
-
-        if (!stralloc_catb (&sa, name, l_name))
-        {
-            errno = ENOMEM;
-            return -1;
-        }
-
-        if (flags & AA_FLAG_AUTO_ENABLE_NEEDS)
-        {
-            if (!stralloc_cats (&sa, "/needs") || !stralloc_0 (&sa))
-            {
-                stralloc_free (&sa);
-                errno = ENOMEM;
-                return -1;
-            }
-            data.flag = AA_FLAG_AUTO_ENABLE_NEEDS;
-            r = aa_scan_dir (&sa, 1, it_cb, &data);
-            if (r == -ERR_IO && errno == ENOENT)
-                r = 0;
-            sa.len = l_name;
-        }
-
-        if (r == 0 && flags & AA_FLAG_AUTO_ENABLE_WANTS)
-        {
-            if (!stralloc_cats (&sa, "/wants") || !stralloc_0 (&sa))
-            {
-                stralloc_free (&sa);
-                errno = ENOMEM;
-                return -1;
-            }
-            data.flag = AA_FLAG_AUTO_ENABLE_WANTS;
-            r = aa_scan_dir (&sa, 1, it_cb, &data);
-            if (r == -ERR_IO && errno == ENOENT)
-                r = 0;
-        }
-
-        stralloc_free (&sa);
-    }
+        r = do_auto_needs_wants (name, flags, ae_cb);
 
     return r;
 }
diff --git a/src/libanopa/exec_longrun.c b/src/libanopa/exec_longrun.c
index e426f35..f8f43cf 100644
--- a/src/libanopa/exec_longrun.c
+++ b/src/libanopa/exec_longrun.c
@@ -89,13 +89,13 @@ _exec_longrun (int si, aa_mode mode)
             aa_strerr_warnu2sys ("write service status file for ", aa_service_name (s));
 
         /* we still process it. Because we checked the service state (from s6)
-         * before adding it to the "treansaction" (i.e. in
+         * before adding it to the "transaction" (i.e. in
          * aa_ensure_service_loaded()) and it wasn't there already.
          * IOW this is likely e.g. that it crashed since then, but it isn't
          * really down, or something. So make sure we do send the request to
          * s6-supervise, so it isn't restarted, or indeed brought down if it's
          * happening right now. Also in STOP_ALL this sends the 'x' event to
-         * both supervise (logger & service) as needed as well.
+         * s6-supervise as needed as well.
          */
     }
     else
@@ -117,18 +117,6 @@ _exec_longrun (int si, aa_mode mode)
         }
     }
 
-    if (mode & AA_MODE_STOP_ALL)
-    {
-        char dir[l_sn + 5 + sizeof (S6_SUPERVISE_CTLDIR) + 8];
-
-        byte_copy (dir, l_sn, aa_service_name (s));
-        byte_copy (dir + l_sn, 13 + sizeof (S6_SUPERVISE_CTLDIR), "/log/" S6_SUPERVISE_CTLDIR "/control");
-
-        /* ignore any error, starting with the fact that there might not be a
-         * logger for this service */
-        s6_svc_write (dir, "x", 1);
-    }
-
     {
         char dir[l_sn + 1 + sizeof (S6_SUPERVISE_CTLDIR) + 8];
         int r;
diff --git a/src/libanopa/service.c b/src/libanopa/service.c
index 68b9613..34e9c8a 100644
--- a/src/libanopa/service.c
+++ b/src/libanopa/service.c
@@ -292,6 +292,32 @@ aa_ensure_service_loaded (int si, aa_mode mode, int no_wants, aa_load_fail_cb lf
 
     stralloc_cats (&sa, aa_service_name (aa_service (si)));
 
+    /* special case: for a longrun that's not a logger, we check if it has one,
+     * and if so auto-add needs & after on said logger */
+    if (aa_service (si)->st.type == AA_TYPE_LONGRUN
+            /* because sa.s is the service name, and the only slashes allowed
+             * are for loggers, i.e. xxxx/log */
+            && (sa.len < 5 || sa.s[sa.len - 4] != '/'))
+    {
+        stralloc_catb (&sa, "/log/run", strlen ("/log/run") + 1);
+        r = access (sa.s, F_OK);
+        if (r < 0 && (errno != ENOTDIR && errno != ENOENT))
+            goto err;
+
+        if (r == 0)
+        {
+            sa.s[sa.len - 5] = '\0';
+            if (mode & AA_MODE_START)
+                r = _name_start_needs (sa.s, &it_data);
+            else
+                r = _name_stop_needs (sa.s, &it_data);
+            if (r < 0)
+                goto err;
+        }
+
+        sa.len -= strlen ("/log/run") + 1;
+    }
+
     stralloc_catb (&sa, "/needs", strlen ("/needs") + 1);
     r = aa_scan_dir (&sa, 1,
             (mode & AA_MODE_START) ? _it_start_needs : _it_stop_needs,
diff --git a/src/libanopa/service_internal.h b/src/libanopa/service_internal.h
index e173bb3..a2d5ab6 100644
--- a/src/libanopa/service_internal.h
+++ b/src/libanopa/service_internal.h
@@ -39,11 +39,13 @@ struct it_data
 
 extern int _is_valid_service_name (const char *name, int len);
 
+extern int _name_start_needs (const char *name, struct it_data *it_data);
 extern int _it_start_needs  (direntry *d, void *data);
 extern int _it_start_wants  (direntry *d, void *data);
 extern int _it_start_after  (direntry *d, void *data);
 extern int _it_start_before (direntry *d, void *data);
 
+extern int _name_stop_needs (const char *name, struct it_data *it_data);
 extern int _it_stop_needs   (direntry *d, void *data);
 extern int _it_stop_after   (direntry *d, void *data);
 extern int _it_stop_before  (direntry *d, void *data);
diff --git a/src/libanopa/service_name.c b/src/libanopa/service_name.c
index 32bad28..f81fe95 100644
--- a/src/libanopa/service_name.c
+++ b/src/libanopa/service_name.c
@@ -25,13 +25,16 @@
 int
 _is_valid_service_name (const char *name, int len)
 {
+    int r;
+
     if (len <= 0)
         return 0;
     if (name[0] == '.')
         return 0;
     if (name[0] == '@' || name[len - 1] == '@')
         return 0;
-    if (byte_chr (name, len, '/') < len)
+    r = byte_chr (name, len, '/');
+    if (r < len && !str_equal (name + r, "/log"))
         return 0;
     return 1;
 }
diff --git a/src/libanopa/service_start.c b/src/libanopa/service_start.c
index 4bac929..7a558d9 100644
--- a/src/libanopa/service_start.c
+++ b/src/libanopa/service_start.c
@@ -73,15 +73,14 @@ aa_mark_service (aa_mode mode, int si, int in_main, int no_wants, aa_load_fail_c
 }
 
 int
-_it_start_needs (direntry *d, void *data)
+_name_start_needs (const char *name, struct it_data *it_data)
 {
-    struct it_data *it_data = data;
     int type;
     int sni;
     int r;
 
     tain_now_g ();
-    type = aa_get_service (d->d_name, &sni, 1);
+    type = aa_get_service (name, &sni, 1);
     if (type < 0)
         r = type;
     else
@@ -100,7 +99,6 @@ _it_start_needs (direntry *d, void *data)
 
         if (!(it_data->mode & AA_MODE_IS_DRY))
         {
-            const char *name = d->d_name;
             int l_n = strlen (name);
             int l_em = strlen (errmsg[-r]);
             char buf[l_n + 2 + l_em + 1];
@@ -115,7 +113,7 @@ _it_start_needs (direntry *d, void *data)
         }
 
         if (it_data->lf_cb)
-            it_data->lf_cb (it_data->si, AA_LOADFAIL_NEEDS, d->d_name, -r);
+            it_data->lf_cb (it_data->si, AA_LOADFAIL_NEEDS, name, -r);
 
         return -ERR_DEPEND;
     }
@@ -125,6 +123,12 @@ _it_start_needs (direntry *d, void *data)
     return 0;
 }
 
+int
+_it_start_needs (direntry *d, void *data)
+{
+    return _name_start_needs (d->d_name, (struct it_data *) data);
+}
+
 int
 _it_start_wants (direntry *d, void *data)
 {
diff --git a/src/libanopa/service_stop.c b/src/libanopa/service_stop.c
index 6564144..84e9f52 100644
--- a/src/libanopa/service_stop.c
+++ b/src/libanopa/service_stop.c
@@ -26,14 +26,13 @@
 #include "service_internal.h"
 
 int
-_it_stop_needs (direntry *d, void *data)
+_name_stop_needs (const char *name, struct it_data *it_data)
 {
-    struct it_data *it_data = data;
     int sni;
     int r;
 
     tain_now_g ();
-    r = aa_get_service (d->d_name, &sni, 0);
+    r = aa_get_service (name, &sni, 0);
     if (r < 0)
         return 0;
 
@@ -42,6 +41,12 @@ _it_stop_needs (direntry *d, void *data)
     return 0;
 }
 
+int
+_it_stop_needs (direntry *d, void *data)
+{
+    return _name_stop_needs (d->d_name, (struct it_data *) data);
+}
+
 int
 _it_stop_after (direntry *d, void *data)
 {