Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions agent/session/shell/shell_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,16 +344,23 @@ func (p *ShellPlugin) generateLogData(log log.T, config agentContracts.Configura
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Minute)
defer cancel()

cmdWithFlag := exec.CommandContext(ctx, startRecordSessionCmd, p.logger.logFilePath, scriptFlag, loggerCmd)
// Invoke script with options before the logfile argument. util-linux >= 2.39 rejects
// positional arguments after the logfile (ec96a89, "script: abort if unused arguments
// are given", Nov 2022). The old forms "script <file> -c <cmd>" and
// "script <file> cmd args" both fail on util-linux >= 2.39 / >= 2.42 respectively.
// Using "-c" with the logfile last is valid on all util-linux versions and on BSD script.
// See: https://github.com/aws/amazon-ssm-agent/issues/667
cmdWithFlag := exec.CommandContext(ctx, startRecordSessionCmd, scriptFlag, loggerCmd, p.logger.logFilePath)
cmdWithFlag.Stderr = &flagStderr
flagErr := cmdWithFlag.Run()
if flagErr != nil {
log.Debugf("Failed to generate transcript with -c flag: %v: %s", flagErr, flagStderr.String())

var noFlagStderr bytes.Buffer

// some versions of "script" does not take a -c flag when passing in commands.
cmdWithoutFlag := exec.CommandContext(ctx, startRecordSessionCmd, p.logger.logFilePath, catCmd, p.logger.ipcFilePath)
// Fallback for script implementations that predate -c support.
// Arguments are ordered to satisfy util-linux >= 2.39 strict parsing.
cmdWithoutFlag := exec.CommandContext(ctx, startRecordSessionCmd, catCmd, p.logger.ipcFilePath, p.logger.logFilePath)
cmdWithoutFlag.Stderr = &noFlagStderr
noFlagErr := cmdWithoutFlag.Run()
if noFlagErr != nil {
Expand Down
35 changes: 35 additions & 0 deletions agent/session/shell/shell_unix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,41 @@ func (suite *ShellTestSuite) TestProcessStreamMessage() {
assert.Equal(suite.T(), "testPayload", string(stdinFileContent))
}

// TestGenerateLogData verifies that generateLogData invokes script(1) with the correct
// argument order for util-linux >= 2.39. Prior to the fix, the logfile was passed as
// the first positional argument ("script <file> -c <cmd>"), which util-linux >= 2.39
// rejects with "unexpected number of arguments". The correct form is
// "script -q -c <cmd> <file>".
//
// This test requires the `script` binary to be present on the system (util-linux or BSD).
func (suite *ShellTestSuite) TestGenerateLogData() {
ipcFile, err := os.CreateTemp("", "ipc-*.log")
assert.Nil(suite.T(), err)
defer os.Remove(ipcFile.Name())

logFile, err := os.CreateTemp("", "transcript-*.log")
assert.Nil(suite.T(), err)
logFile.Close()
defer os.Remove(logFile.Name())

testContent := "hello from generateLogData test"
_, err = ipcFile.WriteString(testContent)
assert.Nil(suite.T(), err)
ipcFile.Close()

suite.plugin.logger = logger{
ipcFilePath: ipcFile.Name(),
logFilePath: logFile.Name(),
}

err = suite.plugin.generateLogData(suite.mockLog, contracts.Configuration{})
assert.Nil(suite.T(), err, "generateLogData should succeed; check that script(1) is installed and util-linux argument order is correct")

transcript, err := os.ReadFile(logFile.Name())
assert.Nil(suite.T(), err)
assert.Contains(suite.T(), string(transcript), testContent, "transcript file should contain the ipc file content")
}

// getAgentMessage constructs and returns AgentMessage with given sequenceNumber, messageType & payload
func getAgentMessage(payloadType uint32, payload []byte) *mgsContracts.AgentMessage {
messageUUID, _ := uuid.Parse(messageId)
Expand Down