Пример #1
0
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) ;
}
Пример #2
0
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;
}
Пример #3
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;
}
Пример #4
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;
}
Пример #5
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;
}
Пример #6
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 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;
}
Пример #7
0
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);
}