Skip to content
Michael Santos edited this page Nov 28, 2018 · 50 revisions

Module prx

Copyright (c) 2015-2018 Michael Santos michael.santos@gmail.com

Behaviours: gen_statem.

Data Types

call()


call() = alcove_proto:call() | reexec | replace_process_image | getcpid

constant()


constant() = atom() | integer()

cpid()


cpid() = #{pid => pid_t(), flowcontrol => uint32_t(), signaloneof => uint32_t(), exec => boolean(), fdctl => fd(), stdin => fd(), stdout => fd(), stderr => fd()}

cstruct()


cstruct() = [binary() | {ptr, binary() | non_neg_integer()}, ...]

fd()


fd() = int32_t()

gid_t()


gid_t() = uint32_t()

int32_t()


int32_t() = -2147483647..2147483647

int64_t()


int64_t() = -9223372036854775807..9223372036854775807

mode_t()


mode_t() = uint32_t()

off_t()


off_t() = uint64_t()

pid_t()


pid_t() = int32_t()

posix()


posix() = alcove:posix()

prx_opt()


prx_opt() = maxchild |exit_status |maxforkdepth |termsig |flowcontrol |signaloneof

ptr_arg()


ptr_arg() = binary() | constant() | cstruct()

ptr_val()


ptr_val() = binary() | integer() | cstruct()

size_t()


size_t() = uint64_t()

ssize_t()


ssize_t() = int64_t()

task()


task() = pid()

uid_t()


uid_t() = uint32_t()

uint32_t()


uint32_t() = 0..4294967295

uint64_t()


uint64_t() = 0..18446744073709551615

waitstatus()


waitstatus() = {exit_status, int32_t()} |{termsig, atom()} |{stopsig, atom()} |continued

Function Index

atexit/2 Register a function to be called at task termination.
call/3 Make a synchronous call into the port driver.
cap_enter/1 (FreeBSD only) cap_enter(2) : put process into capability mode.
cap_fcntls_get/2 (FreeBSD only) cap_fcntls_get(2) : get allowed fnctl(2) commands on file descriptor.
cap_fcntls_limit/3 (FreeBSD only) cap_fcntls_limit(2) : set allowed fnctl(2) commands on file descriptor.
cap_getmode/1 (FreeBSD only) cap_getmode(2) : returns capability mode status of process.
cap_ioctls_limit/3 (FreeBSD only) cap_ioctls_limit(2) : set allowed ioctl(2) commands on file descriptor.
cap_rights_limit/3 (FreeBSD only) cap_rights_limit(2) : set allowed rights(4) of file descriptor.
chdir/2 chdir(2) : change process current working directory.
chmod/3 chmod(2) : change file permissions.
chown/4 chown(2) : change file ownership.
chroot/2 chroot(2) : change root directory.
clearenv/1 clearenv(3) : zero process environment.
clone/2 (Linux only) clone(2) : create a new process.
close/2 close(2) : close a file descriptor.
cmd/2
controlling_process/2 assign a new process owner.
cpid/1 Returns the list of child PIDs for this process.
cpid/2 retrieve process info for forked processes.
drv/1
environ/1 environ(7) : return the process environment variables.
eof/2 close stdin of child process.
eof/3 close stdin, stdout or stderr of child process.
execed/1 test if the task has called exec(2).
execve/3 execve(2) : replace the process image, specifying the environment for the new process image.
execve/4 execve(2) : replace the process image, specifying the environment for the new process image.
execvp/2 execvp(2) : replace the current process image using the search path.
execvp/3 execvp(2) : replace the current process image using the search path.
exit/2 exit(3) : cause the child process to exit.
fcntl/3 fcntl(2) : perform operation on a file descriptor.
fcntl/4 fcntl(2) : perform operation on a file descriptor with argument.
fexecve/4 fexecve(2) : replace the process image, specifying the environment for the new process image, using a previously opened file descriptor.
filter/2 filter() : restrict calls available to a control process.
fork/0 fork(2) : create a new system process.
fork/1 fork(2) : create a child process.
forkchain/1
getcpid/2 getcpid() : Get options for child process of prx control process.
getcpid/3 getcpid() : Retrieve attributes set by the prx control process for a child process.
getcwd/1 getcwd(3) : return the current working directory.
getenv/2 getenv(3) : retrieve an environment variable.
getgid/1 getgid(2) : retrieve the processes' group ID.
getgroups/1 getgroups(2) : retrieve the list of supplementary groups.
gethostname/1 gethostname(2) : retrieve the system hostname.
getopt/2 getopt() : get options for the prx control process.
getpgrp/1 getpgrp(2) : retrieve the process group.
getpid/1 getpid(2) : retrieve the system PID of the process.
getpriority/3 getpriority(2) : retrieve scheduling priority of process, process group or user.
getresgid/1 getresgid(2) : get real, effective and saved group ID.
getresuid/1 getresuid(2) : get real, effective and saved user ID.
getrlimit/2 getrlimit(2) : retrieve the resource limits for a process.
getsid/2 getsid(2) : retrieve the session ID.
getuid/1 getuid(2) : returns the process user ID.
ioctl/4 ioctl(2) : control device.
jail/2 (FreeBSD only) jail(2) : restrict the current process in a system jail.
kill/3 kill(2) : terminate a process.
lseek/4 lseek(2) : set file offset for read/write.
mkdir/3 mkdir(2) : create a directory.
mkfifo/3 mkfifo(3) : create a named pipe.
mount/6 mount(2) : mount a filesystem, Linux style.
mount/7 (Solaris only) mount(2) : mount a filesystem.
open/3 open(2) : returns a file descriptor associated with a file.
open/4 open(2) : create a file, specifying permissions.
parent/1
pidof/1 retrieves the system PID of the process similar to getpid(2).
pivot_root/3 (Linux only) pivot_root(2) : change the root filesystem.
pledge/3 (OpenBSD only) pledge(2) : restrict system operations
  prx:pledge(Task, "stdio proc exec", [])
