internal.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  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 library is free software; you can redistribute it and/or
  9. # modify it under the terms of the GNU Lesser General Public
  10. # License as published by the Free Software Foundation; either
  11. # version 2.1 of the License, or (at your option) any later version.
  12. #
  13. # This library 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 GNU
  16. # Lesser General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU Lesser General Public
  19. # License along with this library; if not, see <https://www.gnu.org/licenses/>.
  20. ''' Internal interface for dom0 components to communicate with qubesd. '''
  21. import asyncio
  22. import json
  23. import subprocess
  24. import qubes.api
  25. import qubes.api.admin
  26. import qubes.vm.adminvm
  27. import qubes.vm.dispvm
  28. class QubesInternalAPI(qubes.api.AbstractQubesAPI):
  29. ''' Communication interface for dom0 components,
  30. by design the input here is trusted.'''
  31. SOCKNAME = '/var/run/qubesd.internal.sock'
  32. @qubes.api.method('internal.GetSystemInfo', no_payload=True)
  33. @asyncio.coroutine
  34. def getsysteminfo(self):
  35. assert self.dest.name == 'dom0'
  36. assert not self.arg
  37. system_info = {'domains': {
  38. domain.name: {
  39. 'tags': list(domain.tags),
  40. 'type': domain.__class__.__name__,
  41. 'template_for_dispvms':
  42. getattr(domain, 'template_for_dispvms', False),
  43. 'default_dispvm': (str(domain.default_dispvm) if
  44. getattr(domain, 'default_dispvm', None) else None),
  45. 'icon': str(domain.label.icon),
  46. } for domain in self.app.domains
  47. }}
  48. return json.dumps(system_info)
  49. @qubes.api.method('internal.vm.volume.ImportEnd')
  50. @asyncio.coroutine
  51. def vm_volume_import_end(self, untrusted_payload):
  52. '''
  53. This is second half of admin.vm.volume.Import handling. It is called
  54. when actual import is finished. Response from this method is sent do
  55. the client (as a response for admin.vm.volume.Import call).
  56. '''
  57. assert self.arg in self.dest.volumes.keys()
  58. success = untrusted_payload == b'ok'
  59. try:
  60. self.dest.storage.import_data_end(self.arg, success=success)
  61. except:
  62. self.dest.fire_event('domain-volume-import-end', volume=self.arg,
  63. succeess=False)
  64. raise
  65. self.dest.fire_event('domain-volume-import-end', volume=self.arg,
  66. succeess=success)
  67. if not success:
  68. raise qubes.exc.QubesException('Data import failed')
  69. @qubes.api.method('internal.SuspendPre', no_payload=True)
  70. @asyncio.coroutine
  71. def suspend_pre(self):
  72. '''
  73. Method called before host system goes to sleep.
  74. :return:
  75. '''
  76. # first notify all VMs
  77. processes = []
  78. for vm in self.app.domains:
  79. if isinstance(vm, qubes.vm.adminvm.AdminVM):
  80. continue
  81. if vm.is_running():
  82. proc = yield from vm.run_service(
  83. 'qubes.SuspendPreAll', user='root',
  84. stdin=subprocess.DEVNULL,
  85. stdout=subprocess.DEVNULL,
  86. stderr=subprocess.DEVNULL)
  87. processes.append(proc)
  88. # FIXME: some timeout?
  89. if processes:
  90. yield from asyncio.wait([p.wait() for p in processes])
  91. coros = []
  92. # then suspend/pause VMs
  93. for vm in self.app.domains:
  94. if isinstance(vm, qubes.vm.adminvm.AdminVM):
  95. continue
  96. if vm.is_running():
  97. coros.append(vm.suspend())
  98. if coros:
  99. yield from asyncio.wait(coros)
  100. @qubes.api.method('internal.SuspendPost', no_payload=True)
  101. @asyncio.coroutine
  102. def suspend_post(self):
  103. '''
  104. Method called after host system wake up from sleep.
  105. :return:
  106. '''
  107. coros = []
  108. # first resume/unpause VMs
  109. for vm in self.app.domains:
  110. if isinstance(vm, qubes.vm.adminvm.AdminVM):
  111. continue
  112. if vm.get_power_state() in ["Paused", "Suspended"]:
  113. coros.append(vm.resume())
  114. if coros:
  115. yield from asyncio.wait(coros)
  116. # then notify all VMs
  117. processes = []
  118. for vm in self.app.domains:
  119. if isinstance(vm, qubes.vm.adminvm.AdminVM):
  120. continue
  121. if vm.is_running():
  122. proc = yield from vm.run_service(
  123. 'qubes.SuspendPostAll', user='root',
  124. stdin=subprocess.DEVNULL,
  125. stdout=subprocess.DEVNULL,
  126. stderr=subprocess.DEVNULL)
  127. processes.append(proc)
  128. # FIXME: some timeout?
  129. if processes:
  130. yield from asyncio.wait([p.wait() for p in processes])