| 1 |
|
-module(klsn_bwrap). |
| 2 |
|
|
| 3 |
|
-export([ |
| 4 |
|
run/2 |
| 5 |
|
, open/2 |
| 6 |
|
, send/2 |
| 7 |
|
, send_eof/1 |
| 8 |
|
, stop/1 |
| 9 |
|
]). |
| 10 |
|
|
| 11 |
|
-export_type([ |
| 12 |
|
command/0 |
| 13 |
|
, opts/0 |
| 14 |
|
, open_opts/0 |
| 15 |
|
, bwrap_opt/0 |
| 16 |
|
, result/0 |
| 17 |
|
, stream/0 |
| 18 |
|
]). |
| 19 |
|
|
| 20 |
|
%% argv-style command where each element is a single argument. |
| 21 |
|
-type command() :: [klsn:binstr()]. |
| 22 |
|
|
| 23 |
|
-type bwrap_opt() :: |
| 24 |
|
help |
| 25 |
|
| version |
| 26 |
|
| {args, non_neg_integer()} |
| 27 |
|
| unshare_all |
| 28 |
|
| share_net |
| 29 |
|
| unshare_user |
| 30 |
|
| unshare_user_try |
| 31 |
|
| unshare_ipc |
| 32 |
|
| unshare_pid |
| 33 |
|
| unshare_net |
| 34 |
|
| unshare_uts |
| 35 |
|
| unshare_cgroup |
| 36 |
|
| unshare_cgroup_try |
| 37 |
|
| {userns, non_neg_integer()} |
| 38 |
|
| {userns2, non_neg_integer()} |
| 39 |
|
| {pidns, non_neg_integer()} |
| 40 |
|
| {uid, non_neg_integer()} |
| 41 |
|
| {gid, non_neg_integer()} |
| 42 |
|
| {hostname, klsn:binstr()} |
| 43 |
|
| {chdir, klsn:binstr()} |
| 44 |
|
| clearenv |
| 45 |
|
| {setenv, klsn:binstr(), klsn:binstr()} |
| 46 |
|
| {unsetenv, klsn:binstr()} |
| 47 |
|
| {lock_file, klsn:binstr()} |
| 48 |
|
| {sync_fd, non_neg_integer()} |
| 49 |
|
| {bind, klsn:binstr(), klsn:binstr()} |
| 50 |
|
| {bind_try, klsn:binstr(), klsn:binstr()} |
| 51 |
|
| {dev_bind, klsn:binstr(), klsn:binstr()} |
| 52 |
|
| {dev_bind_try, klsn:binstr(), klsn:binstr()} |
| 53 |
|
| {ro_bind, klsn:binstr(), klsn:binstr()} |
| 54 |
|
| {ro_bind_try, klsn:binstr(), klsn:binstr()} |
| 55 |
|
| {bind_fd, non_neg_integer(), klsn:binstr()} |
| 56 |
|
| {ro_bind_fd, non_neg_integer(), klsn:binstr()} |
| 57 |
|
| {remount_ro, klsn:binstr()} |
| 58 |
|
| {exec_label, klsn:binstr()} |
| 59 |
|
| {file_label, klsn:binstr()} |
| 60 |
|
| {proc, klsn:binstr()} |
| 61 |
|
| {dev, klsn:binstr()} |
| 62 |
|
| {tmpfs, klsn:binstr()} |
| 63 |
|
| {mqueue, klsn:binstr()} |
| 64 |
|
| {dir, klsn:binstr()} |
| 65 |
|
| {file, non_neg_integer(), klsn:binstr()} |
| 66 |
|
| {bind_data, non_neg_integer(), klsn:binstr()} |
| 67 |
|
| {ro_bind_data, non_neg_integer(), klsn:binstr()} |
| 68 |
|
| {symlink, klsn:binstr(), klsn:binstr()} |
| 69 |
|
| {seccomp, non_neg_integer()} |
| 70 |
|
| {add_seccomp, non_neg_integer()} |
| 71 |
|
| {block_fd, non_neg_integer()} |
| 72 |
|
| {userns_block_fd, non_neg_integer()} |
| 73 |
|
| {info_fd, non_neg_integer()} |
| 74 |
|
| {json_status_fd, non_neg_integer()} |
| 75 |
|
| new_session |
| 76 |
|
| die_with_parent |
| 77 |
|
| as_pid_1 |
| 78 |
|
| {cap_add, klsn:binstr()} |
| 79 |
|
| {cap_drop, klsn:binstr()} |
| 80 |
|
| {perms, non_neg_integer()} |
| 81 |
|
| {chmod, non_neg_integer(), klsn:binstr()} |
| 82 |
|
. |
| 83 |
|
|
| 84 |
|
%% Options for run/2. |
| 85 |
|
%% |
| 86 |
|
%% - bwrap: required list of bubblewrap options |
| 87 |
|
%% - stdin: binary to write then close. |
| 88 |
|
%% - timeout: timeout in milliseconds or infinity |
| 89 |
|
-type opts() :: #{ |
| 90 |
|
bwrap := [bwrap_opt()] |
| 91 |
|
, stdin => binary() % Unspecified: No stdin |
| 92 |
|
, timeout => timeout() % Default: infinity |
| 93 |
|
}. |
| 94 |
|
|
| 95 |
|
%% Options for open/2. |
| 96 |
|
%% |
| 97 |
|
%% - bwrap: required list of bubblewrap options |
| 98 |
|
%% - stdin: binary to write without closing stdin |
| 99 |
|
-type open_opts() :: #{ |
| 100 |
|
bwrap := [bwrap_opt()] |
| 101 |
|
, stdin => binary() % Unspecified: No stdin |
| 102 |
|
}. |
| 103 |
|
|
| 104 |
|
%% Result of run/2. |
| 105 |
|
-type result() :: #{ |
| 106 |
|
exit_code := non_neg_integer() |
| 107 |
|
, stdout := binary() |
| 108 |
|
, stderr := binary() |
| 109 |
|
}. |
| 110 |
|
|
| 111 |
|
%% Stream handle returned from open/2. |
| 112 |
|
-type stream() :: #{ |
| 113 |
|
os_pid := integer() |
| 114 |
|
, exec_pid := pid() |
| 115 |
|
}. |
| 116 |
|
|
| 117 |
|
-spec run(command(), opts()) -> result(). |
| 118 |
|
run(Command, Opts) when is_list(Command), is_map(Opts) -> |
| 119 |
:-( |
ensure_erlexec_started(), |
| 120 |
|
|
| 121 |
:-( |
BwrapOpts = maps:get(bwrap, Opts), |
| 122 |
:-( |
Timeout = maps:get(timeout, Opts, infinity), |
| 123 |
|
|
| 124 |
:-( |
Argv0 = [ |
| 125 |
|
bwrap_executable() |
| 126 |
|
| bwrap_opts_to_argv(BwrapOpts) |
| 127 |
|
], |
| 128 |
:-( |
Argv = Argv0 ++ [<<"--">>] ++ Command, |
| 129 |
|
|
| 130 |
:-( |
ExecOpts0 = [stdout, stderr, monitor], |
| 131 |
:-( |
{ExecOpts, MaybeStdin} = case klsn_map:lookup([stdin], Opts) of |
| 132 |
|
none -> |
| 133 |
:-( |
{ExecOpts0, none}; |
| 134 |
|
{value, StdinBinary0} when is_binary(StdinBinary0) -> |
| 135 |
:-( |
{[stdin | ExecOpts0], {value, StdinBinary0}} |
| 136 |
|
; {value, _} -> |
| 137 |
:-( |
erlang:error(badarg, [Command, Opts]) |
| 138 |
|
end, |
| 139 |
|
|
| 140 |
:-( |
case exec:run(Argv, ExecOpts) of |
| 141 |
|
{ok, _Pid, OsPid} -> |
| 142 |
:-( |
case MaybeStdin of |
| 143 |
|
{value, StdinBinary1} -> |
| 144 |
:-( |
ok = send_stdin_chunked(OsPid, StdinBinary1), |
| 145 |
:-( |
ok = exec:send(OsPid, eof); |
| 146 |
|
none -> |
| 147 |
:-( |
ok |
| 148 |
|
end, |
| 149 |
:-( |
wait_result(OsPid, timeout_deadline(Timeout), [], []); |
| 150 |
|
{error, Reason} -> |
| 151 |
:-( |
erlang:error(Reason, [Command, Opts]) |
| 152 |
|
end; |
| 153 |
|
run(Command, Opts) -> |
| 154 |
:-( |
erlang:error(badarg, [Command, Opts]). |
| 155 |
|
|
| 156 |
|
%% @doc |
| 157 |
|
%% Start a bubblewrap sandbox and keep stdin/stdout open for streaming. |
| 158 |
|
-spec open(command(), open_opts()) -> stream(). |
| 159 |
|
open(Command, Opts) when is_list(Command), is_map(Opts) -> |
| 160 |
:-( |
ensure_erlexec_started(), |
| 161 |
|
|
| 162 |
:-( |
BwrapOpts = maps:get(bwrap, Opts), |
| 163 |
|
|
| 164 |
:-( |
Argv0 = [ |
| 165 |
|
bwrap_executable() |
| 166 |
|
| bwrap_opts_to_argv(BwrapOpts) |
| 167 |
|
], |
| 168 |
:-( |
Argv = Argv0 ++ [<<"--">>] ++ Command, |
| 169 |
|
|
| 170 |
:-( |
ExecOpts = [stdout, stderr, monitor, stdin], |
| 171 |
:-( |
MaybeStdin = case klsn_map:lookup([stdin], Opts) of |
| 172 |
|
none -> |
| 173 |
:-( |
none; |
| 174 |
|
{value, StdinBinary0} when is_binary(StdinBinary0) -> |
| 175 |
:-( |
{value, StdinBinary0}; |
| 176 |
|
{value, _} -> |
| 177 |
:-( |
erlang:error(badarg, [Command, Opts]) |
| 178 |
|
end, |
| 179 |
|
|
| 180 |
:-( |
case exec:run(Argv, ExecOpts) of |
| 181 |
|
{ok, Pid, OsPid} -> |
| 182 |
:-( |
case MaybeStdin of |
| 183 |
|
{value, StdinBinary1} -> |
| 184 |
:-( |
ok = send_stdin_chunked(OsPid, StdinBinary1); |
| 185 |
|
none -> |
| 186 |
:-( |
ok |
| 187 |
|
end, |
| 188 |
:-( |
#{ |
| 189 |
|
os_pid => OsPid |
| 190 |
|
, exec_pid => Pid |
| 191 |
|
}; |
| 192 |
|
{error, Reason} -> |
| 193 |
:-( |
erlang:error(Reason, [Command, Opts]) |
| 194 |
|
end; |
| 195 |
|
open(Command, Opts) -> |
| 196 |
:-( |
erlang:error(badarg, [Command, Opts]). |
| 197 |
|
|
| 198 |
|
%% @doc |
| 199 |
|
%% Send a binary chunk to a streaming sandbox. |
| 200 |
|
-spec send(stream(), binary()) -> ok. |
| 201 |
|
send(#{os_pid := OsPid}, Data) when is_integer(OsPid), is_binary(Data) -> |
| 202 |
:-( |
ok = send_stdin_chunked(OsPid, Data); |
| 203 |
|
send(Handle, Data) -> |
| 204 |
:-( |
erlang:error(badarg, [Handle, Data]). |
| 205 |
|
|
| 206 |
|
%% @doc |
| 207 |
|
%% Close stdin for a streaming sandbox. |
| 208 |
|
-spec send_eof(stream()) -> ok. |
| 209 |
|
send_eof(#{os_pid := OsPid}) when is_integer(OsPid) -> |
| 210 |
:-( |
ok = exec:send(OsPid, eof); |
| 211 |
|
send_eof(Handle) -> |
| 212 |
:-( |
erlang:error(badarg, [Handle]). |
| 213 |
|
|
| 214 |
|
%% @doc |
| 215 |
|
%% Stop a streaming sandbox. |
| 216 |
|
-spec stop(stream()) -> ok. |
| 217 |
|
stop(#{os_pid := OsPid}) when is_integer(OsPid) -> |
| 218 |
:-( |
ok = exec:stop(OsPid); |
| 219 |
|
stop(Handle) -> |
| 220 |
:-( |
erlang:error(badarg, [Handle]). |
| 221 |
|
|
| 222 |
|
ensure_erlexec_started() -> |
| 223 |
:-( |
case whereis(exec) of |
| 224 |
|
Pid when is_pid(Pid) -> |
| 225 |
:-( |
ok; |
| 226 |
|
undefined -> |
| 227 |
:-( |
case os:getenv("SHELL") of |
| 228 |
|
false -> |
| 229 |
:-( |
os:putenv("SHELL", find_shell()); |
| 230 |
|
_ -> |
| 231 |
:-( |
ok |
| 232 |
|
end, |
| 233 |
:-( |
{ok, _} = application:ensure_all_started(erlexec) |
| 234 |
|
end. |
| 235 |
|
|
| 236 |
|
find_shell() -> |
| 237 |
:-( |
case os:find_executable("bash") of |
| 238 |
|
false -> |
| 239 |
:-( |
case os:find_executable("sh") of |
| 240 |
|
false -> |
| 241 |
:-( |
case filelib:is_file("/bin/bash") of |
| 242 |
|
true -> |
| 243 |
:-( |
"/bin/bash"; |
| 244 |
|
false -> |
| 245 |
:-( |
"/bin/sh" |
| 246 |
|
end; |
| 247 |
|
Sh -> |
| 248 |
:-( |
Sh |
| 249 |
|
end; |
| 250 |
|
Bash -> |
| 251 |
:-( |
Bash |
| 252 |
|
end. |
| 253 |
|
|
| 254 |
|
send_stdin_chunked(_OsPid, <<>>) -> |
| 255 |
:-( |
ok; |
| 256 |
|
send_stdin_chunked(OsPid, StdinBinary) when is_integer(OsPid), is_binary(StdinBinary) -> |
| 257 |
:-( |
ChunkSize = 60000, |
| 258 |
:-( |
send_stdin_chunked(OsPid, StdinBinary, ChunkSize, 0). |
| 259 |
|
|
| 260 |
|
send_stdin_chunked(_OsPid, StdinBinary, _ChunkSize, Pos) when Pos >= byte_size(StdinBinary) -> |
| 261 |
:-( |
ok; |
| 262 |
|
send_stdin_chunked(OsPid, StdinBinary, ChunkSize, Pos) -> |
| 263 |
:-( |
Remaining = byte_size(StdinBinary) - Pos, |
| 264 |
:-( |
Len = erlang:min(ChunkSize, Remaining), |
| 265 |
:-( |
Chunk = binary:part(StdinBinary, Pos, Len), |
| 266 |
:-( |
ok = exec:send(OsPid, Chunk), |
| 267 |
:-( |
send_stdin_chunked(OsPid, StdinBinary, ChunkSize, Pos + Len). |
| 268 |
|
|
| 269 |
|
wait_result(OsPid, Deadline, StdoutAcc, StderrAcc) -> |
| 270 |
:-( |
Timeout = timeout_remaining(Deadline), |
| 271 |
:-( |
receive |
| 272 |
|
{stdout, OsPid, Data} when is_binary(Data) -> |
| 273 |
:-( |
wait_result(OsPid, Deadline, [StdoutAcc, Data], StderrAcc); |
| 274 |
|
{stderr, OsPid, Data} when is_binary(Data) -> |
| 275 |
:-( |
wait_result(OsPid, Deadline, StdoutAcc, [StderrAcc, Data]); |
| 276 |
|
{'DOWN', OsPid, process, _Pid, {exit_status, ExitStatus}} when is_integer(ExitStatus) -> |
| 277 |
:-( |
ExitCode = exit_code(exec:status(ExitStatus)), |
| 278 |
:-( |
{StdoutAcc1, StderrAcc1} = drain_output(OsPid, StdoutAcc, StderrAcc), |
| 279 |
:-( |
#{ |
| 280 |
|
exit_code => ExitCode |
| 281 |
|
, stdout => iolist_to_binary(StdoutAcc1) |
| 282 |
|
, stderr => iolist_to_binary(StderrAcc1) |
| 283 |
|
}; |
| 284 |
|
{'DOWN', OsPid, process, _Pid, normal} -> |
| 285 |
:-( |
{StdoutAcc1, StderrAcc1} = drain_output(OsPid, StdoutAcc, StderrAcc), |
| 286 |
:-( |
#{ |
| 287 |
|
exit_code => 0 |
| 288 |
|
, stdout => iolist_to_binary(StdoutAcc1) |
| 289 |
|
, stderr => iolist_to_binary(StderrAcc1) |
| 290 |
|
}; |
| 291 |
|
{'DOWN', OsPid, process, _Pid, Reason} -> |
| 292 |
:-( |
erlang:error(Reason, [OsPid]) |
| 293 |
|
after Timeout -> |
| 294 |
:-( |
exec:stop(OsPid), |
| 295 |
:-( |
drain_down(OsPid), |
| 296 |
:-( |
erlang:error(timeout, [OsPid]) |
| 297 |
|
end. |
| 298 |
|
|
| 299 |
|
drain_output(OsPid, StdoutAcc, StderrAcc) -> |
| 300 |
:-( |
receive |
| 301 |
|
{stdout, OsPid, Data} when is_binary(Data) -> |
| 302 |
:-( |
drain_output(OsPid, [StdoutAcc, Data], StderrAcc); |
| 303 |
|
{stderr, OsPid, Data} when is_binary(Data) -> |
| 304 |
:-( |
drain_output(OsPid, StdoutAcc, [StderrAcc, Data]) |
| 305 |
|
after 0 -> |
| 306 |
:-( |
{StdoutAcc, StderrAcc} |
| 307 |
|
end. |
| 308 |
|
|
| 309 |
|
drain_down(OsPid) -> |
| 310 |
:-( |
receive |
| 311 |
|
{stdout, OsPid, _} -> |
| 312 |
:-( |
drain_down(OsPid); |
| 313 |
|
{stderr, OsPid, _} -> |
| 314 |
:-( |
drain_down(OsPid); |
| 315 |
|
{'DOWN', OsPid, process, _Pid, _Reason} -> |
| 316 |
:-( |
ok |
| 317 |
|
after 5000 -> |
| 318 |
:-( |
ok |
| 319 |
|
end. |
| 320 |
|
|
| 321 |
|
bwrap_executable() -> |
| 322 |
:-( |
case os:find_executable("bwrap") of |
| 323 |
|
false -> |
| 324 |
:-( |
erlang:error(not_found, [bwrap]); |
| 325 |
|
Path -> |
| 326 |
:-( |
unicode:characters_to_binary(Path) |
| 327 |
|
end. |
| 328 |
|
|
| 329 |
|
bwrap_opts_to_argv(Opts) when is_list(Opts) -> |
| 330 |
:-( |
lists:append(lists:map(fun bwrap_opt_to_argv/1, Opts)); |
| 331 |
|
bwrap_opts_to_argv(Other) -> |
| 332 |
:-( |
erlang:error(badarg, [Other]). |
| 333 |
|
|
| 334 |
|
%% Strict option parsing: reject unknown atoms/tuples (catch typos). |
| 335 |
:-( |
bwrap_opt_to_argv(help) -> [flag(<<"help">>)]; |
| 336 |
:-( |
bwrap_opt_to_argv(version) -> [flag(<<"version">>)]; |
| 337 |
:-( |
bwrap_opt_to_argv({args, N}) when is_integer(N), N >= 0 -> [flag(<<"args">>), integer_to_binary(N)]; |
| 338 |
|
|
| 339 |
:-( |
bwrap_opt_to_argv(unshare_all) -> [flag(<<"unshare-all">>)]; |
| 340 |
:-( |
bwrap_opt_to_argv(share_net) -> [flag(<<"share-net">>)]; |
| 341 |
:-( |
bwrap_opt_to_argv(unshare_user) -> [flag(<<"unshare-user">>)]; |
| 342 |
:-( |
bwrap_opt_to_argv(unshare_user_try) -> [flag(<<"unshare-user-try">>)]; |
| 343 |
:-( |
bwrap_opt_to_argv(unshare_ipc) -> [flag(<<"unshare-ipc">>)]; |
| 344 |
:-( |
bwrap_opt_to_argv(unshare_pid) -> [flag(<<"unshare-pid">>)]; |
| 345 |
:-( |
bwrap_opt_to_argv(unshare_net) -> [flag(<<"unshare-net">>)]; |
| 346 |
:-( |
bwrap_opt_to_argv(unshare_uts) -> [flag(<<"unshare-uts">>)]; |
| 347 |
:-( |
bwrap_opt_to_argv(unshare_cgroup) -> [flag(<<"unshare-cgroup">>)]; |
| 348 |
:-( |
bwrap_opt_to_argv(unshare_cgroup_try) -> [flag(<<"unshare-cgroup-try">>)]; |
| 349 |
|
|
| 350 |
:-( |
bwrap_opt_to_argv({userns, Fd}) when is_integer(Fd), Fd >= 0 -> [flag(<<"userns">>), integer_to_binary(Fd)]; |
| 351 |
:-( |
bwrap_opt_to_argv({userns2, Fd}) when is_integer(Fd), Fd >= 0 -> [flag(<<"userns2">>), integer_to_binary(Fd)]; |
| 352 |
:-( |
bwrap_opt_to_argv({pidns, Fd}) when is_integer(Fd), Fd >= 0 -> [flag(<<"pidns">>), integer_to_binary(Fd)]; |
| 353 |
:-( |
bwrap_opt_to_argv({uid, Uid}) when is_integer(Uid), Uid >= 0 -> [flag(<<"uid">>), integer_to_binary(Uid)]; |
| 354 |
:-( |
bwrap_opt_to_argv({gid, Gid}) when is_integer(Gid), Gid >= 0 -> [flag(<<"gid">>), integer_to_binary(Gid)]; |
| 355 |
|
|
| 356 |
|
bwrap_opt_to_argv({hostname, Hostname}) when is_binary(Hostname) -> |
| 357 |
:-( |
[flag(<<"hostname">>), Hostname]; |
| 358 |
|
bwrap_opt_to_argv({chdir, Path}) when is_binary(Path) -> |
| 359 |
:-( |
[flag(<<"chdir">>), Path]; |
| 360 |
|
|
| 361 |
:-( |
bwrap_opt_to_argv(clearenv) -> [flag(<<"clearenv">>)]; |
| 362 |
|
bwrap_opt_to_argv({setenv, Key, Val}) when is_binary(Key), is_binary(Val) -> |
| 363 |
:-( |
[flag(<<"setenv">>), Key, Val]; |
| 364 |
|
bwrap_opt_to_argv({unsetenv, Key}) when is_binary(Key) -> |
| 365 |
:-( |
[flag(<<"unsetenv">>), Key]; |
| 366 |
|
bwrap_opt_to_argv({lock_file, Path}) when is_binary(Path) -> |
| 367 |
:-( |
[flag(<<"lock-file">>), Path]; |
| 368 |
|
bwrap_opt_to_argv({sync_fd, Fd}) when is_integer(Fd), Fd >= 0 -> |
| 369 |
:-( |
[flag(<<"sync-fd">>), integer_to_binary(Fd)]; |
| 370 |
|
|
| 371 |
|
bwrap_opt_to_argv({bind, Src, Dst}) when is_binary(Src), is_binary(Dst) -> |
| 372 |
:-( |
[flag(<<"bind">>), Src, Dst]; |
| 373 |
|
bwrap_opt_to_argv({bind_try, Src, Dst}) when is_binary(Src), is_binary(Dst) -> |
| 374 |
:-( |
[flag(<<"bind-try">>), Src, Dst]; |
| 375 |
|
bwrap_opt_to_argv({dev_bind, Src, Dst}) when is_binary(Src), is_binary(Dst) -> |
| 376 |
:-( |
[flag(<<"dev-bind">>), Src, Dst]; |
| 377 |
|
bwrap_opt_to_argv({dev_bind_try, Src, Dst}) when is_binary(Src), is_binary(Dst) -> |
| 378 |
:-( |
[flag(<<"dev-bind-try">>), Src, Dst]; |
| 379 |
|
bwrap_opt_to_argv({ro_bind, Src, Dst}) when is_binary(Src), is_binary(Dst) -> |
| 380 |
:-( |
[flag(<<"ro-bind">>), Src, Dst]; |
| 381 |
|
bwrap_opt_to_argv({ro_bind_try, Src, Dst}) when is_binary(Src), is_binary(Dst) -> |
| 382 |
:-( |
[flag(<<"ro-bind-try">>), Src, Dst]; |
| 383 |
|
|
| 384 |
|
bwrap_opt_to_argv({bind_fd, Fd, Path}) when is_integer(Fd), Fd >= 0, is_binary(Path) -> |
| 385 |
:-( |
[flag(<<"bind-fd">>), integer_to_binary(Fd), Path]; |
| 386 |
|
bwrap_opt_to_argv({ro_bind_fd, Fd, Path}) when is_integer(Fd), Fd >= 0, is_binary(Path) -> |
| 387 |
:-( |
[flag(<<"ro-bind-fd">>), integer_to_binary(Fd), Path]; |
| 388 |
|
bwrap_opt_to_argv({remount_ro, Path}) when is_binary(Path) -> |
| 389 |
:-( |
[flag(<<"remount-ro">>), Path]; |
| 390 |
|
|
| 391 |
|
bwrap_opt_to_argv({exec_label, Label}) when is_binary(Label) -> |
| 392 |
:-( |
[flag(<<"exec-label">>), Label]; |
| 393 |
|
bwrap_opt_to_argv({file_label, Label}) when is_binary(Label) -> |
| 394 |
:-( |
[flag(<<"file-label">>), Label]; |
| 395 |
|
|
| 396 |
|
bwrap_opt_to_argv({proc, Path}) when is_binary(Path) -> |
| 397 |
:-( |
[flag(<<"proc">>), Path]; |
| 398 |
|
bwrap_opt_to_argv({dev, Path}) when is_binary(Path) -> |
| 399 |
:-( |
[flag(<<"dev">>), Path]; |
| 400 |
|
bwrap_opt_to_argv({tmpfs, Path}) when is_binary(Path) -> |
| 401 |
:-( |
[flag(<<"tmpfs">>), Path]; |
| 402 |
|
bwrap_opt_to_argv({mqueue, Path}) when is_binary(Path) -> |
| 403 |
:-( |
[flag(<<"mqueue">>), Path]; |
| 404 |
|
bwrap_opt_to_argv({dir, Path}) when is_binary(Path) -> |
| 405 |
:-( |
[flag(<<"dir">>), Path]; |
| 406 |
|
|
| 407 |
|
bwrap_opt_to_argv({file, Fd, Path}) when is_integer(Fd), Fd >= 0, is_binary(Path) -> |
| 408 |
:-( |
[flag(<<"file">>), integer_to_binary(Fd), Path]; |
| 409 |
|
bwrap_opt_to_argv({bind_data, Fd, Path}) when is_integer(Fd), Fd >= 0, is_binary(Path) -> |
| 410 |
:-( |
[flag(<<"bind-data">>), integer_to_binary(Fd), Path]; |
| 411 |
|
bwrap_opt_to_argv({ro_bind_data, Fd, Path}) when is_integer(Fd), Fd >= 0, is_binary(Path) -> |
| 412 |
:-( |
[flag(<<"ro-bind-data">>), integer_to_binary(Fd), Path]; |
| 413 |
|
bwrap_opt_to_argv({symlink, Src, Dst}) when is_binary(Src), is_binary(Dst) -> |
| 414 |
:-( |
[flag(<<"symlink">>), Src, Dst]; |
| 415 |
|
|
| 416 |
|
bwrap_opt_to_argv({seccomp, Fd}) when is_integer(Fd), Fd >= 0 -> |
| 417 |
:-( |
[flag(<<"seccomp">>), integer_to_binary(Fd)]; |
| 418 |
|
bwrap_opt_to_argv({add_seccomp, Fd}) when is_integer(Fd), Fd >= 0 -> |
| 419 |
:-( |
[flag(<<"add-seccomp">>), integer_to_binary(Fd)]; |
| 420 |
|
bwrap_opt_to_argv({block_fd, Fd}) when is_integer(Fd), Fd >= 0 -> |
| 421 |
:-( |
[flag(<<"block-fd">>), integer_to_binary(Fd)]; |
| 422 |
|
bwrap_opt_to_argv({userns_block_fd, Fd}) when is_integer(Fd), Fd >= 0 -> |
| 423 |
:-( |
[flag(<<"userns-block-fd">>), integer_to_binary(Fd)]; |
| 424 |
|
bwrap_opt_to_argv({info_fd, Fd}) when is_integer(Fd), Fd >= 0 -> |
| 425 |
:-( |
[flag(<<"info-fd">>), integer_to_binary(Fd)]; |
| 426 |
|
bwrap_opt_to_argv({json_status_fd, Fd}) when is_integer(Fd), Fd >= 0 -> |
| 427 |
:-( |
[flag(<<"json-status-fd">>), integer_to_binary(Fd)]; |
| 428 |
|
|
| 429 |
:-( |
bwrap_opt_to_argv(new_session) -> [flag(<<"new-session">>)]; |
| 430 |
:-( |
bwrap_opt_to_argv(die_with_parent) -> [flag(<<"die-with-parent">>)]; |
| 431 |
:-( |
bwrap_opt_to_argv(as_pid_1) -> [flag(<<"as-pid-1">>)]; |
| 432 |
|
|
| 433 |
|
bwrap_opt_to_argv({cap_add, Cap}) when is_binary(Cap) -> |
| 434 |
:-( |
[flag(<<"cap-add">>), Cap]; |
| 435 |
|
bwrap_opt_to_argv({cap_drop, Cap}) when is_binary(Cap) -> |
| 436 |
:-( |
[flag(<<"cap-drop">>), Cap]; |
| 437 |
|
bwrap_opt_to_argv({perms, Mode}) when is_integer(Mode), Mode >= 0 -> |
| 438 |
:-( |
[flag(<<"perms">>), integer_to_binary(Mode)]; |
| 439 |
|
bwrap_opt_to_argv({chmod, Mode, Path}) when is_integer(Mode), Mode >= 0, is_binary(Path) -> |
| 440 |
:-( |
[flag(<<"chmod">>), integer_to_binary(Mode), Path]; |
| 441 |
|
bwrap_opt_to_argv(Other) -> |
| 442 |
:-( |
erlang:error(badarg, [Other]). |
| 443 |
|
|
| 444 |
|
flag(Name) when is_binary(Name) -> |
| 445 |
:-( |
<<"--", Name/binary>>. |
| 446 |
|
|
| 447 |
|
timeout_deadline(infinity) -> |
| 448 |
:-( |
infinity; |
| 449 |
|
timeout_deadline(Timeout) when is_integer(Timeout), Timeout >= 0 -> |
| 450 |
:-( |
monotonic_ms() + Timeout; |
| 451 |
|
timeout_deadline(Other) -> |
| 452 |
:-( |
erlang:error(badarg, [Other]). |
| 453 |
|
|
| 454 |
|
timeout_remaining(infinity) -> |
| 455 |
:-( |
infinity; |
| 456 |
|
timeout_remaining(DeadlineMs) when is_integer(DeadlineMs) -> |
| 457 |
:-( |
Remaining = DeadlineMs - monotonic_ms(), |
| 458 |
:-( |
case Remaining > 0 of |
| 459 |
:-( |
true -> Remaining; |
| 460 |
:-( |
false -> 0 |
| 461 |
|
end. |
| 462 |
|
|
| 463 |
|
monotonic_ms() -> |
| 464 |
:-( |
erlang:convert_time_unit(erlang:monotonic_time(), native, millisecond). |
| 465 |
|
|
| 466 |
|
exit_code({status, Code}) when is_integer(Code), Code >= 0 -> |
| 467 |
:-( |
Code; |
| 468 |
|
exit_code({signal, Signal, _CoreDump}) -> |
| 469 |
:-( |
128 + signal_to_int(Signal). |
| 470 |
|
|
| 471 |
|
signal_to_int(Signal) when is_atom(Signal) -> |
| 472 |
:-( |
exec:signal_to_int(Signal); |
| 473 |
|
signal_to_int(Signal) when is_integer(Signal) -> |
| 474 |
:-( |
Signal. |