qrexec: add option to wait for VM-VM connection termination
Normally when qrexec-client setup VM-VM connection it exits immediatelly. But it may be useful to wait for the connection to terminate - for example to cleanup DispVM. qrexec-daemon (the one that allocated vchan port) do receive such notification, so expose such option to qrexec-client. QubesOS/qubes-issues#2253
This commit is contained in:
parent
9192bb0d44
commit
849b295384
@ -537,11 +537,12 @@ static void select_loop(libvchan_t *vchan)
|
||||
static void usage(char *name)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: %s [-w timeout] [-t] [-T] -d domain_name ["
|
||||
"usage: %s [-w timeout] [-W] [-t] [-T] -d domain_name ["
|
||||
"-l local_prog|"
|
||||
"-c request_id,src_domain_name,src_domain_id|"
|
||||
"-e] remote_cmdline\n"
|
||||
"-e means exit after sending cmd,\n"
|
||||
"-W waits for connection end even in case of VM-VM (-c)\n"
|
||||
"-t enables replacing ESC character with '_' in command output, -T is the same for stderr\n"
|
||||
"-c: connect to existing process (response to trigger service call)\n"
|
||||
"-w timeout: override default connection timeout of 5s (set 0 for no timeout)\n",
|
||||
@ -641,6 +642,7 @@ int main(int argc, char **argv)
|
||||
int msg_type;
|
||||
int s;
|
||||
int just_exec = 0;
|
||||
int wait_connection_end = 0;
|
||||
int connect_existing = 0;
|
||||
char *local_cmdline = NULL;
|
||||
char *remote_cmdline = NULL;
|
||||
@ -649,7 +651,7 @@ int main(int argc, char **argv)
|
||||
int src_domain_id = 0; /* if not -c given, the process is run in dom0 */
|
||||
int connection_timeout = 5;
|
||||
struct service_params svc_params;
|
||||
while ((opt = getopt(argc, argv, "d:l:ec:tTw:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "d:l:ec:tTw:W")) != -1) {
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
domname = strdup(optarg);
|
||||
@ -674,6 +676,9 @@ int main(int argc, char **argv)
|
||||
case 'w':
|
||||
connection_timeout = atoi(optarg);
|
||||
break;
|
||||
case 'W':
|
||||
wait_connection_end = 1;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
}
|
||||
@ -749,13 +754,25 @@ int main(int argc, char **argv)
|
||||
strlen(remote_cmdline) + 1,
|
||||
&data_domain,
|
||||
&data_port);
|
||||
close(s);
|
||||
if (wait_connection_end && connect_existing)
|
||||
/* save socket fd, 's' will be reused for the other qrexec-daemon
|
||||
* connection */
|
||||
wait_connection_end = s;
|
||||
else
|
||||
close(s);
|
||||
setenv("QREXEC_REMOTE_DOMAIN", domname, 1);
|
||||
prepare_local_fds(local_cmdline);
|
||||
if (connect_existing) {
|
||||
s = connect_unix_socket(src_domain_name);
|
||||
send_service_connect(s, request_id, data_domain, data_port);
|
||||
close(s);
|
||||
if (wait_connection_end) {
|
||||
/* wait for EOF */
|
||||
fd_set read_fd;
|
||||
FD_ZERO(&read_fd);
|
||||
FD_SET(wait_connection_end, &read_fd);
|
||||
select(wait_connection_end+1, &read_fd, NULL, NULL, 0);
|
||||
}
|
||||
} else {
|
||||
data_vchan = libvchan_server_init(data_domain, data_port,
|
||||
VCHAN_BUFFER_SIZE, VCHAN_BUFFER_SIZE);
|
||||
|
@ -71,6 +71,12 @@ int policy_pending_max = -1;
|
||||
* either VCHAN_PORT_* or remote domain id for used port */
|
||||
int used_vchan_ports[MAX_CLIENTS];
|
||||
|
||||
/* notify client (close its connection) when connection initiated by it was
|
||||
* terminated - used by qrexec-policy to cleanup (disposable) VM; indexed with
|
||||
* vchan port number relative to VCHAN_BASE_DATA_PORT; stores fd of given
|
||||
* client or -1 if none requested */
|
||||
int vchan_port_notify_client[MAX_CLIENTS];
|
||||
|
||||
int max_client_fd = -1; // current max fd of all clients; so that we need not to scan all the "clients" table
|
||||
int qrexec_daemon_unix_socket_fd; // /var/run/qubes/qrexec.xid descriptor
|
||||
const char *default_user = "user";
|
||||
@ -312,6 +318,7 @@ void init(int xid)
|
||||
clients[i].state = CLIENT_INVALID;
|
||||
policy_pending[i].pid = 0;
|
||||
used_vchan_ports[i] = VCHAN_PORT_UNUSED;
|
||||
vchan_port_notify_client[i] = VCHAN_PORT_UNUSED;
|
||||
}
|
||||
|
||||
/* When running as root, make the socket accessible; perms on /var/run/qubes still apply */
|
||||
@ -358,14 +365,6 @@ static int allocate_vchan_port(int new_state)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void release_vchan_port(int port, int expected_remote_id)
|
||||
{
|
||||
/* release only if was reserved for connection to given domain */
|
||||
if (used_vchan_ports[port-VCHAN_BASE_DATA_PORT] == expected_remote_id) {
|
||||
used_vchan_ports[port-VCHAN_BASE_DATA_PORT] = VCHAN_PORT_UNUSED;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_new_client()
|
||||
{
|
||||
int fd = do_accept(qrexec_daemon_unix_socket_fd);
|
||||
@ -387,8 +386,25 @@ static void handle_new_client()
|
||||
|
||||
static void terminate_client(int fd)
|
||||
{
|
||||
int port;
|
||||
clients[fd].state = CLIENT_INVALID;
|
||||
close(fd);
|
||||
/* if client requested vchan connection end notify, cancel it */
|
||||
for (port = 0; port < MAX_CLIENTS; port++) {
|
||||
if (vchan_port_notify_client[port] == fd)
|
||||
vchan_port_notify_client[port] = VCHAN_PORT_UNUSED;
|
||||
}
|
||||
}
|
||||
|
||||
static void release_vchan_port(int port, int expected_remote_id)
|
||||
{
|
||||
/* release only if was reserved for connection to given domain */
|
||||
if (used_vchan_ports[port-VCHAN_BASE_DATA_PORT] == expected_remote_id) {
|
||||
used_vchan_ports[port-VCHAN_BASE_DATA_PORT] = VCHAN_PORT_UNUSED;
|
||||
/* notify client if requested - it will clear notification request */
|
||||
if (vchan_port_notify_client[port-VCHAN_BASE_DATA_PORT] != VCHAN_PORT_UNUSED)
|
||||
terminate_client(vchan_port_notify_client[port-VCHAN_BASE_DATA_PORT]);
|
||||
}
|
||||
}
|
||||
|
||||
static int handle_cmdline_body_from_client(int fd, struct msg_header *hdr)
|
||||
@ -433,6 +449,8 @@ static int handle_cmdline_body_from_client(int fd, struct msg_header *hdr)
|
||||
terminate_client(fd);
|
||||
return 0;
|
||||
}
|
||||
/* notify the client when this connection got terminated */
|
||||
vchan_port_notify_client[params.connect_port-VCHAN_BASE_DATA_PORT] = fd;
|
||||
client_params.connect_port = params.connect_port;
|
||||
client_params.connect_domain = remote_domain_id;
|
||||
hdr->len = sizeof(client_params);
|
||||
|
Loading…
Reference in New Issue
Block a user