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 }