func()

in agent/session/shell/shell.go [442:538]


func (p *ShellPlugin) processCommandsWithOutputStreamSeparate(cancelled chan bool,
	cancelFlag task.CancelFlag,
	output iohandler.IOHandler,
	ipcFile *os.File) (err error) {

	log := p.context.Log()

	writeStdOutDone := p.setupRoutineToWriteCmdPipelineOutput(log, ipcFile, false)
	writeStdErrDone := p.setupRoutineToWriteCmdPipelineOutput(log, ipcFile, true)

	if err := p.execCmd.Start(); err != nil {
		errorString := fmt.Errorf("Error occurred starting the command: %s\n", err)
		log.Error(errorString)
		commandExitCode := appconfig.ErrorExitCode
		p.sendExitCode(log, ipcFile, commandExitCode)
		output.MarkAsFailed(errorString)
		return err
	}

	// Wait for session to be completed/cancelled/interrupted
	cmdWaitDone := make(chan error, 1)
	cmdExitCode := make(chan int, 1)
	writeStdOutResult, writeStdErrResult := appconfig.ErrorExitCode, appconfig.ErrorExitCode

	go func() {
		defer func() {
			if err := recover(); err != nil {
				log.Errorf("Write Stdout thread crashed with message: %v\n", err)
				log.Errorf("Stacktrace:\n%s", debug.Stack())
			}
		}()
		log.Debugf("Start separate go routine to wait for command to complete. Pid: %v", p.execCmd.Pid())
		writeStdOutResult, writeStdErrResult = <-writeStdOutDone, <-writeStdErrDone
		log.Debugf("writeStdOutResult: %v, writeStdErrResult: %v", writeStdOutResult, writeStdErrResult)
		close(writeStdOutDone)
		close(writeStdErrDone)

		err := p.execCmd.Wait()
		if err != nil {
			if exiterr, ok := err.(*exec.ExitError); ok {
				log.Infof("Command Exit Status: %d", exiterr.ExitCode())
				cmdExitCode <- exiterr.ExitCode()

			} else {
				log.Errorf("Failed to get exit code, set it to %v", appconfig.ErrorExitCode)
				cmdExitCode <- appconfig.ErrorExitCode
			}
		} else {
			log.Infof("Command success with exit status 0")
			cmdExitCode <- appconfig.SuccessExitCode
		}
		cmdWaitDone <- err
	}()

	select {
	case <-cancelled:
		log.Debug("Session cancelled. Attempting to stop the command execution.")
		if err := p.execCmd.Kill(); err != nil {
			log.Errorf("unable to terminate command execution process %s: %v", p.execCmd.Pid(), err)
		}
		output.SetExitCode(appconfig.SuccessExitCode)
		output.SetStatus(agentContracts.ResultStatusSuccess)
		log.Info("The session was cancelled")

	case cmdWaitErr := <-cmdWaitDone:
		if cmdWaitErr != nil {
			log.Errorf("received error when waiting for command to complete: %v", cmdWaitErr)
		}
		if cancelFlag.Canceled() {
			log.Errorf("the cancellation failed to stop the session.")
		}

		if writeStdOutResult == appconfig.SuccessExitCode && writeStdErrResult == appconfig.SuccessExitCode {
			log.Debugf("Writing session plugin output is done. Exit code: 0.")
			output.SetExitCode(appconfig.SuccessExitCode)
			output.SetStatus(agentContracts.ResultStatusSuccess)
		} else {
			log.Debugf("Writing session plugin output is done. Exit code: 1.")
			output.SetExitCode(appconfig.ErrorExitCode)
			output.SetStatus(agentContracts.ResultStatusFailed)
		}
		commandExitCode := <-cmdExitCode
		close(cmdExitCode)
		log.Infof("The session commandExitCode %d", commandExitCode)
		p.sendExitCode(log, ipcFile, commandExitCode)
	}

	// Call datachannel PrepareToCloseChannel so all messages in the buffer are sent
	p.dataChannel.PrepareToCloseChannel(log)

	// Send session status as Terminating to service on completing command execution
	if err := p.dataChannel.SendAgentSessionStateMessage(log, mgsContracts.Terminating); err != nil {
		log.Errorf("Unable to send AgentSessionState message with session status %s. %v", mgsContracts.Terminating, err)
	}

	return nil
}