Browse Source

qrexec: add qrexec-client-vm --buffer-size option

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).
Marek Marczykowski-Górecki 6 years ago
parent
commit
4a09023451
4 changed files with 61 additions and 21 deletions
  1. 7 1
      doc/vm-tools/qrexec-client-vm.rst
  2. 12 5
      qrexec/qrexec-agent-data.c
  3. 2 1
      qrexec/qrexec-agent.h
  4. 40 14
      qrexec/qrexec-client-vm.c

+ 7 - 1
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

+ 12 - 5
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;
 }

+ 2 - 1
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 {

+ 40 - 14
qrexec/qrexec-client-vm.c

@@ -27,6 +27,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <string.h>
+#include <getopt.h>
 #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;
+
+    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) {
-        fprintf(stderr,
-                "usage: %s target_vmname program_ident [local_program [local program arguments]]\n",
-                argv[0]);
-        exit(1);
+    if (argc - optind < 2) {
+        usage(argv[0]);
     }
-    if (argc > 3) {
+    if (argc - optind > 2) {
         start_local_process = 1;
     }
 
     trigger_fd = connect_unix_socket(QREXEC_AGENT_TRIGGER_PATH);
 
     memset(&params, 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);