Welcome to little lamb

Code » anopa » commit 7dd16fe

enable: Add --upgrade to upgrade servicedirs

author Olivier Brunel
2015-07-24 17:23:21 UTC
committer Olivier Brunel
2015-08-07 17:23:26 UTC
parent a5844b628f5c2e69ac6568c7abd7618c0600526b

enable: Add --upgrade to upgrade servicedirs

repodir & servicedirs must exists; Then we empty the servicedir (save for
status.anopa, down, supervise/ and event/) and do as usual.
We also don't create file "down" or symlink in scandir for longruns.

For auto-added services (from needs/wants) we only check they exists (i.e. are
enabled): if so we do nothing, else we do as usual/as if w/out --upgrade

Also --set-{crash,finish} are ignored when --upgrade is used.

doc/aa-enable.pod +32 -1
src/anopa/aa-enable.c +52 -17
src/include/anopa/enable_service.h +4 -3
src/libanopa/enable_service.c +112 -7

diff --git a/doc/aa-enable.pod b/doc/aa-enable.pod
index da09956..c32bbb0 100644
--- a/doc/aa-enable.pod
+++ b/doc/aa-enable.pod
@@ -6,7 +6,7 @@ aa-enable - Enable services, i.e. copy servicedirs to repodir
 
 B<aa-enable> [B<-D>] [B<-r> I<REPODIR>] [B<-c> I<CRASH>] [B<-f> I<FINISH>]
 [B<-k> I<SERVICE>] [B<-S> I<SOURCEDIR>] [B<-s> I<SOURCEDIR>] [B<-l> I<LISTDIR>]
-[B<-N>] [B<-W>] [I<SERVICE...>]
+[B<-N>] [B<-W>] [B<-u>] [I<SERVICE...>]
 
 =head1 OPTIONS
 
@@ -73,6 +73,10 @@ B<--source>. This is useful to unset the defaults.
 Add I<SOURCEDIR> as new source. Can be specified multiple times; Source
 directories will be processed in the order they were added.
 
+=item B<-u, --upgrade>
+
+Upgrade servicedirs instead of creating them. See below for implications.
+
 =item B<-V, --version>
 
 Show version information and exit.
@@ -198,3 +202,30 @@ destination servicedir, if it exists.
 
 - A file I<+foobar> will have its content be appended to I<foobar> in the
 destination servicedir.
+
+=head2 Upgrading servicedirs
+
+You can upgrade servicedirs using the B<--upgrade> option. This is meant to
+apply changes (from source directories and/or configuration) into existing
+servicedirs.
+
+When used, B<aa-enable>(1) will behave as usual with the following changes:
+
+- The repodir (and the contained scandir) must already exist;
+
+- Each servicedir must already exist as well. Before copying anything, all its
+  content will be recursively removed except for regular file I<status.anopa>,
+  regular file I<down>, folder I<supervise> and folder I<event> (and their
+  content);
+
+- For longruns, no I<down> file will be created, nor will a symlink be added
+  into the scandir;
+
+- If used, options B<--set-finish> and B<--set-crash> are ignored.
+
+Services auto-added from I<needs> or I<wants> will also be processed differently
+that those specified:
+
+- If servicedir already exists in repodir, nothing is done
+
+- Else, they are enabled as usual (i.e. as if without the B<--upgrade> option)
diff --git a/src/anopa/aa-enable.c b/src/anopa/aa-enable.c
index bcd9939..88d4080 100644
--- a/src/anopa/aa-enable.c
+++ b/src/anopa/aa-enable.c
@@ -103,16 +103,17 @@ enable_service (const char *name, intptr_t from_next)
 
     if (!from_next)
     {
-        /* skip what's already planned to be done next (added via auto-enable) */
+        /* check if it was already added to be done next (via auto-enable), in
+         * which case we need to remove it.
+         * We do this instead of simply skipping it and having it done later
+         * because:
+         * - if there's a folder here, we want to use it as config folder
+         * - in upgrade mode, the auto-added are treated differently, so
+         *   anything specified needs to be treated now (even w/out folder)
+         */
         for (i = 0; i < genalloc_len (int, &ga_next); ++i)
             if (str_equal (cur_name, names.s + list_get (&ga_next, i)))
             {
-                /* just a name, we can skip it (and process it later). Saves the
-                 * memmove needed to remove from ga_next */
-                if (cur_name == name)
-                    goto done;
-                /* there's a config folder, so we need to process it right now
-                 * (and remove it from ga_next) */
                 offset = list_get (&ga_next, i);
                 ga_remove (int, &ga_next, i);
                 goto process;
@@ -147,7 +148,6 @@ process:
     }
 
     ++nb_enabled;
-done:
     cur_name = NULL;
     return 0;
 }
