Example #1
0
bool GenericAgentArePromisesValid(const GenericAgentConfig *config)
{
    char cmd[CF_BUFSIZE];

    Log(LOG_LEVEL_VERBOSE, "Verifying the syntax of the inputs...");
    {
        char cfpromises[CF_MAXVARSIZE];
        snprintf(cfpromises, sizeof(cfpromises), "%s%cbin%ccf-promises%s", CFWORKDIR, FILE_SEPARATOR, FILE_SEPARATOR,
                 EXEC_SUFFIX);

        struct stat sb;
        if (stat(cfpromises, &sb) == -1)
        {
            Log(LOG_LEVEL_ERR, "cf-promises%s needs to be installed in %s%cbin for pre-validation of full configuration",
                  EXEC_SUFFIX, CFWORKDIR, FILE_SEPARATOR);
            return false;
        }

        if (config->bundlesequence)
        {
            snprintf(cmd, sizeof(cmd), "\"%s\" \"", cfpromises);
        }
        else
        {
            snprintf(cmd, sizeof(cmd), "\"%s\" -c \"", cfpromises);
        }
    }

    strlcat(cmd, config->input_file, CF_BUFSIZE);

    strlcat(cmd, "\"", CF_BUFSIZE);

    if (config->bundlesequence)
    {
        strlcat(cmd, " -b \"", CF_BUFSIZE);
        for (const Rlist *rp = config->bundlesequence; rp; rp = rp->next)
        {
            const char *bundle_ref = RlistScalarValue(rp);
            strlcat(cmd, bundle_ref, CF_BUFSIZE);

            if (rp->next)
            {
                strlcat(cmd, ",", CF_BUFSIZE);
            }
        }
        strlcat(cmd, "\"", CF_BUFSIZE);
    }

    Log(LOG_LEVEL_VERBOSE, "Checking policy with command '%s'", cmd);

    if (!ShellCommandReturnsZero(cmd, true))
    {
        Log(LOG_LEVEL_ERR, "Policy failed validation with command '%s'", cmd);
        return false;
    }

    return true;
}
Example #2
0
static int SelectExecProgram(char *filename, char *command)
  /* command can include $(this.promiser) for the name of the file */
{
    char buf[CF_MAXVARSIZE];

// insert real value of $(this.promiser) in command

    ReplaceStr(command, buf, sizeof(buf), "$(this.promiser)", filename);
    ReplaceStr(command, buf, sizeof(buf), "${this.promiser}", filename);

    if (ShellCommandReturnsZero(buf, false))
    {
        CfDebug(" - ? Select ExecProgram match for %s\n", buf);
        return true;
    }
    else
    {
        return false;
    }
}
Example #3
0
static int SelectExecProgram(char *filename, char *command)
  /* command can include $(this.promiser) for the name of the file */
{
    char buf[CF_MAXVARSIZE];

// insert real value of $(this.promiser) in command

    ReplaceStr(command, buf, sizeof(buf), "$(this.promiser)", filename);
    ReplaceStr(command, buf, sizeof(buf), "${this.promiser}", filename);

    if (ShellCommandReturnsZero(buf, SHELL_TYPE_NONE))
    {
        Log(LOG_LEVEL_DEBUG, "Select ExecProgram match for '%s'", buf);
        return true;
    }
    else
    {
        return false;
    }
}
Example #4
0
static int SelectExecProgram(char *filename, char *command)
  /* command can include $(this.promiser) for the name of the file */
{
// insert real value of $(this.promiser) in command

    char *buf_tmp = SearchAndReplace(command, "$(this.promiser)", filename);
    char *buf = SearchAndReplace(buf_tmp, "${this.promiser}", filename);
    free(buf_tmp);

    bool returns_zero = ShellCommandReturnsZero(buf, SHELL_TYPE_NONE);

    if (returns_zero)
    {
        Log(LOG_LEVEL_DEBUG, "Select ExecProgram match for '%s'", buf);
        free(buf);
        return true;
    }
    else
    {
        free(buf);
        return false;
    }
}
Example #5
0
static void VerifyProcessOp(EvalContext *ctx, Item *procdata, Attributes a, Promise *pp)
{
    int matches = 0, do_signals = true, out_of_range, killed = 0, need_to_restart = true;
    Item *killlist = NULL;

    matches = FindPidMatches(ctx, procdata, &killlist, a, pp->promiser);

/* promise based on number of matches */

    if (a.process_count.min_range != CF_NOINT)  /* if a range is specified */
    {
        if ((matches < a.process_count.min_range) || (matches > a.process_count.max_range))
        {
            cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_CHANGE, pp, a, "Process count for '%s' was out of promised range (%d found)", pp->promiser, matches);
            for (const Rlist *rp = a.process_count.out_of_range_define; rp != NULL; rp = rp->next)
            {
                if (!EvalContextHeapContainsSoft(ctx, rp->item))
                {
                    EvalContextHeapAddSoft(ctx, rp->item, PromiseGetNamespace(pp));
                }
            }
            out_of_range = true;
        }
        else
        {
            for (const Rlist *rp = a.process_count.in_range_define; rp != NULL; rp = rp->next)
            {
                if (!EvalContextHeapContainsSoft(ctx, rp->item))
                {
                    EvalContextHeapAddSoft(ctx, rp->item, PromiseGetNamespace(pp));
                }
            }
            cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Process promise for '%s' is kept", pp->promiser);
            out_of_range = false;
        }
    }
    else
    {
        out_of_range = true;
    }

    if (!out_of_range)
    {
        return;
    }

    if (a.transaction.action == cfa_warn)
    {
        do_signals = false;
    }
    else
    {
        do_signals = true;
    }

