From 0a5da7025e651fa14570fe967d7fc36b333345a2 Mon Sep 17 00:00:00 2001 From: Kevin DuBois Date: Tue, 5 Oct 2021 15:41:02 -0700 Subject: [PATCH] sbox: run commands using script for large commands Linux has limit of 32 pages for a single argument. Sbox's method of handling commands as a single large string can run into this limitation. Write the large command to a script file and then execute that file. This better accommodates the large commands, and leaves a script in the temporary directory useful for manual inspection if necessary. Bug: 177070955 Bug: 174232579 Fixes: 202297224 Test: m Test: make particular target with very long command exceeding limit. Change-Id: Ia298fdfd7a759821c37f540deaf800026041e511 --- cmd/sbox/sbox.go | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go index c7f3f6a44..4fa748660 100644 --- a/cmd/sbox/sbox.go +++ b/cmd/sbox/sbox.go @@ -164,7 +164,7 @@ func run() error { if useSubDir { localTempDir = filepath.Join(localTempDir, strconv.Itoa(i)) } - depFile, err := runCommand(command, localTempDir) + depFile, err := runCommand(command, localTempDir, i) if err != nil { // Running the command failed, keep the temporary output directory around in // case a user wants to inspect it for debugging purposes. Soong will delete @@ -194,6 +194,28 @@ func run() error { return nil } +// createCommandScript will create and return an exec.Cmd that runs rawCommand. +// +// rawCommand is executed via a script in the sandbox. +// tempDir is the temporary where the script is created. +// toDirInSandBox is the path containing the script in the sbox environment. +// toDirInSandBox is the path containing the script in the sbox environment. +// seed is a unique integer used to distinguish different scripts that might be at location. +// +// returns an exec.Cmd that can be ran from within sbox context if no error, or nil if error. +// caller must ensure script is cleaned up if function succeeds. +// +func createCommandScript(rawCommand string, tempDir, toDirInSandbox string, seed int) (*exec.Cmd, error) { + scriptName := fmt.Sprintf("sbox_command.%d.bash", seed) + scriptPathAndName := joinPath(tempDir, scriptName) + err := os.WriteFile(scriptPathAndName, []byte(rawCommand), 0644) + if err != nil { + return nil, fmt.Errorf("failed to write command %s... to %s", + rawCommand[0:40], scriptPathAndName) + } + return exec.Command("bash", joinPath(toDirInSandbox, filepath.Base(scriptName))), nil +} + // readManifest reads an sbox manifest from a textproto file. func readManifest(file string) (*sbox_proto.Manifest, error) { manifestData, err := ioutil.ReadFile(file) @@ -213,7 +235,7 @@ func readManifest(file string) (*sbox_proto.Manifest, error) { // runCommand runs a single command from a manifest. If the command references the // __SBOX_DEPFILE__ placeholder it returns the name of the depfile that was used. -func runCommand(command *sbox_proto.Command, tempDir string) (depFile string, err error) { +func runCommand(command *sbox_proto.Command, tempDir string, commandIndex int) (depFile string, err error) { rawCommand := command.GetCommand() if rawCommand == "" { return "", fmt.Errorf("command is required") @@ -255,7 +277,11 @@ func runCommand(command *sbox_proto.Command, tempDir string) (depFile string, er return "", err } - cmd := exec.Command("bash", "-c", rawCommand) + cmd, err := createCommandScript(rawCommand, tempDir, pathToTempDirInSbox, commandIndex) + if err != nil { + return "", err + } + buf := &bytes.Buffer{} cmd.Stdin = os.Stdin cmd.Stdout = buf