blob: 756617cba1a36d8b23fb11645b4176d5d0294076 [file] [log] [blame]
Googler9475ce82022-10-19 07:07:33 -07001// Copyright 2017 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.devtools.build.lib.sandbox;
16
17import com.google.common.base.Preconditions;
18import com.google.common.collect.ImmutableList;
19import com.google.common.collect.ImmutableMap;
20import com.google.common.collect.ImmutableSet;
21import com.google.devtools.build.lib.actions.ExecutionRequirements;
22import com.google.devtools.build.lib.vfs.Path;
23import com.google.devtools.build.lib.vfs.PathFragment;
24import com.google.errorprone.annotations.CanIgnoreReturnValue;
25import java.time.Duration;
26import java.util.List;
27import java.util.Map;
28import java.util.Set;
29
30/**
31 * A builder class for constructing the full command line to run a command using the {@code
32 * linux-sandbox} tool.
33 */
34public class LinuxSandboxCommandLineBuilder {
35 private final Path linuxSandboxPath;
36 private final List<String> commandArguments;
37 private Path hermeticSandboxPath;
38 private Path workingDirectory;
39 private Duration timeout;
40 private Duration killDelay;
Googler77652262022-12-01 02:15:33 -080041 private boolean persistentProcess;
Googler9475ce82022-10-19 07:07:33 -070042 private Path stdoutPath;
43 private Path stderrPath;
44 private Set<Path> writableFilesAndDirectories = ImmutableSet.of();
45 private ImmutableSet<PathFragment> tmpfsDirectories = ImmutableSet.of();
46 private Map<Path, Path> bindMounts = ImmutableMap.of();
47 private Path statisticsPath;
48 private boolean useFakeHostname = false;
49 private boolean createNetworkNamespace = false;
50 private boolean useFakeRoot = false;
51 private boolean useFakeUsername = false;
52 private boolean enablePseudoterminal = false;
53 private boolean useDebugMode = false;
54 private boolean sigintSendsSigterm = false;
Googler03996c12022-12-08 08:39:47 -080055 private String cgroupsDir;
Googler9475ce82022-10-19 07:07:33 -070056
57 private LinuxSandboxCommandLineBuilder(Path linuxSandboxPath, List<String> commandArguments) {
58 this.linuxSandboxPath = linuxSandboxPath;
59 this.commandArguments = commandArguments;
60 }
61
62 /** Returns a new command line builder for the {@code linux-sandbox} tool. */
63 public static LinuxSandboxCommandLineBuilder commandLineBuilder(
64 Path linuxSandboxPath, List<String> commandArguments) {
65 return new LinuxSandboxCommandLineBuilder(linuxSandboxPath, commandArguments);
66 }
67
68 /**
69 * Sets the sandbox path to chroot to, required for the hermetic linux sandbox to figure out where
70 * the working directory is.
71 */
72 @CanIgnoreReturnValue
73 public LinuxSandboxCommandLineBuilder setHermeticSandboxPath(Path sandboxPath) {
74 this.hermeticSandboxPath = sandboxPath;
75 return this;
76 }
77
78 /** Sets the working directory to use, if any. */
79 @CanIgnoreReturnValue
80 public LinuxSandboxCommandLineBuilder setWorkingDirectory(Path workingDirectory) {
81 this.workingDirectory = workingDirectory;
82 return this;
83 }
84
85 /** Sets the timeout for the command run using the {@code linux-sandbox} tool. */
86 @CanIgnoreReturnValue
87 public LinuxSandboxCommandLineBuilder setTimeout(Duration timeout) {
88 this.timeout = timeout;
89 return this;
90 }
91
92 /**
93 * Sets the kill delay for commands run using the {@code linux-sandbox} tool that exceed their
94 * timeout.
95 */
96 @CanIgnoreReturnValue
97 public LinuxSandboxCommandLineBuilder setKillDelay(Duration killDelay) {
98 this.killDelay = killDelay;
99 return this;
100 }
101
Googler77652262022-12-01 02:15:33 -0800102 @CanIgnoreReturnValue
103 public LinuxSandboxCommandLineBuilder setPersistentProcess(boolean persistentProcess) {
104 this.persistentProcess = persistentProcess;
105 return this;
106 }
107
Googler9475ce82022-10-19 07:07:33 -0700108 /** Sets the path to use for redirecting stdout, if any. */
109 @CanIgnoreReturnValue
110 public LinuxSandboxCommandLineBuilder setStdoutPath(Path stdoutPath) {
111 this.stdoutPath = stdoutPath;
112 return this;
113 }
114
115 /** Sets the path to use for redirecting stderr, if any. */
116 @CanIgnoreReturnValue
117 public LinuxSandboxCommandLineBuilder setStderrPath(Path stderrPath) {
118 this.stderrPath = stderrPath;
119 return this;
120 }
121
122 /** Sets the files or directories to make writable for the sandboxed process, if any. */
123 @CanIgnoreReturnValue
124 public LinuxSandboxCommandLineBuilder setWritableFilesAndDirectories(
125 Set<Path> writableFilesAndDirectories) {
126 this.writableFilesAndDirectories = writableFilesAndDirectories;
127 return this;
128 }
129
130 /** Sets the directories where to mount an empty tmpfs, if any. */
131 @CanIgnoreReturnValue
132 public LinuxSandboxCommandLineBuilder setTmpfsDirectories(
133 ImmutableSet<PathFragment> tmpfsDirectories) {
134 this.tmpfsDirectories = tmpfsDirectories;
135 return this;
136 }
137
138 /**
139 * Sets the sources and targets of files or directories to explicitly bind-mount in the sandbox,
140 * if any.
141 */
142 @CanIgnoreReturnValue
143 public LinuxSandboxCommandLineBuilder setBindMounts(Map<Path, Path> bindMounts) {
144 this.bindMounts = bindMounts;
145 return this;
146 }
147
148 /** Sets the path for writing execution statistics (e.g. resource usage). */
149 @CanIgnoreReturnValue
150 public LinuxSandboxCommandLineBuilder setStatisticsPath(Path statisticsPath) {
151 this.statisticsPath = statisticsPath;
152 return this;
153 }
154
155 /** Sets whether to use a fake 'localhost' hostname inside the sandbox. */
156 @CanIgnoreReturnValue
157 public LinuxSandboxCommandLineBuilder setUseFakeHostname(boolean useFakeHostname) {
158 this.useFakeHostname = useFakeHostname;
159 return this;
160 }
161
162 /** Sets whether to create a new network namespace. */
163 @CanIgnoreReturnValue
164 public LinuxSandboxCommandLineBuilder setCreateNetworkNamespace(boolean createNetworkNamespace) {
165 this.createNetworkNamespace = createNetworkNamespace;
166 return this;
167 }
168
169 /** Sets whether to pretend to be 'root' inside the namespace. */
170 @CanIgnoreReturnValue
171 public LinuxSandboxCommandLineBuilder setUseFakeRoot(boolean useFakeRoot) {
172 this.useFakeRoot = useFakeRoot;
173 return this;
174 }
175
176 /** Sets whether to use a fake 'nobody' username inside the sandbox. */
177 @CanIgnoreReturnValue
178 public LinuxSandboxCommandLineBuilder setUseFakeUsername(boolean useFakeUsername) {
179 this.useFakeUsername = useFakeUsername;
180 return this;
181 }
182
183 /**
184 * Sets whether to set group to 'tty' and make /dev/pts writable inside the sandbox in order to
185 * enable the use of pseudoterminals.
186 */
187 @CanIgnoreReturnValue
188 public LinuxSandboxCommandLineBuilder setEnablePseudoterminal(boolean enablePseudoterminal) {
189 this.enablePseudoterminal = enablePseudoterminal;
190 return this;
191 }
192
193 /** Sets whether to enable debug mode (e.g. to print debugging messages). */
194 @CanIgnoreReturnValue
195 public LinuxSandboxCommandLineBuilder setUseDebugMode(boolean useDebugMode) {
196 this.useDebugMode = useDebugMode;
197 return this;
198 }
199
Googler03996c12022-12-08 08:39:47 -0800200 /**
201 * Sets the directory to be used for cgroups. Cgroups can be used to set limits on resource usage
202 * of a subprocess tree, and to gather statistics. Requires cgroups v2 and systemd. This directory
203 * must be under {@code /sys/fs/cgroup} and the user running Bazel must have write permissions to
204 * this directory, its parent directory, and the cgroup directory for the Bazel process.
205 */
206 @CanIgnoreReturnValue
207 public LinuxSandboxCommandLineBuilder setCgroupsDir(String cgroupsDir) {
208 this.cgroupsDir = cgroupsDir;
209 return this;
210 }
211
Googler9475ce82022-10-19 07:07:33 -0700212 /** Incorporates settings from a spawn's execution info. */
213 @CanIgnoreReturnValue
214 public LinuxSandboxCommandLineBuilder addExecutionInfo(Map<String, String> executionInfo) {
215 if (executionInfo.containsKey(ExecutionRequirements.GRACEFUL_TERMINATION)) {
216 sigintSendsSigterm = true;
217 }
218 return this;
219 }
220
221 /** Builds the command line to invoke a specific command using the {@code linux-sandbox} tool. */
222 public ImmutableList<String> build() {
223 Preconditions.checkState(
224 !(this.useFakeUsername && this.useFakeRoot),
225 "useFakeUsername and useFakeRoot are exclusive");
226
227 ImmutableList.Builder<String> commandLineBuilder = ImmutableList.builder();
228
229 commandLineBuilder.add(linuxSandboxPath.getPathString());
230 if (workingDirectory != null) {
231 commandLineBuilder.add("-W", workingDirectory.getPathString());
232 }
233 if (timeout != null) {
234 commandLineBuilder.add("-T", Long.toString(timeout.getSeconds()));
235 }
236 if (killDelay != null) {
237 commandLineBuilder.add("-t", Long.toString(killDelay.getSeconds()));
238 }
239 if (stdoutPath != null) {
240 commandLineBuilder.add("-l", stdoutPath.getPathString());
241 }
242 if (stderrPath != null) {
243 commandLineBuilder.add("-L", stderrPath.getPathString());
244 }
245 for (Path writablePath : writableFilesAndDirectories) {
246 commandLineBuilder.add("-w", writablePath.getPathString());
247 }
248 for (PathFragment tmpfsPath : tmpfsDirectories) {
249 commandLineBuilder.add("-e", tmpfsPath.getPathString());
250 }
251 for (Path bindMountTarget : bindMounts.keySet()) {
252 Path bindMountSource = bindMounts.get(bindMountTarget);
253 commandLineBuilder.add("-M", bindMountSource.getPathString());
254 // The file is mounted in a custom location inside the sandbox.
255 if (!bindMountSource.equals(bindMountTarget)) {
256 commandLineBuilder.add("-m", bindMountTarget.getPathString());
257 }
258 }
259 if (statisticsPath != null) {
260 commandLineBuilder.add("-S", statisticsPath.getPathString());
261 }
262 if (hermeticSandboxPath != null) {
263 commandLineBuilder.add("-h", hermeticSandboxPath.getPathString());
264 }
265 if (useFakeHostname) {
266 commandLineBuilder.add("-H");
267 }
268 if (createNetworkNamespace) {
269 commandLineBuilder.add("-N");
270 }
271 if (useFakeRoot) {
272 commandLineBuilder.add("-R");
273 }
274 if (useFakeUsername) {
275 commandLineBuilder.add("-U");
276 }
277 if (enablePseudoterminal) {
278 commandLineBuilder.add("-P");
279 }
280 if (useDebugMode) {
281 commandLineBuilder.add("-D");
282 }
283 if (sigintSendsSigterm) {
284 commandLineBuilder.add("-i");
285 }
Googler77652262022-12-01 02:15:33 -0800286 if (persistentProcess) {
287 commandLineBuilder.add("-p");
288 }
Googler03996c12022-12-08 08:39:47 -0800289 if (cgroupsDir != null) {
290 commandLineBuilder.add("-C", cgroupsDir);
291 }
Googler9475ce82022-10-19 07:07:33 -0700292 commandLineBuilder.add("--");
293 commandLineBuilder.addAll(commandArguments);
294
295 return commandLineBuilder.build();
296 }
297}