Skip to content

Commit

Permalink
Add atomvm:subprocess/4 performing pipe/fork/execve`
Browse files Browse the repository at this point in the history
This is implemented using pipe/fork/execve or posix_spawn depending on
platforms and availability of `HAVE_POSIX_SPAWN_CLOEXEC_DEFAULT` attribute.
The VM does not leak any file descriptor to the subprocess except for the
pipe used to read stdout.

The function is explicitely disabled on microcontrollers and is meant to be
used on generic_unix platform, for example for tests.

Signed-off-by: Paul Guyot <[email protected]>
  • Loading branch information
pguyot committed Dec 27, 2024
1 parent a0a0a5b commit a21bd0c
Show file tree
Hide file tree
Showing 12 changed files with 276 additions and 52 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added support for external refs and encoded refs in external terms
- Introduce ports to represent native processes and added support for external ports and encoded ports in external terms
- Added `atomvm:get_creation/0`, equivalent to `erts_internal:get_creation/0`
- Added `atomvm:subprocess/4` to perform pipe/fork/execve on POSIX platforms

### Fixed

Expand Down
19 changes: 18 additions & 1 deletion libs/eavmlib/src/atomvm.erl
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
posix_opendir/1,
posix_closedir/1,
posix_readdir/1,
get_creation/0
get_creation/0,
subprocess/4
]).

-export_type([
Expand Down Expand Up @@ -341,3 +342,19 @@ posix_readdir(_Dir) ->
-spec get_creation() -> non_neg_integer().
get_creation() ->
erlang:nif_error(undefined).

%%-----------------------------------------------------------------------------
%% @param Path path to the command to execute
%% @param Args arguments to pass to the command. First item is the name
%% of the command
%% @param Envp environment variables to pass to the command.
%% @param Options options to run execve. Should be `[stdout]'
%% @returns a tuple with the process id and a fd to the stdout of the process.
%% @doc Fork and execute a program using fork(2) and execve(2). Pipe stdout
%% so output of the program can be read with `atomvm:posix_read/2'.
%% @end
%%-----------------------------------------------------------------------------
-spec subprocess(Path :: iodata(), Args :: [iodata()], Env :: [iodata()], Options :: [stdout]) ->
{ok, non_neg_integer(), posix_fd()} | {error, posix_error()}.
subprocess(_Path, _Args, _Env, _Options) ->
erlang:nif_error(undefined).
3 changes: 3 additions & 0 deletions src/libAtomVM/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@ define_if_function_exists(libAtomVM closedir "dirent.h" PUBLIC HAVE_CLOSEDIR)
define_if_function_exists(libAtomVM mkfifo "sys/stat.h" PRIVATE HAVE_MKFIFO)
define_if_function_exists(libAtomVM readdir "dirent.h" PUBLIC HAVE_READDIR)
define_if_function_exists(libAtomVM unlink "unistd.h" PRIVATE HAVE_UNLINK)
define_if_function_exists(libAtomVM execve "unistd.h" PRIVATE HAVE_EXECVE)
define_if_function_exists(libAtomVM closefrom "unistd.h" PRIVATE HAVE_CLOSEFROM)
define_if_symbol_exists(libAtomVM POSIX_SPAWN_CLOEXEC_DEFAULT "spawn.h" PRIVATE HAVE_POSIX_SPAWN_CLOEXEC_DEFAULT)
define_if_symbol_exists(libAtomVM O_CLOEXEC "fcntl.h" PRIVATE HAVE_O_CLOEXEC)
define_if_symbol_exists(libAtomVM O_DIRECTORY "fcntl.h" PRIVATE HAVE_O_DIRECTORY)
define_if_symbol_exists(libAtomVM O_DSYNC "fcntl.h" PRIVATE HAVE_O_DSYNC)
Expand Down
6 changes: 6 additions & 0 deletions src/libAtomVM/nifs.c
Original file line number Diff line number Diff line change
Expand Up @@ -851,8 +851,14 @@ DEFINE_MATH_NIF(tanh)
//Handle optional nifs
#if HAVE_OPEN && HAVE_CLOSE
#define IF_HAVE_OPEN_CLOSE(expr) (expr)
#if HAVE_EXECVE
#define IF_HAVE_EXECVE(expr) (expr)
#else
#define IF_HAVE_EXECVE(expr) NULL
#endif
#else
#define IF_HAVE_OPEN_CLOSE(expr) NULL
#define IF_HAVE_EXECVE(expr) NULL
#endif
#if HAVE_MKFIFO
#define IF_HAVE_MKFIFO(expr) (expr)
Expand Down
1 change: 1 addition & 0 deletions src/libAtomVM/nifs.gperf
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ atomvm:posix_write/2, IF_HAVE_OPEN_CLOSE(&atomvm_posix_write_nif)
atomvm:posix_select_read/3, IF_HAVE_OPEN_CLOSE(&atomvm_posix_select_read_nif)
atomvm:posix_select_write/3, IF_HAVE_OPEN_CLOSE(&atomvm_posix_select_write_nif)
atomvm:posix_select_stop/1, IF_HAVE_OPEN_CLOSE(&atomvm_posix_select_stop_nif)
atomvm:subprocess/4, IF_HAVE_EXECVE(&atomvm_subprocess_nif)
atomvm:posix_mkfifo/2, IF_HAVE_MKFIFO(&atomvm_posix_mkfifo_nif)
atomvm:posix_unlink/1, IF_HAVE_UNLINK(&atomvm_posix_unlink_nif)
atomvm:posix_clock_settime/2, IF_HAVE_CLOCK_SETTIME_OR_SETTIMEOFDAY(&atomvm_posix_clock_settime_nif)
Expand Down
Loading

0 comments on commit a21bd0c

Please sign in to comment.