Sending data to the stdin of a non-existent process will also return
'badpid'. Mark the type as a control message rather than the result of
a call.
Probably there only needs to be 2 types of messages: sync (calls) and
async (events). The control message type might be removed and these
events labelled as events.
Make the result of alcove_cgroup:set/6 more consistent:
* return ok on success
* return an errno tuple if open() or write() fails
* return {error,enoent} if the cgroup does not exist, instead of []
In the case of a partial write, currently the code will crash, taking
care to close the fd in the unix child process first. The code could
select on the fd and attempt another write.
Ubuntu 15.04 sets the MS_SHARED flag on system mounts. Since this flag
is inherited by container mounts and the container filesystem is visible
to the global namespace.
Use the MS_PRIVATE flag on container mounts to prevent this issue.
I go back and forth with this: be explicit or brief. Especially with
strcmp() where I always WTF.
The code is simpler and easier to read when the checks are explicit. For
example:
// NULL pointer or integer?
if (!x) return;
Enforce the use of the fork path. Having an optional fork path was nice
when working in the shell:
{ok, Child} = alcove:fork(Drv).
Instead of:
{ok, Child} = alcove:fork(Drv, []). % port process forks
However it introduced a few problems:
* made the interface inconsistent and ambiguous
alcove:kill(Drv, Pid, 9)
% vs
alcove:kill(Drv, [], Pid, 9) % the port process is sending the signal
* calls could not have optional arguments
Whether or not calls should have optional arguments is an open question
but the optional fork path would have conflicting arities:
For example, the last argument to mount/8 is used only by Solaris:
% arity 6
-spec mount(alcove_drv:ref(),iodata(),iodata(),iodata(),uint64_t() | [constant()],iodata()) -> 'ok' | {'error', file:posix() | 'unsupported'}.
% arity 7
-spec mount(alcove_drv:ref(),iodata(),iodata(),iodata(),uint64_t() | [constant()],iodata(),iodata()) -> 'ok' | {'error', file:posix() | 'unsupported'}.
% arity 7
-spec mount(alcove_drv:ref(),fork_path(),iodata(),iodata(),iodata(),uint64_t() | [constant()],iodata()) -> 'ok' | {'error', file:posix() | 'unsupported'}.
% arity 8
-spec mount(alcove_drv:ref(),fork_path(),iodata(),iodata(),iodata(),uint64_t() | [constant()],iodata(),iodata()) -> 'ok' | {'error', file:posix() | 'unsupported'}.
* because of the ambiguity in arity, each call can't have an optional
timeout
call/5 sets a timeout of 'infinity'. The timeout isn't accessible from
the "named" functions (e.g., fork, chdir, ...), which means that users
who need the timeout will have to resort to using the call/5
interface. An unfortunate side effect of using call/5 is that dialzyer
won't be able to type check the arguments.
The result of removing the functions without the fork path is that the
code ends up being simpler and more consistent.
Return {error, unsupported} if an atom is used as an argument and the
constant the atom represents does not exist on the current platform.
The previous behaviour was inconsistent and non-deterministic. The
constant might:
* return {error,einval}. System return values could not be distinguished
from alcove return values.
* cause an exception
* be silently ignored
Move out the clone constants to a header since fork() does not need
them. Include sched.h with the constants to ensure the CLONE_ constants
are defined.
Leave common utility functions in c_src/sys/alcove_fork.(c|h). Maybe
these files should be renamed "fork_common.c" or "alcove_fork_common.c".
Move alcove_setfd() to alcove.c since the pid_foreach() is also there.
These common functions should also eventually be moved out to another
file.
Support building with rebar3 and rebar2 using the makefile generated
from "rebar3 new cmake".
Do a straightforward port of the compiler options from the rebar port
compiler. The optimizations/warnings can be re-enabled later.
Use the fancy pants incremental building of each compilation unit from
the template. It's a lot slower than compiling in one go but is slightly
faster when running any rebar command (rebar runs make on each command
which recreates the target. The Makefile needs to be fixed).
Use POSIX 200112L/C99 mode on Solaris to suppress compilation warnings.
"-D__EXTENSIONS__=1" is required for NSIG (max number of signals). This
also pulls in an ERR macro which necessitates renaming the goto label
from ERR -> ERROR.
If the alcove port exits, cause the gen_server to crash.
The reason for crashing:
1. The type specs for all calls was implictly extended to include
{error,closed} on port failure.
2. There is no error recovery the calling process could do (except
cleaning up state). The caller can still catch errors, just like when
dealing with a normal port.
3. Asynchronous messages into the port (stdin) had to be handled as calls,
rather than casts.
A message sent to a non-existent Unix PID would be discarded, causing
the calling erlang process to block forever in receive.
This change modifies the behaviour of the port to return "badpid" if an
element in the fork path is not found. Since any element of the path may
be invalid, an event (as opposed to a call reply) is generated.
Currently any "badpid" event will cause the erlang process to exit.
Since only one call can be active at a time, it shouldn't interfere with
other operations but the mailbox should pattern match on the list
prefix (i.e., a call to [1,2,3,-1,4] would generate badpid for
[1,2,3,-1]).
Also define the behaviour when writing to invalid PIDs (<= 0). Since 0
is used to initialize the list of child PIDs in the port, passing in a
PID of 0 would match the first available slot in the PID table, causing
the port to write to fd 0.
The port will accept negative PIDs. A PID is usually defined as a signed
32-bit integer.
For fork paths, values will always be positive (as returned by fork;
fork errors will return a tuple rather than -1). If the user passes a
negative number as a component of the fork path, the message will be
discared.