@@ -182,6 +182,7 @@ dieusage (int rc)
             " -S, --reset-source DIR        Reset list of source directories to DIR\n"
             " -s, --source DIR              Add DIR as source directories\n"
             " -k, --skip-down SERVICE       Don't create file 'down' for SERVICE\n"
+            " -u, --upgrade                 Upgrade service dirs instead of creating them\n"
             " -l, --listdir DIR             Use DIR to list services to enable\n"
             " -f, --set-finish TARGET       Create s6-svscan symlink finish to TARGET\n"
             " -c, --set-crash TARGET        Create s6-svscan symlink crash to TARGET\n"
@@ -222,13 +223,14 @@ main (int argc, char * const argv[])
             { "repodir",            required_argument,  NULL,   'r' },
             { "reset-source",       required_argument,  NULL,   'S' },
             { "source",             required_argument,  NULL,   's' },
+            { "upgrade",            no_argument,        NULL,   'u' },
             { "version",            no_argument,        NULL,   'V' },
             { "no-wants",           no_argument,        NULL,   'W' },
             { NULL, 0, 0, 0 }
         };
         int c;
 
-        c = getopt_long (argc, argv, "c:Df:hk:l:Nr:S:s:VW", longopts, NULL);
+        c = getopt_long (argc, argv, "c:Df:hk:l:Nr:S:s:uVW", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -276,6 +278,10 @@ main (int argc, char * const argv[])
                     strerr_diefu1sys (1, "stralloc_catb");
                 break;
 
+            case 'u':
+                flags |= AA_FLAG_UPGRADE_SERVICEDIR;
+                break;
+
             case 'V':
                 aa_die_version ();
 
@@ -295,7 +301,8 @@ main (int argc, char * const argv[])
     if (!path_list && argc < 1)
         dieusage (1);
 
-    r = aa_init_repo (path_repo, AA_REPO_CREATE);
+    r = aa_init_repo (path_repo,
+            (flags & AA_FLAG_UPGRADE_SERVICEDIR) ? AA_REPO_WRITE : AA_REPO_CREATE);
     if (r < 0)
     {
         if (r == -ERR_IO_REPODIR)
@@ -337,7 +344,32 @@ main (int argc, char * const argv[])
         i = genalloc_len (int, &ga_next) - 1;
         offset = list_get (&ga_next, i);
         genalloc_setlen (int, &ga_next, i);
-        enable_service (names.s + offset, 1 + offset);
+        if (!(flags & AA_FLAG_UPGRADE_SERVICEDIR))
+            enable_service (names.s + offset, 1 + offset);
+        else
+        {
+            /* upgrade mode: check if it already exists or not. If so, we do
+             * nothing. If not however, we do the "standard" enabling */
+
+            if (access (names.s + offset, F_OK) < 0)
+            {
+                if (errno != ENOENT)
+                {
+                    int e = errno;
+
+                    aa_put_err (names.s + offset, errmsg[ERR_IO], 1);
+                    aa_bs_noflush (AA_ERR, ": " "unable to check for existing servicedir" ": ");
+                    aa_bs_noflush (AA_ERR, error_str (e));
+                    aa_end_err ();
+                }
+                else
+                {
+                    flags &= ~AA_FLAG_UPGRADE_SERVICEDIR;
+                    enable_service (names.s + offset, 1 + offset);
+                    flags |= AA_FLAG_UPGRADE_SERVICEDIR;
+                }
+            }
+        }
     }
 
     aa_bs_noflush (AA_OUT, "\n");
@@ -345,12 +377,15 @@ main (int argc, char * const argv[])
     aa_show_stat_nb (nb_enabled, "Enabled", ANSI_HIGHLIGHT_GREEN_ON);
     aa_show_stat_names (names.s, &ga_failed, "Failed", ANSI_HIGHLIGHT_RED_ON);
 
-    if ((set_crash || set_finish) && mkdir (SVSCANDIR, S_IRWXU) < 0)
-        aa_put_err ("Failed to create " SVSCANDIR, error_str (errno), 1);
-    if (set_crash && symlink (set_crash, SCANDIR_CRASH) < 0)
-        aa_put_err ("Failed to create symlink " SCANDIR_CRASH, error_str (errno), 1);
-    if (set_finish && symlink (set_finish, SCANDIR_FINISH) < 0)
-        aa_put_err ("Failed to create symlink " SCANDIR_FINISH, error_str (errno), 1);
+    if (!(flags & AA_FLAG_UPGRADE_SERVICEDIR))
+    {
+        if ((set_crash || set_finish) && mkdir (SVSCANDIR, S_IRWXU) < 0)
+            aa_put_err ("Failed to create " SVSCANDIR, error_str (errno), 1);
+        if (set_crash && symlink (set_crash, SCANDIR_CRASH) < 0)
+            aa_put_err ("Failed to create symlink " SCANDIR_CRASH, error_str (errno), 1);
+        if (set_finish && symlink (set_finish, SCANDIR_FINISH) < 0)
+            aa_put_err ("Failed to create symlink " SCANDIR_FINISH, error_str (errno), 1);
+    }
 
     genalloc_free (int, &ga_failed);
     genalloc_free (int, &ga_next);
diff --git a/src/include/anopa/enable_service.h b/src/include/anopa/enable_service.h
index abd9fc8..b994cf4 100644
--- a/src/include/anopa/enable_service.h
+++ b/src/include/anopa/enable_service.h
@@ -31,10 +31,11 @@ typedef enum
     AA_FLAG_AUTO_ENABLE_NEEDS   = (1 << 0),
     AA_FLAG_AUTO_ENABLE_WANTS   = (1 << 1),
     AA_FLAG_SKIP_DOWN           = (1 << 2),
+    AA_FLAG_UPGRADE_SERVICEDIR  = (1 << 3),
     /* private */
-    _AA_FLAG_IS_SERVICEDIR      = (1 << 3),
-    _AA_FLAG_IS_CONFIGDIR       = (1 << 4),
-    _AA_FLAG_IS_1OF4            = (1 << 5)
+    _AA_FLAG_IS_SERVICEDIR      = (1 << 4),
+    _AA_FLAG_IS_CONFIGDIR       = (1 << 5),
+    _AA_FLAG_IS_1OF4            = (1 << 6)
 } aa_enable_flags;
 
 extern stralloc aa_sa_sources;
