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)
|
static void usage(char *name)
|
||||||
{
|
{
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"usage: %s [-w timeout] [-t] [-T] -d domain_name ["
|
"usage: %s [-w timeout] [-W] [-t] [-T] -d domain_name ["
|
||||||
"-l local_prog|"
|
"-l local_prog|"
|
||||||
"-c request_id,src_domain_name,src_domain_id|"
|
"-c request_id,src_domain_name,src_domain_id|"
|
||||||
"-e] remote_cmdline\n"
|
"-e] remote_cmdline\n"
|
||||||
"-e means exit after sending cmd,\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"
|
"-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"
|
"-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",
|
"-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 msg_type;
|
||||||
int s;
|
int s;
|
||||||
int just_exec = 0;
|
int just_exec = 0;
|
||||||
|
int wait_connection_end = 0;
|
||||||
int connect_existing = 0;
|
int connect_existing = 0;
|
||||||
char *local_cmdline = NULL;
|
char *local_cmdline = NULL;
|
||||||
char *remote_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 src_domain_id = 0; /* if not -c given, the process is run in dom0 */
|
||||||
int connection_timeout = 5;
|
int connection_timeout = 5;
|
||||||
struct service_params svc_params;
|
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) {
|
switch (opt) {
|
||||||
case 'd':
|
case 'd':
|
||||||
domname = strdup(optarg);
|
domname = strdup(optarg);
|
||||||
@ -674,6 +676,9 @@ int main(int argc, char **argv)
|
|||||||
case 'w':
|
case 'w':
|
||||||
connection_timeout = atoi(optarg);
|
connection_timeout = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
|
case 'W':
|
||||||
|
wait_connection_end = 1;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
}
|
}
|
||||||
@ -749,6 +754,11 @@ int main(int argc, char **argv)
|
|||||||
strlen(remote_cmdline) + 1,
|
strlen(remote_cmdline) + 1,
|
||||||
&data_domain,
|
&data_domain,
|
||||||
&data_port);
|
&data_port);
|
||||||
|
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);
|
close(s);
|
||||||
setenv("QREXEC_REMOTE_DOMAIN", domname, 1);
|
setenv("QREXEC_REMOTE_DOMAIN", domname, 1);
|
||||||
prepare_local_fds(local_cmdline);
|
prepare_local_fds(local_cmdline);
|
||||||
@ -756,6 +766,13 @@ int main(int argc, char **argv)
|
|||||||
s = connect_unix_socket(src_domain_name);
|
s = connect_unix_socket(src_domain_name);
|
||||||
send_service_connect(s, request_id, data_domain, data_port);
|
send_service_connect(s, request_id, data_domain, data_port);
|
||||||
close(s);
|
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 {
|
} else {
|
||||||
data_vchan = libvchan_server_init(data_domain, data_port,
|
data_vchan = libvchan_server_init(data_domain, data_port,
|
||||||
VCHAN_BUFFER_SIZE, VCHAN_BUFFER_SIZE);
|
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 */
|
* either VCHAN_PORT_* or remote domain id for used port */
|
||||||
int used_vchan_ports[MAX_CLIENTS];
|
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 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
|
int qrexec_daemon_unix_socket_fd; // /var/run/qubes/qrexec.xid descriptor
|
||||||
const char *default_user = "user";
|
const char *default_user = "user";
|
||||||
@ -312,6 +318,7 @@ void init(int xid)
|
|||||||
clients[i].state = CLIENT_INVALID;
|
clients[i].state = CLIENT_INVALID;
|
||||||
policy_pending[i].pid = 0;
|
policy_pending[i].pid = 0;
|
||||||
used_vchan_ports[i] = VCHAN_PORT_UNUSED;
|
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 */
|
/* 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;
|
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()
|
static void handle_new_client()
|
||||||
{
|
{
|
||||||
int fd = do_accept(qrexec_daemon_unix_socket_fd);
|
int fd = do_accept(qrexec_daemon_unix_socket_fd);
|
||||||
@ -387,8 +386,25 @@ static void handle_new_client()
|
|||||||
|
|
||||||
static void terminate_client(int fd)
|
static void terminate_client(int fd)
|
||||||
{
|
{
|
||||||
|
int port;
|
||||||
clients[fd].state = CLIENT_INVALID;
|
clients[fd].state = CLIENT_INVALID;
|
||||||
close(fd);
|
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)
|
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);
|
terminate_client(fd);
|
||||||
return 0;
|
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_port = params.connect_port;
|
||||||
client_params.connect_domain = remote_domain_id;
|
client_params.connect_domain = remote_domain_id;
|
||||||
hdr->len = sizeof(client_params);
|
hdr->len = sizeof(client_params);
|
||||||
|
Loading…
Reference in New Issue
Block a user