/* signal/kill promises for existing matches */

    if (do_signals && (matches > 0))
    {
        if (a.process_stop != NULL)
        {
            if (DONTDO)
            {
                cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_WARN, pp, a,
                     "Need to keep process-stop promise for '%s', but only a warning is promised", pp->promiser);
            }
            else
            {
                if (IsExecutable(CommandArg0(a.process_stop)))
                {
                    ShellCommandReturnsZero(a.process_stop, SHELL_TYPE_NONE);
                }
                else
                {
                    cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_FAIL, pp, a,
                         "Process promise to stop '%s' could not be kept because '%s' the stop operator failed",
                         pp->promiser, a.process_stop);
                    DeleteItemList(killlist);
                    return;
                }
            }
        }

        killed = DoAllSignals(ctx, killlist, a, pp);
    }

/* delegated promise to restart killed or non-existent entries */

    need_to_restart = (a.restart_class != NULL) && (killed || (matches == 0));

    DeleteItemList(killlist);

    if (!need_to_restart)
    {
        cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "No restart promised for %s", pp->promiser);
        return;
    }
    else
    {
        if (a.transaction.action == cfa_warn)
        {
            cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_WARN, pp, a,
                 "Need to keep restart promise for '%s', but only a warning is promised", pp->promiser);
        }
        else
        {
            cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a, "Making a one-time restart promise for '%s'", pp->promiser);
            EvalContextHeapAddSoft(ctx, a.restart_class, PromiseGetNamespace(pp));
        }
    }
}
Example #6
0
static PromiseResult VerifyProcessOp(EvalContext *ctx, Item *procdata, Attributes a, const Promise *pp)
{
    bool do_signals = true;
    int out_of_range;
    int killed = 0;
    bool need_to_restart = true;
    Item *killlist = NULL;

    int matches = FindPidMatches(procdata, &killlist, a, pp->promiser);

/* promise based on number of matches */

    PromiseResult result = PROMISE_RESULT_NOOP;
    if (a.process_count.min_range != CF_NOINT)  /* if a range is specified */
    {
        if ((matches < a.process_count.min_range) || (matches > a.process_count.max_range))
        {
            cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_CHANGE, pp, a, "Process count for '%s' was out of promised range (%d found)", pp->promiser, matches);
            result = PromiseResultUpdate(result, PROMISE_RESULT_CHANGE);
            for (const Rlist *rp = a.process_count.out_of_range_define; rp != NULL; rp = rp->next)
            {
                ClassRef ref = ClassRefParse(RlistScalarValue(rp));
                EvalContextClassPutSoft(ctx, RlistScalarValue(rp), CONTEXT_SCOPE_NAMESPACE, "source=promise");
                ClassRefDestroy(ref);
            }
            out_of_range = true;
        }
        else
        {
            for (const Rlist *rp = a.process_count.in_range_define; rp != NULL; rp = rp->next)
            {
                ClassRef ref = ClassRefParse(RlistScalarValue(rp));
                EvalContextClassPutSoft(ctx, RlistScalarValue(rp), CONTEXT_SCOPE_NAMESPACE, "source=promise");
                ClassRefDestroy(ref);
            }
            cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "Process promise for '%s' is kept", pp->promiser);
            out_of_range = false;
        }
    }
    else
    {
        out_of_range = true;
    }

    if (!out_of_range)
    {
        DeleteItemList(killlist);
        return result;
    }

    if (a.transaction.action == cfa_warn)
    {
        do_signals = false;
        result = PromiseResultUpdate(result, PROMISE_RESULT_WARN);
    }
    else
    {
        do_signals = true;
    }

