### Start and Manage an External Process Source: https://context7.com/saleyn/erlexec/llms.txt Demonstrates starting an external process, reading its PID, and then managing it using erlexec. Ensure the file path for the PID is accessible. ```erlang %% Start a process externally 1> spawn(fun() -> os:cmd("echo $$ > /tmp/external.pid; sleep 30") end). <0.110.0> %% Read the external PID 2> {ok, Bin} = file:read_file("/tmp/external.pid"). {ok, @"12345\n"} 3> ExtPid = list_to_integer(string:trim(binary_to_list(Bin))). 12345 %% Manage the external process 4> {ok, Pid, OsPid} = exec:manage(ExtPid, [monitor]). {ok, <0.115.0>, 12345} %% Now you can monitor, send signals, or stop it 5> exec:kill(OsPid, sigterm). ok 6> flush(). Shell got {'DOWN', 12345, process, <0.115.0>, {exit_status, 15}} ``` -------------------------------- ### Starting the Exec Application Source: https://context7.com/saleyn/erlexec/llms.txt The exec application must be started before running any commands. It spawns the `exec-port` C++ program that manages OS processes. ```APIDOC ## Starting the Exec Application The exec application must be started before running any commands. It spawns the `exec-port` C++ program that manages OS processes. ### Code Examples ```erlang %% Start without supervision 1> exec:start(). {ok, <0.32.0>} %% Start with supervision (recommended for production) 2> exec:start_link([]). {ok, <0.32.0>} %% Start with debug options 3> exec:start([{debug, 1}, verbose]). {ok, <0.32.0>} %% Start allowing execution as different users (requires root/sudo) 4> exec:start([root, {user, "wheel"}, {limit_users, ["alex", "guest"]}]) {ok, <0.32.0>} ``` ``` -------------------------------- ### Start and Run OS Process in Elixir Source: https://github.com/saleyn/erlexec/blob/master/README.md Shows how to start the erlexec port program and execute commands in Elixir, including synchronous execution with stdout retrieval. ```elixir iex(1)> :exec.start {:ok, #PID<0.112.0>} iex(2)> :exec.run("echo ok", [:sync, :stdout]) {:ok, [stdout: ["ok\n"]]} iex(3)> :exec.run(["/bin/echo", "ok"], [:sync, :stdout]) {:ok, [stdout: ["ok\n"]]} ``` -------------------------------- ### Start and Run OS Process Source: https://github.com/saleyn/erlexec/blob/master/README.md Demonstrates how to start the erlexec port program and run a shell command, linking it to the Erlang VM. It also shows how to stop the process using its PID. ```erlang 1> exec:start(). % Start the port program. {ok,<0.32.0>} 2> {ok, _, I} = exec:run_link("sleep 1000", []). % Run a shell command to sleep for 1000s. {ok,<0.34.0>,23584} 3> exec:stop(I). % Kill the shell command. ok % Note that this could also be accomplished % by doing exec:stop(pid(0,34,0)). ``` -------------------------------- ### Execute Commands as Different Users Source: https://context7.com/saleyn/erlexec/llms.txt Allows starting the exec-port as a specific user and running commands as different allowed users. Requires proper setup with capabilities or sudo. ```erlang %% Start exec-port as a specific effective user 1> exec:start([root, {user, "appuser"}, {limit_users, ["appuser", "deploy"]}]) {ok, <0.32.0>} %% Run command as a different allowed user 2> exec:run("whoami", [sync, stdout, {user, "deploy"}]). {ok, [{stdout, [<<"deploy\n">>]}]} %% Set process priority (nice value from -20 to 20) 3> exec:run("sleep 10", [{nice, 10}]). {ok, <0.125.0>, 26100} ``` -------------------------------- ### Start Erlexec Application Source: https://context7.com/saleyn/erlexec/llms.txt Starts the Erlexec application, which spawns the C++ port program for managing OS processes. Use `exec:start_link/1` for supervised startup in production. ```erlang %% Start without supervision 1> exec:start(). {ok, <0.32.0>} ``` ```erlang %% Start with supervision (recommended for production) 2> exec:start_link([]). {ok, <0.32.0>} ``` ```erlang %% Start with debug options 3> exec:start([{debug, 1}, verbose]). {ok, <0.32.0>} ``` ```erlang %% Start allowing execution as different users (requires root/sudo) 4> exec:start([root, {user, "wheel"}, {limit_users, ["alex", "guest"]}]) {ok, <0.32.0>} ``` -------------------------------- ### Start erlexec as Another User Source: https://github.com/saleyn/erlexec/blob/master/README.md Demonstrates how to start the erlexec port program as a different effective user using the `{user, ...}` option. This requires appropriate sudo rights or SUID permissions on the `exec-port` binary. ```bash $ cp $(find . -name exec-port) /tmp $ chmod 755 /tmp/exec-port $ whoami serge $ erl 1> exec:start([{user, "wheel"}, {portexe, "/tmp/exec-port"}]). % Start the port program as effective user "wheel". {ok,<0.32.0>} $ ps haxo user,comm | grep exec-port wheel exec-port ``` -------------------------------- ### Manage an externally started OS process Source: https://github.com/saleyn/erlexec/blob/master/README.md Instructs erlexec to monitor an existing OS process by its PID and receive notifications upon exit. ```erlang % Start an externally managed OS process and retrieve its OS PID: 17> spawn(fun() -> os:cmd("echo $$ > /tmp/pid; sleep 15") end). <0.330.0> 18> f(P), P = list_to_integer(lists:reverse(tl(lists:reverse(binary_to_list(element(2, file:read_file("/tmp/pid"))))))). 19355 % Manage the process and get notified by a monitor when it exits: 19> exec:manage(P, [monitor]). {ok,<0.334.0>,19355} % Wait for monitor notification 20> f(M), receive M -> M end. {'DOWN',19355,process,<0.334.0>,{exit_status,10}} ok 21> file:delete("/tmp/pid"). ok ``` -------------------------------- ### Build Erlexec from source Source: https://github.com/saleyn/erlexec/blob/master/README.md Commands to clone and build the project from the repository. ```bash $ git clone git@github.com:saleyn/erlexec.git $ make # NOTE: for disabling optimized build of exec-port, do the following instead: $ OPTIMIZE=0 make ``` -------------------------------- ### Monitor an OS process Source: https://github.com/saleyn/erlexec/blob/master/README.md Sets up a monitor to receive notifications when an OS process exits. ```erlang > f(I), f(P), {ok, P, I} = exec:run("echo ok", [{stdout, self()}, monitor]). {ok,<0.263.0>,18950} 16> flush(). Shell got {stdout,18950,<<"ok\n">>} Shell got {'DOWN',18950,process,<0.263.0>,normal} ok ``` -------------------------------- ### Configure Working Directory and Executable Source: https://context7.com/saleyn/erlexec/llms.txt Setting the working directory and overriding the default shell executable for spawned processes. ```erlang %% Set working directory 1> exec:run("pwd", [sync, stdout, {cd, "/tmp"}]). {ok, [{stdout, [<<"/tmp\n">>]}]} %% Use a custom executable (replaces the shell) 2> exec:run("echo ok", [sync, stdout, {executable, "/bin/bash"}]). {ok, [{stdout, [<<"ok\n">>]}]} %% Replace executable for list-form commands 3> exec:run(["/bin/custom_echo", "hello"], [sync, stdout, {executable, "/bin/echo"}]). {ok, [{stdout, [<<"hello\n">>]}]} ``` -------------------------------- ### Running OS Commands Synchronously Source: https://context7.com/saleyn/erlexec/llms.txt Execute commands synchronously with the `sync` option, blocking until completion and returning captured output. ```APIDOC ## Running OS Commands Synchronously Execute commands synchronously with the `sync` option, blocking until completion and returning captured output. ### Code Examples ```erlang %% Run synchronously and capture stdout 1> exec:run("echo Test", [sync, stdout]). {ok, [{stdout, [<<"Test\n">>]}]} %% Capture both stdout and stderr 2> exec:run("echo Test; echo Err 1>&2", [sync, stdout, stderr]). {ok, [{stdout, [<<"Test\n">>]}, {stderr, [<<"Err\n">>]}]} %% Handle command failure 3> exec:run("exit 1", [sync, stdout]). {error, [{exit_status, 256}]} %% Run non-existent command 4> exec:run("nonexistent_command", [sync, stdout, stderr]). {error, [{exit_status, 32512}, {stderr, [<<"/bin/bash: nonexistent_command: command not found\n">>]}]} %% Use custom success exit code 5> exec:run("exit 1", [{success_exit_code, 1}, sync]). {ok, []} ``` ``` -------------------------------- ### Manage Child Process Environment Variables Source: https://github.com/saleyn/erlexec/blob/master/README.md Illustrates how to clear the environment, add new environment variables, or unset existing ones for a child process using the `{env, ...}` option. ```erlang %% Clear environment with {env, [clear]} option: 10> f(Bin), {ok, [{stdout, [Bin]}]} = exec:run("env", [sync, stdout, {env, [clear]}]), io:format("~s\n", [re:split(Bin, <<"\n">>)]). [<<"PWD=/home/...">>,<<"SHLVL=0">>, <<"_=/usr/bin/env">>,<<>>] ok %% Clear env and add a "TEST" env variable: 11> f(Bin), {ok, [{stdout, [Bin]}]} = exec:run("env", [sync, stdout, {env, [clear, {"TEST", "xxx"}]}]), io:format("~s\n", [re:split(Bin, <<"\n">>)]). [<<"PWD=/home/...">>,<<"SHLVL=0">>, <<"_=/usr/bin/env">>,<<"TEST=xxx">>,<<>>] %% Unset an "EMU" env variable: 11> f(Bin), {ok, [{stdout, [Bin]}]} = exec:run("env", [sync, stdout, {env, [{"EMU", false}]}]), io:format("~s\n", [re:split(Bin, <<"\n">>)]). [...] ok %% Set an env variable to an empty string (use "" as the value, not false): 12> f(Bin), {ok, [{stdout, [Bin]}]} = exec:run("env", [sync, stdout, {env, [{"MY_VAR", ""}]}]), io:format("~s\n", [re:split(Bin, <<"\n">>)]). [..., <<"MY_VAR=">>, ...] ok ``` -------------------------------- ### Run the port program as root Source: https://github.com/saleyn/erlexec/blob/master/README.md Executes the port program with root privileges. This is highly discouraged due to security risks. ```erlang $ whoami serge # Make sure the exec-port can run as root: $ sudo _build/default/lib/erlexec/priv/*/exec-port --whoami root $ erl 1> exec:start([root, {user, "root"}, {limit_users, ["root"]}]). 2> exec:run("whoami", [sync, stdout]). {ok, [{stdout, [<<"root\n">>]}]} $ ps haxo user,comm | grep exec-port root exec-port ``` -------------------------------- ### Running OS Commands Asynchronously Source: https://context7.com/saleyn/erlexec/llms.txt Execute OS commands asynchronously using `exec:run/2`. Returns an Erlang Pid linked to the OS process and the OS process ID. ```APIDOC ## Running OS Commands Asynchronously Execute OS commands asynchronously using `exec:run/2`. Returns an Erlang Pid linked to the OS process and the OS process ID. ### Code Examples ```erlang %% Run a simple shell command 1> {ok, Pid, OsPid} = exec:run("sleep 10", []). {ok, <0.34.0>, 23584} %% Run with monitoring - receive DOWN message when process exits 2> {ok, Pid, OsPid} = exec:run("echo hello", [{stdout, null}, monitor]). {ok, <0.36.0>, 23586} 3> flush(). Shell got {'DOWN', 23586, process, <0.36.0>, normal} %% Run with linking - calling process dies if OS process dies abnormally 4> {ok, Pid, OsPid} = exec:run_link("sleep 1000", []). {ok, <0.38.0>, 23588} %% Run without shell (list format) - safer, no shell injection risk 5> {ok, Pid, OsPid} = exec:run(["/bin/echo", "hello", "world"], [stdout]). {ok, <0.40.0>, 23590} ``` ``` -------------------------------- ### Build Erlexec with select(2) Source: https://github.com/saleyn/erlexec/blob/master/README.md Configure the build to use select(2) instead of the default poll(2) for event demultiplexing. ```bash $ USE_POLL=0 make ``` -------------------------------- ### Run OS Commands with Pseudo Terminal (pty) in Erlang Source: https://github.com/saleyn/erlexec/blob/master/README.md Demonstrates executing commands with and without a pseudo-terminal (pty). Using pty can affect output formatting, especially for interactive commands like 'cat'. ```erlang % Execute a command without a pty 37> exec:run("echo hello", [sync, stdout]). {ok, [{stdout,[<<"hello\n">>]}]} ``` ```erlang % Execute a command with a pty 38> exec:run("echo hello", [sync, stdout, pty]). {ok,[{stdout,[<<"hello">>,<<"\r\n">>]}]} ``` ```erlang % Execute a command with pty echo 39> {ok, P0, I0} = exec:run("cat", [stdin, stdout, {stderr, stdout}, pty, pty_echo]). {ok,<0.162.0>,17086} 40> exec:send(I0, <<"hello">>). ok 41> flush(). Shell got {stdout,17086,<<"hello">>} ok 42> exec:send(I0, <<"\n">>). ok 43> flush(). Shell got {stdout,17086,<<"\r\n">>} Shell got {stdout,17086,<<"hello\r\n">>} ok 44> exec:send(I, <<3>>). ok 45> flush(). Shell got {stdout,17086,<<"^C">>} Shell got {'DOWN',17086,process,<0.162.0>,{exit_status,2}} ok ``` ```erlang % Execute a command with custom pty options 46> {ok, P1, I1} = exec:run("cat", [stdin, stdout, {stderr, stdout}, {pty, [{vintr, 2}]}, monitor]). {ok,<0.199.0>,16662} 47> exec:send(I1, <<3>>). ok 48> flush(). ok 49> exec:send(I1, <<2>>). ok 50> flush(). Shell got {'DOWN',16662,process,<0.199.0>,{exit_status,2}} ok ``` -------------------------------- ### Redirect OS process stdout to a file Source: https://github.com/saleyn/erlexec/blob/master/README.md Captures standard output of an OS process directly into a file. ```erlang 7> f(I), {ok, _, I} = exec:run_link("for i in 1 2 3; do echo \"Test$i\"; done", [{stdout, "/tmp/output"}]). 8> io:format("~s", [binary_to_list(element(2, file:read_file("/tmp/output")))]), file:delete("/tmp/output"). Test1 Test2 Test3 ok ``` -------------------------------- ### Sending Data to STDIN Source: https://context7.com/saleyn/erlexec/llms.txt Communicate with running OS processes via their standard input stream. ```APIDOC ## Sending Data to STDIN Communicate with running OS processes via their standard input stream. ### Code Examples ```erlang %% Start a process that reads from stdin 1> {ok, Pid, OsPid} = exec:run("read x; echo \"Got: $x\"", [stdin, stdout, monitor]). {ok, <0.50.0>, 23600} %% Send data to the process stdin 2> exec:send(OsPid,Ꭵ"Hello World\n"). ok 3> flush(). Shell got {stdout, 23600,Ꭵ"Got: Hello World\n"} Shell got {'DOWN', 23600, process, <0.50.0>, normal} %% Send EOF to signal end of input (useful for programs like tac) 4> {ok, Pid, OsPid} = exec:run("tac", [stdin, stdout, monitor]). {ok, <0.55.0>, 23605} 5> exec:send(OsPid,Ꭵ"line1\n"). ok 6> exec:send(OsPid,Ꭵ"line2\n"). ok 7> exec:send(OsPid, eof). ok 8> flush(). Shell got {stdout, 23605,Ꭵ"line2\nline1\n"} ``` ``` -------------------------------- ### Enable Debugging and Diagnostics Source: https://context7.com/saleyn/erlexec/llms.txt Configure debugging levels for the erlexec port program and enable tools like Valgrind for memory leak detection. Supports verbose output and debugging specific command executions. ```erlang %% Set debug level for port program (0-10) 1> exec:debug(3). {ok, 0} %% Returns old debug level %% Start with Valgrind for memory leak detection 2> exec:start([valgrind]). {ok, <0.32.0>} %% Creates valgrind.YYYYMMDDhhmmss.log file %% Custom Valgrind options 3> exec:start([{valgrind, "/usr/bin/valgrind --leak-check=full"}]). {ok, <0.32.0>} %% Enable verbose output in Erlang code 4> exec:start([verbose, {debug, 1}]). {ok, <0.32.0>} %% Debug specific command execution 5> exec:run("echo test", [sync, stdout, {debug, 2}]). {ok, [{stdout, [<<"test\n">>]}]} ``` -------------------------------- ### Run OS Commands Synchronously Source: https://context7.com/saleyn/erlexec/llms.txt Executes commands synchronously, blocking until completion and returning captured output. Use `sync` option. Handles command failures and non-existent commands. ```erlang %% Run synchronously and capture stdout 1> exec:run("echo Test", [sync, stdout]). {ok, [{stdout, [<<"Test\n">>]}]} ``` ```erlang %% Capture both stdout and stderr 2> exec:run("echo Test; echo Err 1>&2", [sync, stdout, stderr]). {ok, [{stdout, [<<"Test\n">>]}, {stderr, [<<"Err\n">>]}]} ``` ```erlang %% Handle command failure 3> exec:run("exit 1", [sync, stdout]). {error, [{exit_status, 256}]} ``` ```erlang %% Run non-existent command 4> exec:run("nonexistent_command", [sync, stdout, stderr]). {error, [{exit_status, 32512}, {stderr, [<<"/bin/bash: nonexistent_command: command not found\n">>]}]} ``` ```erlang %% Use custom success exit code 5> exec:run("exit 1", [{success_exit_code, 1}, sync]). {ok, []} ``` -------------------------------- ### Redirect OS process stdout to screen or Erlang process Source: https://github.com/saleyn/erlexec/blob/master/README.md Handles stdout by printing to the console, using a custom function, or sending messages to the current process. ```erlang 9> exec:run("echo Test", [{stdout, print}]). {ok,<0.119.0>,29651} Got stdout from 29651: <<"Test\n">> 10> exec:run("for i in 1 2 3; do sleep 1; echo \"Iter$i\"; done", [{stdout, fun(S,OsPid,D) -> io:format("Got ~w from ~w: ~p\n", [S,OsPid,D]) end}]). {ok,<0.121.0>,29652} Got stdout from 29652: <<"Iter1\n">> Got stdout from 29652: <<"Iter2\n">> Got stdout from 29652: <<"Iter3\n">> % Note that stdout/stderr options are equivanet to {stdout, self()}, {stderr, self()} 11> exec:run("echo Hello World!; echo ERR!! 1>&2", [stdout, stderr]). {ok,<0.244.0>,18382} 12> flush(). Shell got {stdout,18382,<<"Hello World!\n">>} Shell got {stderr,18382,<<"ERR!!\n">>} ok ``` -------------------------------- ### Append OS process stdout to a file Source: https://github.com/saleyn/erlexec/blob/master/README.md Appends output to an existing file with specified file modes. ```erlang 13> exec:run("for i in 1 2 3; do echo TEST$i; done", [{stdout, "/tmp/out", [append, {mode, 8#600}]}, sync]), file:read_file("/tmp/out"). {ok,<<"TEST1\nTEST2\nTEST3\n">>} 14> exec:run("echo Test4; done", [{stdout, "/tmp/out", [append, {mode, 8#600}]}, sync]), file:read_file("/tmp/out"). {ok,<<"TEST1\nTEST2\nTEST3\nTest4\n">>} 15> file:delete("/tmp/out"). ``` -------------------------------- ### Set Environment Variables Source: https://context7.com/saleyn/erlexec/llms.txt Configuring environment variables for spawned processes, including clearing inherited variables or unsetting specific ones. ```erlang %% Set environment variables 1> exec:run("echo $FOO-$BAR", [sync, stdout, {env, [{"FOO", "hello"}, {"BAR", "world"}]}]). {ok, [{stdout, [<<"hello-world\n">>]}]} %% Clear entire environment and set only specific variables 2> exec:run("env", [sync, stdout, {env, [clear, {"TEST", "value"}]}]). {ok, [{stdout, [<<"PWD=/home/user\nSHLVL=0\n_=/usr/bin/env\nTEST=value\n">>]}]} %% Unset a specific environment variable 3> exec:run("env", [sync, stdout, {env, [{"PATH", false}]}]). {ok, [{stdout, [<<"...">>]}]} %% PATH not in output %% Set variable to empty string 4> exec:run("echo \"VAR='$MY_VAR'\"", [sync, stdout, {env, [{"MY_VAR", ""}]}]). {ok, [{stdout, [<<"VAR=''\n">>]}]} ``` -------------------------------- ### Include Erlexec in Erlang application configuration Source: https://github.com/saleyn/erlexec/blob/master/README.md Add erlexec to the applications list in your *.app.src file. ```erlang {applications, [kernel, stdlib, % ... erlexec ]} ``` -------------------------------- ### Pseudo-Terminal (PTY) Support Source: https://context7.com/saleyn/erlexec/llms.txt Running interactive processes with PTY support, including terminal size, echo modes, and termios settings. ```erlang %% Run with PTY enabled 1> exec:run("tty", [sync, stdout, pty]). {ok, [{stdout, [<<"/dev/pts/0\n">>]}]} %% Interactive bash session with PTY 2> {ok, Pid, OsPid} = exec:run("/bin/bash --norc -i", [stdin, stdout, pty, monitor]). {ok, <0.90.0>, 26000} 3> exec:send(OsPid, <<"echo hello\n">>). ok 4> flush(). Shell got {stdout, 26000, <<"hello\r\n">>} 5> exec:send(OsPid, <<"exit\n">>). ok %% Set terminal window size 6> {ok, _, OsPid} = exec:run("bash -c 'echo LINES=$(tput lines)'", [stdin, stdout, pty, {env, [{"TERM", "xterm"}]}, {winsz, {24, 80}}]). {ok, <0.95.0>, 26005} %% Change window size dynamically 7> exec:winsz(OsPid, 50, 120). ok %% Enable PTY echo mode 8> {ok, _, OsPid} = exec:run("cat", [stdin, stdout, pty, pty_echo, monitor]). {ok, <0.100.0>, 26010} 9> exec:send(OsPid, <<"test\n">>). ok 10> flush(). Shell got {stdout, 26010, <<"test\r\ntest\r\n">>} %% Echoed input + output %% Custom PTY options (termios settings) 11> {ok, _, OsPid} = exec:run("cat", [stdin, stdout, {pty, [{echo, true}, {vintr, 2}]}, monitor]). {ok, <0.105.0>, 26015} 12> exec:send(OsPid, <<2>>). %% Send Ctrl+B (vintr=2) to interrupt ok %% Dynamically change PTY options 13> exec:pty_opts(OsPid, [{echo, false}, {icanon, false}]). ok ``` -------------------------------- ### Run OS commands synchronously Source: https://github.com/saleyn/erlexec/blob/master/README.md Executes commands and waits for completion, with options to capture STDOUT and STDERR. ```erlang % Execute an shell script that blocks for 1 second and return its termination code 29> exec:run("sleep 1; echo Test", [sync]). % By default all I/O is redirected to /dev/null, so no output is captured {ok,[]} % 'stdout' option instructs the port program to capture stdout and return it to caller 30> exec:run("sleep 1; echo Test", [stdout, sync]). {ok,[{stdout, [<<"Test\n">>]}]} % Execute a non-existing command 31> exec:run("echo1 Test", [sync, stdout, stderr]). {error,[{exit_status,32512}, {stderr,[<<"/bin/bash: echo1: command not found\n">>]}]} % Capture stdout/stderr of the executed command 32> exec:run("echo Test; echo Err 1>&2", [sync, stdout, stderr]). {ok,[{stdout,[<<"Test\n">>]},{stderr,[<<"Err\n">>]}]} % Redirect stderr to stdout 33> exec:run("echo Test 1>&2", [{stderr, stdout}, stdout, sync]). {ok, [{stdout, [<<"Test\n">>]}]} ``` -------------------------------- ### Run commands as other effective users Source: https://github.com/saleyn/erlexec/blob/master/README.md Configures the port program to switch effective users and limit child process execution rights. ```erlang $ whoami serge $ erl 1> Opts = [root, {user, "wheel"}, {limit_users, ["alex","guest"]}], 2> exec:start(Opts). % Start the port program as effective user "wheel" % and allow it to execute commands as "alex" or "guest". {ok,<0.32.0>} 3> exec:run("whoami", [sync, stdout, {user, "alex"}]). % Command is executed under effective user "alex" {ok,[{stdout,[<<"alex\n">>]}]} $ ps haxo user,comm | grep exec-port wheel exec-port ``` -------------------------------- ### Stop and Kill Processes Source: https://context7.com/saleyn/erlexec/llms.txt Methods for gracefully stopping or forcefully terminating OS processes using PIDs or OS PIDs. ```erlang %% Stop a process gracefully (sends SIGTERM, then SIGKILL after timeout) 1> {ok, Pid, OsPid} = exec:run("sleep 1000", []). {ok, <0.60.0>, 23610} 2> exec:stop(OsPid). ok %% Stop using Erlang Pid 3> exec:stop(Pid). ok %% Kill with a specific signal 4> {ok, _, OsPid} = exec:run("sleep 1000", []). {ok, <0.65.0>, 23615} 5> exec:kill(OsPid, sigterm). ok 6> exec:kill(OsPid, 9). %% SIGKILL by number ok %% Stop and wait for exit with timeout 7> {ok, Pid, OsPid} = exec:run("sleep 1", [monitor]). {ok, <0.70.0>, 23620} 8> exec:stop_and_wait(OsPid, 5000). normal %% Use custom kill command 9> {ok, _, OsPid} = exec:run("trap '' SIGTERM; sleep 30", [{kill, "kill -INT ${CHILD_PID}"}, {kill_timeout, 2}, monitor]). {ok, <0.75.0>, 23625} 10> exec:stop(OsPid). ok ``` -------------------------------- ### Manage Child Processes and PIDs Source: https://context7.com/saleyn/erlexec/llms.txt Lists all managed children, retrieves the OS PID from an Erlang PID, and vice versa. Useful for cross-referencing process identifiers. ```erlang %% Get list of all managed children 8> exec:which_children(). [23584, 23590, 23600] %% Get OsPid from Erlang Pid 9> exec:ospid(Pid). 23584 %% Get Erlang Pid from OsPid 10> exec:pid(23584). <0.120.0> ``` -------------------------------- ### Run OS Commands Asynchronously Source: https://context7.com/saleyn/erlexec/llms.txt Executes OS commands asynchronously. Returns an Erlang Pid linked to the OS process and the OS process ID. Use `monitor` to receive DOWN messages upon process exit. ```erlang %% Run a simple shell command 1> {ok, Pid, OsPid} = exec:run("sleep 10", []). {ok, <0.34.0>, 23584} ``` ```erlang %% Run with monitoring - receive DOWN message when process exits 2> {ok, Pid, OsPid} = exec:run("echo hello", [{stdout, null}, monitor]). {ok, <0.36.0>, 23586} 3> flush(). Shell got {'DOWN', 23586, process, <0.36.0>, normal} ``` ```erlang %% Run with linking - calling process dies if OS process dies abnormally 4> {ok, Pid, OsPid} = exec:run_link("sleep 1000", []). {ok, <0.38.0>, 23588} ``` ```erlang %% Run without shell (list format) - safer, no shell injection risk 5> {ok, Pid, OsPid} = exec:run(["/bin/echo", "hello", "world"], [stdout]). {ok, <0.40.0>, 23590} ``` -------------------------------- ### Communicate with an OS process via STDIN Source: https://github.com/saleyn/erlexec/blob/master/README.md Sends data to an OS process's standard input and captures output from its standard output. ```erlang % Execute an OS process (script) that reads STDIN and echoes it back to Erlang 25> f(I), {ok, _, I} = exec:run("read x; echo \"Got: $x\"", [stdin, stdout, monitor]). {ok,<0.427.0>,26431} % Send the OS process some data via its stdin 26> exec:send(I, <<"Test data\n">>). ok % Get the response written to processes stdout 27> f(M), receive M -> M after 10000 -> timeout end. {stdout,26431,<<"Got: Test data\n">>} % Confirm that the process exited 28> f(M), receive M -> M after 10000 -> timeout end. {'DOWN',26431,process,<0.427.0>,normal} ``` -------------------------------- ### Add Erlexec as a dependency in Elixir Source: https://github.com/saleyn/erlexec/blob/master/README.md Include the erlexec dependency in your mix.exs file. ```elixir defp deps do [ # ... {:erlexec, "~> 2.0"} ] end ``` -------------------------------- ### Add Erlexec as a dependency in Erlang Source: https://github.com/saleyn/erlexec/blob/master/README.md Include the erlexec dependency in your rebar.config file. ```erlang {deps, [% ... {erlexec, "~> 2.0"} ]}. ``` -------------------------------- ### Use a custom success return code Source: https://github.com/saleyn/erlexec/blob/master/README.md Configures which exit status is considered a successful execution. ```erlang 1> exec:start_link([]). {ok,<0.35.0>} 2> exec:run_link("sleep 1", [{success_exit_code, 0}, sync]). {ok,[]} 3> exec:run("sleep 1", [{success_exit_code, 1}, sync]). {error,[{exit_status,1}]} % Note that the command returns exit code 1 ``` -------------------------------- ### Run OS Commands with/without Shell in Erlang Source: https://github.com/saleyn/erlexec/blob/master/README.md Execute OS commands using erlexec. Use a string for shell execution or a list for direct executable calls. The full path is required when not using a shell. ```erlang % Execute a command by an OS shell interpreter 34> exec:run("echo ok", [sync, stdout]). {ok, [{stdout, [<<"ok\n">>]}]} ``` ```erlang % Execute an executable without a shell (note that in this case % the full path to the executable is required): 35> exec:run(["/bin/echo", "ok"], [sync, stdout]). {ok, [{stdout, [<<"ok\n">>]}]} ``` ```erlang % Execute a shell with custom options 36> exec:run(["/bin/bash", "-c", "echo ok"], [sync, stdout]). {ok, [{stdout, [<<"ok\n">>]}]} ``` -------------------------------- ### Redirecting STDOUT and STDERR Source: https://context7.com/saleyn/erlexec/llms.txt Control where process output goes: to files, Erlang processes, or custom callback functions. ```APIDOC ## Redirecting STDOUT and STDERR Control where process output goes: to files, Erlang processes, or custom callback functions. ### Code Examples ```erlang %% Redirect stdout to a file 1> exec:run("echo Test", [{stdout, "/tmp/output.txt"}, sync]). {ok, []} 2> file:read_file("/tmp/output.txt"). {ok,Ꭵ"Test\n"} %% Append to file with specific permissions 3> exec:run("echo Line1", [{stdout, "/tmp/log.txt", [append, {mode, 8#600}]}, sync]). {ok, []} %% Redirect to the calling Erlang process 4> {ok, _, OsPid} = exec:run("echo hello; echo world", [stdout]). {ok, <0.45.0>, 12345} 5> flush(). Shell got {stdout, 12345,Ꭵ"hello\n"} Shell got {stdout, 12345,Ꭵ"world\n"} %% Redirect stderr to stdout 6> exec:run("echo Error 1>&2", [{stderr, stdout}, stdout, sync]). {ok, [{stdout, [Ꭵ"Error\n"]}]} %% Use a custom callback function 7> Fun = fun(Stream, OsPid, Data) -> io:format("~w from ~w: ~p~n", [Stream, OsPid, Data]) end. 8> exec:run("echo callback test", [{stdout, Fun}]). stdout from 12350:Ꭵ"callback test\n" %% Use print helper for debugging 9> exec:run("echo debug", [{stdout, print}]). Got stdout from 12352:Ꭵ"debug\n" ``` ``` -------------------------------- ### Redirect STDOUT and STDERR Source: https://context7.com/saleyn/erlexec/llms.txt Controls where process output goes: to files, Erlang processes, or custom callback functions. Supports appending to files and setting permissions. ```erlang %% Redirect stdout to a file 1> exec:run("echo Test", [{stdout, "/tmp/output.txt"}, sync]). {ok, []} 2> file:read_file("/tmp/output.txt"). {ok,Ꭵ<< ``` ```erlang %% Append to file with specific permissions 3> exec:run("echo Line1", [{stdout, "/tmp/log.txt", [append, {mode, 8#600}]}, sync]). {ok, []} ``` ```erlang %% Redirect to the calling Erlang process 4> {ok, _, OsPid} = exec:run("echo hello; echo world", [stdout]). {ok, <0.45.0>, 12345} 5> flush(). Shell got {stdout, 12345, << ``` ```erlang %% Redirect stderr to stdout 6> exec:run("echo Error 1>&2", [{stderr, stdout}, stdout, sync]). {ok, [{stdout, [<<"Error\n">>]}]} ``` ```erlang %% Use a custom callback function 7> Fun = fun(Stream, OsPid, Data) -> io:format("~w from ~w: ~p~n", [Stream, OsPid, Data]) end. 8> exec:run("echo callback test", [{stdout, Fun}]). stdout from 12350: << ``` ```erlang %% Use print helper for debugging 9> exec:run("echo debug", [{stdout, print}]). Got stdout from 12352: << ``` -------------------------------- ### Manage Process Groups Source: https://context7.com/saleyn/erlexec/llms.txt Grouping processes to allow for collective termination or dynamic process group ID management. ```erlang %% Create a new process group (group = 0 means use OsPid as group ID) 1> {ok, _, GroupId} = exec:run("sleep 10", [{group, 0}, kill_group]). {ok, <0.80.0>, 25306} %% Add processes to the same group 2> {ok, P1, _} = exec:run("sleep 15", [{group, GroupId}, monitor]). {ok, <0.82.0>, 25307} 3> {ok, P2, _} = exec:run("sleep 15", [{group, GroupId}, monitor]). {ok, <0.84.0>, 25308} %% When the first process exits or is killed with kill_group, all group members are terminated 4> exec:stop(GroupId). ok 5> flush(). Shell got {'DOWN', 25307, process, <0.82.0>, {exit_status, 15}} Shell got {'DOWN', 25308, process, <0.84.0>, {exit_status, 15}} %% Change process group ID dynamically 6> exec:setpgid(OsPid, NewGroupId). ok ``` -------------------------------- ### Specify a custom process shutdown delay Source: https://github.com/saleyn/erlexec/blob/master/README.md Configures a custom timeout for process termination when using exec:stop, useful for processes that block standard signals. ```erlang % Execute an OS process (script) that blocks SIGTERM with custom kill timeout, and monitor 22> f(I), {ok, _, I} = exec:run("trap '' SIGTERM; sleep 30", [{kill_timeout, 3}, monitor]). {ok,<0.399.0>,26347} % Attempt to stop the OS process 23> exec:stop(I). ok % Wait for its completion 24> f(M), receive M -> M after 10000 -> timeout end. {'DOWN',26347,process,<0.403.0>,normal} ``` -------------------------------- ### Specify a custom kill command Source: https://github.com/saleyn/erlexec/blob/master/README.md Defines a custom command to terminate a process, utilizing the CHILD_PID environment variable provided by the port program. ```erlang % Execute an OS process (script) that blocks SIGTERM, and uses a custom kill command, % which kills it with a SIGINT. Add a monitor so that we can wait for process exit % notification. Note the use of the special environment variable "CHILD_PID" by the % kill command. This environment variable is set by the port program before invoking % the kill command: 2> f(I), {ok, _, I} = exec:run("trap '' SIGTERM; sleep 30", [{kill, "kill -n 2 ${CHILD_PID}"}, {kill_timeout, 2}, monitor]). {ok,<0.399.0>,26347} % Try to kill by SIGTERM. This does nothing, since the process is blocking SIGTERM: 3> exec:kill(I, sigterm), f(M), receive M -> M after 0 -> timeout end. timeout % Attempt to stop the OS process 4> exec:stop(I). ok % Wait for its completion 5> f(M), receive M -> M after 1000 -> timeout end. {'DOWN',26347,process,<0.403.0>,normal} ``` -------------------------------- ### Kill an OS process Source: https://github.com/saleyn/erlexec/blob/master/README.md Terminates an OS process using exec:kill/2 or by sending signals. ```erlang 1> f(I), {ok, _, I} = exec:run_link("sleep 1000", []). {ok,<0.37.0>,2350} 2> exec:kill(I, 15). ok ** exception error: {exit_status,15} % Our shell died because we linked to the % killed shell process via exec:run_link/2. 3> exec:status(15). % Examine the exit status. {signal,15,false} % The program got SIGTERM signal and produced % no core file. ``` -------------------------------- ### Send Data to STDIN Source: https://context7.com/saleyn/erlexec/llms.txt Communicates with running OS processes by sending data to their standard input stream. Supports sending strings and `eof` to signal end of input. ```erlang %% Start a process that reads from stdin 1> {ok, Pid, OsPid} = exec:run("read x; echo \"Got: $x\"", [stdin, stdout, monitor]). {ok, <0.50.0>, 23600} ``` ```erlang %% Send data to the process stdin 2> exec:send(OsPid, << ``` ```erlang %% Send EOF to signal end of input (useful for programs like tac) 4> {ok, Pid, OsPid} = exec:run("tac", [stdin, stdout, monitor]). {ok, <0.55.0>, 23605} 5> exec:send(OsPid, << ``` -------------------------------- ### Decode Process Exit Status and Signals Source: https://context7.com/saleyn/erlexec/llms.txt Provides functions to decode exit statuses and convert between signal names and numbers. Useful for interpreting how an external process terminated. ```erlang %% Decode exit status 1> exec:status(0). {status, 0} 2> exec:status(256). %% Exit code 1 {status, 1} 3> exec:status(15). %% Killed by signal 15 (SIGTERM) {signal, sigterm, false} 4> exec:status(137). %% Killed by signal 9 with core dump {signal, sigkill, true} %% Convert signal names to numbers 5> exec:signal(15). sigterm 6> exec:signal_to_int(sigkill). 9 7> exec:signal_to_int(sigterm). 15 ``` -------------------------------- ### Send EOF to an OS process via STDIN Source: https://github.com/saleyn/erlexec/blob/master/README.md Signals the end of input to a process reading from STDIN by sending the 'eof' atom. ```erlang 2> Watcher = spawn(fun F() -> receive Msg -> io:format("Got: ~p\n", [Msg]), F() after 60000 -> ok end end). <0.112.0> 3> f(Pid), f(OsPid), {ok, Pid, OsPid} = exec:run("tac", [stdin, {stdout, Watcher}, {stderr, Watcher}]). {ok,<0.114.0>,26143} 4> exec:send(Pid, <<"foo\n">>). ok 5> exec:send(Pid, <<"bar\n">>). ok 6> exec:send(Pid, <<"baz\n">>). ok 7> exec:send(Pid, eof). %% <--- sending the EOF command to the STDIN ok Got: {stdout,26143,<<"baz\nbar\nfoo\n">>} ``` -------------------------------- ### Kill Process Group on Process Exit in Erlang Source: https://github.com/saleyn/erlexec/blob/master/README.md Configure processes to be part of a specific process group. When the parent process exits, all processes in the same group are terminated with SIGTERM. ```erlang % In the following scenario the process P0 will create a new process group % equal to the OS pid of that process (value = GID). The next two commands % are assigned to the same process group GID. As soon as the P0 process exits % P1 and P2 will also get terminated by signal 15 (SIGTERM): 51> {ok, P2, GID} = exec:run("sleep 10", [{group, 0}, kill_group]). {ok,<0.37.0>,25306} 52> {ok, P3, _} = exec:run("sleep 15", [{group, GID}, monitor]). {ok,<0.39.0>,25307} 53> {ok, P4, _} = exec:run("sleep 15", [{group, GID}, monitor]). {ok,<0.41.0>,25308} 54> flush(). Shell got {'DOWN',25307,process,<0.39.0>,{exit_status,15}} Shell got {'DOWN',25308,process,<0.41.0>,{exit_status,15}} ok ```