Ejemplo n.º 1
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;
}
Ejemplo n.º 2
0
static
void
report(const char *name, const uchar_t *status, const tain_t *now)
{
  pid_t    pid;
  tain_t   when;
  uint64_t uptime;
  uchar_t  flags;
  int      haslog;
  char     nbuf[NFMT_SIZE];

  /* svdef: */
  tain_unpack(&when, &status[16]);
  flags = status[28];
  haslog = (flags & SVDEF_FLAG_HASLOG) ? 1 : 0;

  /* uptime since activation: */
  uptime = tain_uptime(now, &when);
  vputs(name, ": activated ", nfmt_uint64(nbuf, uptime), " seconds");
  if(flags & SVDEF_FLAG_CULL){
      vputs(", deactivation in progress");
  }
  if(flags & SVDEF_FLAG_CYCLE){
      vputs(", flagged for reactivation");
  }
  vputs("\n");

  /* main: */
  pid = upak32_unpack(&status[30]); 
  tain_unpack(&when, &status[34]);
  flags = status[46];
  uptime = tain_uptime(now, &when);
  vputs("  main: ");
  if(pid == 0){
      vputs("down ", nfmt_uint64(nbuf, uptime), " seconds");
      if(!(flags & SUBSV_FLAG_WANTDOWN)) vputs(", want up!");
      if(flags & SUBSV_FLAG_ISONCE) vputs(", flagged once");
  }else if((uptime < 1) && !(flags & SUBSV_FLAG_ISRESET)){
      /* munge uptime < 1 second to want up: */
      vputs("down, want up!");
      if(flags & SUBSV_FLAG_ISONCE) vputs(", flagged once");
  }else{
      vputs((flags & SUBSV_FLAG_ISRESET) ? "resetting " : "up ");
      vputs(nfmt_uint64(nbuf, uptime), " seconds");
      vputs(" (pid ", nfmt_uint32(nbuf, (uint32_t)pid), ")");
      if(flags & SUBSV_FLAG_ISPAUSED) vputs(", paused");
      if(flags & SUBSV_FLAG_ISONCE) vputs(", flagged once");
  }

  /* log: */
  vputs("\n   log: ");
  if(!haslog){
      vputs("(no log)\n");
      return;
  }

  /* else: */
  pid = upak32_unpack(&status[48]);
  tain_unpack(&when, &status[52]);
  flags = status[64];
  uptime = tain_uptime(now, &when);
  if(pid == 0){
      vputs("down ", nfmt_uint64(nbuf, uptime), " seconds");
      if(!(flags & SUBSV_FLAG_WANTDOWN)) vputs(", want up!");
      if(flags & SUBSV_FLAG_ISONCE) vputs(", flagged once");
  }else if((uptime < 1) && !(flags & SUBSV_FLAG_ISRESET)){
      /* munge uptime < 1 second to want up: */
      vputs("down, want up!");
      if(flags & SUBSV_FLAG_ISONCE) vputs(", flagged once");
  }else{
      vputs((flags & SUBSV_FLAG_ISRESET) ? "resetting " : "up ");
      vputs(nfmt_uint64(nbuf, uptime), " seconds");
      vputs(" (pid ", nfmt_uint32(nbuf, (uint32_t)pid), ")");
      if(flags & SUBSV_FLAG_ISPAUSED) vputs(", paused");
      if(flags & SUBSV_FLAG_ISONCE) vputs(", flagged once");
  }
  vputs("\n");

  return;
}