/* signal/kill promises for existing matches */

    if (do_signals && (matches > 0))
    {
        if (a.process_stop != NULL)
        {
            if (DONTDO)
            {
                cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_WARN, pp, a,
                     "Need to keep process-stop promise for '%s', but only a warning is promised", pp->promiser);
                result = PromiseResultUpdate(result, PROMISE_RESULT_WARN);
            }
            else
            {
                if (IsExecutable(CommandArg0(a.process_stop)))
                {
                    ShellCommandReturnsZero(a.process_stop, SHELL_TYPE_NONE);
                }
                else
                {
                    cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_FAIL, pp, a,
                         "Process promise to stop '%s' could not be kept because '%s' the stop operator failed",
                         pp->promiser, a.process_stop);
                    result = PromiseResultUpdate(result, PROMISE_RESULT_FAIL);
                    DeleteItemList(killlist);
                    return result;
                }
            }
        }

        killed = DoAllSignals(ctx, killlist, a, pp, &result);
    }

/* delegated promise to restart killed or non-existent entries */

    need_to_restart = (a.restart_class != NULL) && (killed || (matches == 0));

    DeleteItemList(killlist);

    if (!need_to_restart)
    {
        cfPS(ctx, LOG_LEVEL_VERBOSE, PROMISE_RESULT_NOOP, pp, a, "No restart promised for %s", pp->promiser);
        return result;
    }
    else
    {
        if (a.transaction.action == cfa_warn)
        {
            cfPS(ctx, LOG_LEVEL_ERR, PROMISE_RESULT_WARN, pp, a,
                 "Need to keep restart promise for '%s', but only a warning is promised", pp->promiser);
            result = PromiseResultUpdate(result, PROMISE_RESULT_WARN);
        }
        else
        {
            cfPS(ctx, LOG_LEVEL_INFO, PROMISE_RESULT_CHANGE, pp, a, "Making a one-time restart promise for '%s'", pp->promiser);
            result = PromiseResultUpdate(result, PROMISE_RESULT_CHANGE);
            EvalContextClassPutSoft(ctx, a.restart_class, CONTEXT_SCOPE_NAMESPACE, "source=promise");
        }
    }

    return result;
}
Example #7
0
bool GenericAgentCheckPromises(const GenericAgentConfig *config)
{
    char cmd[CF_BUFSIZE];

    Log(LOG_LEVEL_VERBOSE, "Verifying the syntax of the inputs...");
    {
        char cfpromises[CF_MAXVARSIZE];
        snprintf(cfpromises, sizeof(cfpromises), "%s%cbin%ccf-promises%s", CFWORKDIR, FILE_SEPARATOR, FILE_SEPARATOR,
                 EXEC_SUFFIX);

        struct stat sb;
        if (stat(cfpromises, &sb) == -1)
        {
            Log(LOG_LEVEL_ERR, "cf-promises%s needs to be installed in %s%cbin for pre-validation of full configuration",
                  EXEC_SUFFIX, CFWORKDIR, FILE_SEPARATOR);
            return false;
        }

        if (config->bundlesequence)
        {
            snprintf(cmd, sizeof(cmd), "\"%s\" \"", cfpromises);
        }
        else
        {
            snprintf(cmd, sizeof(cmd), "\"%s\" -c \"", cfpromises);
        }
    }

    strlcat(cmd, config->input_file, CF_BUFSIZE);

    strlcat(cmd, "\"", CF_BUFSIZE);

    if (config->bundlesequence)
    {
        strlcat(cmd, " -b \"", CF_BUFSIZE);
        for (const Rlist *rp = config->bundlesequence; rp; rp = rp->next)
        {
            const char *bundle_ref = rp->item;
            strlcat(cmd, bundle_ref, CF_BUFSIZE);

            if (rp->next)
            {
                strlcat(cmd, ",", CF_BUFSIZE);
            }
        }
        strlcat(cmd, "\"", CF_BUFSIZE);
    }

    if (config->agent_specific.agent.bootstrap_policy_server)
    {
        // avoids license complains from commercial cf-promises during bootstrap - see Nova_CheckLicensePromise
        strlcat(cmd, " -D bootstrap_mode", CF_BUFSIZE);
    }

    Log(LOG_LEVEL_VERBOSE, "Checking policy with command '%s'", cmd);

    if (!ShellCommandReturnsZero(cmd, true))
    {
        Log(LOG_LEVEL_ERR, "Policy failed validation with command '%s'", cmd);
        return false;
    }

    if (!IsFileOutsideDefaultRepository(config->original_input_file))
    {
        WritePolicyValidatedFile(config);
    }

    return true;
}
int CheckPromises(enum cfagenttype ag)

