Welcome to little lamb

Code » anopa » commit f04dbbf

status: Add --sort (also --name & --reverse)

author Olivier Brunel
2015-10-16 13:15:58 UTC
committer Olivier Brunel
2015-10-16 18:17:11 UTC
parent 1ec37b3c854aef6d09a4d42211c93582b83c76a4

status: Add --sort (also --name & --reverse)

And services are now sorted by time by default.

doc/aa-status.pod +22 -1
src/anopa/aa-status.c +72 -1

diff --git a/doc/aa-status.pod b/doc/aa-status.pod
index bcacff5..457463f 100644
--- a/doc/aa-status.pod
+++ b/doc/aa-status.pod
@@ -5,7 +5,7 @@ aa-status - Show status of services
 =head1 SYNOPSIS
 
 B<aa-status> [B<-D>] [B<-r> I<repodir>] [B<-a>] [B<-f> I<filter>] [B<-L>]
-[B<-n>] [B<-l> I<listdir>] [I<service...>]
+[B<-n>] [B<-l> I<listdir>] [B<-s> I<sort>] [B<-R>] [B<-N>] [I<service...>]
 
 =head1 OPTIONS
 
@@ -45,15 +45,36 @@ I</etc/anopa/listdirs/>
 Show statuses as a list, with one service per line, elipsizing service name
 and/or status if needed.
 
+=item B<-N, --name>
+
+Sort services by name. Default is by time, see B<--sort> for more.
+
 =item B<-n, --dry-list>
 
 Only show service names, one per line.
 
+=item B<-R, --reverse>
+
+Reverse sort order.
+
 =item B<-r, --repodir> I<dir>
 
 Use I<dir> as repository directory. This is where servicedirs will be looked
 for.
 
+=item B<-s, --sort> I<sort>
+
+Sort services according to I<sort>, which can be one of:
+
+- I<name> : to sort by names. Can use B<--name> as an alias.
+
+- I<time> : to sort by times, i.e. the timestamp of the last event for the
+  service; This can be e.g. when it was started, failed to, or got ready. This
+  is the default sort order.
+
+- I<none> : do not sort services. Order will be the one in which they were
+  processed.
+
 =item B<-V, --version>
 
 Show version information and exit.
diff --git a/src/anopa/aa-status.c b/src/anopa/aa-status.c
index 74cb001..403c9cf 100644
--- a/src/anopa/aa-status.c
+++ b/src/anopa/aa-status.c
@@ -29,6 +29,7 @@
 #include <errno.h>
 #include <getopt.h>
 #include <unistd.h>
+#include <stdlib.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
 #include <skalibs/bytestr.h>
@@ -37,6 +38,7 @@
 #include <skalibs/stralloc.h>
 #include <skalibs/uint.h>
 #include <skalibs/djbtime.h>
+#include <skalibs/tai.h>
 #include <skalibs/sig.h>
 #include <skalibs/error.h>
 #include <s6/s6-supervise.h>
@@ -80,10 +82,17 @@ enum
     FILTER_ERROR
 };
 
+enum
+{
+    SORT_ASC,
+    SORT_DESC
+};
+
 static genalloc ga_serv = GENALLOC_ZERO;
 
 static unsigned int filter_type = AA_TYPE_UNKNOWN;
 static unsigned int filter_status = FILTER_NONE;
+static unsigned int sort_order = SORT_ASC;
 
 static int put_s_max (const char *s, int max, int pad);
 
@@ -641,6 +650,35 @@ set_filter (const char *filter)
     return 1;
 }
 
