Add an option for custom vchan buffer size, to override default 64k (for
each direction). This is especially useful when the other side of
connection is MirageOS based, because of limited memory and default
grant table size (128 entries).
When qrexec-client-vm is started with socket on its stdout and no local
process requested, it will try to shutdown(SHUT_WR) this socket when
remote process exists. This is wrong, because this socket may be still
needed by other processes (for example shell from where qrexec-client-vm
was called).
In such a case, simple close() should be used.
1. If local process is started, report its exit code, instaed of remote
one. To get remote exit code, simply start qrexec-client-vm without
third argument (and connect its stdin/stdout with the other process some
other way).
2. Report process terminated by signal.
Don't pretend that process terminated by signal finished successfuly.
Copy shell behaviour of reporting it as 128+signum.
3. Do not wait() for any child process, just the one we expect. In case
of qrexec-client-vm the child process is started differently and
wait()ing on it inside main loop would break its exit code reporting.
FixesQubesOS/qubes-issues#2861
Always set QREXEC_AGENT_PID variable, setup SIGUSR1 handler. And do that
before starting child process to avoid race conditions.
Required for QubesOS/qubes-issues#
FixesQubesOS/qubes-issues#1863
Implement one of TODOs left in the code. Without this buffering, it may
happen that qrexec-agent will hang waiting on write(2) to the child
process, while that child will do the same (try to write something to
the qrexec-agent), without reading its stdin. This would end up in a
deadlock.
FixesQubesOS/qubes-issues#1347
This will ensure that the child process will receive info that the
connection is closed. Otherwise it could hang on write() or in some
cases read() - on its stdin/stdout.
Thanks @adrelanos for help with debugging.
It should be remote process exit code, not the local one.
Also do not 'return' from the middle of the look, just use 'break' to
execute common cleanup code (which will be introduced in next commit).
In case of remote process exit even when some messages are still
waiting, vchan connection can be already closed. If we try to send some
data in this case (for example stdout of local process), there will be
an error, which will terminate qrexec-client-vm/qrexec-agent child. So
first check vchan data (where could be MSG_EXIT_CODE queued) , then
local process.
There is still some race condition in this code - remote process could
exit just after we check vchan, but before we send some data. But this
is much less probable and in the worst case we only loose remote process
exit code.
Child process can request to use single socket for both stdin and
stdout by sending SIGUSR1 signal. If it does so twice or more, previous
code broke the connection by closing the socket.
This doesn't cover all the cases, because local process could want to
receive that value (currently it cant), but I can't think of any simple,
*compatible* way to pass it there.
This way qrexec-client-vm will have much more information, at least:
- will know whether the service call was accepted or refused
- potentially will know remote process exit code
This commit implements the first point - the local process will not be
started if service call was refused.
Move (qrexec-agent version of) do_exec to qrexec-agent.c, move
handle_handshake to qrexec-agent-data.c (common to all agent binaries).
Fix indentation (tabs -> spaces).
The main advantage is possible use of single socket for both stdin and
stdout. This is strictly required for using USBIP over qrexec.
For compatibility qrexec still creates three socket pairs (instead of
pipes) for stdin/out/err respectively. When qrexec-agent receives
SIGUSR1, it will close stdout socket and use stdin socket for both
directions.
Some additional work is needed here to actually allow child process to
send that signal - qrexec is running as root, but child as "user" in
most cases.