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
}