+static int
+cmp_serv_name (const void *_serv1, const void *_serv2)
+{
+    const struct serv *serv1 = _serv1;
+    const struct serv *serv2 = _serv2;
+    int r;
+
+    r = strcmp (aa_service_name (aa_service (serv1->si)),
+            aa_service_name (aa_service (serv2->si)));
+    return (sort_order == SORT_ASC) ? r : -r;
+}
+
+static int
+cmp_serv_stamp (const void *_serv1, const void *_serv2)
+{
+    const struct serv *serv1 = _serv1;
+    const struct serv *serv2 = _serv2;
+    int r;
+
+    if (!serv1->is_s6 && aa_service (serv1->si)->st.event == AA_EVT_NONE)
+        r = (!serv2->is_s6 && aa_service (serv2->si)->st.event == AA_EVT_NONE) ? 0 : -1;
+    else if (!serv2->is_s6 && aa_service (serv2->si)->st.event == AA_EVT_NONE)
+        r = 1;
+    else
+        r = (tain_less (&serv1->stamp, &serv2->stamp) != 0) ? -1 : 1;
+
+    return (sort_order == SORT_ASC) ? r : -r;
+}
+
 static void
 dieusage (int rc)
 {
@@ -651,6 +689,9 @@ dieusage (int rc)
             " -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"
+            " -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"
             " -L, --list                    Show statuses as one-liners list\n"
             " -n, --dry-list                Only show service names\n"
             " -h, --help                    Show this help screen and exit\n"
@@ -665,6 +706,7 @@ main (int argc, char * const argv[])
     const char *path_repo = "/run/services";
     const char *path_list = NULL;
     struct config cfg = { 0, };
+    int (*sort_fn) (const void *, const void *) = cmp_serv_stamp;
     int all = 0;
     int i;
     int r;
@@ -678,14 +720,17 @@ main (int argc, char * const argv[])
             { "help",               no_argument,        NULL,   'h' },
             { "listdir",            required_argument,  NULL,   'l' },
             { "list",               no_argument,        NULL,   'L' },
+            { "name",               no_argument,        NULL,   'N' },
             { "dry-list",           no_argument,        NULL,   'n' },
+            { "reverse",            no_argument,        NULL,   'R' },
             { "repodir",            required_argument,  NULL,   'r' },
+            { "sort",               required_argument,  NULL,   's' },
             { "version",            no_argument,        NULL,   'V' },
             { NULL, 0, 0, 0 }
         };
         int c;
 
-        c = getopt_long (argc, argv, "aDf:hl:Lnr:V", longopts, NULL);
+        c = getopt_long (argc, argv, "aDf:hl:LNnRr:s:V", longopts, NULL);
         if (c == -1)
             break;
         switch (c)
@@ -715,15 +760,37 @@ main (int argc, char * const argv[])
                 cfg.mode = MODE_LIST;
                 break;
 
+            case 'N':
+                sort_fn = cmp_serv_name;
+                break;
+
             case 'n':
                 cfg.mode = MODE_DRY_LIST;
                 break;
 
+            case 'R':
+                sort_order = SORT_DESC;
+                break;
+
             case 'r':
                 unslash (optarg);
                 path_repo = optarg;
                 break;
 
+            case 's':
+                if (str_equal (optarg, "none"))
+                    sort_fn = NULL;
+                else if (str_equal (optarg, "name"))
+                    sort_fn = cmp_serv_name;
+                else if (str_equal (optarg, "time"))
+                    sort_fn = cmp_serv_stamp;
+                else
+                {
+                    errno = EINVAL;
+                    aa_strerr_diefu3sys (1, "set sort order '", optarg, "'");
+                }
+                break;
+
             case 'V':
                 aa_die_version ();
 
@@ -790,6 +857,10 @@ main (int argc, char * const argv[])
             else
                 load_service (argv[i], &cfg);
 
+    if (sort_fn)
+        qsort (genalloc_s(struct serv, &ga_serv), genalloc_len (struct serv, &ga_serv),
+                sizeof (struct serv), sort_fn);
+
     for (i = 0; i < genalloc_len (struct serv, &ga_serv); ++i)
         status_service (&genalloc_s (struct serv, &ga_serv)[i], &cfg);