prctl/6 (Linux only) prctl(2) : operations on a process.
ptrace/5 (Linux only) ptrace(2) : trace processes.
read/3 read(2) : read bytes from a file descriptor.
readdir/2 readdir(3) : retrieve list of objects in a directory.
reexec/1 Fork+reexec prx process.
reexec/3 Replace the port process image using execve(2)/fexecve(2).
replace_process_image/1 Alias for reexec/1.
replace_process_image/3 Alias for reexec/3.
rmdir/2 rmdir(2) : delete a directory.
seccomp/4 seccomp(2) : restrict system operations.
select/5 select(2) : poll a list of file descriptor for events.
setcpid/3 setcpid() : Set options for child process of prx control process.
setcpid/4 setcpid() : Set options for child process of prx control process.
setenv/4 setenv(3) : set an environment variable.
setgid/2 setgid(2) : set the GID of the process.
setgroups/2 setgroups(2) : set the supplementary groups of the process.
sethostname/2 sethostname(2) : set the system hostname.
setns/2 (Linux only) setns(2) : attach to a namespace.
setns/3 (Linux only) setns(2) : attach to a namespace, specifying namespace type.
setopt/3 setopt() : set options for the prx control process.
setpgid/3 setpgid(2) : set process group.
setpriority/4 setpriority(2) : set scheduling priority of process, process group or user.
setproctitle/2 setproctitle(3) : set the process title.
setresgid/4 setresgid(2) : set real, effective and saved group ID.
setresuid/4 setresuid(2) : set real, effective and saved user ID.
setrlimit/3 setrlimit(2) : set a resource limit.
setsid/1 setsid(2) : create a new session.
setuid/2 setuid(2) : change UID.
sh/2
sigaction/3 sigaction(2) : set process behaviour for signals.
socket/4 socket(2) : retrieve file descriptor for communication endpoint.
start_link/1
stdin/2 Send data to the standard input of the process.
stdio/2 assign a process to receive stdio.
stop/1 terminate the task.
sudo/0 Convenience function to fork a privileged process in the shell.
sudo/1 Convenience function to fork a privileged process in the shell.
task/3
task/4
umount/2 umount(2) : unmount a filesystem.
umount2/3 umount2(2) : unmount a filesystem.
unlink/2 unlink(2) : delete references to a file.
unsetenv/2 unsetenv(3) : remove an environment variable.
unshare/2 (Linux only) unshare(2) : allows creating a new namespace in the current process.
unveil/3 (OpenBSD only) unveil(2) : restrict filesystem view
  prx:unveil(Task, "/bin", "rx")
