README.md

perc is an Erlang interface for controlling Unix processes.

## COMPILING

```
rebar3 do compile, eunit
```

## EXPORTS

### perc

```
kill(Pid, Signal) -> ok | {error, posix()}

    Types   Pid = integer()
            Signal = integer() | atom()

    Send a signal to a Unix process.

    Process IDs and signals are signed 32-bit integers. A signal
    can also be the lower cased signal name as an atom.

    Sending a signal to PID 0 will send the signal to the Erlang VM.

getuid() -> uint32_t()

    See getuid(2).

    Get user identity.

getgid() -> uint32_t()

    See getgid(2).

    Get group identity.

geteuid() -> uint32_t()

    See geteuid(2).

    Get effective user id.

getegid() -> uint32_t()

    See getegid(2).

    Get effective group id.

getgroups() -> [uint32_t()]

    See getgroups(2).

    Retrieve the list of supplementary groups.

setgroups([uint32_t()]) -> ok | {error, posix()}

    See setgroups(2).

    Set the list of supplementary groups.

setresuid(Ruid, Euid, Suid) -> ok | {error, posix()}

    Types   Ruid = uint32_t()
            Euid = uint32_t()
            Suid = uint32_t()

    See setresuid(2).

    Set real, effective and saved user id.

setresgid(Rgid, Egid, Sgid) -> ok | {error, posix()}

    Types   Rgid = uint32_t()
            Egid = uint32_t()
            Sgid = uint32_t()

    See setresgid(2).

    Set real, effective and saved group id.

getpriority(Which, Who) -> {ok, integer()} | {error, posix()}

    Types   Which = integer()
            Who = integer()

    See getpriority(2).

    Get the priority (the "nice" value) of processes by pid, process
    group or user.

setpriority(Which, Who, Prio) -> ok | {error, posix()}

    Types   Which = integer()
            Who = integer()
            Prio = integer()

    See setpriority(2).

    Set the priority (the "nice" value) of processes by pid, process
    group or user.

renice(Type, Prio) -> ok | {error, posix()}

    Types   Type = {Which, Who}
            Which = pid | pgrp | user
            Who = integer()
            Prio = integer() | string()

    Convenience wrapper around getpriority/2 and setpriority/3,
    similar to renice(1).

    WARNING: renice/2 makes successive calls to getpriority/2 and
    setpriority/3. Since this sequence is not atomic, the priority
    may change between calls or the process may have been terminated.

    Sets the priority of a process or processes by pid, pgroup or
    user. The new priority may be an integer or a list containing a
    relative priority, indicated by using a "+" or "-". For example,
    using "+10" will increase the niceness of the process by 10.

prlimit(Pid, Resource, NewLimit, OldLimit) -> {ok, NewLimit1, OldLimit1} | {error, Error}

    Types   Pid = integer()
            Resource = integer()
                | rlimit_cpu
                | rlimit_fsize
                | rlimit_data
                | rlimit_stack
                | rlimit_core
                | rlimit_rss
                | rlimit_nproc
                | rlimit_nofile
                | rlimit_ofile
                | rlimit_memlock
                | rlimit_as
                | rlimit_locks
                | rlimit_sigpending
                | rlimit_msgqueue
                | rlimit_nice
                | rlimit_rtprio
                | rlimit_rttime
                | rlimit_nlimits
                | rlim_infinity
            NewLimit = NewLimit1 = <<>> | binary()
            OldLimit = OldLimit1 = <<>> | binary()
            Error = unsupported | posix()

    Linux only: on other platforms, {error, unsupported} will be
    returned to the caller.

    Set or retrieve process limits for a process. Passing in an
    empty binary for NewLimit or OldLimit indicates the caller is
    not interested in these values.

    The binary size of NewLimit/OldLimit must otherwise match the size
    of a struct rlimit for the platform. struct rlimit is usually
    composed of two 8 byte values in native format. To retrieve the
    current settings, pass in a zeroed 16 byte value.

getrlimit(Resource) -> {ok, Limit} | {error, Error}

    Types Resource = integer() | rlimit()
          Error = posix()

    Get process limits for beam. See prlimit/4 for a list of the
    resource atoms.

    The value returned is a struct rlimit:

        1> {ok, <<Soft:8/native-unsigned-integer-unit:8, Hard:8/native-unsigned-integer-unit:8>>} = perc:getrlimit(rlimit_nofile).
        2> Soft.
        1024
        3> Hard.
        4096

setrlimit(Resource, Limit) -> {ok, Limit} | {error, Error}

    Types Resource = integer() | rlimit()
          Error = posix()
          Limit = binary()

    Set process limits for beam. See prlimit/4 for a list of the
    resource atoms.

umask() -> CurMask
umask(Mask) -> OldMask

    Types   Mask = integer() | list()
            CurMask = OldMask = integer()

    Sets the file creation mask for beam. The mask may be either
    an integer or a list representing an octal number, e.g., either
    8#022 or "022".

    The old mask value is returned. To retrieve the current umask,
    use umask/0 or getumask/0.

    WARNING: umask/0 is destructive: the umask is retrieved by setting
    the process mask to 0, then re-setting it back to the original
    mask. Between the successive calls to umask(2), the process
    mask is 0. An erlang process calling umask/0 concurrently with
    a process creating a file may have unexpected results.

getumask() -> CurMask

    Types   CurMask = OldMask = integer()

    Obtain the current process mask by parsing /proc/self/status,
    avoiding the race condition in umask/0.

    If /proc/self/status does not exist or is not parsable, getumask/0
    fails back to umask/0.
```

