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
This commit is contained in:
Kevin DuBois 2021-10-05 15:41:02 -07:00
parent 17acadbcb0
commit 0a5da7025e

View file

@ -164,7 +164,7 @@ func run() error {
if useSubDir { if useSubDir {
localTempDir = filepath.Join(localTempDir, strconv.Itoa(i)) localTempDir = filepath.Join(localTempDir, strconv.Itoa(i))
} }
depFile, err := runCommand(command, localTempDir) depFile, err := runCommand(command, localTempDir, i)
if err != nil { if err != nil {
// Running the command failed, keep the temporary output directory around in // Running the command failed, keep the temporary output directory around in
// case a user wants to inspect it for debugging purposes. Soong will delete // case a user wants to inspect it for debugging purposes. Soong will delete
@ -194,6 +194,28 @@ func run() error {
return nil 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. // readManifest reads an sbox manifest from a textproto file.
func readManifest(file string) (*sbox_proto.Manifest, error) { func readManifest(file string) (*sbox_proto.Manifest, error) {
manifestData, err := ioutil.ReadFile(file) 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 // 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. // __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() rawCommand := command.GetCommand()
if rawCommand == "" { if rawCommand == "" {
return "", fmt.Errorf("command is required") return "", fmt.Errorf("command is required")
@ -255,7 +277,11 @@ func runCommand(command *sbox_proto.Command, tempDir string) (depFile string, er
return "", err return "", err
} }
cmd := exec.Command("bash", "-c", rawCommand) cmd, err := createCommandScript(rawCommand, tempDir, pathToTempDirInSbox, commandIndex)
if err != nil {
return "", err
}
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
cmd.Stdout = buf cmd.Stdout = buf