waitpid/3 waitpid(2) : wait for child process.
write/3 write(2): writes a buffer to a file descriptor and returns the number of bytes written.

Function Details

atexit/2


atexit(Task :: task(),Fun :: fun((pid(), [pid_t()], pid_t()) -> any())) ->ok

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.

The default function closes stdin, stdout and stderr of the system process:

  fun(Drv, ForkChain, Pid) ->
   prx_drv:call(Drv, ForkChain, close, [maps:get(stdout, Pid)]),
   prx_drv:call(Drv, ForkChain, close, [maps:get(stdin, Pid)]),
   prx_drv:call(Drv, ForkChain, close, [maps:get(stderr, Pid)])
  end

call/3


call(Task :: task(), Call :: call(), Argv :: [any()]) -> any()

Make a synchronous call into the port driver.

The list of available calls and their arguments can be found here:

https://github.com/msantos/alcove#alcove-1

For example, to directly call alcove:execve/5:

  call(Task, execve,
   ["/bin/ls", ["/bin/ls", "-al"], ["HOME=/home/foo"]])

cap_enter/1


cap_enter(Task :: task()) -> ok | {error, posix()}

(FreeBSD only) cap_enter(2) : put process into capability mode

cap_fcntls_get/2


cap_fcntls_get(Task :: task(), Arg1 :: fd()) ->{ok, int32_t()} | {error, posix()}

(FreeBSD only) cap_fcntls_get(2) : get allowed fnctl(2) commands on file descriptor

cap_fcntls_limit/3


cap_fcntls_limit(Task :: task(),Arg1 :: fd(),Arg2 :: [constant()]) ->ok | {error, posix()}

(FreeBSD only) cap_fcntls_limit(2) : set allowed fnctl(2) commands on file descriptor

cap_getmode/1


cap_getmode(Task :: task()) -> {ok, 0 | 1} | {error, posix()}

(FreeBSD only) cap_getmode(2) : returns capability mode status of process

  • 0 : false
  • 1 : true

cap_ioctls_limit/3


cap_ioctls_limit(Task :: task(),Arg1 :: fd(),Arg2 :: [constant()]) ->ok | {error, posix()}

(FreeBSD only) cap_ioctls_limit(2) : set allowed ioctl(2) commands on file descriptor

cap_rights_limit/3


cap_rights_limit(Task :: task(),Arg1 :: fd(),Arg2 :: [constant()]) ->ok | {error, posix()}

(FreeBSD only) cap_rights_limit(2) : set allowed rights(4) of file descriptor

chdir/2


chdir(Task :: task(), Arg1 :: iodata()) -> ok | {error, posix()}

chdir(2) : change process current working directory.

chmod/3


chmod(Task :: task(), Arg1 :: iodata(), Arg2 :: mode_t()) ->ok | {error, posix()}

chmod(2) : change file permissions

chown/4


chown(Task :: task(),Arg1 :: iodata(),Arg2 :: uid_t(),Arg3 :: gid_t()) ->ok | {error, posix()}

chown(2) : change file ownership

chroot/2


chroot(Task :: task(), Arg1 :: iodata()) -> ok | {error, posix()}

chroot(2) : change root directory

clearenv/1


clearenv(Task :: task()) -> ok | {error, posix()}

clearenv(3) : zero process environment

clone/2


clone(Task :: task(), Flags :: [constant()]) ->{ok, task()} | {error, posix()}

(Linux only) clone(2) : create a new process

close/2


close(Task :: task(), Arg1 :: fd()) -> ok | {error, posix()}

close(2) : close a file descriptor.

cmd/2


cmd(Task :: task(), Cmd :: [iodata()]) ->binary() | {error, posix()}

controlling_process/2


