Browse Source

Add 'wait' argument to vm.run_service()

It is supported only from dom0, but it's still useful to have, to save
on simultaneous vchan connections (only waiting for MSG_DATA_EXIT_CODE).
This is especially important for Windows VMs, as qrexec-agent there have
pretty low limit on simultaneous connections (about 20).

Make qvm-run use it.
Marek Marczykowski-Górecki 7 years ago
parent
commit
938fc9348f
2 changed files with 27 additions and 3 deletions
  1. 21 3
      qubesadmin/app.py
  2. 6 0
      qubesadmin/tools/qvm_run.py

+ 21 - 3
qubesadmin/app.py

@@ -310,7 +310,7 @@ class QubesBase(qubesadmin.base.PropertyHolder):
         return self.domains[new_name]
 
     def run_service(self, dest, service, filter_esc=False, user=None,
-            localcmd=None, **kwargs):
+            localcmd=None, wait=True, **kwargs):
         '''Run qrexec service in a given destination
 
         *kwargs* are passed verbatim to :py:meth:`subprocess.Popen`.
@@ -358,7 +358,7 @@ class QubesLocal(QubesBase):
         return self._parse_qubesd_response(return_data)
 
     def run_service(self, dest, service, filter_esc=False, user=None,
-            localcmd=None, **kwargs):
+            localcmd=None, wait=True, **kwargs):
         '''Run qrexec service in a given destination
 
         :param str dest: Destination - may be a VM name or empty
@@ -368,11 +368,14 @@ class QubesLocal(QubesBase):
             emulator
         :param str user: username to run service as
         :param str localcmd: Command to connect stdin/stdout to
+        :param bool wait: wait for remote process to finish
         :rtype: subprocess.Popen
         '''
 
         if not dest:
             raise ValueError('Empty destination name allowed only from a VM')
+        if not wait and localcmd:
+            raise ValueError('wait=False incompatible with localcmd')
         try:
             self.qubesd_call(dest, 'admin.vm.Start')
         except qubesadmin.exc.QubesVMNotHaltedError:
@@ -384,6 +387,8 @@ class QubesLocal(QubesBase):
             qrexec_opts.extend(['-l', localcmd])
         if user is None:
             user = 'DEFAULT'
+        if not wait:
+            qrexec_opts.extend(['-e'])
         kwargs.setdefault('stdin', subprocess.PIPE)
         kwargs.setdefault('stdout', subprocess.PIPE)
         kwargs.setdefault('stderr', subprocess.PIPE)
@@ -418,7 +423,7 @@ class QubesRemote(QubesBase):
         return self._parse_qubesd_response(stdout)
 
     def run_service(self, dest, service, filter_esc=False, user=None,
-            localcmd=None, **kwargs):
+            localcmd=None, wait=True, **kwargs):
         '''Run qrexec service in a given destination
 
         :param str dest: Destination - may be a VM name or empty
@@ -428,6 +433,7 @@ class QubesRemote(QubesBase):
             emulator
         :param str user: username to run service as
         :param str localcmd: Command to connect stdin/stdout to
+        :param bool wait: wait for process to finish
         :rtype: subprocess.Popen
         '''
         if filter_esc:
@@ -436,6 +442,18 @@ class QubesRemote(QubesBase):
         if user:
             raise ValueError(
                 'non-default user not possible for calls from VM')
+        if not wait and localcmd:
+            raise ValueError('wait=False incompatible with localcmd')
+        if not wait:
+            # qrexec-client-vm can only request service calls, which are
+            # started using MSG_EXEC_CMDLINE qrexec protocol message; this
+            # message means "start the process, pipe its stdin/out/err,
+            # and when it terminates, send exit code back".
+            # According to the protocol qrexec-client-vm needs to wait for
+            # MSG_DATA_EXIT_CODE, so implementing wait=False would require
+            # some protocol change (or protocol violation).
+            raise NotImplementedError(
+                'wait=False not implemented for calls from VM')
         kwargs.setdefault('stdin', subprocess.PIPE)
         kwargs.setdefault('stdout', subprocess.PIPE)
         kwargs.setdefault('stderr', subprocess.PIPE)

+ 6 - 0
qubesadmin/tools/qvm_run.py

@@ -135,6 +135,12 @@ def main(args=None, app=None):
         run_kwargs['stdout'] = None
         run_kwargs['stderr'] = None
 
+    if isinstance(args.app, qubesadmin.app.QubesLocal) and \
+            not args.passio and not args.localcmd:
+        # wait=False works only in dom0; but it's still useful, to save on
+        # simultaneous vchan connections
+        run_kwargs['wait'] = False
+
     verbose = args.verbose - args.quiet
     if args.passio:
         verbose -= 1