{ char cmd[CF_BUFSIZE], cfpromises[CF_MAXVARSIZE];
  char filename[CF_MAXVARSIZE];
  struct stat sb;
  int fd;

if ((ag != cf_agent) && (ag != cf_executor) && (ag != cf_server))
   {
   return true;
   }

CfOut(cf_verbose,""," -> Verifying the syntax of the inputs...\n");

snprintf(cfpromises,sizeof(cfpromises),"%s%cbin%ccf-promises%s",CFWORKDIR,FILE_SEPARATOR,FILE_SEPARATOR,EXEC_SUFFIX);

if (cfstat(cfpromises,&sb) == -1)
   {
   CfOut(cf_error,"","cf-promises%s needs to be installed in %s%cbin for pre-validation of full configuration",EXEC_SUFFIX,CFWORKDIR,FILE_SEPARATOR);
   return false;
   }

/* If we are cf-agent, check syntax before attempting to run */

snprintf(cmd, sizeof(cmd), "\"%s\" -f \"", cfpromises);


if (IsFileOutsideDefaultRepository(VINPUTFILE))
   {
   strlcat(cmd, VINPUTFILE, CF_BUFSIZE);
   }
else
   {
   strlcat(cmd, CFWORKDIR, CF_BUFSIZE);
   strlcat(cmd, FILE_SEPARATOR_STR "inputs" FILE_SEPARATOR_STR, CF_BUFSIZE);
   strlcat(cmd, VINPUTFILE, CF_BUFSIZE);
   }

strlcat(cmd, "\"", CF_BUFSIZE);

if (CBUNDLESEQUENCE)
   {
   strlcat(cmd, " -b \"", CF_BUFSIZE);
   strlcat(cmd, CBUNDLESEQUENCE_STR, CF_BUFSIZE);
   strlcat(cmd, "\"", CF_BUFSIZE);
   }

if(BOOTSTRAP)
   {
   // avoids license complains from commercial cf-promises during bootstrap - see Nova_CheckLicensePromise
   strlcat(cmd, " -D bootstrap_mode", CF_BUFSIZE);
   }

/* Check if reloading policy will succeed */

CfOut(cf_verbose, "", "Checking policy with command \"%s\"", cmd);

if (ShellCommandReturnsZero(cmd,true))
   {
   if (MINUSF)
      {
      snprintf(filename,CF_MAXVARSIZE,"%s/state/validated_%s",CFWORKDIR,CanonifyName(VINPUTFILE));
      MapName(filename);   
      }
   else
      {
      snprintf(filename,CF_MAXVARSIZE,"%s/masterfiles/cf_promises_validated",CFWORKDIR);
      MapName(filename);
      }
   
   MakeParentDirectory(filename,true);
   
   if ((fd = creat(filename,0600)) != -1)
      {
      close(fd);
      CfOut(cf_verbose,""," -> Caching the state of validation\n");
      }
   else
      {
      CfOut(cf_verbose,"creat"," -> Failed to cache the state of validation\n");
      }
   
   return true;
   }
else
   {
   return false;
   }
}
Example #9
0
void SetDefaultRoute()

