// Run starts the caretakerd execution, every service and required resource. // This is a blocking method. func (instance *Execution) Run() (values.ExitCode, error) { autoStartableServices := instance.executable.Services().GetAllAutoStartable() // Start all non-master services first to start the master properly. for _, target := range autoStartableServices { if target.Config().Type != service.Master { instance.startAndLogProblemsIfNeeded(target) } } // Now start the master. masterStarted := false for _, target := range autoStartableServices { if target.Config().Type == service.Master { err := instance.Start(target) if err != nil { (*instance).masterError = err } masterStarted = true } } if !masterStarted { (*instance).masterError = errors.New("No master was started. There are no master configured?") } if (*instance).masterError != nil { instance.stopOthers() } instance.wg.Wait() exitCode := values.ExitCode(-1) if (*instance).masterExitCode != nil { exitCode = *(*instance).masterExitCode } return exitCode, (*instance).masterError }
func (instance *Execution) runCommand(cmd *exec.Cmd) (values.ExitCode, error) { var waitStatus syscall.WaitStatus if err := cmd.Run(); err != nil { if exitError, ok := err.(*exec.ExitError); ok { waitStatus = exitError.Sys().(syscall.WaitStatus) exitSignal := waitStatus.Signal() if exitSignal > 0 { return values.ExitCode(int(exitSignal) + 128), nil } return values.ExitCode(waitStatus.ExitStatus()), nil } return values.ExitCode(0), UnrecoverableError{error: err} } waitStatus = cmd.ProcessState.Sys().(syscall.WaitStatus) return values.ExitCode(waitStatus.ExitStatus()), nil }
// Run runs this execution. // This method is a blocking method and could only be executed at this instance once. func (instance *Execution) Run() (values.ExitCode, error) { err := instance.handleBeforeRun() if err != nil { return values.ExitCode(1), err } exitCode, err := instance.preExecution() if err != nil || exitCode != 0 { return exitCode, err } instance.logger.Log(logger.Debug, "Start service '%s' with command: %s", instance.Name(), instance.commandLineOf(instance.cmd)) exitCode, lastState, err := instance.runBare() if lastState == Killed { err = StoppedOrKilledError{error: errors.New("Process was killed.")} instance.logger.Log(logger.Debug, "Service '%s' ended after kill: %d", instance.Name(), exitCode) } else if lastState == Stopped { err = StoppedOrKilledError{error: errors.New("Process was stopped.")} instance.logger.Log(logger.Debug, "Service '%s' ended successful after stop: %d", instance.Name(), exitCode) } else if err != nil { instance.logger.Log(logger.Fatal, err) } else if instance.service.config.SuccessExitCodes.Contains(exitCode) { instance.logger.Log(logger.Debug, "Service '%s' ended successful: %d", instance.Name(), exitCode) } else { instance.logger.Log(logger.Error, "Service '%s' ended with unexpected code: %d", instance.Name(), exitCode) err = errors.New("Unexpected error code %d generated by service '%s'", exitCode, instance.Name()) } instance.postExecution() return exitCode, err }
func (instance *Execution) runBare() (values.ExitCode, Status, error) { if instance.doTrySetRunningState() { defer instance.doSetDownState() exitCode, err := instance.runCommand((*instance).cmd) // This little sleep is required because there is no guarantee anymore that every lock is // respected if the routines are interrupted. time.Sleep(5 * time.Millisecond) return exitCode, instance.getSyncedCurrentStatus(), err } return values.ExitCode(0), instance.getSyncedCurrentStatus(), UnrecoverableError{error: errors.New("Cannot run service. Already in status: %v", instance.status)} }
func (instance *Config) init() { (*instance).Logger = logger.NewConfig() (*instance).Command = []values.String{} (*instance).PreCommands = [][]values.String{} (*instance).PostCommands = [][]values.String{} (*instance).Type = AutoStart (*instance).CronExpression = NewCronExpression() (*instance).StartDelayInSeconds = values.NonNegativeInteger(0) (*instance).RestartDelayInSeconds = values.NonNegativeInteger(5) (*instance).SuccessExitCodes = values.ExitCodes{values.ExitCode(0)} (*instance).StopSignal = defaultStopSignal() (*instance).StopSignalTarget = values.ProcessGroup (*instance).StopCommand = []values.String{} (*instance).StopWaitInSeconds = values.NonNegativeInteger(30) (*instance).User = values.String("") (*instance).Environment = Environments{} (*instance).Directory = values.String("") (*instance).AutoRestart = values.OnFailures (*instance).InheritEnvironment = values.Boolean(true) (*instance).Access = access.NewNoneConfig() }
func (instance *Execution) preExecution() (values.ExitCode, error) { preCommands := instance.service.config.PreCommands for _, preCommand := range preCommands { command, handleErrors := instance.extractCommandProperties(preCommand) if len(command) > 0 { cmd := instance.generateCmd(command) instance.logger.Log(logger.Debug, "Execute pre command: %s", instance.commandLineOf(cmd)) exitCode, err := instance.runCommand(cmd) if handleErrors { if err != nil { instance.logger.LogProblem(err, logger.Error, "Pre command failed.") return exitCode, err } else if exitCode != 0 { instance.logger.Log(logger.Error, "Pre command failed. Exit with unexpected exit code: %d", exitCode) return exitCode, err } } } } return values.ExitCode(0), nil }