sv.c (12201B)
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 #include <unistd.h> 4 #include "str.h" 5 #include "strerr.h" 6 #include "error.h" 7 #include "sgetopt.h" 8 #include "open.h" 9 #include "env.h" 10 #include "buffer.h" 11 #include "fmt.h" 12 #include "scan.h" 13 #include "tai.h" 14 #include "taia.h" 15 #include "wait.h" 16 17 #define USAGE " [-v] [-w sec] command service ..." 18 #define USAGELSB " [-w sec] command" 19 20 #define FATAL "fatal: " 21 #define FAIL "fail: " 22 #define WARN "warning: " 23 #define OK "ok: " 24 #define RUN "run: " 25 #define FINISH "finish: " 26 #define DOWN "down: " 27 #define TIMEOUT "timeout: " 28 #define KILL "kill: " 29 30 static char *progname; 31 static char *action; 32 static char *acts; 33 static char *varservice ="/var/service/"; 34 static char **service; 35 static char **servicex; 36 static unsigned int services; 37 static unsigned int rc =0; 38 static unsigned int lsb; 39 static unsigned int verbose =0; 40 static unsigned long wait =7; 41 static unsigned int kll =0; 42 static unsigned int islog =0; 43 static struct taia tstart, tnow, tdiff; 44 static struct tai tstatus; 45 46 static int (*act)(char*) =0; 47 static int (*cbk)(char*) =0; 48 49 static int curdir, fd, r; 50 static char svstatus[20]; 51 static char sulong[FMT_ULONG]; 52 53 static void usage() 54 { 55 if (!lsb) strerr_die4x(100, "usage: ", progname, USAGE, "\n"); 56 strerr_die4x(2, "usage: ", progname, USAGELSB, "\n"); 57 } 58 static void done(unsigned int e) { if (curdir != -1) fchdir(curdir); _exit(e); } 59 static void fatal(char *m1) 60 { 61 strerr_warn3(FATAL, m1, ": ", &strerr_sys); 62 done(lsb ? 151 : 100); 63 } 64 static void fatal2(char *m1, char *m2) 65 { 66 strerr_warn4(FATAL, m1, m2, ": ", &strerr_sys); 67 done(lsb ? 151 : 100); 68 } 69 static void out(char *p, char *m1) 70 { 71 buffer_puts(buffer_1, p); 72 buffer_puts(buffer_1, *service); 73 if (islog) buffer_puts(buffer_1, "/log"); 74 buffer_puts(buffer_1, ": "); 75 buffer_puts(buffer_1, m1); 76 if (errno) { 77 buffer_puts(buffer_1, ": "); 78 buffer_puts(buffer_1, error_str(errno)); 79 } 80 buffer_puts(buffer_1, "\n"); 81 buffer_flush(buffer_1); 82 } 83 static void fail(char *m1) { ++rc; out(FAIL, m1); } 84 static void failx(char *m1) { errno =0; fail(m1); } 85 static void warn(char *m1) { ++rc; out(WARN, m1); } 86 static void warnx(char *m1) { errno =0; warn(m1); } 87 static void ok(char *m1) { errno =0; out(OK, m1); } 88 89 static void outs(const char *s) { buffer_puts(buffer_1, s); } 90 static void flush(const char *s) { outs(s); buffer_flush(buffer_1); } 91 static void outs2(const char *s) { buffer_puts(buffer_2, s); } 92 static void flush2(const char *s) { outs2(s); buffer_flush(buffer_2); } 93 94 static int svstatus_get() 95 { 96 if ((fd =open_write("supervise/ok")) == -1) { 97 if (errno == error_nodevice) { 98 *acts == 'x' ? ok("runsv not running") : failx("runsv not running"); 99 return(0); 100 } 101 warn("unable to open supervise/ok"); 102 return(-1); 103 } 104 close(fd); 105 if ((fd =open_read("supervise/status")) == -1) { 106 warn("unable to open supervise/status"); 107 return(-1); 108 } 109 r =read(fd, svstatus, 20); 110 close(fd); 111 switch(r) { 112 case 20: break; 113 case -1: warn("unable to read supervise/status"); return(-1); 114 default: warnx("unable to read supervise/status: bad format"); return(-1); 115 } 116 return(1); 117 } 118 static unsigned int svstatus_print(char *m) { 119 int pid; 120 int normallyup =0; 121 struct stat s; 122 123 if (stat("down", &s) == -1) { 124 if (errno != error_noent) { 125 outs2(WARN); outs2("unable to stat "); outs2(*service); outs2("/down: "); 126 outs2(error_str(errno)); flush2("\n"); 127 return(0); 128 } 129 normallyup =1; 130 } 131 pid =(unsigned char) svstatus[15]; 132 pid <<=8; pid +=(unsigned char)svstatus[14]; 133 pid <<=8; pid +=(unsigned char)svstatus[13]; 134 pid <<=8; pid +=(unsigned char)svstatus[12]; 135 tai_unpack(svstatus, &tstatus); 136 switch (svstatus[19]) { 137 case 0: outs(DOWN); break; 138 case 1: outs(RUN); break; 139 case 2: outs(FINISH); break; 140 } 141 outs(m); outs(": "); 142 if (svstatus[19]) { 143 outs("(pid "); sulong[fmt_ulong(sulong, pid)] =0; 144 outs(sulong); outs(") "); 145 } 146 buffer_put(buffer_1, sulong, 147 fmt_ulong(sulong, tnow.sec.x < tstatus.x ? 0 : tnow.sec.x -tstatus.x)); 148 outs("s"); 149 if (pid && !normallyup) outs(", normally down"); 150 if (!pid && normallyup) outs(", normally up"); 151 if (pid && svstatus[16]) outs(", paused"); 152 if (!pid && (svstatus[17] == 'u')) outs(", want up"); 153 if (pid && (svstatus[17] == 'd')) outs(", want down"); 154 if (pid && svstatus[18]) outs(", got TERM"); 155 return(pid ? 1 : 2); 156 } 157 static int status(char *unused) 158 { 159 int rc; 160 161 rc =svstatus_get(); 162 switch(rc) { 163 case -1: 164 if (lsb) done(4); 165 __attribute__((fallthrough)); 166 case 0: 167 return(0); 168 } 169 rc =svstatus_print(*service); 170 islog =1; 171 if (chdir("log") == -1) { 172 if (errno != error_noent) { 173 outs("; "); 174 warn("unable to change directory"); 175 } 176 else outs("\n"); 177 } 178 else { 179 outs("; "); 180 if (svstatus_get()) { rc =svstatus_print("log"); outs("\n"); } 181 } 182 islog =0; 183 flush(""); 184 if (lsb) switch(rc) { 185 case 1: 186 done(0); 187 __attribute__((fallthrough)); 188 case 2: 189 done(3); 190 __attribute__((fallthrough)); 191 case 0: done(4); 192 } 193 194 return(rc); 195 } 196 197 static int checkscript() 198 { 199 char *prog[2]; 200 struct stat s; 201 int pid, w; 202 203 if (stat("check", &s) == -1) { 204 if (errno == error_noent) return(1); 205 outs2(WARN); outs2("unable to stat "); outs2(*service); outs2("/check: "); 206 outs2(error_str(errno)); flush2("\n"); 207 return(0); 208 } 209 /* if (!(s.st_mode & S_IXUSR)) return(1); */ 210 if ((pid =fork()) == -1) { 211 outs2(WARN); outs2("unable to fork for "); outs2(*service); 212 outs2("/check: "); outs2(error_str(errno)); flush2("\n"); 213 return(0); 214 } 215 if (!pid) { 216 prog[0] ="./check"; 217 prog[1] =0; 218 close(1); 219 execve("check", prog, environ); 220 outs2(WARN); outs2("unable to run "); outs2(*service); outs2("/check: "); 221 outs2(error_str(errno)); flush2("\n"); 222 _exit(0); 223 } 224 while (wait_pid(&w, pid) == -1) { 225 if (errno == error_intr) continue; 226 outs2(WARN); outs2("unable to wait for child "); outs2(*service); 227 outs2("/check: "); outs2(error_str(errno)); flush2("\n"); 228 return(0); 229 } 230 return(!wait_exitcode(w)); 231 } 232 233 static int check(char *a) 234 { 235 unsigned int pid; 236 237 if ((r =svstatus_get()) == -1) return(-1); 238 while (*a) { 239 if (r == 0) { if (*a == 'x') return(1); return(-1); } 240 pid =(unsigned char)svstatus[15]; 241 pid <<=8; pid +=(unsigned char)svstatus[14]; 242 pid <<=8; pid +=(unsigned char)svstatus[13]; 243 pid <<=8; pid +=(unsigned char)svstatus[12]; 244 switch (*a) { 245 case 'x': return(0); 246 case 'u': 247 if (!pid || svstatus[19] != 1) return(0); 248 if (!checkscript()) return(0); 249 break; 250 case 'd': if (pid || svstatus[19] != 0) return(0); break; 251 case 'C': if (pid) if (!checkscript()) return(0); break; 252 case 't': 253 case 'k': 254 if (!pid && svstatus[17] == 'd') break; 255 tai_unpack(svstatus, &tstatus); 256 if ((tstart.sec.x > tstatus.x) || !pid || svstatus[18] || !checkscript()) 257 return(0); 258 break; 259 case 'o': 260 tai_unpack(svstatus, &tstatus); 261 if ((!pid && tstart.sec.x > tstatus.x) || (pid && svstatus[17] != 'd')) 262 return(0); 263 break; 264 case 'p': if (pid && !svstatus[16]) return(0); break; 265 case 'c': if (pid && svstatus[16]) return(0); break; 266 } 267 ++a; 268 } 269 outs(OK); svstatus_print(*service); flush("\n"); 270 return(1); 271 } 272 static int control(char *a) 273 { 274 if (svstatus_get() <= 0) return(-1); 275 if (svstatus[17] == *a) 276 if (*a != 'd' || svstatus[18] == 1) return(0); /* once w/o term */ 277 if ((fd =open_write("supervise/control")) == -1) { 278 if (errno != error_nodevice) 279 warn("unable to open supervise/control"); 280 else 281 *a == 'x' ? ok("runsv not running") : failx("runsv not running"); 282 return(-1); 283 } 284 r =write(fd, a, str_len(a)); 285 close(fd); 286 if (r != str_len(a)) { 287 warn("unable to write to supervise/control"); 288 return(-1); 289 } 290 return(1); 291 } 292 293 int main(int argc, char **argv) 294 { 295 unsigned int i, done; 296 char *x; 297 298 progname =*argv; 299 for (i =str_len(*argv); i; --i) if ((*argv)[i -1] == '/') break; 300 *argv +=i; 301 optprogname =progname =*argv; 302 service =argv; 303 services =1; 304 lsb =(str_diff(progname, "sv")); 305 if ((x =env_get("SVDIR"))) varservice =x; 306 if ((x =env_get("SVWAIT"))) scan_ulong(x, &wait); 307 while ((i =getopt(argc, (const char* const*)argv, "w:vV")) != opteof) { 308 switch(i) { 309 case 'w': 310 scan_ulong(optarg, &wait); 311 __attribute__((fallthrough)); 312 case 'v': verbose =1; break; 313 case 'V': 314 strerr_warn1(VERSION, 0); 315 __attribute__((fallthrough)); 316 case '?': usage(); 317 } 318 } 319 argv +=optind; argc -=optind; 320 if (!(action =*argv++)) usage(); --argc; 321 if (!lsb) { service =argv; services =argc; } 322 if (!*service) usage(); 323 324 taia_now(&tnow); tstart =tnow; 325 if ((curdir =open_read(".")) == -1) 326 fatal("unable to open current directory"); 327 328 act =&control; acts ="s"; 329 if (verbose) cbk =✓ 330 switch (*action) { 331 case 'x': case 'e': 332 acts ="x"; break; 333 case 'X': case 'E': 334 acts ="x"; kll =1; cbk =✓ break; 335 case 'D': 336 acts ="d"; kll =1; cbk =✓ break; 337 case 'T': 338 acts ="tc"; kll =1; cbk =✓ break; 339 case 't': 340 if (!str_diff(action, "try-restart")) { acts ="tc"; cbk =✓ break; } 341 __attribute__((fallthrough)); 342 case 'c': 343 if (!str_diff(action, "check")) { act =0; acts ="C"; cbk =✓ break; } 344 __attribute__((fallthrough)); 345 case 'u': case 'd': case 'o': case 'p': case 'h': 346 case 'a': case 'i': case 'k': case 'q': case '1': case '2': 347 action[1] =0; acts =action; break; 348 case 's': 349 if (!str_diff(action, "shutdown")) { acts ="x"; cbk =✓ break; } 350 if (!str_diff(action, "start")) { acts ="u"; cbk =✓ break; } 351 if (!str_diff(action, "stop")) { acts ="d"; cbk =✓ break; } 352 if (lsb && str_diff(action, "status")) usage(); 353 act =&status; cbk =0; break; 354 case 'r': 355 if (!str_diff(action, "restart")) { acts ="tcu"; cbk =✓ break; } 356 if (!str_diff(action, "reload")) { acts ="h"; cbk =✓ break; } 357 usage(); 358 __attribute__((fallthrough)); 359 case 'f': 360 if (!str_diff(action, "force-reload")) 361 { acts ="tc"; kll =1; cbk =✓ break; } 362 if (!str_diff(action, "force-restart")) 363 { acts ="tcu"; kll =1; cbk =✓ break; } 364 if (!str_diff(action, "force-shutdown")) 365 { acts ="x"; kll =1; cbk =✓ break; } 366 if (!str_diff(action, "force-stop")) 367 { acts ="d"; kll =1; cbk =✓ break; } 368 __attribute__((fallthrough)); 369 default: 370 usage(); 371 } 372 373 servicex =service; 374 for (i =0; i < services; ++i) { 375 if ((**service != '/') && (**service != '.') && **service && 376 ((*service)[str_len(*service) -1] != '/')) { 377 if ((chdir(varservice) == -1) || (chdir(*service) == -1)) { 378 fail("unable to change to service directory"); 379 *service =0; 380 } 381 } 382 else 383 if (chdir(*service) == -1) { 384 fail("unable to change to service directory"); 385 *service =0; 386 } 387 if (*service) if (act && (act(acts) == -1)) *service =0; 388 if (fchdir(curdir) == -1) fatal("unable to change to original directory"); 389 service++; 390 } 391 392 if (*cbk) 393 for (;;) { 394 taia_sub(&tdiff, &tnow, &tstart); 395 service =servicex; done =1; 396 for (i =0; i < services; ++i, ++service) { 397 if (!*service) continue; 398 if ((**service != '/') && (**service != '.')) { 399 if ((chdir(varservice) == -1) || (chdir(*service) == -1)) { 400 fail("unable to change to service directory"); 401 *service =0; 402 } 403 } 404 else 405 if (chdir(*service) == -1) { 406 fail("unable to change to service directory"); 407 *service =0; 408 } 409 if (*service) { if (cbk(acts) != 0) *service =0; else done =0; } 410 if (*service && taia_approx(&tdiff) > wait) { 411 kll ? outs(KILL) : outs(TIMEOUT); 412 if (svstatus_get() > 0) { svstatus_print(*service); ++rc; } 413 flush("\n"); 414 if (kll) control("k"); 415 *service =0; 416 } 417 if (fchdir(curdir) == -1) 418 fatal("unable to change to original directory"); 419 } 420 if (done) break; 421 usleep(420000); 422 taia_now(&tnow); 423 } 424 return(rc > 99 ? 99 : rc); 425 }