| // Copyright 2017 The Bazel Authors. All rights reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| package com.google.devtools.build.lib.sandbox; | |
| import com.google.common.collect.ImmutableMap; | |
| import com.google.common.collect.ImmutableSet; | |
| import com.google.devtools.build.lib.actions.ForbiddenActionInputException; | |
| import com.google.devtools.build.lib.actions.Spawn; | |
| import com.google.devtools.build.lib.exec.TreeDeleter; | |
| import com.google.devtools.build.lib.exec.local.LocalEnvProvider; | |
| import com.google.devtools.build.lib.runtime.CommandEnvironment; | |
| import com.google.devtools.build.lib.runtime.ProcessWrapper; | |
| import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxInputs; | |
| import com.google.devtools.build.lib.sandbox.SandboxHelpers.SandboxOutputs; | |
| import com.google.devtools.build.lib.util.OS; | |
| import com.google.devtools.build.lib.vfs.Path; | |
| import com.google.devtools.build.lib.vfs.PathFragment; | |
| import java.io.IOException; | |
| import java.time.Duration; | |
| import javax.annotation.Nullable; | |
| /** Strategy that uses sandboxing to execute a process. */ | |
| final class ProcessWrapperSandboxedSpawnRunner extends AbstractSandboxSpawnRunner { | |
| public static boolean isSupported(CommandEnvironment cmdEnv) { | |
| return OS.isPosixCompatible() && ProcessWrapper.fromCommandEnvironment(cmdEnv) != null; | |
| } | |
| private final SandboxHelpers helpers; | |
| private final ProcessWrapper processWrapper; | |
| private final Path execRoot; | |
| private final Path sandboxBase; | |
| private final LocalEnvProvider localEnvProvider; | |
| @Nullable private final SandboxfsProcess sandboxfsProcess; | |
| private final boolean sandboxfsMapSymlinkTargets; | |
| private final TreeDeleter treeDeleter; | |
| /** | |
| * Creates a sandboxed spawn runner that uses the {@code process-wrapper} tool. | |
| * | |
| * @param helpers common tools and state across all spawns during sandboxed execution | |
| * @param cmdEnv the command environment to use | |
| * @param sandboxBase path to the sandbox base directory | |
| * @param sandboxfsProcess instance of the sandboxfs process to use; may be null for none, in | |
| * which case the runner uses a symlinked sandbox | |
| * @param sandboxfsMapSymlinkTargets map the targets of symlinks within the sandbox if true | |
| */ | |
| ProcessWrapperSandboxedSpawnRunner( | |
| SandboxHelpers helpers, | |
| CommandEnvironment cmdEnv, | |
| Path sandboxBase, | |
| @Nullable SandboxfsProcess sandboxfsProcess, | |
| boolean sandboxfsMapSymlinkTargets, | |
| TreeDeleter treeDeleter) { | |
| super(cmdEnv); | |
| this.helpers = helpers; | |
| this.processWrapper = ProcessWrapper.fromCommandEnvironment(cmdEnv); | |
| this.execRoot = cmdEnv.getExecRoot(); | |
| this.localEnvProvider = LocalEnvProvider.forCurrentOs(cmdEnv.getClientEnv()); | |
| this.sandboxBase = sandboxBase; | |
| this.sandboxfsProcess = sandboxfsProcess; | |
| this.sandboxfsMapSymlinkTargets = sandboxfsMapSymlinkTargets; | |
| this.treeDeleter = treeDeleter; | |
| } | |
| @Override | |
| protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context) | |
| throws IOException, ForbiddenActionInputException, InterruptedException { | |
| // Each invocation of "exec" gets its own sandbox base. | |
| // Note that the value returned by context.getId() is only unique inside one given SpawnRunner, | |
| // so we have to prefix our name to turn it into a globally unique value. | |
| Path sandboxPath = | |
| sandboxBase.getRelative(getName()).getRelative(Integer.toString(context.getId())); | |
| sandboxPath.getParentDirectory().createDirectory(); | |
| sandboxPath.createDirectory(); | |
| // b/64689608: The execroot of the sandboxed process must end with the workspace name, just like | |
| // the normal execroot does. | |
| String workspaceName = execRoot.getBaseName(); | |
| Path sandboxExecRoot = sandboxPath.getRelative("execroot").getRelative(workspaceName); | |
| sandboxExecRoot.getParentDirectory().createDirectory(); | |
| sandboxExecRoot.createDirectory(); | |
| ImmutableMap<String, String> environment = | |
| localEnvProvider.rewriteLocalEnv(spawn.getEnvironment(), binTools, "/tmp"); | |
| Duration timeout = context.getTimeout(); | |
| ProcessWrapper.CommandLineBuilder commandLineBuilder = | |
| processWrapper | |
| .commandLineBuilder(spawn.getArguments()) | |
| .addExecutionInfo(spawn.getExecutionInfo()) | |
| .setTimeout(timeout); | |
| Path statisticsPath = null; | |
| if (getSandboxOptions().collectLocalSandboxExecutionStatistics) { | |
| statisticsPath = sandboxPath.getRelative("stats.out"); | |
| commandLineBuilder.setStatisticsPath(statisticsPath); | |
| } | |
| SandboxInputs inputs = | |
| helpers.processInputFiles( | |
| context.getInputMapping(PathFragment.EMPTY_FRAGMENT), | |
| spawn, | |
| context.getArtifactExpander(), | |
| execRoot); | |
| SandboxOutputs outputs = helpers.getOutputs(spawn); | |
| if (sandboxfsProcess != null) { | |
| return new SandboxfsSandboxedSpawn( | |
| sandboxfsProcess, | |
| sandboxPath, | |
| workspaceName, | |
| commandLineBuilder.build(), | |
| environment, | |
| inputs, | |
| outputs, | |
| ImmutableSet.of(), | |
| sandboxfsMapSymlinkTargets, | |
| treeDeleter, | |
| statisticsPath); | |
| } else { | |
| return new SymlinkedSandboxedSpawn( | |
| sandboxPath, | |
| sandboxExecRoot, | |
| commandLineBuilder.build(), | |
| environment, | |
| inputs, | |
| outputs, | |
| getWritableDirs(sandboxExecRoot, environment), | |
| treeDeleter, | |
| statisticsPath, | |
| getSandboxOptions().reuseSandboxDirectories, | |
| sandboxBase, | |
| spawn.getMnemonic()); | |
| } | |
| } | |
| @Override | |
| public String getName() { | |
| return "processwrapper-sandbox"; | |
| } | |
| } |