Module prx

Copyright © 2015-2018 Michael Santos <michael.santos@gmail.com>

Behaviours: gen_statem.

Data Types

child()

child() = #{pid := pid_t(), exec := boolean(), fdctl := fd(), stdin := fd(), stdout := fd(), stderr := fd()}

constant()

constant() = atom() | integer()

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

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/2Register a function to be called at task termination.
call/3Make 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/2chdir(2) : change process current working directory.
child/2retrieve process info for forked processes.
children/1Returns the list of child PIDs for this process.
chmod/3chmod(2) : change file permissions.
chown/4chown(2) : change file ownership.
chroot/2chroot(2) : change root directory.
clearenv/1clearenv(3) : zero process environment.
clone/2(Linux only) clone(2) : create a new process.
close/2close(2) : close a file descriptor.
cmd/2
drv/1
environ/1environ(7) : return the process environment variables.
eof/2close stdin of child process.
eof/3close stdin, stdout or stderr of child process.
execed/1test if the task has called exec(2).
execve/3execve(2) : replace the process image, specifying the environment for the new process image.
execve/4execve(2) : replace the process image, specifying the environment for the new process image.
execvp/2execvp(2) : replace the current process image using the search path.
execvp/3execvp(2) : replace the current process image using the search path.
exit/2exit(3) : cause the child process to exit.
fcntl/3fcntl(2) : perform operation on a file descriptor.
fcntl/4fcntl(2) : perform operation on a file descriptor with argument.
fexecve/4fexecve(2) : replace the process image, specifying the environment for the new process image, using a previously opened file descriptor.
fork/0fork(2) : create a new system process.
fork/1fork(2) : create a child process.
forkchain/1
getcwd/1getcwd(3) : return the current working directory.
getenv/2getenv(3) : retrieve an environment variable.
getgid/1getgid(2) : retrieve the processes' group ID.
getgroups/1getgroups(2) : retrieve the list of supplementary groups.
gethostname/1gethostname(2) : retrieve the system hostname.
getpgrp/1getpgrp(2) : retrieve the process group.
getpid/1getpid(2) : retrieve the system PID of the process.
getpriority/3getpriority(2) : retrieve scheduling priority of process, process group or user.
getresgid/1getresgid(2) : get real, effective and saved group ID.
getresuid/1getresuid(2) : get real, effective and saved user ID.
getrlimit/2getrlimit(2) : retrieve the resource limits for a process.
getsid/2getsid(2) : retrieve the session ID.
getuid/1getuid(2) : returns the process user ID.
ioctl/4ioctl(2) : control device.
jail/2(FreeBSD only) jail(2) : restrict the current process in a system jail.
kill/3kill(2) : terminate a process.
lseek/4lseek(2) : set file offset for read/write.
mkdir/3mkdir(2) : create a directory.
mkfifo/3mkfifo(3) : create a named pipe.
mount/6mount(2) : mount a filesystem, Linux style.
mount/7(Solaris only) mount(2) : mount a filesystem.
open/3open(2) : returns a file descriptor associated with a file.
open/4open(2) : create a file, specifying permissions.
parent/1
pidof/1retrieves 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/3read(2) : read bytes from a file descriptor.
readdir/2readdir(3) : retrieve list of objects in a directory.
replace_process_image/1Replace the port process image using execve(2)/fexecve(2).
replace_process_image/3Replace the port process image using execve(2)/fexecve(2).
rmdir/2rmdir(2) : delete a directory.
select/5select(2) : poll a list of file descriptor for events.
setenv/4setenv(3) : set an environment variable.
setgid/2setgid(2) : set the GID of the process.
setgroups/2setgroups(2) : set the supplementary groups of the process.
sethostname/2sethostname(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.
setpgid/3setpgid(2) : set process group.
setpriority/4setpriority(2) : set scheduling priority of process, process group or user.
setproctitle/2setproctitle(3) : set the process title.
setresgid/4setresgid(2) : set real, effective and saved group ID.
setresuid/4setresuid(2) : set real, effective and saved user ID.
setrlimit/3setrlimit(2) : set a resource limit.
setsid/1setsid(2) : create a new session.
setuid/2setuid(2) : change UID.
sh/2
sigaction/3sigaction(2) : set process behaviour for signals.
socket/4socket(2) : retrieve file descriptor for communication endpoint.
start_link/1
stdin/2Send data to the standard input of the process.
stop/1terminate the task.
sudo/0Convenience function to fork a privileged process in the shell.
sudo/1Convenience function to fork a privileged process in the shell.
task/3
task/4
umount/2umount(2) : unmount a filesystem.
unlink/2unlink(2) : delete references to a file.
unsetenv/2unsetenv(3) : remove an environment variable.
unshare/2(Linux only) unshare(2) : allows creating a new namespace in the current process.
waitpid/3waitpid(2) : wait for child process.
write/3write(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::atom(), 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.

child/2

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

retrieve process info for forked processes

Retrieve the map for a child process as returned in prx:children/1.

child/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.

children/1

children(Task::task()) -> [child()]

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

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()}

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

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()]

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

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 represention 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 namepace. 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()

pidof/1

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

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(), Arg2::[iodata()]) -> 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_rt_sigreturn
  %    sys_sigreturn
  %    sys_exit_group
  %    sys_exit
  %    sys_read
  %    sys_write
  %    sys_writev
  %    sys_setrlimit
  %    sys_getrlimit
  %    sys_ugetrlimit
  %    sys_poll
 
  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

replace_process_image/1

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

Replace the port process image using execve(2)/fexecve(2)

The call stack of the child processes grow because the port process forks recursively. The stack layout will also be the same as the parent, defeating ASLR protections.

For most processes this is not a concern: the process will call exec() after performing some operations.

Some "system" or "supervisor" type processes may remain in call mode: these processes can call replace_process_image/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, replace_process_image/1 will fail.

replace_process_image/3

replace_process_image(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.

rmdir/2

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

rmdir(2) : delete a directory

select/5

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

* infinity (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}).

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)

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

?PRX_CALL(Task, prctl, [pr_set_name, maybe_binary(Name), 0,0,0]), Uses prctl(2) on Linux.

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(), Arg1::constant(), Arg2::atom() | <<>>) -> {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.

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

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

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.


Generated by EDoc