Welcome to little lamb

Code » anopa » commit 393c0e1

status: Add --filter

author Olivier Brunel
2015-07-16 17:07:06 UTC
committer Olivier Brunel
2015-07-16 17:07:06 UTC
parent 4870232a142a50bf32933a6348733f524cda5470

status: Add --filter

doc/aa-status.pod +81 -4
src/anopa/aa-status.c +88 -1

diff --git a/doc/aa-status.pod b/doc/aa-status.pod
index 6a86329..3e3dd79 100644
--- a/doc/aa-status.pod
+++ b/doc/aa-status.pod
@@ -4,8 +4,8 @@ aa-status - Show status of services
 
 =head1 SYNOPSIS
 
-B<aa-status> [B<-D>] [B<-r> I<repodir>] [B<-a>] [B<-L>] [B<-l> I<listdir>]
-[I<service...>]
+B<aa-status> [B<-D>] [B<-r> I<repodir>] [B<-a>] [B<-f> I<filter>] [B<-L>]
+[B<-l> I<listdir>] [I<service...>]
 
 =head1 OPTIONS
 
@@ -23,6 +23,11 @@ stderr for warnings and errors, everything is sent both to stdout and stderr.
 This is intended to redirect stderr to a log file, so full output can be both
 shown on console and logged.
 
+=item B<-f, --filter> I<filter>
+
+Only process services matching I<filter>. See L<B<FILTERING>|/FILTERING> below
+for more.
+
 =item B<-h, --help>
 
 Show help screen and exit.
@@ -62,7 +67,76 @@ For long-run services it will also check for the I<ready> file to determine
 whether the service is "Up" or "Ready" (using the timestamp from the I<ready>
 file in the later case).
 
-=head1 POSSIBLE STATUS
+=head1 FILTERING
+
+You can use B<--filter> to filter which services to process amongst those
+provided (meaning to filter all enabled services you need to combine it with
+B<--all>).
+
+There are two kinds of filters available, by type and by status.
+
+=head2 Filter by type
+
+=over
+
+=item I<oneshot>; I<os>
+
+To only process oneshot services.
+
+=item I<longrun>; I<lr>
+
+To only process longrun services.
+
+=back
+
+=head2 Filter by status
+
+=over
+
+=item I<start>; I<started>; I<up>
+
+To only process services that are up/started. Note that this includes services
+that might be in error state, e.g. that failed to be stopped.
+
+=item I<stop>; I<stopped>; I<down>
+
+To only process services that are down/stopped. Note that this includes services
+that might be in error state, e.g. that failed to be started.
+
+=item I<fail>; I<failed>; I<error>
+
+To only process services that are in error state.
+
+=back
+
+As noted below, a given service can match more than one filter, e.g. a service
+that failed to be started will show up both under I<stopped> and I<failed>
+
+You can specify more than one B<--filter> option, though it only make sense to
+combine filters of different kinds. If more than one filter of the same kind is
+given, only the last one is used.
+
+=head2 Examples
+
+To list all oneshot services in error state:
+
+    aa-status --list --all --filter oneshot --filter error
+
+The same could be done using e.g:
+
+    aa-status -Laf fail -f os
+
+To show statues of all longrun process currently up:
+
+    aa-status -a -f longrun -f started
+
+This will list all oneshot services (only the last filter is used since they're
+both of the same kind) :
+
+    aa-status -Laf longrun --filter os
+
+
+=head1 POSSIBLE STATUSES
 
 The different possible statuses are :
 
@@ -70,7 +144,7 @@ The different possible statuses are :
 
 =item B<Unknown status>
 
-E.g. if no status file exist.
+E.g. if no status file exist. Such services will be filtered as I<stopped>.
 
 =item B<Error>
 
@@ -97,6 +171,9 @@ which value was returned, or which signal killed it.
 The script I<start>/I<stop> is currently running; or the command has been sent
 to the B<s6-supervise> of the service.
 
+Such services will be filtered according to their current state, e.g. a service
+that is starting will be matched via I<stopped>.
+
 =item B<Started> / B<Stopped>
 
 (I<one-shot services only.>)
diff --git a/src/anopa/aa-status.c b/src/anopa/aa-status.c
index 23ef15c..bce9bc8 100644
--- a/src/anopa/aa-status.c
+++ b/src/anopa/aa-status.c
@@ -66,8 +66,19 @@ struct serv
     tain_t stamp;
 };
 
+enum
+{
+    FILTER_NONE = 0,
+    FILTER_UP,
+    FILTER_DOWN,
+    FILTER_ERROR
+};
+
 static genalloc ga_serv = GENALLOC_ZERO;
 