{ int sk, defaultokay = 1;
  struct sockaddr_in sindst,singw;
  char oldroute[INET_ADDRSTRLEN];
  char routefmt[CF_MAXVARSIZE];

/* These OSes have these structs defined but use the route command */
# if defined DARWIN || defined FREEBSD || defined OPENBSD || defined SOLARIS
#  undef HAVE_RTENTRY
#  undef HAVE_ORTENTRY
# endif

# ifdef HAVE_ORTENTRY
   struct ortentry route;
# else
#  if HAVE_RTENTRY
   struct rtentry route;
#  endif
# endif

  FILE *pp;

Verbose("Looking for a default route...\n");

if (!IsPrivileged())                            
   {
   snprintf(OUTPUT,CF_BUFSIZE*2,"Only root can set a default route.");
   CfLog(cfinform,OUTPUT,"");
   return;
   }

if (VDEFAULTROUTE == NULL)
   {
   Verbose("cfengine: No default route is defined. Ignoring the routing tables.\n");
   return;
   }

if ((pp = cfpopen(VNETSTAT[VSYSTEMHARDCLASS],"r")) == NULL)
   {
   snprintf(OUTPUT,CF_BUFSIZE*2,"Failed to open pipe from %s\n",VNETSTAT[VSYSTEMHARDCLASS]);
   CfLog(cferror,OUTPUT,"popen");
   return;
   }

while (!feof(pp))
   {
   ReadLine(VBUFF,CF_BUFSIZE,pp);

   Debug("LINE: %s = %s?\n",VBUFF,VDEFAULTROUTE->name);
   
   if ((strncmp(VBUFF,"default",7) == 0)||(strncmp(VBUFF,"0.0.0.0",7) == 0))
      {
      /* extract the default route */
      /* format: default|0.0.0.0 <whitespace> route <whitespace> etc */
      if ((sscanf(VBUFF, "%*[default0. ]%s%*[ ]", &oldroute)) == 1)
        {
        if ((strncmp(VDEFAULTROUTE->name, oldroute, INET_ADDRSTRLEN)) == 0)
          {
          Verbose("cfengine: default route is already set to %s\n",VDEFAULTROUTE->name);
          defaultokay = 1;
          break;
          }
        else
          {
          Verbose("cfengine: default route is set to %s, but should be %s.\n",oldroute,VDEFAULTROUTE->name);
          defaultokay = 2;
          break;
          }
        }
      }
   else
      {
      Debug("No default route is yet registered\n");
      defaultokay = 0;
      }
   }

cfpclose(pp);

if (defaultokay == 1)
   {
   Verbose("Default route is set and agrees with conditional policy\n");
   return;
   }

if (defaultokay == 0)
   {
   AddMultipleClasses("no_default_route");
   }

if (IsExcluded(VDEFAULTROUTE->classes))
   {
   Verbose("cfengine: No default route is applicable. Ignoring the routing tables.\n");
   return;   
   }

CfLog(cferror,"The default route is incorrect, trying to correct\n","");

if ( strcmp(VROUTE[VSYSTEMHARDCLASS], "-") != 0 )
   {

   Debug ("Using route shell commands to set default route\n");
   if (defaultokay == 2)
      {
      if (! DONTDO)
         {
         /* get the route command and the format for the delete argument */
         snprintf(routefmt,CF_MAXVARSIZE,"%s %s",VROUTE[VSYSTEMHARDCLASS],VROUTEDELFMT[VSYSTEMHARDCLASS]);
         snprintf(VBUFF,CF_MAXVARSIZE,routefmt,"default",VDEFAULTROUTE->name);
         if (ShellCommandReturnsZero(VBUFF,false))
            {
            CfLog(cfinform,"Removing old default route","");
            CfLog(cfinform,VBUFF,"");
            }
         else
            {
            CfLog(cferror,"Error removing route","");
            }
         }
      }
   
   if (! DONTDO)
      {
      snprintf(routefmt,CF_MAXVARSIZE,"%s %s",VROUTE[VSYSTEMHARDCLASS],VROUTEADDFMT[VSYSTEMHARDCLASS]);
      snprintf(VBUFF,CF_MAXVARSIZE,routefmt,"default",VDEFAULTROUTE->name);
      if (ShellCommandReturnsZero(VBUFF,false))
         {
         CfLog(cfinform,"Setting default route","");
         CfLog(cfinform,VBUFF,"");
         }
      else
         {
         CfLog(cferror,"Error setting route","");
         }
      }
   return;
   }
else
   {
#if defined HAVE_RTENTRY || defined HAVE_ORTENTRY
   Debug ("Using route ioctl to set default route\n");
   if ((sk = socket(AF_INET,SOCK_RAW,0)) == -1)
      {
      CfLog(cferror,"System class: ", CLASSTEXT[VSYSTEMHARDCLASS]);
      CfLog(cferror,"","Error in SetDefaultRoute():");
      perror("cfengine: socket");
      }
   else
      {
      sindst.sin_family = AF_INET;
      singw.sin_family = AF_INET;

      sindst.sin_addr.s_addr = INADDR_ANY;
      singw.sin_addr.s_addr = inet_addr(VDEFAULTROUTE->name);

      route.rt_dst = *(struct sockaddr *)&sindst;      /* This disgusting method is necessary */
      route.rt_gateway = *(struct sockaddr *)&singw;
      route.rt_flags = RTF_GATEWAY;

      if (! DONTDO)
         {
         if (ioctl(sk,SIOCADDRT, (caddr_t) &route) == -1)   /* Get the device status flags */
            {
            CfLog(cferror,"Error setting route:","");
            perror("cfengine: ioctl SIOCADDRT:");
            }
         else
            {
            CfLog(cferror,"Setting default route.\n","");
            snprintf(OUTPUT,CF_BUFSIZE*2,"I'm setting it to %s\n",VDEFAULTROUTE->name);
            CfLog(cferror,OUTPUT,"");
            }
         }
      }
#else

   /* Socket routing - don't really know how to do this yet */ 

   Verbose("Sorry don't know how to do routing on this platform\n");
 
#endif
   }
}
Example #10
0
static void VerifyProcessOp(Item *procdata, Attributes a, Promise *pp)
{
    int matches = 0, do_signals = true, out_of_range, killed = 0, need_to_restart = true;
    Item *killlist = NULL;

    CfDebug("VerifyProcessOp\n");

    matches = FindPidMatches(procdata, &killlist, a, pp);

/* promise based on number of matches */

    if (a.process_count.min_range != CF_NOINT)  /* if a range is specified */
    {
        if (matches < a.process_count.min_range || matches > a.process_count.max_range)
        {
            cfPS(cf_error, CF_CHG, "", pp, a, " !! Process count for \'%s\' was out of promised range (%d found)\n",
                 pp->promiser, matches);
            AddEphemeralClasses(a.process_count.out_of_range_define);
            out_of_range = true;
        }
        else
        {
            AddEphemeralClasses(a.process_count.in_range_define);
            cfPS(cf_verbose, CF_NOP, "", pp, a, " -> Process promise for %s is kept", pp->promiser);
            out_of_range = false;
        }
    }
    else
    {
        out_of_range = true;
    }

    if (!out_of_range)
    {
        return;
    }

    if (a.transaction.action == cfa_warn)
    {
        do_signals = false;
    }
    else
    {
        do_signals = true;
    }

/* signal/kill promises for existing matches */

    if (do_signals && matches > 0)
    {
        if (a.process_stop != NULL)
        {
            if (DONTDO)
            {
                cfPS(cf_error, CF_WARN, "", pp, a,
                     " -- Need to keep process-stop promise for %s, but only a warning is promised", pp->promiser);
            }
            else
            {
                if (IsExecutable(GetArg0(a.process_stop)))
                {
                    ShellCommandReturnsZero(a.process_stop, false);
                }
                else
                {
                    cfPS(cf_verbose, CF_FAIL, "", pp, a,
                         "Process promise to stop %s could not be kept because %s the stop operator failed",
                         pp->promiser, a.process_stop);
                    DeleteItemList(killlist);
                    return;
                }
            }
        }

        killed = DoAllSignals(killlist, a, pp);
    }

/* delegated promise to restart killed or non-existent entries */

    need_to_restart = (a.restart_class != NULL) && (killed || matches == 0);

    DeleteItemList(killlist);

    if (!need_to_restart)
    {
        cfPS(cf_verbose, CF_NOP, "", pp, a, " -> No restart promised for %s\n", pp->promiser);
        return;
    }
    else
    {
        if (a.transaction.action == cfa_warn)
        {
            cfPS(cf_error, CF_WARN, "", pp, a,
                 " -- Need to keep restart promise for %s, but only a warning is promised", pp->promiser);
        }
        else
        {
            cfPS(cf_inform, CF_CHG, "", pp, a, " -> Making a one-time restart promise for %s", pp->promiser);
            NewClass(a.restart_class);
        }
    }
}