s6-permafailon.c (3531B)
1 /* ISC license. */ 2 3 #include <sys/stat.h> 4 #include <string.h> 5 #include <signal.h> 6 7 #include <skalibs/types.h> 8 #include <skalibs/strerr.h> 9 #include <skalibs/bitarray.h> 10 #include <skalibs/sig.h> 11 #include <skalibs/tai.h> 12 #include <skalibs/exec.h> 13 14 #include <s6/supervise.h> 15 16 #define USAGE "s6-permafailon seconds deathcount statuslist prog..." 17 #define dieusage() strerr_dieusage(100, USAGE) 18 19 static inline void list_scan (char const *s, unsigned char *codes, sigset_t *sigs) 20 { 21 size_t pos = 0 ; 22 memset(codes, 0, 32) ; 23 sigemptyset(sigs) ; 24 while (s[pos]) 25 { 26 unsigned int u ; 27 size_t len = uint_scan(s + pos, &u) ; 28 if (len) 29 { 30 if (u > 255) strerr_dief1x(100, "invalid exit code") ; 31 pos += len ; 32 if (s[pos] == '-') 33 { 34 unsigned int v ; 35 pos++ ; 36 len = uint_scan(s + pos, &v) ; 37 if (!len) strerr_dief1x(100, "invalid interval specification") ; 38 if (v > 255) strerr_dief1x(100, "invalid exit code") ; 39 if (v < u) strerr_dief1x(100, "invalid interval") ; 40 pos += len ; 41 bitarray_setn(codes, u, v - u + 1) ; 42 } 43 else bitarray_set(codes, u) ; 44 } 45 else 46 { 47 int sig ; 48 size_t next = pos ; 49 while (!strchr(",; \n\r\t", s[next])) next++ ; 50 char tmp[next - pos + 1] ; 51 memcpy(tmp, s + pos, next - pos) ; 52 tmp[next - pos] = 0 ; 53 len = sig0_scan(tmp, &sig) ; 54 if (!len) strerr_dief1x(100, "invalid status list specification") ; 55 pos += len ; 56 if (sigaddset(sigs, sig) < 0) strerr_dief1x(100, "invalid signal") ; 57 } 58 while (memchr(",; \n\r\t", s[pos], 6)) pos++ ; 59 } 60 } 61 62 int main (int argc, char const *const *argv) 63 { 64 unsigned char codes[32] ; 65 sigset_t sigs ; 66 unsigned int total, seconds, n ; 67 struct stat st ; 68 PROG = "s6-permafailon" ; 69 if (argc < 4) dieusage() ; 70 71 if (!uint0_scan(argv[1], &seconds)) dieusage() ; 72 if (!uint0_scan(argv[2], &n)) dieusage() ; 73 if (!n) dieusage() ; 74 if (n > S6_MAX_DEATH_TALLY) n = S6_MAX_DEATH_TALLY ; 75 list_scan(argv[3], codes, &sigs) ; 76 77 if (stat(S6_DTALLY_FILENAME, &st) < 0) 78 { 79 strerr_warnwu2sys("stat ", S6_DTALLY_FILENAME) ; 80 goto cont ; 81 } 82 if (st.st_size % S6_DTALLY_PACK || st.st_size > S6_DTALLY_PACK * S6_MAX_DEATH_TALLY) 83 { 84 strerr_warnw2x("invalid ", S6_DTALLY_FILENAME) ; 85 goto cont ; 86 } 87 total = st.st_size / S6_DTALLY_PACK ; 88 { 89 tain mintime ; 90 unsigned int matches = 0 ; 91 s6_dtally_t tab[total] ; 92 ssize_t r = s6_dtally_read(".", tab, total) ; 93 if (r <= 0) 94 { 95 if (r < 0) strerr_warnwu2sys("read ", S6_DTALLY_FILENAME) ; 96 goto cont ; 97 } 98 if (r < n) goto cont ; 99 tain_uint(&mintime, seconds) ; 100 { 101 tain now ; 102 tain_wallclock_read(&now) ; 103 tain_sub(&mintime, &now, &mintime) ; 104 } 105 106 for (unsigned int i = 0 ; i < r ; i++) 107 { 108 if (!tain_less(&tab[i].stamp, &mintime) 109 && ((tab[i].sig && sigismember(&sigs, tab[i].sig)) || bitarray_peek(codes, tab[i].exitcode)) 110 && ++matches >= n) 111 { 112 char fmtevent[4] ; 113 char fmtseconds[UINT_FMT] ; 114 char fmtn[UINT_FMT] ; 115 fmtevent[uint_fmt(fmtevent, tab[i].sig ? tab[i].sig : tab[i].exitcode)] = 0 ; 116 fmtseconds[uint_fmt(fmtseconds, seconds)] = 0 ; 117 fmtn[uint_fmt(fmtn, n)] = 0 ; 118 strerr_warni8x("PERMANENT FAILURE triggered after ", fmtn, " events involving ", tab[i].sig ? "signal " : "exit code ", fmtevent, " in the last ", fmtseconds, " seconds") ; 119 return 125 ; 120 } 121 } 122 } 123 124 cont: 125 xexec0(argv+4) ; 126 }