controlling_process(Task :: task(), Pid :: 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

cpid/1


cpid(Task :: task()) -> [cpid()]

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

cpid/2


cpid(Task :: task(), Pid :: task() | pid_t()) -> cpid() | error

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.

drv/1


drv(Task :: task()) -> pid()

environ/1


environ(Task :: task()) -> [binary()]

environ(7) : return the process environment variables

eof/2


eof(Task :: task(), Pid :: task() | pid_t()) ->ok | {error, posix()}

close stdin of child process

eof/3


eof(Task :: task(),Pid :: task() | pid_t(),Stdio :: stdin | stdout | stderr) ->ok | {error, posix()}

close stdin, stdout or stderr of child process

execed/1


execed(Task :: task()) -> boolean()

test if the task has called exec(2)

Returns true if the task is running in exec mode.

execve/3


execve(Task :: task(), Argv :: [iodata()], Env :: [iodata()]) ->ok | {error, posix()}

execve(2) : replace the process image, specifying the environment for the new process image.

execve/4


execve(Task :: task(),Arg0 :: iodata(),Argv :: [iodata()],Env :: [iodata()]) ->ok | {error, posix()}

execve(2) : replace the process image, specifying the environment for the new process image.

Allows setting the command name in the process list:

  prx:execve(Task, "/bin/cat", ["name-in-process-list", "-n"], ["VAR=1"])

execvp/2


execvp(Task :: task(), Argv :: [iodata()]) ->ok | {error, posix()}

execvp(2) : replace the current process image using the search path

execvp/3


execvp(Task :: task(), Arg0 :: iodata(), Argv :: [iodata()]) ->ok | {error, posix()}

execvp(2) : replace the current process image using the search path

Allows setting the command name in the process list:

  prx:execvp(Task, "cat", ["name-in-process-list", "-n"])

exit/2


exit(Task :: task(), Arg1 :: int32_t()) -> ok

exit(3) : cause the child process to exit

fcntl/3


fcntl(Task :: task(), Arg1 :: fd(), Arg2 :: constant()) ->{ok, int64_t()} | {error, posix()}

fcntl(2) : perform operation on a file descriptor

fcntl/4


fcntl(Task :: task(),Arg1 :: fd(),Arg2 :: constant(),Arg3 :: int64_t()) ->{ok, int64_t()} | {error, posix()}

fcntl(2) : perform operation on a file descriptor with argument

fexecve/4


fexecve(Task :: task(),FD :: int32_t(),Argv :: [iodata()],Env :: [iodata()]) ->ok | {error, posix()}

fexecve(2) : 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 and FreeBSD only. Linux requires an environment be set unlike with execve(2). The environment can be empty:

  % Environment required on Linux
  ok = prx:fexecve(Task, FD, ["-al"], [""]),
  [<<>>] = prx:environ(Task).

filter/2


filter(Task :: task(),Calls :: [call()] | {allow, [call()]} | {deny, [call()]}) ->ok

filter() : restrict calls available to a control process

filter/2 restricts calls for a prx control process. A control process will continue to proxy data as well as monitor and reap subprocesses.

Invoking a filtered call will crash the process with 'undef'.

If the filter/1 call is filtered, subsequent calls to filter/1 will fail.

Calls can be either whitelisted or blacklisted. If a call is whitelisted, all other calls are filtered:

  % only these calls are filtered
  prx:filter(Task, {deny, [fork, clone, execve, execvp]})
  % equivalent to {deny, [fork, clone, execve, execvp]}
  prx:filter(Task, [fork, clone, execve, execvp])
  % all other calls are filtered including filter
  prx:filter(Task, {allow, [fork, clone, execve, execvp]})

Once a filter for a call is added, the call cannot be removed from the filter set.

Filters are inherited by the child process from the parent.

  {ok, Ctrl} = prx:fork(),
  {ok, Task} = prx:fork(Ctrl),
  ok = prx:filter(Ctrl, [fork]),
  {'EXIT', {undef, _}} = (catch prx:fork(Ctrl)).

fork/0


fork() -> {ok, task()} | {error, posix()}

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/alcove/priv/alcove !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().

fork/1


fork(Task :: task()) -> {ok, task()} | {error, posix()}

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)

forkchain/1


forkchain(Task :: task()) -> [pid_t()]

getcpid/2


getcpid(Task :: task(), Opt :: atom()) -> int32_t() | false

getcpid() : Get options for child process of prx control process

Control behaviour of an exec()'ed process.

See getcpid/3 for options.

getcpid/3


