int socket_timeoutconn (int s, char const *ip, uint16 port, unsigned int timeout) { tain_t stamp, deadline ; tain_now(&stamp) ; tain_addsec(&deadline, &stamp, timeout) ; return socket_deadlineconnstamp4(s, ip, port, &deadline, &stamp) ; }
int main() { tai_t now = tai_now(); printf("Current time: %"PRIi64"\n", now); tain_t now_ns = tain_now(); printf("Current time (with nanoseconds): %"PRIi64".%"PRIi32"\n", now_ns.sec, now_ns.ns); printf("\n"); printf("Accounting for leap seconds:\n"); struct tm tm; printf("2015-06-30T23:59:59Z\n"); strptime("2015-06-30T23:59:59Z", "%Y-%m-%dT%H:%M:%SZ", &tm); printf("TAI: %"PRIi64"\n", tai_mktime(&tm)); printf("UNIX: %ld\n", mktime(&tm)); printf("2015-06-30T23:59:60Z\n"); strptime("2015-06-30T23:59:60Z", "%Y-%m-%dT%H:%M:%SZ", &tm); printf("TAI: %"PRIi64"\n", tai_mktime(&tm)); printf("UNIX: %ld <== REPEATS\n", mktime(&tm)); printf("2015-07-01T00:00:00Z\n"); strptime("2015-07-01T00:00:00Z", "%Y-%m-%dT%H:%M:%SZ", &tm); printf("TAI: %"PRIi64"\n", tai_mktime(&tm)); printf("UNIX: %ld\n", mktime(&tm)); printf("\n"); printf("Date with nanoseconds:\n"); printf("2015-07-02T12:34:56,28931029Z\n"); strptime("2015-07-02T12:34:56Z", "%Y-%m-%dT%H:%M:%SZ", &tm); tain_t time = tain_mktime(&tm, 28931029); printf("TAI: %"PRIi64".%"PRIi32"\n", time.sec, time.ns); return 0; }
/* perpd_svdef_activate() ** activate service definition ** called by perpd_scan() ** ** return: ** 0: success ** -1: failure ** ** notes: ** failures include: ** - service definition directory name too long ** (must me less than, say, 240 characters) ** - open() on service definition directory ** - pipe() for logpipe ** service is activated only on success */ int perpd_svdef_activate(struct svdef *svdef, const char *svdir, const struct stat *st_dir) { struct stat st; char path_buf[256]; int fd; perpd_svdef_clear(svdef); if(cstr_len(svdir) > 240){ errno = ENAMETOOLONG; warn_syserr("service definition directory name error: ", svdir); return -1; } svdef->dev = st_dir->st_dev; svdef->ino = st_dir->st_ino; cstr_lcpy(svdef->name, svdir, sizeof svdef->name); tain_now(&svdef->when); svdef->bitflags |= SVDEF_FLAG_ACTIVE; /* open an fd to use for fchdir() in perpd_svrun(): */ cstr_vcopy(path_buf, "./", svdir); if((fd = open(path_buf, O_RDONLY)) == -1){ warn_syserr("failure open() on service definition directory ", svdir); return -1; } fd_cloexec(fd); svdef->fd_dir = fd; /* inspect service definition directory: */ cstr_vcopy(path_buf, "./", svdir, "/flag.down"); if(stat(path_buf, &st) != -1){ svdef->bitflags |= SVDEF_FLAG_DOWN; } cstr_vcopy(path_buf, "./", svdir, "/flag.once"); if(stat(path_buf, &st) != -1){ svdef->bitflags |= SVDEF_FLAG_ONCE; } /* logging? */ cstr_vcopy(path_buf, "./", svdir, "/rc.log"); if(stat(path_buf, &st) != -1){ if(st.st_mode & S_IXUSR){ svdef->bitflags |= SVDEF_FLAG_HASLOG; log_debug("rc.log exists and is executable for ", svdir); }else{ log_warning("rc.log exists but is not set executable for ", svdir); } } /* setup logpipe: */ if(svdef->bitflags & SVDEF_FLAG_HASLOG){ if(pipe(svdef->logpipe) == -1){ warn_syserr("failure pipe() on logpipe for ", svdir); close(fd); return -1; } fd_cloexec(svdef->logpipe[0]); fd_cloexec(svdef->logpipe[1]); } /* ** from here on, the service is considered activated */ /* first time startup: */ /* log: if FLAG_HASLOG, start irrespective of any other svdef->bitflags: */ if(svdef->bitflags & SVDEF_FLAG_HASLOG){ svdef->svpair[SUBSV_LOG].bitflags |= SUBSV_FLAG_ISLOG; perpd_svdef_run(svdef, SUBSV_LOG, SVRUN_START); } /* XXX, bail here if log startup fails on fork() ? */ /* main: */ if(!(svdef->bitflags & SVDEF_FLAG_DOWN)){ /* setup for running once? */ if(svdef->bitflags & SVDEF_FLAG_ONCE){ svdef->svpair[SUBSV_MAIN].bitflags |= SUBSV_FLAG_ISONCE; } perpd_svdef_run(svdef, SUBSV_MAIN, SVRUN_START); } else { svdef->svpair[SUBSV_MAIN].bitflags |= SUBSV_FLAG_WANTDOWN; } return 0; }
/* perpd_svdef_run() ** exec() a service: ** "which" is SUBSV_MAIN or SUBSV_LOG ** "target" is SVRUN_START or SVRUN_RESET ** ** side effects: ** global flag_failing set on fail of fork() ** child uses (but does not alter) global poll_sigset */ int perpd_svdef_run(struct svdef *svdef, int which, int target) { struct subsv *subsv = &svdef->svpair[which]; char *prog[7]; tain_t now, when_ok; tain_t towait = tain_INIT(0,0); pid_t pid; int wstat; char nbuf_reset[NFMT_SIZE]; int i; /* insanity checks: */ if((which == SUBSV_LOG) && !(svdef->bitflags & SVDEF_FLAG_HASLOG)){ log_debug("logging service requested but not enabled"); return 0; } if(subsv->pid > 0){ log_debug("perpd_svrun() requested for service still running"); return 0; } /* initialize (attempted) target, etc: */ switch(target){ case SVRUN_RESET: subsv->bitflags |= SUBSV_FLAG_ISRESET; break; default: subsv->bitflags &= ~SUBSV_FLAG_ISRESET; break; } wstat = subsv->wstat; subsv->pid = 0; subsv->wstat = 0; subsv->bitflags &= ~SUBSV_FLAG_FAILING; /* setup argv: */ prog[0] = (which == SUBSV_LOG) ? "./rc.log" : "./rc.main"; prog[1] = (target == SVRUN_START) ? "start" : "reset"; prog[2] = svdef->name; prog[3] = NULL; /* additional args if running "reset": */ if(target == SVRUN_RESET){ if(WIFEXITED(wstat)){ prog[3] = "exit"; prog[4] = nfmt_uint32(nbuf_reset, (uint32_t)WEXITSTATUS(wstat)); prog[5] = NULL; } else { int n = (WIFSIGNALED(wstat) ? WTERMSIG(wstat) : WSTOPSIG(wstat)); char *s = (char *)sysstr_signal(n); prog[3] = (WIFSIGNALED(wstat) ? "signal" : "stopped"); prog[4] = nfmt_uint32(nbuf_reset, (uint32_t)n); prog[5] = ((s != NULL) ? s : "SIGUNKNOWN"); prog[6] = NULL; } } /* timestamps and respawn governor: */ tain_now(&now); tain_assign(&when_ok, &subsv->when_ok); if((target == SVRUN_START) && tain_less(&now, &when_ok)){ log_warning("setting respawn governor on 'start' target of service ", svdef->name, " for ", prog[0]); tain_minus(&towait, &when_ok, &now); } /* fork/exec: */ if((pid = fork()) == -1){ subsv->pid = 0; subsv->bitflags |= SUBSV_FLAG_FAILING; perpd_trigger_fail(); warn_syserr("failure fork() for service ", svdef->name); return -1; } /* XXX, TODO: ** if child error before exec(), die() with a distinctive error code */ /* child: */ if(pid == 0){ /* nfmt buffer for environmental variables (reusable with newenv_set()): */ char nbuf_env[NFMT_SIZE]; /* run child in new process group: */ setsid(); /* cwd for runscripts is svdir: */ if(fchdir(svdef->fd_dir) == -1){ fatal_syserr("(in child for service ", svdef->name, "): failure fchdir() to service directory"); } /* setup logpipe: */ if(svdef->bitflags & SVDEF_FLAG_HASLOG){ if(which == SUBSV_MAIN){ /* set stdout to logpipe: */ close(1); if(dup2(svdef->logpipe[1], 1) != 1){ fatal_syserr("(in child for service ", svdef->name, "): failure dup2() on logpipe[1] to logging service"); } } if((which == SUBSV_LOG) && (target == SVRUN_START)){ /* set stdin to logpipe: ** (but not if this is a resetting log service) */ close(0); if(dup2(svdef->logpipe[0], 0) != 0){ fatal_syserr("(in child for service ", svdef->name, "): failure dup2() on logpipe[0] for logging service"); } } close(svdef->logpipe[0]); close(svdef->logpipe[1]); } /* close extraneous descriptors (shouldn't be any!): */ for(i = 3; i < 1024; ++i) close(i); /* set PERP_BASE in the environment: */ if(newenv_set("PERP_BASE", basedir) == -1){ fatal_syserr("(in child for service ", svdef->name, "): failure setting PERP_BASE environment for ", prog[0], " ", prog[1]); } /* set PERP_SVPID in the environment: */ if(target == SVRUN_RESET){ nfmt_uint64(nbuf_env, (uint64_t)subsv->pid_prev); }else{ nfmt_uint64(nbuf_env, (uint64_t)getpid()); } if(newenv_set("PERP_SVPID", nbuf_env) == -1){ fatal_syserr("(in child for service ", svdef->name, "): failure setting PERP_SVPID environment for ", prog[0], " ", prog[1]); } /* set PERP_SVSECS in the environment (reset target only): */ if(target == SVRUN_RESET){ nfmt_uint64(nbuf_env, tain_uptime(&now, &subsv->when)); if(newenv_set("PERP_SVSECS", nbuf_env) == -1){ fatal_syserr("(in child for service ", svdef->name, "): failure setting PERP_SVSECS environment for ", prog[0], " ", prog[1]); } } /* respawn governor: */ if((target == SVRUN_START) && !(tain_iszero(&towait))){ tain_pause(&towait, NULL); } /* clear signal handlers from child process: */ sig_uncatch(SIGCHLD); sig_uncatch(SIGHUP); sig_uncatch(SIGINT); sig_uncatch(SIGTERM); sig_uncatch(SIGPIPE); sigset_unblock(&poll_sigset); /* go forth my child: */ newenv_run(prog, environ); /* nuts, exec failed: */ fatal_syserr("(in child for service ", svdef->name, "): failure execve()"); } /* parent: */ subsv->pid = pid; /* set timestamps and respawn governor: */ tain_assign(&subsv->when, &now); if(target == SVRUN_START){ /* when_ok = now + 1sec + wait: */ tain_LOAD(&when_ok, 1, 0); tain_plus(&when_ok, &now, &when_ok); tain_plus(&when_ok, &when_ok, &towait); tain_assign(&subsv->when_ok, &when_ok); } return 0; }
static void child_exec(int which) { pid_t pid = 0; tain_t now; tain_t when_ok; char **argv = deux[which].argv; tain_now(&now); tain_load(&when_ok, 1, 0); tain_plus(&when_ok, &deux[which].when, &when_ok); if(tain_less(&now, &when_ok)){ warn("pausing for respawn of ", argv[0], " ..."); sleep(1); } /* if option -x: ** not supervising, don't fork here, just exec into deux[1] */ if(opt_super ? 1 : (which == 0)){ while((pid = fork()) == -1){ warn("failure on fork() while starting ", argv[0]); sleep(2); } } /* child (or, if not opt_super, execing into deux[1]): */ if(pid == 0){ /* setup logpipe: */ fd_dupe(which, my_logpipe[which]); close(my_logpipe[0]); close(my_logpipe[1]); #if 0 if(which == 1){ /* redirect stderr to stdout for program: */ fd_dupe(2, 1); } #endif /* reset default signal handlers, unblock: */ sig_default(SIGTERM); sig_default(SIGCHLD); sig_default(SIGALRM); sig_default(SIGCONT); sig_default(SIGHUP); sig_default(SIGINT); sig_default(SIGQUIT); sig_default(SIGTSTP); sig_default(SIGUSR1); sig_default(SIGUSR2); sig_default(SIGPIPE); sigset_unblock(&my_sigset); /* do it: */ execvx(argv[0], argv, environ, NULL); /* uh oh: */ fatal_syserr("failure on exec of ", argv[0]); } /* parent: */ deux[which].pid = pid; tain_now(&deux[which].when); return; }
static void child_exec(int which) { pid_t pid = 0; tain_t now; tain_t when_ok; char **argv = deux[which].argv; tain_now(&now); tain_load(&when_ok, 1, 0); tain_plus(&when_ok, &deux[which].when, &when_ok); if(tain_less(&now, &when_ok)){ warn("pausing for restart of ", argv[0], " ..."); sleep(1); } /* if option -x: ** not supervising, don't fork here, just exec into deux[1] */ if(opt_super ? 1 : (which == 0)){ while((pid = fork()) == -1){ warn("failure on fork() while starting ", argv[0]); sleep(2); } } /* child (or, if not opt_super, execing into deux[1]): */ if(pid == 0){ int fd, fd_max; struct rlimit rlim; int i; /* setup PERP_BASE in environment: */ if(newenv_set("PERP_BASE", perp_base) == -1){ fatal_syserr("failure setting environment in child for ", argv[0]); } /* prepare for closing unused file descriptors: */ if(getrlimit(RLIMIT_NOFILE, &rlim) == -1){ fatal_syserr("failure getting file rlimit in child for ", argv[0]); } fd_max = (rlim.rlim_max == RLIM_INFINITY) ? 1024 : rlim.rlim_max; /* start fd 0,1,2 on /dev/null: */ if((fd = open("/dev/null", O_RDWR)) == -1){ fatal_syserr("failure opening /dev/null in child for ", argv[0]); } fd_dupe(0, fd); fd_dupe(1, fd); fd_dupe(2, fd); close(fd); /* setup logpipe: */ fd_dupe(which, my_logpipe[which]); if(which == 1){ /* perpd gets stderr redirected to stdout for logger: */ fd_dupe(2, 1); } /* close all other descriptors: */ for(i = 3; i < fd_max; ++i) close(i); /* set default umask: */ umask(0); /* reset default signal handlers, unblock: */ sig_default(SIGTERM); sig_default(SIGCHLD); sig_default(SIGALRM); sig_default(SIGCONT); sig_default(SIGHUP); sig_default(SIGINT); sig_default(SIGQUIT); sig_default(SIGTSTP); sig_default(SIGUSR1); sig_default(SIGUSR2); sig_default(SIGPIPE); sigset_unblock(&my_sigset); /* do it: */ newenv_run(argv, environ); /* uh oh: */ fatal_syserr("failure on exec of ", argv[0]); } /* parent: */ deux[which].pid = pid; tain_now(&deux[which].when); return; }
int main(int argc, char *argv[]) { nextopt_t nopt = nextopt_INIT(argc, argv, ":hVb:"); char opt; const char *basedir = NULL; char pathbuf[256]; tain_t now; size_t n; int fd_conn; progname = nextopt_progname(&nopt); while((opt = nextopt(&nopt))){ char optc[2] = {nopt.opt_got, '\0'}; switch(opt){ case 'h': usage(); die(0); break; case 'V': version(); die(0); break; case 'b': basedir = nopt.opt_arg; break; case ':': fatal_usage("missing argument for option -", optc); break; case '?': if(nopt.opt_got != '?'){ fatal_usage("invalid option -", optc); } /* else fallthrough: */ default : die_usage(); break; } } argc -= nopt.arg_ndx; argv += nopt.arg_ndx; if(!*argv){ fatal_usage("missing argument"); } if(!basedir) basedir = getenv("PERP_BASE"); if(!basedir) basedir = "."; if(chdir(basedir) != 0){ fatal_syserr("fail chdir() to ", basedir); } /* connect to control socket: */ n = cstr_vlen(basedir, "/", PERP_CONTROL, "/", PERPD_SOCKET); if(!(n < sizeof pathbuf)){ errno = ENAMETOOLONG; fatal_syserr("failure locating perpd control socket ", basedir, "/", PERP_CONTROL, "/", PERPD_SOCKET); } cstr_vcopy(pathbuf, basedir, "/", PERP_CONTROL, "/", PERPD_SOCKET); fd_conn = domsock_connect(pathbuf); if(fd_conn == -1){ if(errno == ECONNREFUSED){ fatal_syserr("perpd not running on control socket ", pathbuf); }else{ fatal_syserr("failure connecting to perpd control socket ", pathbuf); } } /* uptimes compared to now: */ tain_now(&now); /* loop through service directory arguments and display report: */ for(; *argv != NULL; ++argv){ pkt_t pkt = pkt_INIT(2, 'Q', 16); struct stat st; if(stat(*argv, &st) == -1){ eputs(*argv, ": error: service directory not found"); continue; } if(! S_ISDIR(st.st_mode)){ eputs(*argv, ": error: not a directory"); continue; } if(!(S_ISVTX & st.st_mode)){ vputs(*argv, ": not activated\n"); continue; } upak_pack(pkt_data(pkt), "LL", (uint64_t)st.st_dev, (uint64_t)st.st_ino); if(pkt_write(fd_conn, pkt, 0) == -1){ eputs_syserr("error: ", *argv, ": error writing query"); continue; } if(pkt_read(fd_conn, pkt, 0) == -1){ eputs_syserr("error: ", *argv, ": error reading response"); continue; } if(pkt[0] != 2){ eputs("error: ", *argv, ": unknown packet protocol in reply"); continue; } if(pkt[1] != 'S'){ if(pkt[1] == 'E'){ errno = (int)upak32_unpack(&pkt[3]); eputs_syserr("error: ", *argv, ": error reported in reply"); } else { eputs("error: ", *argv, ": unknown packet type in reply"); } continue; } report(*argv, pkt_data(pkt), &now); } vputs_flush(); die(0); }