diff --git a/doc/vm-tools/qrexec-client-vm.rst b/doc/vm-tools/qrexec-client-vm.rst index 4cefa9d..342f517 100644 --- a/doc/vm-tools/qrexec-client-vm.rst +++ b/doc/vm-tools/qrexec-client-vm.rst @@ -8,7 +8,7 @@ qrexec-client-vm - call Qubes RPC service SYNOPSIS ======== -| qrexec-client-vm *target_vmname* *service* [*local_program* [*local program arguments*]] +| qrexec-client-vm [--buffer-size=*BUFFER_SIZE*] *target_vmname* *service* [*local_program* [*local program arguments*]] DESCRIPTION =========== @@ -27,6 +27,12 @@ stdin/stdout is connected to those of ``qrexec-client-vm``. OPTIONS ======= +--buffer-size=*BUFFER_SIZE* + + Optional buffer size for vchan connection. This size is used as minimum + size for a buffer in each connection direction (read and write). + Default: 64KiB. + *target_vmname* Name of target VM to which service is requested. Qubes RPC policy may diff --git a/qrexec/qrexec-agent-data.c b/qrexec/qrexec-agent-data.c index 147670c..074ed45 100644 --- a/qrexec/qrexec-agent-data.c +++ b/qrexec/qrexec-agent-data.c @@ -490,10 +490,14 @@ int process_child_io(libvchan_t *data_vchan, * MSG_EXEC_CMDLINE - connect to vchan server, fork+exec process given by * cmdline parameter, pass the data to/from that process, then return local * process exit code + * + * buffer_size is about vchan buffer allocated (only for vchan server cases), + * use 0 to use built-in default (64k); needs to be power of 2 */ int handle_new_process_common(int type, int connect_domain, int connect_port, char *cmdline, int cmdline_len, /* MSG_JUST_EXEC and MSG_EXEC_CMDLINE */ - int stdin_fd, int stdout_fd, int stderr_fd /* MSG_SERVICE_CONNECT */) + int stdin_fd, int stdout_fd, int stderr_fd /* MSG_SERVICE_CONNECT */, + int buffer_size) { libvchan_t *data_vchan; int exit_code = 0; @@ -504,9 +508,12 @@ int handle_new_process_common(int type, int connect_domain, int connect_port, cmdline[cmdline_len-1] = 0; } + if (buffer_size == 0) + buffer_size = VCHAN_BUFFER_SIZE; + if (type == MSG_SERVICE_CONNECT) { data_vchan = libvchan_server_init(connect_domain, connect_port, - VCHAN_BUFFER_SIZE, VCHAN_BUFFER_SIZE); + buffer_size, buffer_size); if (data_vchan) libvchan_wait(data_vchan); } else { @@ -563,7 +570,7 @@ pid_t handle_new_process(int type, int connect_domain, int connect_port, /* child process */ exit_code = handle_new_process_common(type, connect_domain, connect_port, cmdline, cmdline_len, - -1, -1, -1); + -1, -1, -1, 0); exit(exit_code); /* suppress warning */ @@ -572,13 +579,13 @@ pid_t handle_new_process(int type, int connect_domain, int connect_port, /* Returns exit code of remote process */ int handle_data_client(int type, int connect_domain, int connect_port, - int stdin_fd, int stdout_fd, int stderr_fd) + int stdin_fd, int stdout_fd, int stderr_fd, int buffer_size) { int exit_code; assert(type == MSG_SERVICE_CONNECT); exit_code = handle_new_process_common(type, connect_domain, connect_port, - NULL, 0, stdin_fd, stdout_fd, stderr_fd); + NULL, 0, stdin_fd, stdout_fd, stderr_fd, buffer_size); return exit_code; } diff --git a/qrexec/qrexec-agent.h b/qrexec/qrexec-agent.h index 05b86c1..7cd11a2 100644 --- a/qrexec/qrexec-agent.h +++ b/qrexec/qrexec-agent.h @@ -37,7 +37,8 @@ pid_t handle_new_process(int type, char *cmdline, int cmdline_len); int handle_data_client(int type, int connect_domain, int connect_port, - int stdin_fd, int stdout_fd, int stderr_fd); + int stdin_fd, int stdout_fd, int stderr_fd, + int buffer_size); struct qrexec_cmd_info { diff --git a/qrexec/qrexec-client-vm.c b/qrexec/qrexec-client-vm.c index b5bd86d..0ae7795 100644 --- a/qrexec/qrexec-client-vm.c +++ b/qrexec/qrexec-client-vm.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "libqrexec-utils.h" #include "qrexec.h" #include "qrexec-agent.h" @@ -85,6 +86,19 @@ void convert_target_name_keyword(char *target) target[i] = '@'; } +struct option longopts[] = { + { "buffer-size", required_argument, 0, 'b' }, + { NULL, 0, 0, 0}, +}; + +_Noreturn void usage(const char *argv0) { + fprintf(stderr, + "usage: %s [--buffer-size=BUFFER_SIZE] target_vmname program_ident [local_program [local program arguments]]\n", + argv0); + fprintf(stderr, "BUFFER_SIZE is minimum vchan buffer size (default: 64k)\n"); + exit(2); +} + int main(int argc, char **argv) { int trigger_fd; @@ -95,24 +109,36 @@ int main(int argc, char **argv) char *abs_exec_path; pid_t child_pid = 0; int inpipe[2], outpipe[2]; + int buffer_size = 0; + int opt; - if (argc < 3) { - fprintf(stderr, - "usage: %s target_vmname program_ident [local_program [local program arguments]]\n", - argv[0]); - exit(1); + while (1) { + opt = getopt_long(argc, argv, "", longopts, NULL); + if (opt == -1) + break; + switch (opt) { + case 'b': + buffer_size = atoi(optarg); + break; + case '?': + usage(argv[0]); + } } - if (argc > 3) { + + if (argc - optind < 2) { + usage(argv[0]); + } + if (argc - optind > 2) { start_local_process = 1; } trigger_fd = connect_unix_socket(QREXEC_AGENT_TRIGGER_PATH); memset(¶ms, 0, sizeof(params)); - strncpy(params.service_name, argv[2], sizeof(params.service_name)); + strncpy(params.service_name, argv[optind + 1], sizeof(params.service_name)); - convert_target_name_keyword(argv[1]); - strncpy(params.target_domain, argv[1], + convert_target_name_keyword(argv[optind]); + strncpy(params.target_domain, argv[optind], sizeof(params.target_domain)); snprintf(params.request_id.ident, @@ -164,9 +190,9 @@ int main(int argc, char **argv) close(inpipe[0]); close(outpipe[1]); - abs_exec_path = strdup(argv[3]); - argv[3] = get_program_name(argv[3]); - execv(abs_exec_path, argv + 3); + abs_exec_path = strdup(argv[optind + 2]); + argv[optind + 2] = get_program_name(argv[optind + 2]); + execv(abs_exec_path, argv + optind + 2); perror("execv"); exit(-1); } @@ -175,11 +201,11 @@ int main(int argc, char **argv) ret = handle_data_client(MSG_SERVICE_CONNECT, exec_params.connect_domain, exec_params.connect_port, - inpipe[1], outpipe[0], -1); + inpipe[1], outpipe[0], -1, buffer_size); } else { ret = handle_data_client(MSG_SERVICE_CONNECT, exec_params.connect_domain, exec_params.connect_port, - 1, 0, -1); + 1, 0, -1, buffer_size); } close(trigger_fd);