app.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. # -*- encoding: utf8 -*-
  2. #
  3. # The Qubes OS Project, http://www.qubes-os.org
  4. #
  5. # Copyright (C) 2017 Marek Marczykowski-Górecki
  6. # <marmarek@invisiblethingslab.com>
  7. #
  8. # This program is free software; you can redistribute it and/or modify
  9. # it under the terms of the GNU Lesser General Public License as published by
  10. # the Free Software Foundation; either version 2.1 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU Lesser General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU Lesser General Public License along
  19. # with this program; if not, see <http://www.gnu.org/licenses/>.
  20. import socket
  21. import subprocess
  22. import qubesmgmt
  23. import qubesmgmt.vm
  24. import qubesmgmt.exc
  25. QUBESD_SOCK = '/var/run/qubesd.sock'
  26. BUF_SIZE = 4096
  27. class VMCollection(object):
  28. def __init__(self, app):
  29. self.app = app
  30. self._vm_list = None
  31. def refresh_cache(self, force=False):
  32. if not force and self._vm_list is not None:
  33. return
  34. vm_list_data = self.app._do_qubesd_call(
  35. 'dom0',
  36. 'mgmt.vm.List'
  37. )
  38. new_vm_list = {}
  39. # FIXME: this will probably change
  40. for vm_data in vm_list_data.splitlines():
  41. vm_name, props = vm_data.split(b' ', 1)
  42. props = props.split(b' ')
  43. new_vm_list[vm_name] = dict(
  44. [vm_prop.split(b'=', 1) for vm_prop in props])
  45. self._vm_list = new_vm_list
  46. def __getitem__(self, item):
  47. if item not in self:
  48. raise KeyError(item)
  49. return qubesmgmt.vm.QubesVM(
  50. self.app, item, self._vm_list[item]['class'])
  51. def __contains__(self, item):
  52. self.refresh_cache()
  53. return item in self._vm_list
  54. def __iter__(self):
  55. self.refresh_cache()
  56. for vm in self._vm_list:
  57. yield self[vm]
  58. def keys(self):
  59. self.refresh_cache()
  60. return self._vm_list.keys()
  61. class QubesBase(qubesmgmt.PropertyHolder):
  62. '''Main Qubes application'''
  63. #: domains (VMs) collection
  64. domains = None
  65. def __init__(self):
  66. super(QubesBase, self).__init__(self, 'mgmt.global.', 'dom0')
  67. self.domains = VMCollection(self)
  68. class QubesLocal(QubesBase):
  69. def _do_qubesd_call(self, dest, method, arg=None, payload=None):
  70. try:
  71. client_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
  72. client_socket.connect(QUBESD_SOCK)
  73. except IOError:
  74. # TODO:
  75. raise
  76. # src, method, dest, arg
  77. for call_arg in ('dom0', method, dest, arg):
  78. client_socket.sendall(call_arg.encode('ascii'))
  79. client_socket.sendall(b'\0')
  80. if payload is not None:
  81. client_socket.sendall(payload)
  82. return_data = b''.join(iter(client_socket.recv(BUF_SIZE), b''))
  83. return self._parse_qubesd_response(return_data)
  84. class QubesRemote(QubesBase):
  85. def _do_qubesd_call(self, dest, method, arg=None, payload=None):
  86. service_name = method
  87. if arg is not None:
  88. service_name += '+' + arg
  89. p = subprocess.Popen(['qrexec-client-vm', dest, service_name],
  90. stdin=subprocess.PIPE, stdout=subprocess.PIPE,
  91. stderr=subprocess.PIPE)
  92. (stdout, stderr) = p.communicate(payload)
  93. if p.returncode != 0:
  94. # TODO: use dedicated exception
  95. raise qubesmgmt.exc.QubesException('Service call error: %s',
  96. stderr.decode())
  97. return self._parse_qubesd_response(stdout)