+static unsigned int filter_type = AA_TYPE_UNKNOWN;
+static unsigned int filter_status = FILTER_NONE;
+
 static int put_s_max (const char *s, int max, int pad);
 
 static void
@@ -443,6 +454,43 @@ status_service (struct serv *serv, struct config *cfg)
         first = 0;
 }
 
+static int
+match_status (struct serv *serv, unsigned int filter)
+{
+    unsigned int current;
+
+    if (serv->is_s6)
+        current = (serv->st6.pid && !serv->st6.flagfinishing) ? FILTER_UP : FILTER_DOWN;
+    else
+        switch (aa_service (serv->si)->st.event)
+        {
+            case AA_EVT_STARTED:
+            case AA_EVT_STOPPING:
+                current = FILTER_UP;
+                break;
+
+            case AA_EVT_NONE:
+            case AA_EVT_STOPPED:
+            case AA_EVT_STARTING:
+                current = FILTER_DOWN;
+                break;
+
+            case AA_EVT_ERROR: /* == ERR_DEPEND */
+            case AA_EVT_STARTING_FAILED:
+            case AA_EVT_START_FAILED:
+                return filter == FILTER_ERROR || filter == FILTER_DOWN;
+
+            case AA_EVT_STOPPING_FAILED:
+            case AA_EVT_STOP_FAILED:
+                return filter == FILTER_ERROR || filter == FILTER_UP;
+
+            case _AA_NB_EVT: /* silence warning */
+                break;
+        }
+
+    return current == filter;
+}
+
 static void
 load_service (const char *name, struct config *cfg)
 {
@@ -475,6 +523,9 @@ load_service (const char *name, struct config *cfg)
         return;
     }
 
+    if (filter_type != AA_TYPE_UNKNOWN && s->st.type != filter_type)
+        return;
+
     if (s->st.type == AA_TYPE_LONGRUN)
     {
         if (!s6_svstatus_read (name, &serv.st6))
@@ -543,6 +594,9 @@ load_service (const char *name, struct config *cfg)
         }
     }
 
+    if (filter_status != FILTER_NONE && !match_status (&serv, filter_status))
+        return;
+
     if (cfg->mode_list)
     {
         int l = strlen (name);
@@ -572,6 +626,31 @@ it_listdir (direntry *d, void *data)
     return 0;
 }
 
+static int
+set_filter (const char *filter)
+{
+    if (str_equal (filter, "os") || str_equal (filter, "oneshot"))
+        filter_type = AA_TYPE_ONESHOT;
+    else if (str_equal (filter, "lr") || str_equal (filter, "longrun"))
+        filter_type = AA_TYPE_LONGRUN;
+    else if (str_equal (filter, "up") || str_equal (filter, "start")
+            || str_equal (filter, "started"))
+        filter_status = FILTER_UP;
+    else if (str_equal (filter, "down") || str_equal (filter, "stop")
+            || str_equal (filter, "stopped"))
+        filter_status = FILTER_DOWN;
+    else if (str_equal (filter, "error") || str_equal (filter, "fail")
+            || str_equal (filter, "failed"))
+        filter_status = FILTER_ERROR;
+    else
+    {
+        errno = EINVAL;
+        return 0;
+    }
+
+    return 1;
+}
+
 static void
 dieusage (int rc)
 {
@@ -580,6 +659,8 @@ dieusage (int rc)
             " -r, --repodir DIR             Use DIR as repository directory\n"
             " -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"
             " -L, --list                    Show statuses as one-liners list\n"
             " -h, --help                    Show this help screen and exit\n"
             " -V, --version                 Show version information and exit\n"
@@ -603,6 +684,7 @@ main (int argc, char * const argv[])
         struct option longopts[] = {
             { "all",                no_argument,        NULL,   'a' },
             { "double-output",      no_argument,        NULL,   'D' },
+            { "filter",             required_argument,  NULL,   'f' },
             { "help",               no_argument,        NULL,   'h' },
             { "listdir",            required_argument,  NULL,   'l' },
             { "list",               no_argument,        NULL,   'L' },
@@ -612,7 +694,7 @@ main (int argc, char * const argv[])
         };
         int c;
 
-        c = getopt_long (argc, argv, "aDhl:Lr:V", longopts, NULL);
+        c = getopt_long (argc, argv, "aDf:hl:Lr:V", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -625,6 +707,11 @@ main (int argc, char * const argv[])
                 mode_both = 1;
                 break;
 
+            case 'f':
+                if (!set_filter (optarg))
+                    strerr_diefu3sys (1, "set filter '", optarg, "'");
+                break;
+
             case 'h':
                 dieusage (0);