s6

Mirror/fork of https://skarnet.org/software/s6/
git clone https://ccx.te2000.cz/git/s6
Log | Files | Refs | README | LICENSE

s6-svstat.c (8564B)


      1 /* ISC license. */
      2 
      3 #include <sys/wait.h>
      4 #include <string.h>
      5 #include <unistd.h>
      6 #include <errno.h>
      7 #include <skalibs/uint64.h>
      8 #include <skalibs/types.h>
      9 #include <skalibs/bytestr.h>
     10 #include <skalibs/buffer.h>
     11 #include <skalibs/strerr.h>
     12 #include <skalibs/sgetopt.h>
     13 #include <skalibs/sig.h>
     14 #include <skalibs/tai.h>
     15 #include <skalibs/djbunix.h>
     16 #include <s6/supervise.h>
     17 
     18 #define USAGE "s6-svstat [ -uwNrpest | -o up,wantedup,normallyup,ready,paused,pid,exitcode,signal,signum,updownsince,readysince,updownfor,readyfor ] [ -n ] servicedir"
     19 #define dieusage() strerr_dieusage(100, USAGE)
     20 
     21 #define MAXFIELDS 16
     22 #define checkfields() if (n >= MAXFIELDS) strerr_dief1x(100, "too many option fields")
     23 
     24 static int normallyup ;
     25 
     26 typedef void pr_func (buffer *, s6_svstatus_t const *) ;
     27 typedef pr_func * pr_func_ref ;
     28 
     29 typedef struct funcmap_s funcmap_t ;
     30 struct funcmap_s
     31 {
     32   char const *s ;
     33   pr_func_ref f ;
     34 } ;
     35 
     36 static void pr_up (buffer *b, s6_svstatus_t const *st)
     37 {
     38   buffer_putsnoflush(b, st->pid && !st->flagfinishing ? "true" : "false") ;
     39 }
     40 
     41 static void pr_wantedup (buffer *b, s6_svstatus_t const *st)
     42 {
     43   buffer_putsnoflush(b, st->flagwantup ? "true" : "false") ;
     44 }
     45 
     46 static void pr_ready (buffer *b, s6_svstatus_t const *st)
     47 {
     48   buffer_putsnoflush(b, st->pid && st->flagready ? "true" : "false") ;
     49 }
     50 
     51 static void pr_paused (buffer *b, s6_svstatus_t const *st)
     52 {
     53   buffer_putsnoflush(b, st->flagpaused ? "true" : "false") ;
     54 }
     55 
     56 static void pr_pid (buffer *b, s6_svstatus_t const *st)
     57 {
     58   if (st->pid && !st->flagfinishing)
     59   {
     60     char fmt[PID_FMT] ;
     61     buffer_putnoflush(b, fmt, pid_fmt(fmt, st->pid)) ;
     62   }
     63   else buffer_putsnoflush(b, "-1") ;
     64 }
     65 
     66 static void pr_tain (buffer *b, tain const *a)
     67 {
     68   char fmt[TIMESTAMP] ;
     69   buffer_putnoflush(b, fmt, timestamp_fmt(fmt, a)) ;
     70 }
     71 
     72 static void pr_stamp (buffer *b, s6_svstatus_t const *st)
     73 {
     74   pr_tain(b, &st->stamp) ;
     75 }
     76 
     77 static void pr_readystamp (buffer *b, s6_svstatus_t const *st)
     78 {
     79   pr_tain(b, &st->readystamp) ;
     80 }
     81 
     82 static void pr_seconds (buffer *b, tain const *a)
     83 {
     84   tain d ;
     85   char fmt[UINT64_FMT] ;
     86   tain_sub(&d, &STAMP, a) ;
     87   buffer_putnoflush(b, fmt, uint64_fmt(fmt, tai_sec(tain_secp(&d)))) ;
     88 }
     89 
     90 static void pr_upseconds (buffer *b, s6_svstatus_t const *st)
     91 {
     92   pr_seconds(b, &st->stamp) ;
     93 }
     94 
     95 static void pr_readyseconds (buffer *b, s6_svstatus_t const *st)
     96 {
     97   pr_seconds(b, &st->readystamp) ;
     98 }
     99 
    100 static void pr_exitcode (buffer *b, s6_svstatus_t const *st)
    101 {
    102   int e = st->pid && !st->flagfinishing ? -1 :
    103           WIFEXITED(st->wstat) ? WEXITSTATUS(st->wstat) : -1 ;
    104   char fmt[INT_FMT] ;
    105   buffer_putnoflush(b, fmt, int_fmt(fmt, e)) ;
    106 }
    107 
    108 static void pr_signum (buffer *b, s6_svstatus_t const *st)
    109 {
    110   int e = st->pid && !st->flagfinishing ? -1 :
    111             WIFSIGNALED(st->wstat) ? WTERMSIG(st->wstat) : -1 ;
    112   char fmt[INT_FMT] ;
    113   buffer_putnoflush(b, fmt, int_fmt(fmt, e)) ;
    114 }
    115 
    116 static void pr_signal (buffer *b, s6_svstatus_t const *st)
    117 {
    118   int e = st->pid && !st->flagfinishing ? -1 :
    119             WIFSIGNALED(st->wstat) ? WTERMSIG(st->wstat) : -1 ;
    120   if (e == -1) buffer_putsnoflush(b, "NA") ;
    121   else
    122   {
    123     buffer_putsnoflush(b, "SIG") ;
    124     buffer_putsnoflush(b, sig_name(e)) ;
    125   }
    126 }
    127 
    128 static void pr_normallyup (buffer *b, s6_svstatus_t const *st)
    129 {
    130   buffer_putsnoflush(b, normallyup ? "true" : "false") ;
    131   (void)st ;
    132 }
    133 
    134 static funcmap_t const fmtable[] =
    135 {
    136   { .s = "up", .f = &pr_up },
    137   { .s = "wantedup", .f = &pr_wantedup },
    138   { .s = "normallyup", .f = &pr_normallyup },
    139   { .s = "ready", .f = &pr_ready },
    140   { .s = "paused", .f = &pr_paused },
    141   { .s = "pid", .f = &pr_pid },
    142   { .s = "exitcode", .f = &pr_exitcode },
    143   { .s = "signal", .f = &pr_signal },
    144   { .s = "signum", .f = &pr_signum },
    145   { .s = "updownsince", .f = &pr_stamp },
    146   { .s = "readysince", .f = &pr_readystamp },
    147   { .s = "updownfor", .f = &pr_upseconds },
    148   { .s = "readyfor", .f = &pr_readyseconds },
    149   { .s = 0, .f = 0 }
    150 } ;
    151 
    152 
    153 static unsigned int parse_options (char const *arg, pr_func_ref *fields, unsigned int n)
    154 {
    155   while (*arg)
    156   {
    157     size_t pos = str_chr(arg, ',') ;
    158     funcmap_t const *p = fmtable ;
    159     if (!pos) strerr_dief1x(100, "invalid null option field") ;
    160     for (; p->s ; p++) if (!strncmp(arg, p->s, pos)) break ;
    161     if (!p->s)
    162     {
    163       char blah[pos+1] ;
    164       memcpy(blah, arg, pos) ;
    165       blah[pos] = 0 ;
    166       strerr_dief2x(100, "invalid option field: ", blah) ;
    167     }
    168     checkfields() ;
    169     fields[n++] = p->f ;
    170     arg += pos ; if (*arg) arg++ ;
    171   }
    172   return n ;
    173 }
    174 
    175 static void legacy (s6_svstatus_t *st, int flagnum)
    176 {
    177   s6_svstatus_t status = *st ;
    178   int isup = status.pid && !status.flagfinishing ;
    179   char fmt[UINT64_FMT] ;
    180 
    181   if (isup)
    182   {
    183     buffer_putnoflush(buffer_1small,"up (pid ", 8) ;
    184     buffer_putnoflush(buffer_1small, fmt, pid_fmt(fmt, status.pid)) ;
    185     buffer_putnoflush(buffer_1small, ") ", 2) ;
    186   }
    187   else
    188   {
    189     buffer_putnoflush(buffer_1small, "down (", 6) ;
    190     if (WIFSIGNALED(status.wstat))
    191     {
    192       buffer_putnoflush(buffer_1small, "signal ", 7) ;
    193       if (flagnum)
    194         buffer_putnoflush(buffer_1small, fmt, uint_fmt(fmt, WTERMSIG(status.wstat))) ;
    195       else
    196       {
    197         buffer_putnoflush(buffer_1small, "SIG", 3) ;
    198         buffer_putsnoflush(buffer_1small, sig_name(WTERMSIG(status.wstat))) ;
    199       }
    200     }
    201     else
    202     {
    203       buffer_putnoflush(buffer_1small, "exitcode ", 9) ;
    204       buffer_putnoflush(buffer_1small, fmt, uint_fmt(fmt, WEXITSTATUS(status.wstat))) ;
    205     }
    206     buffer_putnoflush(buffer_1small, ") ", 2) ;
    207   }
    208 
    209   tain_sub(&status.stamp, &STAMP, &status.stamp) ;
    210   buffer_putnoflush(buffer_1small, fmt, uint64_fmt(fmt, status.stamp.sec.x)) ;
    211   buffer_putnoflush(buffer_1small, " seconds", 8) ;
    212 
    213   if (isup && !normallyup)
    214     buffer_putnoflush(buffer_1small, ", normally down", 15) ;
    215   if (!isup && normallyup)
    216     buffer_putnoflush(buffer_1small, ", normally up", 13) ;
    217   if (isup && status.flagpaused)
    218     buffer_putnoflush(buffer_1small, ", paused", 8) ;
    219   if (!isup && status.flagwantup)
    220     buffer_putnoflush(buffer_1small, ", want up", 9) ;
    221   if (isup && !status.flagwantup)
    222     buffer_putnoflush(buffer_1small, ", want down", 11) ;
    223 
    224   if (status.flagready)
    225   {
    226     tain_sub(&status.readystamp, &STAMP, &status.readystamp) ;
    227     buffer_putnoflush(buffer_1small, ", ready ", 8) ;
    228     buffer_putnoflush(buffer_1small, fmt, uint64_fmt(fmt, status.readystamp.sec.x)) ;
    229     buffer_putnoflush(buffer_1small, " seconds", 8) ;
    230   }
    231 }
    232 
    233 int main (int argc, char const *const *argv)
    234 {
    235   s6_svstatus_t status ;
    236   int flagnum = 0 ;
    237   pr_func_ref fields[MAXFIELDS] ;
    238   unsigned int n = 0 ;
    239   PROG = "s6-svstat" ;
    240 
    241   {
    242     subgetopt l = SUBGETOPT_ZERO ;
    243     for (;;)
    244     {
    245       int opt = subgetopt_r(argc, argv, "no:uwNrpest", &l) ;
    246       if (opt == -1) break ;
    247       switch (opt)
    248       {
    249         case 'n' : flagnum = 1 ; break ;
    250         case 'o' : n = parse_options(l.arg, fields, n) ; break ;
    251         case 'u' : checkfields() ; fields[n++] = &pr_up ; break ;
    252         case 'w' : checkfields() ; fields[n++] = &pr_wantedup ; break ;
    253         case 'N' : checkfields() ; fields[n++] = &pr_normallyup ; break ;
    254         case 'r' : checkfields() ; fields[n++] = &pr_ready ; break ;
    255         case 'p' : checkfields() ; fields[n++] = &pr_pid ; break ;
    256         case 'e' : checkfields() ; fields[n++] = &pr_exitcode ; break ;
    257         case 's' : checkfields() ; fields[n++] = &pr_signal ; break ;
    258         case 't' : checkfields() ; fields[n++] = &pr_upseconds ; break ;
    259         default : dieusage() ;
    260       }
    261     }
    262     argc -= l.ind ; argv += l.ind ;
    263   }
    264   if (!argc) dieusage() ;
    265   fields[n] = 0 ;
    266 
    267   {
    268     int r = s6_svc_ok(argv[0]) ;
    269     if (r < 0) strerr_diefu2sys(111, "check ", argv[0]) ;
    270     if (!r) strerr_diefu3x(1, "read status for ", argv[0], ": s6-supervise not running") ;
    271   }
    272   if (!s6_svstatus_read(argv[0], &status))
    273     strerr_diefu2sys(111, "read status for ", argv[0]) ;
    274 
    275   tain_wallclock_read_g() ;
    276   if (tain_future(&status.stamp)) tain_copynow(&status.stamp) ;
    277 
    278   {
    279     size_t dirlen = strlen(*argv) ;
    280     char fn[dirlen + 6] ;
    281     memcpy(fn, *argv, dirlen) ;
    282     memcpy(fn + dirlen, "/down", 6) ;
    283     if (access(fn, F_OK) < 0)
    284       if (errno != ENOENT) strerr_diefu2sys(111, "access ", fn) ;
    285       else normallyup = 1 ;
    286     else normallyup = 0 ;
    287   }
    288 
    289   if (!n) legacy(&status, flagnum) ;
    290   else
    291   {
    292     unsigned int i = 0 ;
    293     for (; fields[i] ; i++)
    294     {
    295       (*fields[i])(buffer_1small, &status) ;
    296       buffer_putsnoflush(buffer_1small, " ") ;
    297     }
    298     buffer_unput(buffer_1small, 1) ;
    299   }
    300 
    301   if (buffer_putflush(buffer_1small, "\n", 1) < 0)
    302     strerr_diefu1sys(111, "write to stdout") ;
    303   return 0 ;
    304 }