### perc_signal

*WARNING: support for signalfd(2) is experimental only and is disabled by default.*

To enable, add `-DHAVE_SIGNALFD` to the rebar.config tuple for your
architecture. Some of the signals may be caught by beam. signalfd is
likely to work only with a single threaded beam. The interaction between
signals and POSIX threads is mysterious.

```
    start(Signals) -> {ok, pid()} | {error, enomem}

        Types   Signals = [ Signal ]
                Signal = integer() | Names
                Names = sighup
                    | sigint
                    | sigquit
                    | sigill
                    | sigtrap
                    | sigabrt
                    | sigbus
                    | sigfpe
                    | sigkill
                    | sigusr1
                    | sigsegv
                    | sigusr2
                    | sigpipe
                    | sigalrm
                    | sigterm
                    | sigstkflt
                    | sigchld
                    | sigcont
                    | sigstop
                    | sigtstp
                    | sigttin
                    | sigttou
                    | sigurg
                    | sigxcpu
                    | sigxfsz
                    | sigvtalrm
                    | sigprof
                    | sigwinch
                    | sigio
                    | sigpwr
                    | sigsys
                    | sigrtmin
                    | sigrtmax

    Linux only: on other platforms, {error, unsupported} will be
    returned to the caller.

    Receive notification when the beam process receives a signal:

        {signal, pid(), #signalfd_siginfo{}}

    The tuple contains the PID of the gen_server and information
    about the signal. See signalfd(2) for details about the returned
    data. For example, to match the signal number:

        -include_lib("perc/include/perc_signal.hrl").

        start() ->
            {ok, Ref} = perc_signal:start([sighup]),

            receive
                {signal, Ref, #signalfd_siginfo{
                    ssi_signo = 1,
                    ssi_pid = Pid,
                    ssi_uid = UID,
                    }} ->
                    error_logger:info_report([
                        {sending_pid, Pid},
                        {sending_uid, UID}
                        ])
            end.

stop(Ref) -> ok

    Stop the gen_server and close the signalfd file descriptor.
```

## EXAMPLES

### kill(2)

```
$ sleep 1000000 &
[1] 2947

$ erl -pa ebin

1> perc:kill(2947, 9).
ok

2> perc:kill(1, 1).
{error,eperm}

3> perc:kill(31337, 1).
{error,esrch}
```

### prlimit(2)

```
% Get the current value for rlimit_cpu
1> perc:prlimit(0, rlimit_cpu, <<>>, <<0:128>>).
{ok,<<>>,<<"ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ">>}

% Set RLIMIT_NOFILE (7) on the Erlang VM. Sets the current and max file descriptors
% to 8.
1> perc:prlimit(0, 7, <<8:8/native-unsigned-integer-unit:8, 8:8/native-unsigned-integer-unit:8>>, <<>>).
{ok,<<8,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0>>,<<>>}

2> file:open("/etc/passwd", [read]).
{ok,<0.39.0>}

3> file:open("/etc/passwd", [read]).
{error,emfile}
```

### setpriority(2)/getpriority(2)

```
% PRIO_PROCESS = 0
1> perc:getpriority(0, 0).
{ok, 0}

2> perc:setpriority(0, 0, 10).
ok

3> perc:getpriority(0, 0).
{ok, 10}

4> perc:renice({pid, 0}, "-5").
{ok, 5}
```

## TODO

* process lookup

* `atom rlimit_*` constants only supported for 64-bit values