View Source prx (prx v0.16.4)
Link to this section Summary
Functions
Register a function to be called at task termination.
Make a synchronous call into the port driver
cap_enter(2): place process into capability mode
cap_fcntls_get(2): get allowed fcntl commands in capability mode
cap_fcntls_limit(2): manage fcntl commands in capability mode
cap_getmode(2): check if capability mode is enabled
cap_ioctls_limit(2): manage allowed ioctl commands
cap_rights_limit(2): manage process capabilities
chdir(2): change process current working directory
chmod(2): change file permissions
chown(2): change file ownership
chroot(2): change root directory
clearenv(3): zero process environment
clone(2): create a new process
close(2): close a file descriptor
Assign a new process owner
Returns the list of child PIDs for this process.
Retrieve process info for forked processes
Get the gen_server PID for the task
environ(7): return the process environment variables
Close stdin of a task
Close task standard I/O file descriptor
See also: eof/2.
Test if the task has called exec(2)
execve(2): replace process image with environment
execve(2): replace process image with environment
execvp(2): replace the current process image using the search path
execvp(2): replace the current process image using the search path
exit(3): cause an prx control process to exit
See also: fcntl/4.
fcntl(2): perform operations on a file descriptor with argument
fexecve(2): replace the process image
filter/2 : restrict control process calls
filter/3 : restrict control process and subprocess calls
fork(2): create a new system process
fork(2): create a child process
Get control process attributes
Get control process attributes
getcwd(3): return the current working directory
getenv(3): retrieve an environment variable
getgid(2): retrieve the process group ID
getgroups(2): retrieve the list of supplementary groups
gethostname(2): retrieve the system hostname
Retrieve port options for event loop
getpgrp(2): retrieve the process group
getpid(2): retrieve the system PID of the process
getpriority(2): retrieve scheduling priority of process, process group or user
getresgid(2): get real, effective and saved group ID
getresuid(2): get real, effective and saved user ID
getrlimit(2): retrieve the resource limits for a process
getsid(2): retrieve the session ID
getuid(2): returns the process user ID
ioctl(2) : control device
jail(2): restrict the current process in a system jail
kill(2): terminate a process
lseek(2): set file offset for read/write
mount(2) : mount a filesystem, Linux style
(Solaris) mount(2) : mount a filesystem
open(2): returns a file descriptor associated with a file
open(2) : open a file specifying permissions
Get the parent PID for the task
Retrieves the system PID of the process similar to getpid(2)
Get the process pipeline list for the task
pivot_root(2): change the root mount
pledge(2): restrict system operations
prctl(2) : operations on a process
procctl(2): control processes
ptrace(2): process trace
read(2): read bytes from a file descriptor
readdir(3): retrieve list of objects in a directory
Fork+exec prx process.
Replace the port process image using execve(2)/fexecve(2).
Equivalent to reexec / 1.
Equivalent to reexec / 3.
rmdir(2): delete a directory
seccomp(2) : restrict system operations
select(2): poll a list of file descriptor for events
setcpid() : Set options for child process of prx control process
setcpid() : Set options for child process of prx control process
setenv(3): set an environment variable
setgid(2): set the GID of the process
setgroups(2): set the supplementary groups of the process
sethostname(2): set the system hostname
setns(2): attach to a namespace
(Linux) setns(2) : attach to a namespace, specifying namespace type
Set port options
setpgid(2): set process group
setpriority(2): set scheduling priority of process, process group or user
setproctitle(3): set the process title
setresgid(2): set real, effective and saved group ID
setresuid(2): set real, effective and saved user ID
setrlimit(2): set a resource limit
setsid(2): create a new session
setuid(2): change UID
sigaction(2): set process behaviour for signals
socket(2): returns a file descriptor for a communication endpoint
Send data to the standard input of the process
Assign a process to receive stdio
Terminate the task
Convenience function to fork a privileged process in the shell.
Convenience function to fork a privileged process in the shell.
Fork a subprocess and run a sequence of operations
Create a subprocess and run a sequence of operations using optional function calls
umount2(2) : unmount a filesystem
umount(2): unmount a filesystem
unlink(2): delete a name from the filesystem
unsetenv(3): remove an environment variable
unshare(2): create a new namespace in the current process
unveil(2): restrict filesystem view
waitpid(2): wait for process to change state
write(2): write to a file descriptor
Link to this section Types
-type call() :: alcove_proto:call() | reexec | replace_process_image | getcpid.
-type constant() :: atom() | integer().
-type cstruct() :: [binary() | {ptr, binary() | non_neg_integer()}, ...].
-type fd() :: int32_t().
-type gid_t() :: uint32_t().
-type int32_t() :: -2147483647..2147483647.
-type int64_t() :: -9223372036854775807..9223372036854775807.
-type mode_t() :: uint32_t().
-type off_t() :: uint64_t().
-type pid_t() :: int32_t().
-type posix() :: alcove:posix().
-type prx_opt() :: maxchild | exit_status | maxforkdepth | termsig | flowcontrol | signaloneof.
-type ptr_val() :: binary() | integer() | cstruct().
-type size_t() :: uint64_t().
-type ssize_t() :: int64_t().
-type task() :: pid().
-type uid_t() :: uint32_t().
-type uint32_t() :: 0..4294967295.
-type uint64_t() :: 0..18446744073709551615.
-type waitstatus() :: {exit_status, int32_t()} | {termsig, atom()} | {stopsig, atom()} | continued.
Link to this section Functions
Register a function to be called at task termination.
The atexit function runs in the parent of the process. atexit/2 must use prx_drv:call/4 to manipulate the task.
examples
Examples
The default function closes stdin, stdout and stderr of the system process:
fun(Drv, Pipeline, Pid) ->
prx_drv:call(Drv, Pipeline, close, [maps:get(stdout, Pid)]),
prx_drv:call(Drv, Pipeline, close, [maps:get(stdin, Pid)]),
prx_drv:call(Drv, Pipeline, close, [maps:get(stderr, Pid)])
end
Make a synchronous call into the port driver
The list of available calls and their arguments can be found here:
https://hexdocs.pm/alcove/alcove.html#functions
For example, to directly call alcove:execve/5
:
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.180.0>}
2> prx:call(Task, execve, ["/bin/ls", ["/bin/ls", "-al"], ["HOME=/home/foo"]]).
ok
cap_enter(2): place process into capability mode
support
Support
• FreeBSD
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.154.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.158.0>}
3> prx:cap_enter(Task1).
ok
4> prx:kill(Task1, 0, 0).
{error,ecapmode}
5> prx:kill(Task, 0, 0).
ok
cap_fcntls_get(2): get allowed fcntl commands in capability mode
support
Support
• FreeBSD
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.154.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.165.0>}
3> {ok, FD} = prx:open(Task1, "/etc/passwd", [o_rdonly]).
{ok,7}
4> prx:cap_enter(Task1).
ok
5> prx:cap_fcntls_get(Task1, FD).
{ok,120}
cap_fcntls_limit(2): manage fcntl commands in capability mode
support
Support
• FreeBSD
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.154.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.165.0>}
3> {ok, FD} = prx:open(Task1, "/etc/passwd", [o_rdonly]).
{ok,7}
4> prx:cap_enter(Task1).
ok
5> prx:cap_fcntls_get(Task1, FD).
{ok,120}
6> prx:cap_fcntls_limit(Task1, FD, [cap_fcntl_setfl]).
ok
7> prx:cap_fcntls_get(Task1, FD).
{ok,16}
cap_getmode(2): check if capability mode is enabled
• 0
: false
• 1
: true
support
Support
• FreeBSD
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.154.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.165.0>}
3> prx:cap_enter(Task1).
ok
4> prx:cap_getmode(Task).
{ok,0}
5> prx:cap_getmode(Task1).
{ok,1}
cap_ioctls_limit(2): manage allowed ioctl commands
support
Support
• FreeBSD
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.154.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.158.0>}
3> {ok, FD} = prx:open(Task1, "/dev/pts/1", [o_rdwr, o_nonblock]).
{ok,7}
4> prx:cap_enter(Task1).
ok
5> prx:cap_ioctls_limit(Task1, FD, [tiocmget, tiocgwinsz]).
ok
6> prx:ioctl(Task1, FD, tiocmset, <<>>).
{error,enotcapable}
7> prx:ioctl(Task1, FD, tiocmget, <<>>).
{ok,#{arg => <<>>,return_value => 0}}
cap_rights_limit(2): manage process capabilities
support
Support
• FreeBSD
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.154.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.168.0>}
3> {ok, FD} = prx:open(Task1, "/etc/passwd", [o_rdonly]).
{ok,7}
4> prx:cap_enter(Task1).
ok
5> prx:cap_rights_limit(Task1, FD, [cap_read]).
ok
6> prx:read(Task1, FD, 64).
{ok,<<"# $FreeBSD$\n#\nroot:*:0:0:Charlie &:/root:/bin/csh\ntoor:*:0:0:Bou">>}
7> prx:lseek(Task1, FD, 0, 0).
{error,enotcapable}
8> prx:open(Task1, "/etc/passwd", [o_rdonly]).
{error,ecapmode}
chdir(2): change process current working directory
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.154.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.178.0>}
3> prx:chdir(Task, "/").
ok
4> prx:chdir(Task1, "/tmp").
ok
5> prx:getcwd(Task).
{ok,<<"/">>}
6> prx:getcwd(Task1).
{ok,<<"/tmp">>}
chmod(2): change file permissions
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.154.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.178.0>}
3> {ok, FD} = prx:open(Task1, "/tmp/testfile.txt", [o_wronly, o_creat], 8#644).
{ok,7}
4> prx:chmod(Task1, "/tmp/testfile.txt", 8#400).
ok
chown(2): change file ownership
examples
Examples
1> prx:sudo().
ok
2> {ok, Task} = prx:fork().
{ok,<0.155.0>}
3> {ok, Task1} = prx:fork(Task).
{ok,<0.159.0>}
4> {ok, FD} = prx:open(Task1, "/tmp/testfile.txt", [o_wronly, o_creat], 8#644).
{ok,7}
5> prx:chown(Task1, "/tmp/testfile.txt", 0, 0).
ok
chroot(2): change root directory
examples
Examples
1> prx:sudo().
ok
2> {ok, Task} = prx:fork().
{ok,<0.155.0>}
3> {ok, Task1} = prx:fork(Task).
{ok,<0.159.0>}
4> prx:chroot(Task1, "/tmp").
ok
5> prx:chdir(Task1, "/").
ok
6> prx:getcwd(Task1).
{ok,<<"/">>}
clearenv(3): zero process environment
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.155.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.159.0>}
3> prx:clearenv(Task1).
ok
4> prx:environ(Task1).
[]
clone(2): create a new process
support
Support
• Linux
examples
Examples
1> prx:sudo().
ok
2> {ok, Task} = prx:fork().
{ok,<0.180.0>}
3> {ok, Task1} = prx:clone(Task, [clone_newns, clone_newpid, clone_newipc, clone_newuts, clone_newnet]).
{ok,<0.184.0>}
4> prx:getpid(Task1).
1
close(2): close a file descriptor
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.154.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.178.0>}
3> {ok, FD} = prx:open(Task1, "/tmp/testfile.txt", [o_wronly, o_creat], 8#644).
{ok,7}
4> prx:close(Task1, FD).
ok
-spec controlling_process(task(), pid()) -> ok | {error, badarg}.
Assign a new process owner
call mode
: the controlling process is allowed to make calls to the prx process.
exec mode
: the controlling process receives standard output and standard error from the prx process
Returns the list of child PIDs for this process.
Each child task is a map composed of:
• pid: system pid
• exec: true if the child has called exec()
• fdctl: parent end of CLOEXEC file descriptor used to monitor if the child process has called exec()
• stdin: parent end of the child process' standard input
• stdout: parent end of the child process' standard output
• stderr: parent end of the child process' standard error
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.178.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.182.0>}
3> {ok, Task2} = prx:fork(Task).
{ok,<0.184.0>}
4> prx:cpid(Task).
[#{exec => true,fdctl => -2,flowcontrol => -1,pid => 27809,
signaloneof => 15,stderr => 13,stdin => 10,stdout => 11},
#{exec => false,fdctl => 9,flowcontrol => -1,pid => 27810,
signaloneof => 15,stderr => 17,stdin => 14,stdout => 15}]
Retrieve process info for forked processes
Retrieve the map for a child process as returned in prx:cpid/1.
cpid/2 searches the list of a process' children for a PID (an erlang or a system PID) and returns a map containing the parent's file descriptors towards the child.See also: cpid/1.
-spec drv(task()) -> pid().
Get the gen_server PID for the task
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.180.0>}
<0.181.0>
-spec environ(task()) -> [binary()].
environ(7): return the process environment variables
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.154.0>}
2> prx:environ(Task).
[<<"LANG=C.UTF-8">>,
<<"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin">>,
<<"TERM=screen">>, <<"SHELL=/bin/bash">>]
Close stdin of a task
Close the task standard input by sending a request to the parent. The operation may fail if the parent/child are associated with different erlang processes.
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.200.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.204.0>}
3> prx:execvp(Task1, ["cat"]).
ok
4> prx:eof(Task1).
ok
5> flush().
Shell got {exit_status,<0.204.0>,0}
ok
Close task standard I/O file descriptor
Close stdin, stdout or stderr for a task.
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.176.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.211.0>}
3> prx:execvp(Task1, ["cat"]).
ok
4> prx:eof(Task1, stdout).
ok
5> flush().
Shell got {exit_status,<0.211.0>,0}
ok
See also: eof/2.
-spec execed(task()) -> boolean().
Test if the task has called exec(2)
Returns true
if the task is running in exec mode.
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.178.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.182.0>}
3> prx:execed(Task1).
false
4> prx:execvp(Task1, ["cat"]).
ok
5> prx:execed(Task1).
true
execve(2): replace process image with environment
Replace the process image, specifying the environment for the new process image.
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.180.0>}
2> prx:execvp(Task, "cat", ["name-in-process-list", "-n"])
ok
execve(2): replace process image with environment
Replace the process image, specifying the environment for the new process image.
examples
Examples
Set the command name in the process list:
1> {ok, Task} = prx:fork().
{ok,<0.180.0>}
2> prx:execve(Task, "/bin/cat", ["name-in-process-list", "-n"], ["VAR=1"]).
ok
execvp(2): replace the current process image using the search path
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.180.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.194.0>}
3> prx:execvp(Task1, ["cat", "-n"]).
ok
4> prx:stdin(Task1, <<"test\n">>).
ok
5> flush().
Shell got {stdout,<0.194.0>,<<" 1\ttest\n">>}
ok
execvp(2): replace the current process image using the search path
examples
Examples
Set the command name in the process list:
1> {ok, Task} = prx:fork().
{ok,<0.180.0>}
2> prx:execvp(Task, "cat", ["name-in-process-list", "-n"])
ok
exit(3): cause an prx control process to exit
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.154.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.178.0>}
3> prx:exit(Task1, 111).
ok
4> flush().
Shell got {exit_status,<0.159.0>,111}
ok
See also: fcntl/4.
fcntl(2): perform operations on a file descriptor with argument
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.178.0>}
2> Stdin = 0.
0
3> prx:fcntl(Task, Stdin, f_getfd, 0).
{ok,0}
fexecve(2): replace the process image
Replace the process image, specifying the environment for the new process image, using a previously opened file descriptor. The file descriptor can be set to close after exec() by passing the O_CLOEXEC flag to open:
{ok, FD} = prx:open(Task, "/bin/ls", [o_rdonly,o_cloexec]),
ok = prx:fexecve(Task, FD, ["-al"], ["FOO=123"]).
Linux requires an environment to be set unlike with execve(2). The environment can be empty:
% Environment required on Linux
ok = prx:fexecve(Task, FD, ["ls", "-al"], [""]).
support
Support
• Linux
• FreeBSD
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.180.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.184.0>}
3> {ok, FD} = prx:open(Task1, "/usr/bin/env", [o_rdonly,o_cloexec], 0).
{ok,7}
4> prx:fexecve(Task1, FD, ["env", "-0"], ["FOO=123"]).
ok
5> flush().
Shell got {stdout,<0.208.0>,<<70,79,79,61,49,50,51,0>>}
Shell got {exit_status,<0.208.0>,0}
ok
filter/2 : restrict control process calls
Restricts the set of calls available to a prx control process. If fork is allowed, any subsequently forked control processes inherit the set of filtered calls:
{ok, Ctrl} = prx:fork(),
ok = prx:filter(Ctrl, [getpid]),
{ok, Task} = prx:fork(Ctrl),
{'EXIT', {undef, _}} = (catch prx:getpid(Task)).
See also: filter/3.
-spec filter(task(), [call()] | {allow, [call()]} | {deny, [call()]}, [call()] | {allow, [call()]} | {deny, [call()]}) -> ok.
filter/3 : restrict control process and subprocess calls
filter/3 specifies the set of calls available to a prx control process and any subsequently forked control processes. Control processes continue to proxy data and monitor and reap subprocesses.
Invoking a filtered call will crash the process with 'undef'.
If the filter/3 call is filtered, subsequent calls to filter/3 will fail.
Calls can be either allowed or denied. If a call is allowed, all other calls are filtered.
Once a filter for a call is added, the call cannot be removed from the filter set. Passing an empty list ([]) specifies the current filter set should not be modified.
% the set of calls to filter, any forked control subprocesses
% are unrestricted
prx:filter(Task, {deny, [getpid, execve, execvp]}, [])
% equivalent to {deny, [getpid, execve, execvp]}
prx:filter(Task, [getpid, execve, execvp], [])
% all other calls are filtered including filter
prx:filter(Task, {allow, [fork, clone, kill]}, [])
% init: control process can fork, subprocesses can exec a data process
prx:filter(Task, {allow, [fork, clone, kill]}, {allow, [execve, execvp]})
examples
Examples
1> catch_exception(true).
false
1> {ok, Task} = prx:fork().
{ok,<0.178.0>}
%% % Control process: restricted to: fork, filter, getcwd
%% % Any forked control subprocess: restricted to: getpid, gethostname
2> prx:filter(Task, {allow, [fork, filter, getcwd]}, {allow, [getpid, gethosname]}).
ok
3> {ok, Task1} = prx:fork(Task).
{ok,<0.190.0>}
4> prx:getpid(Task).
* exception error: undefined function prx:getpid/1
5> prx:getcwd(Task1).
* exception error: undefined function prx:getcwd/1
6> prx:getcwd(Task).
{ok,<<"/">>}
fork(2): create a new system process
The behaviour of the process can be controlled by setting the application environment:
Option = {exec, string()}
| {progname, string()}
| {ctldir, string()}
• {exec, Exec}
Default: ""
Sets a command to run the port under such as sudo or valgrind.
For example, to start the process as root using sudo
, allow running prx
as root:
sudo visudo -f /etc/sudoers.d/99_prx
<user> ALL = NOPASSWD: /path/to/prx/priv/prx
Defaults!/path/to/prx/priv/prx !requiretty
Then:
application:set_env(prx, options, [{exec, "sudo -n"}])
• {progname, Path}
Default: priv/prx
Sets the path to the prx executable.
• {ctldir, Path}
Default: priv
A control directory writable by the prx port process (the Unix process may be running under a different user than the Erlang VM).
The control directory contains a FIFO shared by beam and the port process which is used to notify the Erlang VM that the port process has called exec().
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.187.0>}
fork(2): create a child process
Forks child processes from an existing task. For example:
{ok, Task} = prx:fork(), % PID 16341
{ok, Child1} = prx:fork(Task), % PID 16349
{ok, Child2} = prx:fork(Task), % PID 16352
{ok, Child2a} = prx:fork(Child2), % PID 16354
{ok, Child2aa} = prx:fork(Child2a), % PID 16357
{ok, Child2ab} = prx:fork(Child2a). % PID 16482
Results in a process tree:
prx(16341)-+-prx(16349)
`-prx(16352)---prx(16354)-+-prx(16357)
`-prx(16482)
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.187.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.191.0>}
3> prx:cpid(Task).
[#{exec => false,fdctl => 8,flowcontrol => -1,pid => 8098,
signaloneof => 15,stderr => 13,stdin => 10,stdout => 11}]
Get control process attributes
Retrieve attributes set by the prx control process %% for a child process.See also: getcpid/3.
Get control process attributes
Retrieves attributes set by the prx control process for a child process.
• flowcontrol
Number of messages allowed from process:
-1 : flowcontrol disabled
0 : stdout/stderr for process is not read
1+ : read this many messages from the process
• signaloneof
Signal sent to child process on shutdown.
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.154.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.178.0>}
3> prx:getcpid(Task, Task1, flowcontrol).
-1
getcwd(3): return the current working directory
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.179.0>}
2> prx:chdir(Task, "/").
ok
3> prx:getcwd(Task).
{ok,<<"/">>}
-spec getenv(task(), iodata()) -> binary() | false.
getenv(3): retrieve an environment variable
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.179.0>}
2> prx:chdir(Task, "/").
ok
6> prx:getenv(Task, "TERM").
<<"screen">>
getgid(2): retrieve the process group ID
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.179.0>}
2> prx:getgid(Task).
1000
getgroups(2): retrieve the list of supplementary groups
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.179.0>}
2> prx:getgroups(Task).
{ok,[24,20,1000]}
gethostname(2): retrieve the system hostname
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.179.0>}
2> prx:gethostname(Task).
{ok,<<"host1">>}
Retrieve port options for event loop
Options are configurable per process, with the default settings inherited from the parent.
• maxchild : non_neg_integer() : 64
Number of child processes allowed for this control process. The value can be modified using setopt/4,5. Additionally, reducing RLIMIT_NOFILE for the process may result in a reduced maxchild value.
• exit_status : 1 | 0 : 1
Controls whether the controlling Erlang process is informed of a process exit value.
• maxforkdepth : non_neg_integer() : 16
Sets the maximum length of the prx process pipeline.
• termsig : 1 | 0 : 1
If a child process exits because of a signal, notify the controlling Erlang process.
• flowcontrol : int32_t() : -1 (disabled)
Sets the default flow control behaviour for a newly forked process. Flow control is applied after the child process calls exec().
See setcpid/5.
• signaloneof : 0-255 : 15
Send a signal to a child process on shutdown (stdin of the prx control process is closed).
See setcpid/5.
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.179.0>}
2> prx:getopt(Task, maxchild).
64
getpgrp(2): retrieve the process group
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.179.0>}
2> prx:getpgrp(Task).
3924
getpid(2): retrieve the system PID of the process
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.179.0>}
2> prx:getpid(Task).
3924
getpriority(2): retrieve scheduling priority of process, process group or user
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.179.0>}
2> prx:getpriority(Task).
{ok,0}
getresgid(2): get real, effective and saved group ID
support
Support
• Linux
• OpenBSD
• FreeBSD
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.179.0>}
2> prx:getresgid(Task).
{ok,1000,1000,1000}
getresuid(2): get real, effective and saved user ID
support
Support
• Linux
• OpenBSD
• FreeBSD
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.179.0>}
2> prx:getresuid(Task).
{ok,1000,1000,1000}
-spec getrlimit(task(), constant()) -> {ok, #{cur => uint64_t(), max => uint64_t()}} | {error, posix()}.
getrlimit(2): retrieve the resource limits for a process
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.158.0>}
2> prx:getrlimit(Task, rlimit_nofile).
{ok,#{cur => 1024,max => 1048576}}
getsid(2): retrieve the session ID
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.179.0>}
2> prx:getsid(Task).
{ok,3924}
getuid(2): returns the process user ID
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.179.0>}
2> prx:getuid(Task).
1000
-spec ioctl(task(), fd(), constant(), cstruct()) -> {ok, #{return_value := integer(), arg := iodata()}} | {error, posix()}.
ioctl(2) : control device
Controls a device using a file descriptor previously obtained using open/4.
Argp can be either a binary or a list representation of a C struct. See prctl/6 below for a description of the list elements.
On success, ioctl/4 returns a 2-tuple containing a map. The map keys are:
• return_value: an integer equal to the return value of the ioctl.
Usually 0, however some ioctl's on Linux use the return value as the output parameter.
• arg: the value depends on the type of the input parameter Argp.
• cstruct: contains the contents of the memory pointed to by Argp
• integer/binary: an empty binary
examples
Examples
An example of creating a tap device in a net namespace on Linux:
{ok, Child} = prx:clone(Task, [clone_newnet]),
{ok, FD} = prx:open(Child, "/dev/net/tun", [o_rdwr], 0),
{ok, #{return_value = 0, arg = <<"tap", N, _/binary>>}} = prx:ioctl(Child, FD,
tunsetiff, <<
0:(16*8), % generate a tuntap device name
(16#0002 bor 16#1000):2/native-unsigned-integer-unit:8, % IFF_TAP, IFF_NO_PI
0:(14*8)
>>),
{ok, <<"tap", N>>}.
-spec jail(task(), #{version => alcove:uint32_t(), path => iodata(), hostname => iodata(), jailname => iodata(), ip4 => [inet:ip4_address()], ip6 => [inet:ip6_address()]} | cstruct()) -> {ok, int32_t()} | {error, posix()}.
jail(2): restrict the current process in a system jail
support
Support
• FreeBSD
examples
Examples
1> prx:sudo().
ok
2> {ok, Task} = prx:fork().
{ok,<0.155.0>}
3> {ok, Task1} = prx:fork(Task).
{ok,<0.159.0>}
4> prx:jail(Task1, #{path => "/rescue", hostname => "test0", jailname => "test0"}).
{ok,23223}
5> prx:gethostname(Task1).
{ok,<<"test0">>}
kill(2): terminate a process
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.154.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.158.0>}
3> Pid = prx:getpid(Task1).
70524
4> prx:kill(Task, 0, 0).
ok
5> prx:kill(Task, 12345, 0).
{error,esrch}
6> prx:kill(Task, Pid, 0).
ok
7> prx:kill(Task, Pid, sigkill).
ok
8> prx:kill(Task, Pid, 0).
{error,esrch}
9> flush().
Shell got {termsig,<0.158.0>,sigkill}
ok
lseek(2): set file offset for read/write
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.154.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.169.0>}
3> {ok, FD} = prx:open(Task1, "/etc/passwd", [o_rdonly]).
{ok,7}
4> prx:lseek(Task1, FD, 0, 0).
ok
-spec mount(task(), iodata(), iodata(), iodata(), uint64_t() | [constant()], iodata()) -> ok | {error, posix()}.
mount(2) : mount a filesystem, Linux style
The arguments are:
• source
• target
• filesystem type
• flags
• data
An empty list may be used to specify NULL.
For example, filesystems mounted in a Linux mount namespace may be visible in the global mount namespace. To avoid this, first remount the root filesystem within mount namespace using the MS_REC|MS_PRIVATE
flags:
{ok, Task} = prx:clone(Parent, [clone_newns]),
ok = prx:mount(Task, "none", "/", "", [ms_rec, ms_private], "").
On BSD systems, the Source
argument is ignored and passed to the system mount call as:
mount(FSType, Target, Flags, Data);
examples
Examples
An example of bind mounting a directory within a linux mount namespace:
1> prx:sudo().
ok
2> {ok, Task} = prx:fork().
{ok,<0.192.0>}
3> {ok, Task1} = prx:clone(Task, [clone_newns]).
{ok,<0.196.0>}
3> prx:mount(Task1, "/tmp", "/mnt", "", [ms_bind, ms_rdonly, ms_noexec], "").
ok
4> prx:umount(Task1, "/mnt").
ok
-spec mount(task(), iodata(), iodata(), iodata(), uint64_t() | [constant()], iodata(), iodata()) -> ok | {error, posix()}.
(Solaris) mount(2) : mount a filesystem
On Solaris, some mount options are passed in theOptions
argument as a string of comma separated values terminated by a NULL. Other platforms ignore the Options parameter.See also: mount/6.
open(2): returns a file descriptor associated with a file
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.192.0>}
2> prx:open(Task, "/etc/hosts", [o_rdonly]).
{ok,7}
open(2) : open a file specifying permissions
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.192.0>}
2> prx:open(Task, "/tmp/prx-open-test", [o_wronly,o_creat], 8#644).
{ok,7}
Get the parent PID for the task
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.180.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.208.0>}
3> prx:parent(Task).
noproc
4> prx:parent(Task1).
<0.180.0>
Retrieves the system PID of the process similar to getpid(2)
Returns the cached value for the PID of the system process. Works with tasks in exec
mode.
Warning: pidof/1
for a task returned by fork/0
returns the PID of the first child process. If using sudo/0
, the PID for the sudo
helper process is returned.
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.178.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.182.0>}
3> {ok, Task2} = prx:fork(Task).
{ok,<0.184.0>}
4> prx:execvp(Task1, ["cat"]).
ok
5> prx:getpid(Task2).
27810
7> prx:pidof(Task2).
27810
8> prx:pidof(Task1).
27809
Get the process pipeline list for the task
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.180.0>}
2> prx:getpid(Task).
8094
3> {ok, Task1} = prx:fork(Task).
{ok,<0.208.0>}
4> prx:getpid(Task1).
8175
5> {ok, Task2} = prx:fork(Task1).
{ok,<0.3006.0>}
6> prx:getpid(Task2).
27224
7> prx:pipeline(Task2).
[8175,27224]
pivot_root(2): change the root mount
Use pivot_root(2) in a Linux mount namespace to change the root filesystem.
Warning: using pivot_root(2) in the global namespace may have unexpected effects.
To use an arbitrary directory as a mount point:
• mark the mount namespace as private
• create a mount point by bind mounting the new root directory over itself
• change the current working directory to the new root directory
• call pivot_root(2) with new and old root set to the current working directory
• unmount the current working directory
support
Support
• Linux
examples
Examples
1> prx:sudo().
ok
2> {ok, Task} = prx:fork().
{ok,<0.192.0>}
3> {ok, Task1} = prx:clone(Task, [clone_newns]).
{ok,<0.196.0>}
4> prx:mkdir(Task, "/tmp/prx-root", 8#755).
ok
5> {ok, Task1} = prx:clone(Task, [clone_newns]).
{ok,<0.210.0>}
6> prx:mount(Task1, "none", "/", [], [ms_rec, ms_private], []).
ok
7> prx:mount(Task1, "/tmp/prx-root", "/tmp/prx-root", [], [ms_bind], []).
ok
8> prx:chdir(Task1, "/tmp/prx-root").
ok
9> prx:pivot_root(Task1, ".", ".").
ok
10> prx:umount2(Task1, ".", [mnt_detach]).
ok
pledge(2): restrict system operations
An empty list ([]) specifies promises should not be changed. Warning: an empty string ("") is equivalent to an empty list.
To specify no capabilities, use an empty binary: <<>>>
or <<"">>
support
Support
• OpenBSD
examples
Examples
Fork a control process:
• restricted to stdio, proc and exec capabilities
• unrestricted after calling exec
1> {ok, Task} = prx:fork().
{ok,<0.152.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.156.0>}
3> prx:pledge(Task, <<"stdio proc exec">>, []).
ok
-spec prctl(task(), constant(), ptr_arg(), ptr_arg(), ptr_arg(), ptr_arg()) -> {ok, integer(), ptr_val(), ptr_val(), ptr_val(), ptr_val()} | {error, posix()}.
prctl(2) : operations on a process
This function can be used to set BPF syscall filters on processes (seccomp mode).
A list can be used for prctl operations requiring a C structure as an argument. List elements are used to contiguously populate a buffer (it is up to the caller to add padding):
• binary()
: the element is copied directly into the buffer
On return, the contents of the binary is returned to the caller.
• {ptr, N}
: N bytes of zero'ed memory is allocated. The pointer is placed in the buffer.
On return, the contents of the memory is returned to the caller.
• {ptr, binary()}
Memory equal to the size of the binary is allocated and initialized with the contents of the binary.
On return, the contents of the memory is returned to the caller.
support
Support
• Linux
examples
Examples
The prx process requires the following syscalls to run:
sys_exit
sys_exit_group
sys_getrlimit
sys_poll
sys_read
sys_restart_syscall
sys_rt_sigreturn
sys_setrlimit
sys_sigreturn
sys_ugetrlimit
sys_write
sys_writev
To enforce a seccomp filter:
-module(seccomp).
-include_lib("alcove/include/alcove_seccomp.hrl").
-export([run/1, run/2, filter/2]).
-define(DENY_SYSCALL(Syscall), [
?BPF_JUMP(?BPF_JMP + ?BPF_JEQ + ?BPF_K, (Syscall), 0, 1),
?BPF_STMT(?BPF_RET + ?BPF_K, ?SECCOMP_RET_KILL)
]).
filter(Task, Syscall) ->
Arch = prx:call(Task, syscall_constant, [alcove:audit_arch()]),
NR = prx:call(Task, syscall_constant, [Syscall]),
[
?VALIDATE_ARCHITECTURE(Arch),
?EXAMINE_SYSCALL,
?DENY_SYSCALL(NR),
?BPF_STMT(?BPF_RET + ?BPF_K, ?SECCOMP_RET_ALLOW)
].
run(Task) ->
run(Task, sys_getcwd).
run(Task, Syscall) ->
Filter = filter(Task, Syscall),
{ok, _, _, _, _, _} = prx:prctl(Task, pr_set_no_new_privs, 1, 0, 0, 0),
Pad = (erlang:system_info({wordsize, external}) - 2) * 8,
Prog = [
<<(iolist_size(Filter) div 8):2/native-unsigned-integer-unit:8>>,
<<0:Pad>>,
{ptr, list_to_binary(Filter)}
],
%% prx:seccomp(Task, seccomp_set_mode_filter, 0, Prog)
prx:prctl(Task, pr_set_seccomp, seccomp_mode_filter, Prog, 0, 0).
To enforce the filter:
1> catch_exception(true).
false
2> {ok, Task} = prx:fork().
{ok,<0.186.0>}
3> {ok, Task1} = prx:fork(Task).
{ok,<0.191.0>}
4> seccomp:run(Task1, sys_getcwd).
{ok,0,2,
[<<7,0>>,
<<0,0,0,0,0,0>>,
{ptr,<<32,0,0,0,4,0,0,0,21,0,1,0,62,0,0,192,6,0,0,0,...>>}],
0,0}
5> prx:getcwd(Task1).
See also: seccomp/4.
-spec procctl(task(), constant(), pid_t(), constant(), [] | cstruct()) -> {ok, binary(), cstruct()} | {error, posix()}.
procctl(2): control processes
support
Support
• FreeBSD
examples
Examples
Pid = prx:pidof(Task),
prx:procctl(Task, 0, Pid, 'PROC_REAP_ACQUIRE', []),
prx:procctl(Task, p_pid, Pid, 'PROC_REAP_STATUS', [
<<0,0,0,0>>, % rs_flags
<<0,0,0,0>>, % rs_children
<<0,0,0,0>>, % rs_descendants
<<0,0,0,0>>, % rs_reaper
<<0,0,0,0>> % rs_pid
]).
-spec ptrace(task(), constant(), pid_t(), ptr_arg(), ptr_arg()) -> {ok, integer(), ptr_val(), ptr_val()} | {error, posix()}.
ptrace(2): process trace
examples
Examples
-module(ptrace).
-export([run/0]).
run() ->
{ok, Task} = prx:fork(),
{ok, Task1} = prx:fork(Task),
{ok, Task2} = prx:fork(Task1),
Pid2 = prx:pidof(Task2),
% disable the prx event loop: child process must be managed by
% the caller
{ok, sig_dfl} = prx:sigaction(Task1, sigchld, sig_info),
% enable ptracing in the child process and exec() a command
{ok, 0, <<>>, <<>>} = prx:ptrace(Task2, ptrace_traceme, 0, 0, 0),
ok = prx:execvp(Task2, "cat", ["cat"]),
% the parent is notified
ok =
receive
{signal, Task1, sigchld, _} ->
ok
after 5000 ->
timeout
end,
{ok, Pid2, _, [{stopsig, sigtrap}]} = prx:waitpid(Task1, -1, [wnohang]),
% should be no other events
{ok, 0, 0, []} = prx:waitpid(Task1, -1, [wnohang]),
% allow the process to continue
{ok, 0, <<>>, <<>>} = prx:ptrace(Task1, ptrace_cont, Pid2, 0, 0),
ok = prx:stdin(Task2, "test\n"),
ok =
receive
{stdout, Task2, <<"test\n">>} ->
ok
after 5000 -> timeout
end,
% Send a SIGTERM and re-write it to a harmless SIGWINCH
ok = prx:kill(Task1, Pid2, sigterm),
ok =
receive
{signal, Task1, sigchld, _} ->
ok
after 5000 ->
timeout
end,
{ok, Pid2, _, [{stopsig, sigterm}]} = prx:waitpid(Task1, -1, [wnohang]),
{ok, 0, <<>>, <<>>} = prx:ptrace(Task1, ptrace_cont, Pid2, 0, 28),
% Convert a SIGWINCH to SIGTERM
ok = prx:kill(Task1, Pid2, sigwinch),
ok =
receive
{signal, Task1, sigchld, _} ->
ok
after 5000 ->
timeout
end,
{ok, 0, <<>>, <<>>} = prx:ptrace(Task1, ptrace_cont, Pid2, 0, 15),
{ok, Pid2, _, [{termsig, sigterm}]} = prx:waitpid(Task1, -1, []).
read(2): read bytes from a file descriptor
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.212.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.216.0>}
3> {ok, FD} = prx:open(Task1, "/etc/hosts", [o_rdonly]).
{ok,7}
4> prx:read(Task1, FD, 64).
{ok,<<"127.0.0.1 localhost\n\n# The following lines are desirable for IPv">>}
readdir(3): retrieve list of objects in a directory
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.212.0>}
2> prx:readdir(Task, "/dev/pts").
{ok,[<<".">>,<<"..">>,<<"66">>,<<"63">>,<<"67">>,<<"64">>,
<<"62">>,<<"61">>,<<"60">>,<<"59">>,<<"58">>,<<"57">>,
<<"56">>,<<"55">>,<<"54">>,<<"53">>,<<"52">>,<<"51">>,
<<"50">>,<<"49">>,<<"48">>,<<"47">>,<<"46">>,<<"45">>,
<<"44">>,<<"43">>,<<...>>|...]}
Fork+exec prx process.
Fork+exec is a way of randomizing the memory space of a process:
https://poolp.org/posts/2016-09-12/opensmtpd-6.0.0-is-released/
prx processes fork recursively:
• the calls stack increases in size
• the memory space layout is identical to the parent
After forking a prx process using fork/1, the controlling process will typically instruct the new prx process to execute a command using one of the exec(3) functions: execvp/2, execve/3.
Some "system" or "supervisor" type processes may remain in call mode: these processes can call reexec/1 to exec() the port.
On platforms supporting fexecve(2) (FreeBSD, Linux), prx will open a file descriptor to the port binary and use it to re-exec() the port.
On other OS'es, execve(2) will be used with the the default path to the port binary.
If the binary is not accessible or, on Linux, /proc is not mounted, reexec/1 will fail.
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.180.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.216.0>}
2> prx:getpid(Task1).
8175
3> prx:reexec(Task1).
ok
4> prx:getpid(Task1).
8175
-spec reexec(task(), {fd, int32_t(), [string() | [string()]]} | [string() | [string()]], iodata()) -> ok | {error, posix()}.
Replace the port process image using execve(2)/fexecve(2).
Specify the port program path or a file descriptor to the binary and the process environment.See also: reexec/1.
Equivalent to reexec / 1.
-spec replace_process_image(task(), {fd, int32_t(), [string() | [string()]]} | [string() | [string()]], iodata()) -> ok | {error, posix()}.
Equivalent to reexec / 3.
rmdir(2): delete a directory
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.212.0>}
2> prx:mkdir(Task, "/tmp/prx-rmdir-test", 8#755).
ok
3> prx:rmdir(Task, "/tmp/prx-rmdir-test").
ok
seccomp(2) : restrict system operations
support
Support
• Linux
examples
Examples
%% Equivalent to:
%% prx:prctl(Task, pr_set_seccomp, seccomp_mode_filter, Prog, 0, 0).
prx:seccomp(Task, seccomp_set_mode_filter, 0, Prog)
See also: prctl/6.
-spec select(task(), Readfds :: [fd()], Writefds :: [fd()], Exceptfds :: [fd()], Timeval :: [] | #{sec => int64_t(), usec => int64_t()}) -> {ok, [fd()], [fd()], [fd()]} | {error, posix()}.
select(2): poll a list of file descriptor for events
select/5 will block until an event occurs on a file descriptor, a timeout is reached or interrupted by a signal.
The Timeout value may be:
• an empty list ([]): causes select to block indefinitely (no timeout)
• a map indicating the timeout
The map contains these fields:
• sec : number of seconds to wait
• usec : number of microseconds to wait
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.178.0>}
2> {ok, FD} = prx:open(Task, "/dev/null", [o_rdwr], 0).
{ok,7}
3> prx:select(Task, [FD], [FD], [FD], []).
{ok,[7],[7],[]}
4> prx:select(Task, [FD], [FD], [FD], #{sec => 1, usec => 1}).
{ok,[7],[7],[]}
setcpid() : Set options for child process of prx control process
Control behaviour of an exec()'ed process.See also: setcpid/4.
setcpid() : Set options for child process of prx control process
flowcontrol
enables rate limiting of the stdout and stderr of a child process. stdin is not rate limited (default: -1 (disabled))
• 0: stdout/stderr for process is not read
• 1-2147483646: read this many messages from the process
• -1: disable flow control
NOTE: the limit applies to stdout and stderr. If the limit is set to 1, it is possible to get:
• 1 message from stdout
• 1 message from stderr
• 1 message from stdout and stderr
signaloneof
delivers a signal to any subprocesses when the alcove control process shuts down (default: 15 (SIGTERM))
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.158.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.162.0>}
3> prx:setcpid(Task1, flowcontrol, 0).
true
4> prx:getcpid(Task1, flowcontrol).
0
setenv(3): set an environment variable
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.158.0>}
2> prx:setenv(Task, "TEST", "foo", 0).
ok
3> prx:getenv(Task, "TEST").
<<"foo">>
4> prx:setenv(Task, "TEST", "bar", 0).
ok
5> prx:getenv(Task, "TEST").
<<"foo">>
6> prx:setenv(Task, "TEST", "bar", 1).
ok
7> prx:getenv(Task, "TEST").
<<"bar">>
setgid(2): set the GID of the process
examples
Examples
1> prx:sudo().
ok
2> {ok, Task} = prx:fork().
{ok,<0.159.0>}
3> {ok, Task1} = prx:fork(Task).
{ok,<0.163.0>}
4> prx:setgid(Task1, 123).
ok
5> prx:getgid(Task1).
123
setgroups(2): set the supplementary groups of the process
examples
Examples
1> prx:sudo().
ok
2> {ok, Task} = prx:fork().
{ok,<0.160.0>}
3> {ok, Task1} = prx:fork(Task).
{ok,<0.164.0>}
4> prx:setgroups(Task1, []).
ok
5> prx:getgroups(Task1).
{ok,[]}
sethostname(2): set the system hostname
This function is probably only useful if running in a uts namespace or a jail.
examples
Examples
{ok, Child} = prx:clone(Task, [clone_newuts]),
ok = prx:sethostname(Child, "test"),
Hostname1 = prx:gethostname(Task),
Hostname2 = prx:gethostname(Child),
Hostname1 =/= Hostname2.
setns(2): attach to a namespace
A process namespace is represented as a path in the /proc filesystem. The path is /proc/<pid>/ns/<ns>
, where:
• pid: the system PID
• ns: a file representing the namespace
The available namespaces is dependent on the kernel version. You can see which are supported by running:
ls -al /proc/$$/ns
support
Support
• Linux
examples
Examples
Attach a process to an existing network namespace:
{ok, Child1} = prx:clone(Task, [clone_newnet]),
{ok, Child2} = prx:fork(Task),
% Move Child2 into the Child1 network namespace
{ok, FD} = prx:open(
Child2,
["/proc/", integer_to_list(Child1), "/ns/net"],
[o_rdonly],
0
),
ok = prx:setns(Child2, FD),
ok = prx:close(Child2, FD).
(Linux) setns(2) : attach to a namespace, specifying namespace type
ok = prx:setns(Task, FD, clone_newnet)
See also: setns/2.
Set port options
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.160.0>}
2> prx:setopt(Task, maxforkdepth, 128).
true
See also: getopt/2.
setpgid(2): set process group
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.158.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.162.0>}
3> prx:setpgid(Task1, 0, 0).
ok
setpriority(2): set scheduling priority of process, process group or user
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.158.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.162.0>}
3> prx:setpriority(Task1, prio_process, 0, 10).
ok
4> prx:getpriority(Task1, prio_process, 0).
{ok,10}
-spec setproctitle(task(), iodata()) -> ok.
setproctitle(3): set the process title
Set the process title displayed in utilities like ps(1) by overwriting the command's arg0.
Linux systems may also want to set the command name using prctl/6
:
prx:prctl(Task, pr_set_name, <<"newname">>, 0, 0, 0)
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.177.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,28210}
3> prx:setproctitle(Task1, "new process name").
ok
See also: prctl/6.
setresgid(2): set real, effective and saved group ID
support
Support
• Linux
• FreeBSD
• OpenBSD
examples
Examples
1> prx:sudo().
ok
2> {ok, Task} = prx:fork().
{ok,<0.158.0>}
3> {ok, Task1} = prx:fork(Task).
{ok,<0.162.0>}
4> prx:setresgid(Task1, 123, 123, 123).
ok
setresuid(2): set real, effective and saved user ID
support
Support
• Linux
• FreeBSD
• OpenBSD
examples
Examples
1> prx:sudo().
ok
2> {ok, Task} = prx:fork().
{ok,<0.158.0>}
3> {ok, Task1} = prx:fork(Task).
{ok,<0.162.0>}
3> prx:setresuid(Task1, 123, 123, 123).
ok
-spec setrlimit(task(), constant(), #{cur => uint64_t(), max => uint64_t()}) -> ok | {error, posix()}.
setrlimit(2): set a resource limit
Note on rlimit_nofile
:
The control process requires a fixed number of file descriptors for each subprocess. Reducing the number of file descriptors will reduce the limit on child processes.
If the file descriptor limit is below the number of file descriptors currently used, setrlimit/4,5 will return {error, einval}
.
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.158.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.162.0>}
3> prx:getrlimit(Task1, rlimit_nofile).
{ok,#{cur => 1048576,max => 1048576}}
4> prx:setrlimit(Task1, rlimit_nofile, #{cur => 64, max => 64}).
ok
5> prx:getrlimit(Task1, rlimit_nofile).
{ok,#{cur => 64,max => 64}}
6> prx:getrlimit(Task, rlimit_nofile).
{ok,#{cur => 1048576,max => 1048576}}
setsid(2): create a new session
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.158.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.162.0>}
3> prx:setsid(Task1).
{ok,32141}
setuid(2): change UID
examples
Examples
1> prx:sudo().
ok
2> {ok, Task} = prx:fork().
{ok,<0.158.0>}
3> {ok, Task1} = prx:fork(Task).
{ok,<0.162.0>}
3> prx:setuid(Task1, 123).
ok
3> prx:getuid(Task1).
123
See also: setresuid/4.
-spec sigaction(task(), constant(), atom() | {sig_info, fun((pid(), [pid_t()], atom(), binary()) -> any())}) -> {ok, atom()} | {error, posix()}.
sigaction(2): set process behaviour for signals
• sig_dfl
Uses the default behaviour for the signal
• sig_ign
Ignores the signal
• sig_info
Catches the signal and sends the controlling Erlang process an event:
{signal, atom(), Info}
Info is a binary containing the siginfo_t structure. See sigaction(2) for details.
• []
Returns the current handler for the signal.
Multiple caught signals of the same type may be reported as one event.
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.178.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.182.0>}
3> prx:kill(Task, prx:pidof(Task1), sigterm).
ok
4> flush().
Shell got {signal,<0.182.0>,sigterm,
<<15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,156,126,0,0,232,3,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>>}
ok
5> prx:sigaction(Task1, sigterm, sig_ign).
{ok,sig_info}
6> prx:kill(Task, prx:pidof(Task1), sigterm).
ok
7> flush().
ok
8> prx:sigaction(Task1, sigterm, sig_info).
{ok,sig_ign}
9> prx:kill(Task, prx:pidof(Task1), sigterm).
ok
10> flush().
Shell got {signal,<0.182.0>,sigterm,
<<15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,156,126,0,0,232,3,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>>}
ok
socket(2): returns a file descriptor for a communication endpoint
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.178.0>}
2> {ok, FD} = prx:socket(Task, af_inet, sock_stream, 0).
{ok,7}
-spec stdin(task(), iodata()) -> ok.
Send data to the standard input of the process
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.180.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.194.0>}
3> prx:execvp(Task1, ["cat", "-n"]).
ok
4> prx:stdin(Task1, <<"test\n">>).
ok
5> flush().
Shell got {stdout,<0.194.0>,<<" 1\ttest\n">>}
ok
-spec stdio(task(), pid()) -> ok | {error, badarg}.
Assign a process to receive stdio
Change the process receiving prx standard output and standard error.
stdio/2 and controlling_process/2 can be used to transfer a prx process between erlang processes without losing output when exec(3) is called:
ok = prx:stdio(Owner, NewOwner),
ok = prx:execvp(Owner, Argv),
ok = prx:controlling_process(Owner, NewOwner).
-spec stop(task()) -> ok.
Terminate the task
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.180.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.184.0>}
4> prx:stop(Task1).
ok
5> prx:cpid(Task).
[]
-spec sudo() -> ok.
Convenience function to fork a privileged process in the shell.
Sets the application environment so prx can fork a privileged process. sudo
must be configured to run the prx binary.
The application environment must be set before prx:fork/0 is called.
Equivalent to:
application:set_env(prx, options, [{exec, "sudo -n"}]),
{ok, Task} = prx:fork(),
0 = prx:getuid(Task).
examples
Examples
1> prx:sudo().
ok
2> {ok, Task} = prx:fork().
{ok,<0.199.0>}
3> prx:getuid(Task).
0
-spec sudo(iodata()) -> ok.
Convenience function to fork a privileged process in the shell.
Allows specifying the command.
examples
Examples
For example, on OpenBSD:
1> prx:sudo("doas").
ok
2> {ok, Task} = prx:fork().
{ok,<0.199.0>}
3> prx:getuid(Task).
0
-spec task(task(), Ops :: [prx_task:op() | [prx_task:op()]], State :: any()) -> {ok, task()} | {error, posix()}.
Fork a subprocess and run a sequence of operations
task/3 uses fork/1
to create a new subprocess and run a sequence of system calls. If an operations fails, the subprocess is sent SIGKILL and exits.
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.349.0>}
2> {ok, Task1} = prx:task(Task, [
2> {chdir, ["/"]},
2> {setrlimit, [rlimit_core, #{cur => 0, max => 0}]},
2> {prx, chdir, ["/nonexistent"], [{errexit, false}]}
2> ], []).
{ok,<0.382.0>}
3> prx:getrlimit(Task1, rlimit_core).
{ok,#{cur => 0,max => 0}}
4> prx:getcwd(Task1).
{ok,<<"/">>}
See also: task/4.
-spec task(task(), Ops :: [prx_task:op() | [prx_task:op()]], State :: any(), Config :: [prx_task:config()]) -> {ok, task()} | {error, posix()}.
Create a subprocess and run a sequence of operations using optional function calls
task/4 calls the optional init
function provided in the Config
argument to create a new subprocess. The default init
function uses fork/1
.
The subprocess next performs a list of operations. Operations are tuples consisting of:
* the module name: optional if modifier is not present, defaults to prx
* the module function
* function arguments
* modifier list
[
% equivalent to prx:setresgid(65534, 65534, 65534)
{setresgid, [65534, 65534, 65534]},
% equivalent to prx:setresuid(65534, 65534, 65534), error is ignored
{prx, setresuid, [65534, 65534, 65534], [{errexit, false}]},
]
If an operation returns {error, term()}
, the sequence of operations is aborted and the terminate
function is run. The default terminate
functions signals the subprocess with SIGKILL.
examples
Examples
1> Init = fun(Parent) ->
1> prx:clone(Parent, [
1> clone_newnet,
1> clone_newuser
1> ])
1> end.
#Fun<erl_eval.44.65746770>
2> Terminate = fun(Parent, Child) ->
2> prx:stop(Child),
2> prx:kill(Parent, prx:pidof(Child), sigterm)
2> end.
#Fun<erl_eval.43.65746770>
3> {ok, Task} = prx:fork().
4> {ok, Task1} = prx:task(Task, [
4> {chdir, ["/"]},
4> {setrlimit, [rlimit_core, #{cur => 0, max => 0}]},
4> {prx, chdir, ["/nonexistent"], [{errexit, false}]}
4> ], [], [
4> {init, Init},
4> {terminate, Terminate}
4> ]).
{ok,<0.398.0>}
5> prx:execvp(Task1, ["ip", "a"]).
ok
27> flush().
Shell got {stdout,<0.398.0>,
<<"1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000\n link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00\n">>}
Shell got {exit_status,<0.398.0>,0}
ok
See also: prx_task.
umount2(2) : unmount a filesystem
On BSD systems, calls unmount(2).
examples
Examples
See also: pivot_root/3.
umount(2): unmount a filesystem
On BSD systems, calls unmount(2).
examples
Examples
An example of bind mounting a directory within a linux mount namespace:
1> prx:sudo().
ok
2> {ok, Task} = prx:fork().
{ok,<0.192.0>}
3> {ok, Task1} = prx:clone(Task, [clone_newns]).
{ok,<0.196.0>}
3> prx:mount(Task1, "/tmp", "/mnt", "", [ms_bind, ms_rdonly, ms_noexec], "").
ok
4> prx:umount(Task1, "/mnt").
ok
unlink(2): delete a name from the filesystem
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.178.0>}
2> prx:open(Task, "/tmp/testfile", [o_wronly, o_creat], 8#644).
{ok,8}
3> prx:unlink(Task, "/tmp/testfile").
ok
unsetenv(3): remove an environment variable
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.178.0>}
2> prx:setenv(Task, "TEST", "foo", 0).
ok
3> prx:getenv(Task, "TEST").
<<"foo">>
4> prx:unsetenv(Task, "TEST").
ok
5> prx:getenv(Task, "TEST").
false
unveil(2): restrict filesystem view
To disable unveil calls, use an empty list ([]) or, equivalently, an empty string ("").
prx:unveil(Task, <<"/etc">>, <<"r">>),
prx:unveil(Task, [], []).
support
Support
• OpenBSD
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.152.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.156.0>}
3> prx:unveil(Task1, <<"/etc">>, <<"r">>).
ok
4> prx:unveil(Task1, [], []).
ok
5> prx:readdir(Task1, "/etc").
{ok,[<<".">>,<<"..">>,<<"acme">>,<<"amd">>,<<"authpf">>,
<<"daily">>,<<"disktab">>,<<"examples">>,<<"firmware">>,
<<"hotplug">>,<<"iked">>,<<"isakmpd">>,<<"ldap">>,
<<"magic">>,<<"mail">>,<<"moduli">>,<<"monthly">>,
<<"mtree">>,<<"netstart">>,<<"npppd">>,<<"pf.os">>,
<<"ppp">>,<<"protocols">>,<<"rc">>,<<"rc.conf">>,<<"rc.d">>,
<<...>>|...]}
6> prx:readdir(Task1, "/tmp").
{error,enoent}
-spec waitpid(task(), pid_t(), int32_t() | [constant()]) -> {ok, pid_t(), int32_t(), [waitstatus()]} | {error, posix()}.
waitpid(2): wait for process to change state
Process state changes are handled by the alcove SIGCHLD event handler by default. To use waitpid/4,5, disable the signal handler:
{ok, sig_dfl} = prx:sigaction(Task, sigchld, sig_info),
{ok, Child} = prx:fork(Task),
Pid = prx:getpid(Child),
ok = prx:exit(Child, 2),
{ok, Pid, _, [{exit_status, 2}]} = prx:waitpid(Task, Pid, [wnohang]).
Note: if the default SIGCHLD handler is disabled, waitpid/4,5 should be called to reap zombie processes:
prx:waitpid(Task, -1, [wnohang])
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.158.0>}
2> {ok, Task1} = prx:fork(Task).
{ok,<0.162.0>}
3> prx:sigaction(Task1, sigchld, sig_info).
{ok,sig_dfl}
4> prx:execvp(Task1, ["sleep", "20"]).
ok
5> prx:waitpid(Task, -1, []).
{ok,30958,0,[{exit_status,0}]}
write(2): write to a file descriptor
Writes a buffer to a file descriptor and returns the number of bytes written.
examples
Examples
1> {ok, Task} = prx:fork().
{ok,<0.178.0>}
2> {ok, FD} = prx:open(Task, "/tmp/testfile", [o_wronly, o_creat], 8#644).
{ok,7}
3> prx:write(Task, FD, <<"test">>).
{ok,4}
-spec write(task(), fd(), iodata()) -> {ok, ssize_t()} | {error, posix()}.