qfile-daemon-dvm 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. #!/usr/bin/python2
  2. # coding=utf-8
  3. #
  4. # The Qubes OS Project, http://www.qubes-os.org
  5. #
  6. # Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
  7. # Copyright (C) 2013-2015 Marek Marczykowski-Górecki
  8. # <marmarek@invisiblethingslab.com>
  9. #
  10. # This program is free software; you can redistribute it and/or
  11. # modify it under the terms of the GNU General Public License
  12. # as published by the Free Software Foundation; either version 2
  13. # of the License, or (at your option) any later version.
  14. #
  15. # This program is distributed in the hope that it will be useful,
  16. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. # GNU General Public License for more details.
  19. #
  20. # You should have received a copy of the GNU General Public License
  21. # along with this program; if not, write to the Free Software
  22. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  23. #
  24. #
  25. import os
  26. import subprocess
  27. import sys
  28. import shutil
  29. import time
  30. from qubes.qubes import QubesVmCollection, QubesException
  31. from qubes.qubes import QubesDispVmLabels
  32. from qubes.notify import tray_notify, tray_notify_error, tray_notify_init
  33. current_savefile = '/var/run/qubes/current-savefile'
  34. current_savefile_vmdir = '/var/lib/qubes/dvmdata/vmdir'
  35. class QfileDaemonDvm:
  36. def __init__(self, name):
  37. self.name = name
  38. @staticmethod
  39. def get_disp_templ():
  40. vmdir = os.readlink(current_savefile_vmdir)
  41. return vmdir.split('/')[-1]
  42. def do_get_dvm(self):
  43. tray_notify("Starting new DispVM...", "red")
  44. qvm_collection = QubesVmCollection()
  45. qvm_collection.lock_db_for_writing()
  46. tar_process = subprocess.Popen(
  47. ['bsdtar', '-C', current_savefile_vmdir,
  48. '-xSUf', os.path.join(current_savefile_vmdir, 'saved-cows.tar')])
  49. qvm_collection.load()
  50. print >>sys.stderr, "time=%s, collection loaded" % (str(time.time()))
  51. vm = qvm_collection.get_vm_by_name(self.name)
  52. if vm is None:
  53. sys.stderr.write('Domain ' + self.name + ' does not exist ?')
  54. qvm_collection.unlock_db()
  55. return None
  56. label = vm.label
  57. if len(sys.argv) > 4 and len(sys.argv[4]) > 0:
  58. assert sys.argv[4] in QubesDispVmLabels.keys(), "Invalid label"
  59. label = QubesDispVmLabels[sys.argv[4]]
  60. disp_templ = self.get_disp_templ()
  61. vm_disptempl = qvm_collection.get_vm_by_name(disp_templ)
  62. if vm_disptempl is None:
  63. sys.stderr.write('Domain ' + disp_templ + ' does not exist ?')
  64. qvm_collection.unlock_db()
  65. return None
  66. dispvm = qvm_collection.add_new_vm('QubesDisposableVm',
  67. disp_template=vm_disptempl,
  68. label=label)
  69. print >>sys.stderr, "time=%s, VM created" % (str(time.time()))
  70. # By default inherit firewall rules from calling VM
  71. disp_firewall_conf = '/var/run/qubes/%s-firewall.xml' % dispvm.name
  72. dispvm.firewall_conf = disp_firewall_conf
  73. if os.path.exists(vm.firewall_conf):
  74. shutil.copy(vm.firewall_conf, disp_firewall_conf)
  75. elif vm.qid == 0 and os.path.exists(vm_disptempl.firewall_conf):
  76. # for DispVM called from dom0, copy use rules from DispVM template
  77. shutil.copy(vm_disptempl.firewall_conf, disp_firewall_conf)
  78. if len(sys.argv) > 5 and len(sys.argv[5]) > 0:
  79. assert os.path.exists(sys.argv[5]), "Invalid firewall.conf location"
  80. dispvm.firewall_conf = sys.argv[5]
  81. if vm.qid != 0:
  82. dispvm.uses_default_netvm = False
  83. # netvm can be changed before restore,
  84. # but cannot be enabled/disabled
  85. if (dispvm.netvm is None) == (vm.dispvm_netvm is None):
  86. dispvm.netvm = vm.dispvm_netvm
  87. # Wait for tar to finish
  88. if tar_process.wait() != 0:
  89. sys.stderr.write('Failed to unpack saved-cows.tar')
  90. qvm_collection.unlock_db()
  91. return None
  92. print >>sys.stderr, "time=%s, VM starting" % (str(time.time()))
  93. try:
  94. dispvm.start()
  95. except (MemoryError, QubesException) as e:
  96. tray_notify_error(str(e))
  97. raise
  98. if vm.qid != 0:
  99. # if need to enable/disable netvm, do it while DispVM is alive
  100. if (dispvm.netvm is None) != (vm.dispvm_netvm is None):
  101. dispvm.netvm = vm.dispvm_netvm
  102. print >>sys.stderr, "time=%s, VM started" % (str(time.time()))
  103. qvm_collection.save()
  104. qvm_collection.unlock_db()
  105. # Reload firewall rules
  106. print >>sys.stderr, "time=%s, reloading firewall" % (str(time.time()))
  107. for vm in qvm_collection.values():
  108. if vm.is_proxyvm() and vm.is_running():
  109. vm.write_iptables_qubesdb_entry()
  110. return dispvm
  111. @staticmethod
  112. def dvm_setup_ok():
  113. dvmdata_dir = '/var/lib/qubes/dvmdata/'
  114. if not os.path.isfile(current_savefile):
  115. return False
  116. if not os.path.isfile(dvmdata_dir+'default-savefile') or \
  117. not os.path.isfile(dvmdata_dir+'savefile-root'):
  118. return False
  119. dvm_mtime = os.stat(current_savefile).st_mtime
  120. root_mtime = os.stat(dvmdata_dir+'savefile-root').st_mtime
  121. if dvm_mtime < root_mtime:
  122. template_name = os.path.basename(
  123. os.path.dirname(os.readlink(dvmdata_dir+'savefile-root')))
  124. if subprocess.call(["xl", "domid", template_name],
  125. stdout=open(os.devnull, "w")) == 0:
  126. tray_notify("For optimum performance, you should not "
  127. "start DispVM when its template is running.", "red")
  128. return False
  129. return True
  130. def get_dvm(self):
  131. if not self.dvm_setup_ok():
  132. if os.system("/usr/lib/qubes/"
  133. "qubes-update-dispvm-savefile-with-progress.sh"
  134. " >/dev/null </dev/null") != 0:
  135. tray_notify_error("DVM savefile creation failed")
  136. return None
  137. return self.do_get_dvm()
  138. @staticmethod
  139. def remove_disposable_from_qdb(name):
  140. qvm_collection = QubesVmCollection()
  141. qvm_collection.lock_db_for_writing()
  142. qvm_collection.load()
  143. vm = qvm_collection.get_vm_by_name(name)
  144. if vm is None:
  145. qvm_collection.unlock_db()
  146. return False
  147. qvm_collection.pop(vm.qid)
  148. qvm_collection.save()
  149. qvm_collection.unlock_db()
  150. def main():
  151. exec_index = sys.argv[1]
  152. src_vmname = sys.argv[2]
  153. user = sys.argv[3]
  154. # accessed directly by get_dvm()
  155. # sys.argv[4] - override label
  156. # sys.argv[5] - override firewall
  157. print >>sys.stderr, "time=%s, qfile-daemon-dvm init" % (str(time.time()))
  158. tray_notify_init()
  159. print >>sys.stderr, "time=%s, creating DispVM" % (str(time.time()))
  160. qfile = QfileDaemonDvm(src_vmname)
  161. dispvm = qfile.get_dvm()
  162. if dispvm is not None:
  163. print >>sys.stderr, "time=%s, starting VM process" % (str(time.time()))
  164. subprocess.call(['/usr/lib/qubes/qrexec-client', '-d', dispvm.name,
  165. user+':exec /usr/lib/qubes/qubes-rpc-multiplexer ' +
  166. exec_index + " " + src_vmname])
  167. try:
  168. dispvm.force_shutdown()
  169. except QubesException:
  170. # VM already destroyed
  171. pass
  172. qfile.remove_disposable_from_qdb(dispvm.name)
  173. main()