Support bytes in iolists. Imitate iolist_to_binary/1 and accept strings,
binaries and lists of binaries, lists and bytes.
Unlike the real iolist_to_binary, this version places an arbitrary limit
of 16 nested lists.
To ensure consistency, exit if a timeout is set and reached. Otherwise,
late messages may arrive in the queue, messing up subsequent calls:
1> {ok,P} = alcove_drv:start().
{ok,<0.45.0>}
2> catch alcove:call(P, [], getpid, [], 0).
{'EXIT',timeout}
3> alcove:version(P).
2897
4> flush().
Shell got {alcove_call,<0.45.0>,[],<<"0.6.1">>}
ok
Using timeouts might be needed if it is not known whether is still
running in the event loop.
Remove the cast functions, since they are dangerous and can be emulated
by using call/5.
Currently, alloc/2,3 tests functionality that only is needed for linux
(for seccomp mode). Run the tests on other plaforms like freebsd, so we
can catch any bugs.
Cleanup of the setopt function:
* use an unsigned integer for holding the arg
* add a typespec for setopt/3,4.
* change the return value to true/false
Previously, if an unknown option was passed to setopt, the process would
crash with badarg. Reserve badarg for wrong types and return a value to
the caller.
inet:getopts/setopts take a list of options. If any of the options are
invalid, {error,einval} is returned to the caller. The valid options are
not changed.
alcove:getopt/2,3 returns an unsigned integer or false if the option is
not supported.
alcove:getopt,setopt will (should?) never fail if a valid argument is
used. So replying with an ok tuple, like the inet functions, is not necessary.
So setopt could more exactly return 'ok' and 'invalid' or 'not_found'
(diverging from getopt) or it could follow getopt and return ok/false,
which, to be more consistent, should be a boolean.
The macro should be a "pure" function. Remove the call to the port. This
forces the caller to catch the case where a syscall does not exist on a
particular platform.
Move the BPF generation macros to the header file. This needs further
cleanup:
* some of the macros require the port pid, others do not
* some of the macros refer to functions
* some of the raw BPF statements should be wrapped in a function or
macro
On some systems (Linux 3.8.11 armv7l), the return value of a trapped call
is {error,unknown}.
On other systems (Linux 3.13.0-24-generic x86_64), the return value is
{ok,<<>>}.
Could be a bug on ARM, but accomodate multiple return values for now.
Fixes issue in commit 1003087.
Depending on the order when the fd's are closed, the control socket
could return from poll after the pid was zero'ed. Since the event loop
uses an event on the control fd to return an 'ok' message, the erlang
process would block forever in receive, waiting for a response from the
port.
Clean up after the forkchain test by forcing the top level process to
exit.
On some systems, the eof test would hang while forcing the process to
exit. Not sure why, this cleanup just covers up the problem.
Add a test for closing stdio. The final test (closing stdin) is marked
as passing if {error,esrch} or {error,ebadf} is returned because there
is a race condition: the event loop may not have noticed the fd is
closed depending on how quickly the test is run.
To support running the tests under valgrind, add two environment
variables:
ALCOVE_TEST_EXEC : set the {exec, string()} parameter to
alcove_drv:start/1.
ALCOVE_TEST_USE_FORK : use fork() instead of clone()
For example:
ALCOVE_TEST_EXEC="sudo valgrind --leak-check=yes --log-file=alcove.log" \
ALCOVE_TEST_USE_FORK=1 make test
Remove define/3. Figure out the function to call based on the name of
the flag.
The behaviour of this function is to crash if passed an unknown
constant. There are 2 ways it can crash:
* name is not matched in case state
* if the port returns badarg
Either way the process will crash followed by the port.
Possibly define/2 should return a value, like 'false' or {badarg,
atom()} (in which case, the process will probably crash anyway when it
attempts to pass the value to another function.)
Alternatively, define/2 could ignore unknown atoms but this could become
a source of creeping errors.
When a C compiler comes across an unknown constant, the compilation
fails. Since we are evaluating the constant at runtime, we should crash.
The maximum number of forks a process can have is limited by the
available file descriptors, with the upper boundary set by the number of
fd's select() can handle.
This option is provided because the number of child process per fork
can't easily be done by other means. For example, setrlimit() with
RLIMIT_NPROC applies to all the user processes and RLIMIT_NFILE will
interfere with normal file operations. Useful for testing and for
preventing accidental fork bombs.
The maxchild options applies to child processes. The value in the
current process is unchanged.
The process environment can be set up in a few ways:
* using the {env, [{Key, Val}]} option in alcove_drv:start/1
Global: will effect all future spawned processes.
* at exec, using execve/5
Per process: will affect the child process
* using getenv(3), setenv(3) and unsetenv(3)
Add exit(3) for testing.
Confirm events are delivered properly, for example, to the parent after
the child has exited.
The event extraction in alcove_drv is still messy, redundant and error
prone. Needs to be cleaned up.
Accidentally the maxchild option. maxchild is used to size an array of
file descriptors. So obviously resetting this value will either crash
the process (read outside the bounds of the array) or cause it leak fd's.
So punt for now and remove support for changing maxchild. I guess the
simplest way to handle this would be to:
* get the new value of maxchild
* allocate an array of maxchild bytes
* copy PIDs from old to new
* if there is not enough space, free new array and return badarg
* otherwise, point the state to the new array and free the old array
Instead of crashing the caller by returning badarg, we could synthesize
an errno value and return {error,enomem} or simply leave the maxchild
unchanged and return ok.
Instead of using a case statement, simplify the lookup by searching
through an array.
Do not modify the name of constants: previously, the names were
downcased and the leading namespace removed, e.g, PR_CAPBSET_READ
would become 'get_capbset_read' and SIG_TERM would be represented by
'term'. Constants are now atoms mirroring the name in the header file:
'PR_CAPBSET_READ', 'SIG_TERM'.
This change will cause issues with non-portable naming of constants. For
example, Linux uses MS_ to preface mount constants and FreeBSD uses
MNT_. A future change will add portable representations of the atom.
Something like 'rdonly' for MS_RDONLY and MNT_RDONLY.
Decode messages as they arrive from the port. Non-matching messages are
converted to tuples and sent back into the process' mailbox.
A busy port that is generating many messages (stdout, signals, stderr)
could cause a call to timeout. Still messy, cleanup required.
Execute a program with an environment:
Port = alcove_drv:start(),
{ok, Child} = alcove:fork(Port),
ok = alcove:execve(Port, [Child], "/usr/bin/env",
["/usr/bin/env"], ["FOO=bar", "BAR=1234567"]),
<<"FOO=bar\nBAR=1234567\n">> = alcove:stdout(Port, [Child], 5000).
An empty list is treated as NULL.
Notify the parent when the child has called exec by using a pair of file
descripts with the "close on exec" flag set.
socketpair() was used for possible bidirectional communication in the
future. The socket is only monitored by the parent for EOF right now.
Return a tuple from event/4. Modify alcove:event/1,2,3 to:
* only request event types
* return the data portion of the tuple
Probably alcove_drv:event/4 should be renamed and a new event function
that returns the data should be added.
Fix dialyzer warnings. Since the return value from event is the huge
mess of values from the port, a timeout cannot be distinguised.
The return value of event should be changed to:
{alcove_drv:type(), [integer()], reply()} | 'false'
{alcove_drv:type(), [integer()], binary()} | 'false'
On slow platforms like the beaglebone, SIGCHLD is received by the parent
but a slot in the process table has not been freed so the next call to
fork fails with {error, eagain} and the subsequent fork succeeds.
Check for the child by sending a kill(0).