Packages
gleam_erlexec
0.0.1
A wrapper for erlexec
Retired package: Renamed - this package is now known as glexec
Current section
Files
Jump to
Current section
Files
src/gleam/erlexec.gleam
//// OS shell command runner. It communicates with a separate C++ port process
//// exec-port spawned by this module, which is responsible for starting,
//// killing, listing, terminating, and notifying of state changes.
////
//// For more detailed documentation, please refer to
//// https://hexdocs.pm/erlexec/exec.html
import gleam/dynamic.{Dynamic, from}
import gleam/list
import gleam/option.{None, Option, Some}
import gleam/erlang/atom.{Atom}
import gleam/erlang/process.{Pid}
// Just a bunch of symbols used all over the place
type ExecAtom {
Cd
Args
LimitUsers
Portexe
Verbose
Root
Alarm
Clear
Close
Debug
Env
Executable
Group
Kill
Winsz
KillGroup
KillTimeout
Link
Monitor
Nice
Null
Print
Pty
PtyEcho
Stdin
String
SuccessExitCode
Sync
User
}
/// Representation of OS process ID
pub type OsPid =
Int
/// Representation of OS group ID
pub type OsGid =
Int
/// Keeps track of all the options that will be passed to
/// [run_async](#run_async) or [run_sync](#run_sync)
pub opaque type Options {
Options(
cd: Option(String),
debug: Option(Int),
env: EnvOptions,
executable: Option(String),
group: Option(Int),
kill_group: Bool,
kill: Option(String),
kill_timeout: Option(Int),
link: Bool,
monitor: Bool,
nice: Option(Int),
pty: Bool,
pty_echo: Bool,
stderr: Option(StderrOptions),
stdin: Option(StdinOptions),
stdout: Option(StdoutOptions),
success_exit_code: Option(Int),
sync: Bool,
user: Option(String),
winsz: Option(WinszOptions),
// this is used in `get_server:call`, not the actual command execution
timeout: Int,
)
}
/// Initial constructor for Options. Changing these settings is done with the
/// `with_*` functions.
pub fn new() -> Options {
Options(
cd: None,
debug: None,
env: [],
executable: None,
group: None,
kill_group: False,
kill: None,
kill_timeout: None,
link: False,
monitor: False,
nice: None,
pty_echo: False,
pty: False,
stderr: None,
stdin: None,
stdout: None,
success_exit_code: None,
sync: False,
user: None,
winsz: None,
timeout: 30_000,
)
}
/// Command to be executed.
pub type Command {
/// The specified command will be executed through the shell. The current shell
/// is obtained from environment variable `SHELL`.
///
/// This can be useful primarily for the enhanced control flow it offers over
/// most system shells and still want convenient access to other shell features
/// such as shell pipes, filename wildcards, environment variable expansion,
/// and expansion of `~` to a user's home directory. All command arguments must
/// be properly escaped including whitespace and shell metacharacters.
///
/// Warning: Executing shell commands that incorporate unsanitized input from
/// an untrusted source makes a program vulnerable to shell injection, a
/// serious security flaw which can result in arbitrary command execution. For
/// this reason, the use of shell is strongly discouraged in cases where the
/// command string is constructed from external input.
Shell(String)
/// Command is passed to `execve(3)` library call directly without involving
/// the shell process, so the list of strings represents the program to be
/// executed with arguments. In this case all shell-based features are disabled
/// and there's no shell injection vulnerability.
Execve(List(String))
}
pub type EnvOption {
EnvString(String)
EnvClear
EnvKV(String, String)
EnvUnset(String)
}
pub type EnvOptions =
List(EnvOption)
pub type StdoutOptions {
StdoutCapture
StdoutClose
StdoutFun(fn(Atom, Int, String) -> Nil)
StdoutNull
StdoutPid(Pid)
StdoutPrint
StdoutStderr
StdoutString(String)
}
pub type StderrOptions {
StderrCapture
StderrClose
StderrFun(fn(Atom, Int, String) -> Nil)
StderrNull
StderrPid(Pid)
StderrPrint
StderrStdout
StderrString(String)
}
pub type PtyOptions {
PtyEnable
PtyOpts(List(PtyOption))
}
/// For documentation please check:
/// - [termios(3)](https://man7.org/linux/man-pages/man3/termios.3.html)
/// - [RFC4254](https://datatracker.ietf.org/doc/html/rfc4254#section-8)
pub type PtyOption {
// tty_char
Vintr(Int)
Vquit(Int)
Verase(Int)
Vkill(Int)
Veof(Int)
Veol(Int)
Veol2(Int)
Vstart(Int)
Vstop(Int)
Vsusp(Int)
Vdsusp(Int)
Vreprint(Int)
Vwerase(Int)
Vlnext(Int)
Vflush(Int)
Vswtch(Int)
Vstatus(Int)
Vdiscard(Int)
// tty_mode
Ignpar(Bool)
Parmrk(Bool)
Inpck(Bool)
Istrip(Bool)
Inlcr(Bool)
Igncr(Bool)
Icrnl(Bool)
Xcase(Bool)
Iuclc(Bool)
Ixon(Bool)
Ixany(Bool)
Ixoff(Bool)
Imaxbel(Bool)
Iutf8(Bool)
Isig(Bool)
Icanon(Bool)
Echo(Bool)
Echoe(Bool)
Echok(Bool)
Echonl(Bool)
Noflsh(Bool)
Tostop(Bool)
Iexten(Bool)
Echoctl(Bool)
Echoke(Bool)
Pendin(Bool)
Opost(Bool)
Olcuc(Bool)
Onlcr(Bool)
Ocrnl(Bool)
Onocr(Bool)
Onlret(Bool)
Cs7(Bool)
Cs8(Bool)
Parenb(Bool)
Parodd(Bool)
// tty_speed
TtyOpIspeed(Int)
TtyOpOspeed(Int)
}
pub type StdinOptions {
// Close the `stdin` stream
StdinClose
// Redirect from `/dev/null`
StdinNull
// Enable communication with an OS process via its `stdin`.
// The input to the process is sent by `exec.send(OsPid, String)`.
StdinPipe
// Take input from a file.
StdinFrom(String)
}
type WinszOptions {
PseudoTerminalSize(rows: Int, columns: Int)
}
external fn do_exec_debug(Int) -> Result(Int, Atom) =
"exec" "debug"
/// Set the global debug level
pub fn debug(level: Int) -> Result(Int, Atom) {
do_exec_debug(level)
}
pub type StartError {
AlreadyStarted(Pid)
}
pub type StartOption {
StartDebug(level: Int)
StartRoot(enable: Bool)
StartVerbose
StartArgs(List(String))
StartAlarm(seconds: Int)
StartUser(String)
StartLimitUsers(List(String))
StartPortexe(String)
StartEnv(EnvOptions)
}
external fn do_exec_start(List(Dynamic)) -> Result(Pid, StartError) =
"exec" "start"
/// Start the `exec` application
pub fn start(options: List(StartOption)) -> Result(Pid, StartError) {
do_exec_start(start_options_to_list(options))
}
fn start_options_to_list(options) {
options
|> list.map(fn(o) {
case o {
StartDebug(v) -> from(#(Debug, v))
StartAlarm(v) -> from(#(Alarm, v))
StartArgs(v) -> from(#(Args, v))
StartLimitUsers(v) -> from(#(LimitUsers, v))
StartPortexe(v) -> from(#(Portexe, v))
StartRoot(v) -> from(#(Root, v))
StartUser(v) -> from(#(User, v))
StartVerbose -> from(Verbose)
StartEnv(v) -> env_to_dynamic(v)
}
})
}
pub type StopError {
NoProcess
}
external fn do_stop(Int) -> Result(Nil, Dynamic) =
"gleam_erlexec_ffi" "stop"
/// Terminate a managed `Pid`, `OsPid`, or Port process.
/// The OS process is terminated gracefully. If it was given a `{kill, Cmd}`
/// option at startup, that command is executed and a timer is started. If the
/// program doesn't exit, then the default termination is performed. Default
/// termination implies sending a `SIGTERM` command followed by a `SIGKILL` in 5
/// seconds, if the program doesn't get killed.
pub fn stop(pid: Int) -> Result(Nil, Dynamic) {
do_stop(pid)
}
external fn do_manage(Dynamic, List(Dynamic), Int) -> Dynamic =
"exec" "manage"
pub fn manage_pid(pid: Pid, options: Options) {
do_manage(from(pid), options_to_list(options), options.timeout)
}
/// Get a list of children OsPids managed by the port program.
pub external fn which_children() -> List(OsPid) =
"exec" "which_children"
external fn do_kill(Dynamic, Int) -> Result(Nil, Dynamic) =
"gleam_erlexec_ffi" "kill"
/// Send a signal to a child Pid.
pub fn kill_pid(pid: Pid, signal: Int) -> Result(Nil, Dynamic) {
do_kill(from(pid), signal)
}
/// Send a signal to an OsPid.
pub fn kill_ospid(ospid: OsPid, signal: Int) -> Result(Nil, Dynamic) {
do_kill(from(ospid), signal)
}
/// Set up a monitor for the spawned process.
///
/// The monitor is not a standard `erlang:monitor/2` function call, but
/// it's emulated by ensuring that the monitoring process receives
/// notifications in the form:
/// `{'DOWN', OsPid::integer(), process, Pid::pid(), Reason}`.
///
/// If the `Reason` is `normal`, then process exited with status `0`,
/// otherwise there was an error. If the Reason is `{status, Status}` the
/// returned `Status` can be decoded with the [status](#status) function to
/// determine the exit code of the process and if it was killed by signal.
pub fn with_monitor(old: Options, given: Bool) -> Options {
Options(..old, monitor: given)
}
pub fn get_monitor(options: Options) {
options.monitor
}
fn set_monitor(old: List(Dynamic), option: Options) -> List(Dynamic) {
case option.monitor {
True -> [from(Monitor), ..old]
False -> old
}
}
fn set_sync(old: List(Dynamic), option: Options) -> List(Dynamic) {
case option.sync {
True -> [from(Sync), ..old]
False -> old
}
}
/// Link to the OsPid. If OsPid exits, the calling process will be killed or if
/// it's trapping exits, it'll get {'EXIT', OsPid, Status} message. If the
/// calling process dies the OsPid will be killed.
/// The `Status` can be decoded with [status](#status) to determine the
/// process's exit code and if it was killed by signal.
pub fn with_link(old: Options, given: Bool) -> Options {
Options(..old, link: given)
}
pub fn get_link(options: Options) {
options.link
}
fn set_link(old: List(Dynamic), option: Options) -> List(Dynamic) {
case option.link {
True -> [from(Link), ..old]
False -> old
}
}
/// Specifies a replacement program to execute.
///
/// It is very seldom needed. When the port program executes a child process
/// using `execve(3)` call, the call takes the following arguments:
/// `(executable, args, env)`.
/// When the `command` argument is specified as the list of strings, the
/// executable replaces the first parameter in the call, and the original
/// `args` provided in the `command` parameter are passed as as the second
/// parameter.
/// Most programs treat the program specified by `args` as the command name,
/// which can then be different from the program actually executed.
///
/// ### Unix specific:
///
/// The `args` name becomes the display name for the executable in utilities
/// such as `ps`.
///
/// If the `command` argument is a Shell, the `Executable` specifies a
/// replacement shell for the default `/bin/sh`.
pub fn with_executable(old: Options, given: String) -> Options {
Options(..old, executable: Some(given))
}
pub fn get_executable(options: Options) {
options.executable
}
fn set_executable(old: List(Dynamic), option: Options) -> List(Dynamic) {
case option.executable {
Some(executable) -> [from(#(Executable, executable)), ..old]
None -> old
}
}
/// Set a working directory
pub fn with_cd(old: Options, given: String) -> Options {
Options(..old, cd: Some(given))
}
pub fn get_cd(options: Options) {
options.cd
}
fn set_cd(old: List(Dynamic), option: Options) -> List(Dynamic) {
case option.cd {
Some(cd) -> [from(#(Cd, cd)), ..old]
None -> old
}
}
/// Modify the program environment. See [EnvOption](#EnvOption)
pub fn with_env(old: Options, given: EnvOptions) -> Options {
Options(..old, env: given)
}
pub fn get_env(options: Options) {
options.env
}
fn set_env(old: List(Dynamic), option: Options) -> List(Dynamic) {
case list.length(option.env) {
0 -> old
_ -> [env_to_dynamic(option.env), ..old]
}
}
fn env_to_dynamic(env: EnvOptions) {
from(#(Env, list.map(env, env_element_to_dynamic)))
}
fn env_element_to_dynamic(element) {
case element {
EnvString(s) -> from(s)
EnvClear -> from(Clear)
EnvKV(k, v) -> from(#(k, v))
EnvUnset(k) -> from(#(k, False))
}
}
/// Configure `stdout` communication with the OS process.
/// See [StdoutOptions](#StdoutOptions)
pub fn with_stdout(old: Options, given: StdoutOptions) -> Options {
Options(..old, stdout: Some(given))
}
pub fn get_stdout(options: Options) {
options.stdout
}
fn set_stdout(old: List(Dynamic), option: Options) -> List(Dynamic) {
let stdout = atom.create_from_string("stdout")
let stderr = atom.create_from_string("stderr")
case option.stdout {
Some(value) -> [
case value {
StdoutCapture -> from(stdout)
StdoutNull -> from(#(stdout, Null))
StdoutClose -> from(#(stdout, Close))
StdoutPid(pid) -> from(#(stdout, pid))
StdoutPrint -> from(#(stdout, Print))
StdoutStderr -> from(#(stdout, stderr))
StdoutString(s) -> from(#(stdout, s))
StdoutFun(f) -> from(#(stdout, f))
},
..old
]
None -> old
}
}
/// Configure `stderr` communication with the OS process.
/// See [StderrOptions](#StderrOptions)
pub fn with_stderr(old: Options, given: StderrOptions) -> Options {
Options(..old, stderr: Some(given))
}
pub fn get_stderr(options: Options) {
options.stderr
}
fn set_stderr(old: List(Dynamic), option: Options) -> List(Dynamic) {
let stdout = atom.create_from_string("stdout")
let stderr = atom.create_from_string("stderr")
case option.stderr {
Some(value) -> [
case value {
StderrCapture -> from(stderr)
StderrNull -> from(#(stderr, Null))
StderrClose -> from(#(stderr, Close))
StderrPid(pid) -> from(#(stderr, pid))
StderrPrint -> from(#(stderr, Print))
StderrStdout -> from(#(stderr, stdout))
StderrString(s) -> from(#(stderr, s))
StderrFun(f) -> from(#(stderr, f))
},
..old
]
None -> old
}
}
/// Configure `stdin` communication with the OS process.
/// See [StdinOptions](#StdinOptions)
pub fn with_stdin(old: Options, given: StdinOptions) -> Options {
Options(..old, stdin: Some(given))
}
pub fn get_stdin(options: Options) {
options.stdin
}
fn set_stdin(old: List(Dynamic), option: Options) -> List(Dynamic) {
case option.stdin {
Some(stdin) -> [
case stdin {
StdinClose -> from(#(Stdin, Close))
StdinNull -> from(#(Stdin, Null))
StdinPipe -> from(Stdin)
StdinFrom(s) -> from(#(Stdin, s))
},
..old
]
None -> old
}
}
/// Use pseudo terminal for the process's stdin, stdout and stderr
pub fn with_pty(old: Options, enable_pty: Bool) -> Options {
Options(..old, pty: enable_pty)
}
pub fn get_pty(options: Options) {
options.pty
}
fn set_pty(old: List(Dynamic), option: Options) -> List(Dynamic) {
case option.pty {
True -> [from(Pty), ..old]
False -> old
}
}
/// This command will be used for killing the process.
///
/// If the process is still alive after 5 seconds, it receives a `SIGKILL`.
///
/// The kill command will have a `CHILD_PID` environment variable set to the
/// pid of the process it is expected to kill.
///
/// If the `kill` option is not specified, by default first the command is sent
/// a `SIGTERM` signal, followed by `SIGKILL` after a default timeout.
pub fn with_kill(old: Options, command: String) -> Options {
Options(..old, kill: Some(command))
}
pub fn get_kill(options: Options) {
options.kill
}
fn set_kill(old: List(Dynamic), option: Options) -> List(Dynamic) {
case option.kill {
Some(kill) -> [from(#(Kill, kill)), ..old]
None -> old
}
}
/// Number of seconds to wait after issuing a SIGTERM or
/// executing the custom `kill` command (if specified) before
/// killing the process with the `SIGKILL` signal
pub fn with_kill_timeout(old: Options, given: Int) -> Options {
Options(..old, kill_timeout: Some(given))
}
fn set_kill_timeout(old: List(Dynamic), option: Options) -> List(Dynamic) {
case option.kill_timeout {
Some(kill_timeout) -> [from(#(KillTimeout, kill_timeout)), ..old]
None -> old
}
}
/// Sets the effective group ID of the spawned process. The value `0` means to
/// create a new group ID equal to the OS pid of the process.
pub fn with_group(old: Options, given: Int) -> Options {
Options(..old, group: Some(given))
}
fn set_group(old: List(Dynamic), option: Options) -> List(Dynamic) {
case option.group {
Some(group) -> [from(#(Group, group)), ..old]
None -> old
}
}
/// When `exec-port` was compiled with capability (Linux) support enabled
/// and has a suid bit set, it's capable of running commands with a
/// different effective user. The value "root" is prohibited.
pub fn with_user(old: Options, given: String) -> Options {
Options(..old, user: Some(given))
}
fn set_user(old: List(Dynamic), option: Options) -> List(Dynamic) {
case option.user {
Some(user) -> [from(#(User, user)), ..old]
None -> old
}
}
// Set the (psudo) terminal's dimensions.
pub fn with_winsz(old: Options, rows: Int, columns: Int) -> Options {
Options(..old, winsz: Some(PseudoTerminalSize(rows: rows, columns: columns)))
}
fn set_winsz(old: List(Dynamic), option: Options) -> List(Dynamic) {
case option.winsz {
Some(PseudoTerminalSize(rows: rows, columns: columns)) -> [
from(#(Winsz, #(rows, columns))),
..old
]
None -> old
}
}
external fn do_winsz(Int, Int, Int) -> Result(Nil, Nil) =
"gleam_erlexec_ffi" "winsz"
pub fn winsz(pid, rows, columns) -> Result(Nil, Nil) {
do_winsz(pid, rows, columns)
}
/// Set process priority between `-20` and `20`. Note that negative values
/// can be specified only when `exec-port` is started with a root suid bit
/// set.
pub fn with_nice(old: Options, priority: Int) -> Options {
Options(..old, nice: Some(priority))
}
fn set_nice(old: List(Dynamic), option: Options) -> List(Dynamic) {
case option.nice {
Some(nice) -> [from(#(Nice, nice)), ..old]
None -> old
}
}
/// Configure debug printing for this command.
///
/// The `level` should be `0 <= level =< 10`
pub fn with_debug(old: Options, level: Int) -> Options {
Options(..old, debug: Some(level))
}
fn set_debug(old: List(Dynamic), option: Options) -> List(Dynamic) {
case option.debug {
Some(debug) -> [from(#(Debug, debug)), ..old]
None -> old
}
}
/// Return value on success.
pub fn with_success_exit_code(old: Options, given: Int) -> Options {
Options(..old, success_exit_code: Some(given))
}
fn set_success_exit_code(old: List(Dynamic), option: Options) -> List(Dynamic) {
case option.success_exit_code {
Some(success_exit_code) -> [
from(#(SuccessExitCode, success_exit_code)),
..old
]
None -> old
}
}
/// At process exit kill the whole process group associated with this pid.
/// The process group is obtained by the call to `getpgid(3)`.
pub fn with_kill_group(old: Options, given: Bool) -> Options {
Options(..old, kill_group: given)
}
fn set_kill_group(old: List(Dynamic), option: Options) -> List(Dynamic) {
case option.kill_group {
True -> [from(KillGroup), ..old]
False -> old
}
}
/// Allow the pty to run in echo mode, disabled by default.
pub fn with_pty_echo(old: Options, given: Bool) -> Options {
Options(..old, pty_echo: given)
}
fn set_pty_echo(old: List(Dynamic), option: Options) -> List(Dynamic) {
case option.pty_echo {
True -> [from(PtyEcho), ..old]
False -> old
}
}
pub type Pids {
/// `Pid` is the Erlang process identifier.
/// `OsPid` is the OS process identifier of the new process.
Pids(Pid, OsPid)
}
pub type Output {
Output(List(StdoutOrStderr))
}
pub type StdoutOrStderr {
Stdout(List(String))
Stderr(List(String))
}
external fn do_run_async(Dynamic, List(Dynamic), Int) -> Result(Pids, String) =
"gleam_erlexec_ffi" "run"
external fn do_run_sync(Dynamic, List(Dynamic), Int) -> Result(Output, String) =
"gleam_erlexec_ffi" "run"
/// Run command with the given options, don't wait for it to finish.
pub fn run_async(options: Options, command: Command) -> Result(Pids, String) {
let cmd_options = options_to_list(Options(..options, sync: False))
case command {
Execve(cmd) -> do_run_async(from(cmd), cmd_options, options.timeout)
Shell(cmd) -> do_run_async(from(cmd), cmd_options, options.timeout)
}
}
/// Run command with the given options, block the caller until it exits.
pub fn run_sync(options: Options, command: Command) -> Result(Output, String) {
let cmd_options = options_to_list(Options(..options, sync: True))
case command {
Execve(cmd) -> do_run_sync(from(cmd), cmd_options, options.timeout)
Shell(cmd) -> do_run_sync(from(cmd), cmd_options, options.timeout)
}
}
pub fn options_to_list(options: Options) -> List(Dynamic) {
let setters = [
set_monitor,
set_sync,
set_link,
set_executable,
set_cd,
set_env,
set_group,
set_kill,
set_kill_timeout,
set_kill_group,
set_group,
set_user,
set_nice,
set_success_exit_code,
set_stdout,
set_stdin,
set_stderr,
set_winsz,
set_pty,
set_pty_echo,
set_debug,
]
setters
|> list.fold(
[],
fn(acc, fun) {
acc
|> fun(options)
},
)
}
external fn do_send(OsPid, String) -> Result(Nil, Dynamic) =
"gleam_erlexec_ffi" "send"
pub fn send(ospid: OsPid, data: String) -> Result(Nil, Dynamic) {
do_send(ospid, data)
}
type Eof {
Eof
}
external fn do_send_eof(OsPid, Eof) -> Result(Nil, Dynamic) =
"gleam_erlexec_ffi" "send"
/// This will close `stdin`.
pub fn send_eof(ospid: OsPid) -> Result(Nil, Dynamic) {
do_send_eof(ospid, Eof)
}
external fn do_ospid(Pid) -> Result(OsPid, Dynamic) =
"gleam_erlexec_ffi" "ospid"
pub fn to_ospid(ospid: Pid) -> Result(OsPid, Dynamic) {
do_ospid(ospid)
}
pub type FindExecutableError {
ExecutableNotFound
}
/// Tries to find the given executable name and returns the full path to it.
pub external fn find_executable(
name: String,
) -> Result(String, FindExecutableError) =
"gleam_erlexec_ffi" "find_executable"
pub type ExitStatusOrSignal {
ExitStatus(Int)
Signal(
// The exit status
status: Signal,
// Whether a core file was generated
core_dump: Bool,
)
}
external fn and(Int, Int) -> Int =
"erlang" "band"
external fn bsr(Int, Int) -> Int =
"erlang" "bsr"
/// Decode the program's exit_status.
///
/// If the program exited by signal the function returns [Signal](#Signal),
/// otherwise it will return an [ExitStatus](#ExitStatus)
pub fn status(status: Int) -> ExitStatusOrSignal {
let term_signal = and(status, 0x7f)
let if_signaled = bsr(term_signal + 1, 1) > 0
case if_signaled {
True ->
Signal(
status: int_to_signal(term_signal),
core_dump: and(status, 0x80) == 0x80,
)
False -> ExitStatus(bsr(and(status, 0xFF00), 8))
}
}
pub type Signal {
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
Other(Int)
}
pub fn signal_to_int(signal: Signal) -> Int {
case signal {
SIGHUP -> 1
SIGINT -> 2
SIGQUIT -> 3
SIGILL -> 4
SIGTRAP -> 5
SIGABRT -> 6
SIGBUS -> 7
SIGFPE -> 8
SIGKILL -> 9
SIGUSR1 -> 10
SIGSEGV -> 11
SIGUSR2 -> 12
SIGPIPE -> 13
SIGALRM -> 14
SIGTERM -> 15
SIGSTKFLT -> 16
SIGCHLD -> 17
SIGCONT -> 18
SIGSTOP -> 19
SIGTSTP -> 20
SIGTTIN -> 21
SIGTTOU -> 22
SIGURG -> 23
SIGXCPU -> 24
SIGXFSZ -> 25
SIGVTALRM -> 26
SIGPROF -> 27
SIGWINCH -> 28
SIGIO -> 29
SIGPWR -> 30
SIGSYS -> 31
Other(other) -> other
}
}
pub fn int_to_signal(signal: Int) -> Signal {
case signal {
1 -> SIGHUP
2 -> SIGINT
3 -> SIGQUIT
4 -> SIGILL
5 -> SIGTRAP
6 -> SIGABRT
7 -> SIGBUS
8 -> SIGFPE
9 -> SIGKILL
10 -> SIGUSR1
11 -> SIGSEGV
12 -> SIGUSR2
13 -> SIGPIPE
14 -> SIGALRM
15 -> SIGTERM
16 -> SIGSTKFLT
17 -> SIGCHLD
18 -> SIGCONT
19 -> SIGSTOP
20 -> SIGTSTP
21 -> SIGTTIN
22 -> SIGTTOU
23 -> SIGURG
24 -> SIGXCPU
25 -> SIGXFSZ
26 -> SIGVTALRM
27 -> SIGPROF
28 -> SIGWINCH
29 -> SIGIO
30 -> SIGPWR
31 -> SIGSYS
other -> Other(other)
}
}
pub type ObtainOk {
ObtainStdout(Int, String)
ObtainStderr(Int, String)
ObtainDown(Pid, OsPid)
}
pub type ObtainError {
ObtainTimeout
ObtainDownStatus(Pid, OsPid, Int)
}
// a wrapper around `receive`.
pub external fn obtain(
timeout_in_milliseconds: Int,
) -> Result(ObtainOk, ObtainError) =
"gleam_erlexec_ffi" "obtain"