getcpid(Task :: task(),Pid :: task() | cpid() | pid_t(),Opt :: atom()) ->int32_t() | false

getcpid() : Retrieve 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 0+ : read this many messages from the process

  • signaloneof: signal sent to child process on shutdown

getcwd/1


getcwd(Task :: task()) -> {ok, binary()} | {error, posix()}

getcwd(3) : return the current working directory

getenv/2


getenv(Task :: task(), Arg1 :: iodata()) -> binary() | false

getenv(3) : retrieve an environment variable

getgid/1


getgid(Task :: task()) -> gid_t()

getgid(2) : retrieve the processes' group ID

getgroups/1


getgroups(Task :: task()) -> {ok, [gid_t()]} | {error, posix()}

getgroups(2) : retrieve the list of supplementary groups

gethostname/1


gethostname(Task :: task()) -> {ok, binary()} | {error, posix()}

gethostname(2) : retrieve the system hostname

getopt/2


getopt(Task :: task(), Arg1 :: prx_opt()) -> false | int32_t()

getopt() : get options for the prx control process

Retrieve port options for a prx control process. These options are configurable per process, with the default settings inherited from the parent.

The initial values for these options are set for the port by prx:fork/0:

maxchild : non_neg_integer() : (ulimit -n) / 4 - 4

Number of child processes allowed for this process. This value can be modified by adjusting RLIMIT_NOFILE for the process.

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 fork chain.

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/3,4.

signaloneof : 0-254 : 15

Send a signal to a child process on shutdown (stdin of the alcove control process is closed).

See setcpid/3,4.

getpgrp/1


getpgrp(Task :: task()) -> pid_t()

getpgrp(2) : retrieve the process group.

getpid/1


getpid(Task :: task()) -> pid_t()

getpid(2) : retrieve the system PID of the process.

getpriority/3


getpriority(Task :: task(), Arg1 :: constant(), Arg2 :: int32_t()) ->{ok, int32_t()} | {error, posix()}

getpriority(2) : retrieve scheduling priority of process, process group or user

getresgid/1


getresgid(Task :: task()) ->{ok, gid_t(), gid_t(), gid_t()} | {error, posix()}

getresgid(2) : get real, effective and saved group ID

Supported on Linux and BSD's.

getresuid/1


getresuid(Task :: task()) ->{ok, uid_t(), uid_t(), uid_t()} | {error, posix()}

getresuid(2) : get real, effective and saved user ID

Supported on Linux and BSD's.

getrlimit/2