diff --git a/src/libanopa/enable_service.c b/src/libanopa/enable_service.c
index 6f2b298..12e50f7 100644
--- a/src/libanopa/enable_service.c
+++ b/src/libanopa/enable_service.c
@@ -20,6 +20,8 @@
  * anopa. If not, see http://www.gnu.org/licenses/
  */
 
+#define _BSD_SOURCE
+
 #include <errno.h>
 #include <unistd.h>
 #include <sys/stat.h>
@@ -104,6 +106,91 @@ copy_log (const char *name, const char *cfg, mode_t mode, aa_warn_fn warn_fn)
     return r;
 }
 
+static int
+clear_dir (const char *path, int excludes, aa_warn_fn warn_fn)
+{
+    DIR *dir;
+    int salen = satmp.len;
+
+    dir = opendir (path);
+    if (!dir)
+        return -ERR_IO;
+    errno = 0;
+    for (;;)
+    {
+        direntry *d;
+        int r = 0;
+
+        d = readdir (dir);
+        if (!d)
+            break;
+        if (d->d_name[0] == '.'
+                && (d->d_name[1] == '\0' || (d->d_name[1] == '.' && d->d_name[2] == '\0')))
+            continue;
+
+        if (stralloc_cats (&satmp, path) < 0)
+            goto err;
+        if (stralloc_catb (&satmp, "/", 1) < 0)
+            goto err;
+        if (stralloc_cats (&satmp, d->d_name) < 0)
+            goto err;
+        if (!stralloc_0 (&satmp))
+            goto err;
+
+        if (d->d_type == DT_UNKNOWN)
+        {
+            struct stat st;
+
+            r = stat (satmp.s + salen, &st);
+            if (r < 0)
+                goto err;
+
+            if (S_ISREG (st.st_mode))
+                d->d_type = DT_REG;
+            else if (S_ISDIR (st.st_mode))
+                d->d_type = DT_DIR;
+        }
+
+        if (excludes)
+        {
+            if (d->d_type == DT_REG
+                    && (str_equal (d->d_name, "status.anopa")
+                        || str_equal (d->d_name, "down")))
+                goto skip;
+            else if (d->d_type == DT_DIR
+                    && (str_equal (d->d_name, "supervise")
+                        || str_equal (d->d_name, "event")))
+                goto skip;
+        }
+
+        if (d->d_type == DT_DIR)
+        {
+            r = clear_dir (satmp.s + salen, 0, warn_fn);
+            if (r == 0)
+                r = rmdir (satmp.s + salen);
+        }
+        else
+            r = unlink (satmp.s + salen);
+err:
+        if (r < 0)
+            warn_fn (satmp.s + salen, errno);
+skip:
+        satmp.len = salen;
+        if (r < 0)
+            break;
+    }
+    if (errno)
+    {
+        int e = errno;
+        dir_close (dir);
+        errno = e;
+        return -ERR_IO;
+    }
+    dir_close (dir);
+
+    return 0;
+}
+
 static int
 copy_dir (const char        *src,
           const char        *dst,
@@ -150,7 +237,9 @@ copy_dir (const char        *src,
             l_max = len;
         if (!stralloc_catb (&satmp, d->d_name, len + 1))
             break;
-        if (depth == 0 && (flags & _AA_FLAG_IS_SERVICEDIR))
+        if (depth == 0 && (flags & _AA_FLAG_IS_SERVICEDIR)
+                /* if UPGRADE we don't need this, so skip those tests */
+                && !(flags & AA_FLAG_UPGRADE_SERVICEDIR))
         {
             if (!has.run && str_equal (d->d_name, "run"))
                 has.run = 1;
@@ -167,19 +256,35 @@ copy_dir (const char        *src,
     }
     dir_close (dir);
 
-    if (mkdir (dst, S_IRWXU) < 0)
+    if ((flags & (_AA_FLAG_IS_SERVICEDIR | AA_FLAG_UPGRADE_SERVICEDIR))
+            == (_AA_FLAG_IS_SERVICEDIR | AA_FLAG_UPGRADE_SERVICEDIR))
     {
-        if (errno != EEXIST || stat (dst, &st) < 0)
+        if (stat (dst, &st) < 0)
             goto err;
         else if (!S_ISDIR (st.st_mode))
         {
             errno = ENOTDIR;
             goto err;
         }
-        else if (flags & _AA_FLAG_IS_SERVICEDIR)
-        {
-            errno = EEXIST;
+        else if (clear_dir (dst, 1, warn_fn) < 0)
             goto err;
+    }
+    else
+    {
+        if (mkdir (dst, S_IRWXU) < 0)
+        {
+            if (errno != EEXIST || stat (dst, &st) < 0)
+                goto err;
+            else if (!S_ISDIR (st.st_mode))
+            {
+                errno = ENOTDIR;
+                goto err;
+            }
+            else if (flags & _AA_FLAG_IS_SERVICEDIR)
+            {
+                errno = EEXIST;
+                goto err;
+            }
         }
     }
 
@@ -316,7 +421,7 @@ next:
             i += len + 1;
         }
 
-        if (has.run)
+        if (has.run && !(flags & AA_FLAG_UPGRADE_SERVICEDIR))
         {
             if (!has.down)
             {