getrlimit(Task::task(), Resource::constant()) -> {ok, #{cur => uint64_t(), max => uint64_t()}} | {error, posix()}

getrlimit(2) : retrieve the resource limits for a process

getsid/2


getsid(Task :: task(), Arg1 :: pid_t()) ->{ok, pid_t()} | {error, posix()}

getsid(2) : retrieve the session ID

getuid/1


getuid(Task :: task()) -> uid_t()

getuid(2) : returns the process user ID

ioctl/4


ioctl(Task::task(), Arg1::fd(), Arg2::constant(), Arg3::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

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>>}.

jail/2


jail(Task::task(), Arg1::#{version => alcove:uint32_t(), path => iodata(), hostname => iodata(), jailname => iodata(), ip4 => [inet:ip4_address()], ip6 => [inet:ip6_address()]} | cstruct()) -> {ok, int32_t()} | {error, posix()}

(FreeBSD only) jail(2) : restrict the current process in a system jail

kill/3


kill(Task :: task(), Arg1 :: pid_t(), Arg2 :: constant()) ->ok | {error, posix()}

kill(2) : terminate a process

lseek/4


lseek(Task :: task(),Arg1 :: fd(),Arg2 :: off_t(),Arg3 :: int32_t()) ->ok | {error, posix()}

lseek(2) : set file offset for read/write

mkdir/3


mkdir(Task :: task(), Arg1 :: iodata(), Arg2 :: mode_t()) ->ok | {error, posix()}

mkdir(2) : create a directory

mkfifo/3


mkfifo(Task :: task(), Arg1 :: iodata(), Arg2 :: mode_t()) ->ok | {error, posix()}

mkfifo(3) : create a named pipe

mount/6


mount(Task :: task(),Arg1 :: iodata(),Arg2 :: iodata(),Arg3 :: iodata(),Arg4 :: uint64_t() | [constant()],Arg5 :: iodata()) ->ok | {error, posix()}

mount(2) : mount a filesystem, Linux style

The arguments are:

  • source
  • target
  • filesystem type
  • flags
  • data

An empty binary 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);

mount/7


mount(Task :: task(),Arg1 :: iodata(),Arg2 :: iodata(),Arg3 :: iodata(),Arg4 :: uint64_t() | [constant()],Arg5 :: iodata(),Arg6 :: iodata()) ->ok | {error, posix()}

(Solaris only) mount(2) : mount a filesystem

On Solaris, some mount options are passed in the Options argument as a string of comma separated values terminated by a NULL. Other platforms ignore the Options parameter.

open/3


open(Task :: task(),Arg1 :: iodata(),Arg2 :: int32_t() | [constant()]) ->{ok, fd()} | {error, posix()}

open(2) : returns a file descriptor associated with a file

Lists of values are OR'ed:

  prx:open(Task, "/etc/motd", [o_rdonly])

open/4


open(Task :: task(),Arg1 :: iodata(),Arg2 :: int32_t() | [constant()],Arg3 :: mode_t()) ->{ok, fd()} | {error, posix()}

open(2) : create a file, specifying permissions

  prx:open(Task, "/tmp/test", [o_wronly,o_creat], 8#644)

parent/1


parent(Task :: task()) -> task() | noproc

pidof/1


pidof(Task :: task()) -> pid_t() | noproc

retrieves the system PID of the process similar to getpid(2)

Returns the cached value for the PID of the system process.

  OSPid = prx:getpid(Task),
  OSPid = prx:pidof(Task).

pivot_root/3


pivot_root(Task :: task(), Arg1 :: iodata(), Arg2 :: iodata()) ->ok | {error, posix()}

(Linux only) pivot_root(2) : change the root filesystem

pledge/3


pledge(Task :: task(),Arg1 :: iodata() | null,Arg2 :: iodata() | null) ->ok | {error, posix()}

(OpenBSD only) pledge(2) : restrict system operations

  prx:pledge(Task, "stdio proc exec", [])

prctl/6


prctl(Task :: task(),Arg1 :: constant(),Arg2 :: ptr_arg(),Arg3 :: ptr_arg(),Arg4 :: ptr_arg(),Arg5 :: ptr_arg()) ->{ok,integer(),ptr_val(),ptr_val(),ptr_val(),ptr_val()} |{error, posix()}

(Linux only) 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.

For example, to enforce a seccomp filter:

  % NOTE: this filter will result in the port being sent a SIGSYS
  % 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
  Arch = prx:call(Task, syscall_constant, [alcove:audit_arch]),
  Filter = [
      ?VALIDATE_ARCHITECTURE(Arch),
      ?EXAMINE_SYSCALL,
      sys_read,
      sys_write
  ],
  {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:prctl(Task, pr_set_seccomp, seccomp_mode_filter, Prog, 0, 0).

ptrace/5


ptrace(Task :: task(),Arg1 :: constant(),Arg2 :: pid_t(),Arg3 :: ptr_arg(),Arg4 :: ptr_arg()) ->{ok, integer(), ptr_val(), ptr_val()} | {error, posix()}

(Linux only) ptrace(2) : trace processes

read/3


read(Task :: task(), Arg1 :: fd(), Arg2 :: size_t()) ->{ok, binary()} | {error, posix()}

read(2) : read bytes from a file descriptor

readdir/2


readdir(Task :: task(), Arg1 :: iodata()) ->{ok, [binary()]} | {error, posix()}

readdir(3) : retrieve list of objects in a directory

reexec/1


reexec(Task :: task()) -> ok | {error, posix()}

Fork+reexec prx process

Fork+reexec is a method of randomizing the memory space of a process:

https://poolp.org/posts/2016-09-12/opensmtpd-600-is-released/

prx processes fork recursively:

  • the calls stack increases in size
  • the memory space layout is identical to the parent

Usually after forking, a prx process will call exec.

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.

reexec/3


reexec(Task :: task(),Argv :: {fd, int32_t(), iodata()} | iodata(),Env :: 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.

replace_process_image/1


replace_process_image(Task :: task()) -> ok | {error, posix()}

Alias for reexec/1

replace_process_image/3


replace_process_image(Task :: task(),Argv :: {fd, int32_t(), iodata()} | iodata(),Env :: iodata()) ->ok | {error, posix()}

Alias for reexec/3

rmdir/2


rmdir(Task :: task(), Arg1 :: iodata()) -> ok | {error, posix()}

rmdir(2) : delete a directory

seccomp/4


seccomp(Task :: task(),Arg1 :: constant(),Arg2 :: constant(),Arg3 :: cstruct()) ->boolean()

seccomp(2) : restrict system operations

See prctl/6.

select/5


select(Task::task(), Readfds::[fd()], Writefds::[fd()], Exceptfds::[fd()], Timeout::null | NULL | #{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:

  • null (block forever)

  • a map containing:

    sec : number of seconds to wait
    usec : number of microseconds to wait

For example:

  {ok,[],[],[]} = prx:select(Task, [], [], [], #{sec => 10, usec => 100}).

setcpid/3


setcpid(Task :: task(), Opt :: atom(), Val :: int32_t()) ->boolean()

setcpid() : Set options for child process of prx control process

Control behaviour of an exec()'ed process.

See setcpid/4 for options.

setcpid/4


setcpid(Task :: task(),Pid :: task() | cpid() | pid_t(),Opt :: atom(),Val :: int32_t()) ->boolean()

setcpid() : Set options for child process of prx control process

  • flowcontrol: enable 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: the prx control process sends this signal to the child process on shutdown (default: 15 (SIGTERM))

setenv/4


setenv(Task :: task(),Arg1 :: iodata(),Arg2 :: iodata(),Arg3 :: int32_t()) ->ok | {error, posix()}

setenv(3) : set an environment variable

setgid/2


setgid(Task :: task(), Arg1 :: gid_t()) -> ok | {error, posix()}

setgid(2) : set the GID of the process

setgroups/2


setgroups(Task :: task(), Arg1 :: [gid_t()]) ->ok | {error, posix()}

setgroups(2) : set the supplementary groups of the process

sethostname/2


sethostname(Task :: task(), Arg1 :: iodata()) ->ok | {error, posix()}

sethostname(2) : set the system hostname

This function is probably only useful if running in a uts namespace:

  {ok, Child} = prx:clone(Task, [clone_newuts]),
  ok = prx:sethostname(Child, "test"),
  Hostname1 = prx:gethostname(Task),
  Hostname2 = prx:gethostname(Child),
  Hostname1 =/= Hostname2.

setns/2


setns(Task :: task(), Arg1 :: iodata()) -> ok | {error, posix()}

(Linux only) 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

For example, to attach to another process' 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, 0),
  ok = prx:close(Child2, FD).

setns/3


setns(Task :: task(), Arg1 :: iodata(), Arg2 :: constant()) ->ok | {error, posix()}

(Linux only) setns(2) : attach to a namespace, specifying namespace type

  ok = prx:setns(Task, FD, clone_newnet)

setopt/3


setopt(Task :: task(), Arg1 :: prx_opt(), Arg2 :: int32_t()) ->boolean()

setopt() : set options for the prx control process

See getopt/3 for options.

setpgid/3


setpgid(Task :: task(), Arg1 :: pid_t(), Arg2 :: pid_t()) ->ok | {error, posix()}

setpgid(2) : set process group

setpriority/4


setpriority(Task :: task(),Arg1 :: constant(),Arg2 :: int32_t(),Arg3 :: int32_t()) ->ok | {error, posix()}

setpriority(2) : set scheduling priority of process, process group or user

setproctitle/2


setproctitle(Task :: task(), Name :: iodata()) -> ok

setproctitle(3) : set the process title

Set the process title displayed in utilities like ps(1).

Linux systems may also want to set the command name using prctl/6:

  prx:prctl(Task, pr_set_name, <<"newname">>, 0, 0, 0)

setresgid/4


setresgid(Task :: task(),Arg1 :: gid_t(),Arg2 :: gid_t(),Arg3 :: gid_t()) ->ok | {error, posix()}

setresgid(2) : set real, effective and saved group ID

Supported on Linux and BSD's.

setresuid/4


setresuid(Task :: task(),Arg1 :: uid_t(),Arg2 :: uid_t(),Arg3 :: uid_t()) ->ok | {error, posix()}

setresuid(2) : set real, effective and saved user ID

Supported on Linux and BSD's.

setrlimit/3


setrlimit(Task::task(), Resource::constant(), Rlim::#{cur => uint64_t(), max => uint64_t()}) -> ok | {error, posix()}

setrlimit(2) : set a resource limit

setsid/1


setsid(Task :: task()) -> {ok, pid_t()} | {error, posix()}

setsid(2) : create a new session

setuid/2


setuid(Task :: task(), Arg1 :: uid_t()) -> ok | {error, posix()}

setuid(2) : change UID

sh/2


sh(Task :: task(), Cmd :: iodata()) -> binary() | {error, posix()}

sigaction/3


sigaction(Task :: task(),Signal :: constant(),Arg2 ::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.

  • <<>> : retrieve current handler for signal

Multiple caught signals of the same type may be reported as one event.

socket/4


socket(Task :: task(),Arg1 :: constant(),Arg2 :: constant(),Arg3 :: int32_t()) ->{ok, fd()} | {error, posix()}

socket(2) : retrieve file descriptor for communication endpoint

  {ok, FD} = prx:socket(Task, af_inet, sock_stream, 0).

start_link/1


start_link(Owner :: pid()) -> {ok, task()} | {error, posix()}

stdin/2


stdin(Task :: task(), Buf :: iodata()) -> ok

Send data to the standard input of the process.

stdio/2


stdio(Task :: task(), Pid :: 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).

stop/1


stop(Task :: task()) -> ok

terminate the task

sudo/0


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).

sudo/1


sudo(Exec :: string()) -> ok

Convenience function to fork a privileged process in the shell

Allows specifying the command. For example, on OpenBSD:

  prx:sudo("doas"),
  {ok, Task} = prx:fork(),
  0 = prx:getuid(Task).

task/3


task(Task :: task(),Ops :: [prx_task:op() | [prx_task:op()]],State :: any()) ->{ok, task()} | {error, posix()}

task/4


task(Task :: task(),Ops :: [prx_task:op() | [prx_task:op()]],State :: any(),Config :: [prx_task:config()]) ->{ok, task()} | {error, posix()}

umount/2


umount(Task :: task(), Arg1 :: iodata()) -> ok | {error, posix()}

umount(2) : unmount a filesystem

On BSD systems, calls unmount(2).

umount2/3


umount2(Task :: task(), Arg1 :: iodata(), Arg2 :: [constant()]) ->ok | {error, posix()}

umount2(2) : unmount a filesystem

On BSD systems, calls unmount(2).

unlink/2


unlink(Task :: task(), Arg1 :: iodata()) -> ok | {error, posix()}

unlink(2) : delete references to a file

unsetenv/2


unsetenv(Task :: task(), Arg1 :: iodata()) ->ok | {error, posix()}

unsetenv(3) : remove an environment variable

unshare/2


unshare(Task :: task(), Arg1 :: int32_t() | [constant()]) ->ok | {error, posix()}

(Linux only) unshare(2) : allows creating a new namespace in the current process

unshare(2) lets you make a new namespace without calling clone(2):

  % The port is now running in a namespace without network access.
  ok = prx:unshare(Task, [clone_newnet]).

unveil/3


unveil(Task :: task(),Arg1 :: iodata() | null,Arg2 :: iodata() | null) ->ok | {error, posix()}

(OpenBSD only) unveil(2) : restrict filesystem view

  prx:unveil(Task, "/bin", "rx")

waitpid/3


waitpid(Task :: task(),Arg1 :: pid_t(),Arg2 :: int32_t() | [constant()]) ->{ok, pid_t(), int32_t(), [waitstatus()]} |{error, posix()}

waitpid(2) : wait for child process

To use waitpid/3, disable handling of child processes by the event loop:

  {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]).

write/3


write(Task :: task(), Arg1 :: fd(), Arg2 :: iodata()) ->{ok, ssize_t()} | {error, posix()}

write(2): writes a buffer to a file descriptor and returns the number of bytes written.

Clone this wiki locally
You can’t perform that action at this time.