qubes.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796
  1. #!/usr/bin/python2
  2. # -*- coding: utf-8 -*-
  3. #
  4. # The Qubes OS Project, http://www.qubes-os.org
  5. #
  6. # Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
  7. #
  8. # This program is free software; you can redistribute it and/or
  9. # modify it under the terms of the GNU General Public License
  10. # as published by the Free Software Foundation; either version 2
  11. # of the License, or (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 General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with this program; if not, write to the Free Software
  20. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  21. #
  22. #
  23. from __future__ import absolute_import
  24. import atexit
  25. import grp
  26. import logging
  27. import os
  28. import os.path
  29. import sys
  30. import tempfile
  31. import time
  32. import warnings
  33. import xml.parsers.expat
  34. import lxml.etree
  35. if os.name == 'posix':
  36. import fcntl
  37. elif os.name == 'nt':
  38. import win32con
  39. import win32file
  40. import pywintypes
  41. else:
  42. raise RuntimeError, "Qubes works only on POSIX or WinNT systems"
  43. # Do not use XenAPI or create/read any VM files
  44. # This is for testing only!
  45. dry_run = False
  46. #dry_run = True
  47. if not dry_run:
  48. import libvirt
  49. try:
  50. import xen.lowlevel.xs
  51. except ImportError:
  52. pass
  53. qubes_base_dir = "/var/lib/qubes"
  54. system_path = {
  55. 'qubes_guid_path': '/usr/bin/qubes-guid',
  56. 'qrexec_daemon_path': '/usr/lib/qubes/qrexec-daemon',
  57. 'qrexec_client_path': '/usr/lib/qubes/qrexec-client',
  58. 'qubesdb_daemon_path': '/usr/sbin/qubesdb-daemon',
  59. 'qubes_base_dir': qubes_base_dir,
  60. # Relative to qubes_base_dir
  61. 'qubes_appvms_dir': 'appvms',
  62. 'qubes_templates_dir': 'vm-templates',
  63. 'qubes_servicevms_dir': 'servicevms',
  64. 'qubes_store_filename': 'qubes.xml',
  65. 'qubes_kernels_base_dir': 'vm-kernels',
  66. # qubes_icon_dir is obsolete
  67. # use QIcon.fromTheme() where applicable
  68. 'qubes_icon_dir': '/usr/share/icons/hicolor/128x128/devices',
  69. 'qrexec_policy_dir': '/etc/qubes-rpc/policy',
  70. 'config_template_pv': '/usr/share/qubes/vm-template.xml',
  71. 'qubes_pciback_cmd': '/usr/lib/qubes/unbind-pci-device.sh',
  72. 'prepare_volatile_img_cmd': '/usr/lib/qubes/prepare-volatile-img.sh',
  73. }
  74. vm_files = {
  75. 'root_img': 'root.img',
  76. 'rootcow_img': 'root-cow.img',
  77. 'volatile_img': 'volatile.img',
  78. 'private_img': 'private.img',
  79. 'kernels_subdir': 'kernels',
  80. 'firewall_conf': 'firewall.xml',
  81. 'whitelisted_appmenus': 'whitelisted-appmenus.list',
  82. 'updates_stat_file': 'updates.stat',
  83. }
  84. defaults = {
  85. 'libvirt_uri': 'xen:///',
  86. 'memory': 400,
  87. 'kernelopts': "nopat",
  88. 'kernelopts_pcidevs': "nopat iommu=soft swiotlb=4096",
  89. 'dom0_update_check_interval': 6*3600,
  90. 'private_img_size': 2*1024*1024*1024,
  91. 'root_img_size': 10*1024*1024*1024,
  92. 'storage_class': None,
  93. # how long (in sec) to wait for VMs to shutdown,
  94. # before killing them (when used qvm-run with --wait option),
  95. 'shutdown_counter_max': 60,
  96. 'vm_default_netmask': "255.255.255.0",
  97. # Set later
  98. 'appvm_label': None,
  99. 'template_label': None,
  100. 'servicevm_label': None,
  101. }
  102. qubes_max_qid = 254
  103. qubes_max_netid = 254
  104. ##########################################
  105. def register_qubes_vm_class(vm_class):
  106. QubesVmClasses[vm_class.__name__] = vm_class
  107. # register class as local for this module - to make it easy to import from
  108. # other modules
  109. setattr(sys.modules[__name__], vm_class.__name__, vm_class)
  110. class QubesVmCollection(dict):
  111. """
  112. A collection of Qubes VMs indexed by Qubes id (qid)
  113. """
  114. def __init__(self, store_filename=None):
  115. super(QubesVmCollection, self).__init__()
  116. self.default_netvm_qid = None
  117. self.default_fw_netvm_qid = None
  118. self.default_template_qid = None
  119. self.default_kernel = None
  120. self.updatevm_qid = None
  121. self.qubes_store_filename = store_filename
  122. if not store_filename:
  123. self.qubes_store_filename = system_path["qubes_store_filename"]
  124. self.clockvm_qid = None
  125. self.qubes_store_file = None
  126. self.log = logging.getLogger('qubes.qvmc.{:x}'.format(id(self)))
  127. self.log.debug('instantiated store_filename={!r}'.format(
  128. self.qubes_store_filename))
  129. def __repr__(self):
  130. return '<{} {!r}>'.format(self.__class__.__name__, list(sorted(self.keys())))
  131. def clear(self):
  132. self.log.debug('clear()')
  133. super(QubesVmCollection, self).clear()
  134. def values(self):
  135. for qid in self.keys():
  136. yield self[qid]
  137. def items(self):
  138. for qid in self.keys():
  139. yield (qid, self[qid])
  140. def __iter__(self):
  141. for qid in sorted(super(QubesVmCollection, self).keys()):
  142. yield qid
  143. keys = __iter__
  144. def __setitem__(self, key, value):
  145. self.log.debug('[{!r}] = {!r}'.format(key, value))
  146. if key not in self:
  147. return super(QubesVmCollection, self).__setitem__(key, value)
  148. else:
  149. assert False, "Attempt to add VM with qid that already exists in the collection!"
  150. def add_new_vm(self, vm_type, **kwargs):
  151. self.log.debug('add_new_vm(vm_type={}, **kwargs={!r})'.format(
  152. vm_type, kwargs))
  153. if vm_type not in QubesVmClasses.keys():
  154. raise ValueError("Unknown VM type: %s" % vm_type)
  155. qid = self.get_new_unused_qid()
  156. vm_cls = QubesVmClasses[vm_type]
  157. if 'template' in kwargs:
  158. if not vm_cls.is_template_compatible(kwargs['template']):
  159. raise QubesException("Template not compatible with selected "
  160. "VM type")
  161. vm = vm_cls(qid=qid, collection=self, **kwargs)
  162. if not self.verify_new_vm(vm):
  163. raise QubesException("Wrong VM description!")
  164. self[vm.qid] = vm
  165. # make first created NetVM the default one
  166. if self.default_fw_netvm_qid is None and vm.is_netvm():
  167. self.set_default_fw_netvm(vm)
  168. if self.default_netvm_qid is None and vm.is_proxyvm():
  169. self.set_default_netvm(vm)
  170. # make first created TemplateVM the default one
  171. if self.default_template_qid is None and vm.is_template():
  172. self.set_default_template(vm)
  173. # make first created ProxyVM the UpdateVM
  174. if self.updatevm_qid is None and vm.is_proxyvm():
  175. self.set_updatevm_vm(vm)
  176. # by default ClockVM is the first NetVM
  177. if self.clockvm_qid is None and vm.is_netvm():
  178. self.set_clockvm_vm(vm)
  179. return vm
  180. def add_new_appvm(self, name, template,
  181. dir_path = None, conf_file = None,
  182. private_img = None,
  183. label = None):
  184. warnings.warn("Call to deprecated function, use add_new_vm instead",
  185. DeprecationWarning, stacklevel=2)
  186. return self.add_new_vm("QubesAppVm", name=name, template=template,
  187. dir_path=dir_path, conf_file=conf_file,
  188. private_img=private_img,
  189. netvm = self.get_default_netvm(),
  190. kernel = self.get_default_kernel(),
  191. uses_default_kernel = True,
  192. label=label)
  193. def add_new_hvm(self, name, label = None):
  194. warnings.warn("Call to deprecated function, use add_new_vm instead",
  195. DeprecationWarning, stacklevel=2)
  196. return self.add_new_vm("QubesHVm", name=name, label=label)
  197. def add_new_disposablevm(self, name, template, dispid,
  198. label = None, netvm = None):
  199. warnings.warn("Call to deprecated function, use add_new_vm instead",
  200. DeprecationWarning, stacklevel=2)
  201. return self.add_new_vm("QubesDisposableVm", name=name, template=template,
  202. netvm = netvm,
  203. label=label, dispid=dispid)
  204. def add_new_templatevm(self, name,
  205. dir_path = None, conf_file = None,
  206. root_img = None, private_img = None,
  207. installed_by_rpm = True):
  208. warnings.warn("Call to deprecated function, use add_new_vm instead",
  209. DeprecationWarning, stacklevel=2)
  210. return self.add_new_vm("QubesTemplateVm", name=name,
  211. dir_path=dir_path, conf_file=conf_file,
  212. root_img=root_img, private_img=private_img,
  213. installed_by_rpm=installed_by_rpm,
  214. netvm = self.get_default_netvm(),
  215. kernel = self.get_default_kernel(),
  216. uses_default_kernel = True)
  217. def add_new_netvm(self, name, template,
  218. dir_path = None, conf_file = None,
  219. private_img = None, installed_by_rpm = False,
  220. label = None):
  221. warnings.warn("Call to deprecated function, use add_new_vm instead",
  222. DeprecationWarning, stacklevel=2)
  223. return self.add_new_vm("QubesNetVm", name=name, template=template,
  224. label=label,
  225. private_img=private_img, installed_by_rpm=installed_by_rpm,
  226. uses_default_kernel = True,
  227. dir_path=dir_path, conf_file=conf_file)
  228. def add_new_proxyvm(self, name, template,
  229. dir_path = None, conf_file = None,
  230. private_img = None, installed_by_rpm = False,
  231. label = None):
  232. warnings.warn("Call to deprecated function, use add_new_vm instead",
  233. DeprecationWarning, stacklevel=2)
  234. return self.add_new_vm("QubesProxyVm", name=name, template=template,
  235. label=label,
  236. private_img=private_img, installed_by_rpm=installed_by_rpm,
  237. dir_path=dir_path, conf_file=conf_file,
  238. uses_default_kernel = True,
  239. netvm = self.get_default_fw_netvm())
  240. def set_default_template(self, vm):
  241. self.log.debug('set_default_template({!r})'.format(vm))
  242. if vm is None:
  243. self.default_template_qid = None
  244. else:
  245. assert vm.is_template(), "VM {0} is not a TemplateVM!".format(vm.name)
  246. self.default_template_qid = vm.qid
  247. def get_default_template(self):
  248. if self.default_template_qid is None:
  249. return None
  250. else:
  251. return self[self.default_template_qid]
  252. def set_default_netvm(self, vm):
  253. self.log.debug('set_default_netvm({!r})'.format(vm))
  254. if vm is None:
  255. self.default_netvm_qid = None
  256. else:
  257. assert vm.is_netvm(), "VM {0} does not provide network!".format(vm.name)
  258. self.default_netvm_qid = vm.qid
  259. def get_default_netvm(self):
  260. if self.default_netvm_qid is None:
  261. return None
  262. else:
  263. return self[self.default_netvm_qid]
  264. def set_default_kernel(self, kernel):
  265. self.log.debug('set_default_kernel({!r})'.format(kernel))
  266. assert os.path.exists(
  267. os.path.join(system_path["qubes_kernels_base_dir"], kernel)), \
  268. "Kerel {0} not installed!".format(kernel)
  269. self.default_kernel = kernel
  270. def get_default_kernel(self):
  271. return self.default_kernel
  272. def set_default_fw_netvm(self, vm):
  273. self.log.debug('set_default_fw_netvm({!r})'.format(vm))
  274. if vm is None:
  275. self.default_fw_netvm_qid = None
  276. else:
  277. assert vm.is_netvm(), "VM {0} does not provide network!".format(vm.name)
  278. self.default_fw_netvm_qid = vm.qid
  279. def get_default_fw_netvm(self):
  280. if self.default_fw_netvm_qid is None:
  281. return None
  282. else:
  283. return self[self.default_fw_netvm_qid]
  284. def set_updatevm_vm(self, vm):
  285. self.log.debug('set_updatevm_vm({!r})'.format(vm))
  286. if vm is None:
  287. self.updatevm_qid = None
  288. else:
  289. self.updatevm_qid = vm.qid
  290. def get_updatevm_vm(self):
  291. if self.updatevm_qid is None:
  292. return None
  293. else:
  294. return self[self.updatevm_qid]
  295. def set_clockvm_vm(self, vm):
  296. self.log.debug('set_clockvm({!r})'.format(vm))
  297. if vm is None:
  298. self.clockvm_qid = None
  299. else:
  300. self.clockvm_qid = vm.qid
  301. def get_clockvm_vm(self):
  302. if self.clockvm_qid is None:
  303. return None
  304. else:
  305. return self[self.clockvm_qid]
  306. def get_vm_by_name(self, name):
  307. for vm in self.values():
  308. if (vm.name == name):
  309. return vm
  310. return None
  311. def get_qid_by_name(self, name):
  312. vm = self.get_vm_by_name(name)
  313. return vm.qid if vm is not None else None
  314. def get_vms_based_on(self, template_qid):
  315. vms = set([vm for vm in self.values()
  316. if (vm.template and vm.template.qid == template_qid)])
  317. return vms
  318. def get_vms_connected_to(self, netvm_qid):
  319. new_vms = [ netvm_qid ]
  320. dependend_vms_qid = []
  321. # Dependency resolving only makes sense on NetVM (or derivative)
  322. if not self[netvm_qid].is_netvm():
  323. return set([])
  324. while len(new_vms) > 0:
  325. cur_vm = new_vms.pop()
  326. for vm in self[cur_vm].connected_vms.values():
  327. if vm.qid not in dependend_vms_qid:
  328. dependend_vms_qid.append(vm.qid)
  329. if vm.is_netvm():
  330. new_vms.append(vm.qid)
  331. vms = [vm for vm in self.values() if vm.qid in dependend_vms_qid]
  332. return vms
  333. def verify_new_vm(self, new_vm):
  334. # Verify that qid is unique
  335. for vm in self.values():
  336. if vm.qid == new_vm.qid:
  337. print >> sys.stderr, "ERROR: The qid={0} is already used by VM '{1}'!".\
  338. format(vm.qid, vm.name)
  339. return False
  340. # Verify that name is unique
  341. for vm in self.values():
  342. if vm.name == new_vm.name:
  343. print >> sys.stderr, \
  344. "ERROR: The name={0} is already used by other VM with qid='{1}'!".\
  345. format(vm.name, vm.qid)
  346. return False
  347. return True
  348. def get_new_unused_qid(self):
  349. used_ids = set([vm.qid for vm in self.values()])
  350. for id in range (1, qubes_max_qid):
  351. if id not in used_ids:
  352. return id
  353. raise LookupError ("Cannot find unused qid!")
  354. def get_new_unused_netid(self):
  355. used_ids = set([vm.netid for vm in self.values() if vm.is_netvm()])
  356. for id in range (1, qubes_max_netid):
  357. if id not in used_ids:
  358. return id
  359. raise LookupError ("Cannot find unused netid!")
  360. def check_if_storage_exists(self):
  361. try:
  362. f = open (self.qubes_store_filename, 'r')
  363. except IOError:
  364. return False
  365. f.close()
  366. return True
  367. def create_empty_storage(self):
  368. self.log.debug('create_empty_storage()')
  369. self.qubes_store_file = open (self.qubes_store_filename, 'w')
  370. self.clear()
  371. self.save()
  372. def lock_db_for_reading(self):
  373. if self.qubes_store_file is not None:
  374. raise QubesException("lock already taken")
  375. # save() would rename the file over qubes.xml, _then_ release lock,
  376. # so we need to ensure that the file for which we've got the lock is
  377. # still the right file
  378. self.log.debug('lock_db_for_reading()')
  379. while True:
  380. self.qubes_store_file = open (self.qubes_store_filename, 'r')
  381. if os.name == 'posix':
  382. fcntl.lockf (self.qubes_store_file, fcntl.LOCK_SH)
  383. elif os.name == 'nt':
  384. overlapped = pywintypes.OVERLAPPED()
  385. win32file.LockFileEx(win32file._get_osfhandle(self.qubes_store_file.fileno()),
  386. 0, 0, -0x10000, overlapped)
  387. if os.fstat(self.qubes_store_file.fileno()) == os.stat(
  388. self.qubes_store_filename):
  389. break
  390. self.qubes_store_file.close()
  391. def lock_db_for_writing(self):
  392. if self.qubes_store_file is not None:
  393. raise QubesException("lock already taken")
  394. # save() would rename the file over qubes.xml, _then_ release lock,
  395. # so we need to ensure that the file for which we've got the lock is
  396. # still the right file
  397. self.log.debug('lock_db_for_writing()')
  398. while True:
  399. self.qubes_store_file = open (self.qubes_store_filename, 'r+')
  400. if os.name == 'posix':
  401. fcntl.lockf (self.qubes_store_file, fcntl.LOCK_EX)
  402. elif os.name == 'nt':
  403. overlapped = pywintypes.OVERLAPPED()
  404. win32file.LockFileEx(win32file._get_osfhandle(self.qubes_store_file.fileno()),
  405. win32con.LOCKFILE_EXCLUSIVE_LOCK, 0, -0x10000, overlapped)
  406. if os.fstat(self.qubes_store_file.fileno()) == os.stat(
  407. self.qubes_store_filename):
  408. break
  409. self.qubes_store_file.close()
  410. def unlock_db(self):
  411. # intentionally do not call explicit unlock to not unlock the file
  412. # before all buffers are flushed
  413. self.log.debug('unlock_db()')
  414. self.qubes_store_file.close()
  415. self.qubes_store_file = None
  416. def save(self):
  417. self.log.debug('save()')
  418. root = lxml.etree.Element(
  419. "QubesVmCollection",
  420. default_template=str(self.default_template_qid) \
  421. if self.default_template_qid is not None else "None",
  422. default_netvm=str(self.default_netvm_qid) \
  423. if self.default_netvm_qid is not None else "None",
  424. default_fw_netvm=str(self.default_fw_netvm_qid) \
  425. if self.default_fw_netvm_qid is not None else "None",
  426. updatevm=str(self.updatevm_qid) \
  427. if self.updatevm_qid is not None else "None",
  428. clockvm=str(self.clockvm_qid) \
  429. if self.clockvm_qid is not None else "None",
  430. default_kernel=str(self.default_kernel) \
  431. if self.default_kernel is not None else "None",
  432. )
  433. for vm in self.values():
  434. element = vm.create_xml_element()
  435. if element is not None:
  436. root.append(element)
  437. tree = lxml.etree.ElementTree(root)
  438. try:
  439. new_store_file = tempfile.NamedTemporaryFile(prefix=self.qubes_store_filename, delete=False)
  440. if os.name == 'posix':
  441. fcntl.lockf (new_store_file, fcntl.LOCK_EX)
  442. elif os.name == 'nt':
  443. overlapped = pywintypes.OVERLAPPED()
  444. win32file.LockFileEx(win32file._get_osfhandle(new_store_file.fileno()),
  445. win32con.LOCKFILE_EXCLUSIVE_LOCK, 0, -0x10000, overlapped)
  446. tree.write(new_store_file, encoding="UTF-8", pretty_print=True)
  447. new_store_file.flush()
  448. os.chmod(new_store_file.name, 0660)
  449. os.chown(new_store_file.name, -1, grp.getgrnam('qubes').gr_gid)
  450. os.rename(new_store_file.name, self.qubes_store_filename)
  451. self.qubes_store_file.close()
  452. self.qubes_store_file = new_store_file
  453. except EnvironmentError as err:
  454. print("{0}: export error: {1}".format(
  455. os.path.basename(sys.argv[0]), err))
  456. return False
  457. return True
  458. def set_netvm_dependency(self, element):
  459. kwargs = {}
  460. attr_list = ("qid", "uses_default_netvm", "netvm_qid")
  461. for attribute in attr_list:
  462. kwargs[attribute] = element.get(attribute)
  463. vm = self[int(kwargs["qid"])]
  464. if "uses_default_netvm" not in kwargs:
  465. vm.uses_default_netvm = True
  466. else:
  467. vm.uses_default_netvm = (
  468. True if kwargs["uses_default_netvm"] == "True" else False)
  469. if vm.uses_default_netvm is True:
  470. if vm.is_proxyvm():
  471. netvm = self.get_default_fw_netvm()
  472. else:
  473. netvm = self.get_default_netvm()
  474. kwargs.pop("netvm_qid")
  475. else:
  476. if kwargs["netvm_qid"] == "none" or kwargs["netvm_qid"] is None:
  477. netvm = None
  478. kwargs.pop("netvm_qid")
  479. else:
  480. netvm_qid = int(kwargs.pop("netvm_qid"))
  481. if netvm_qid not in self:
  482. netvm = None
  483. else:
  484. netvm = self[netvm_qid]
  485. # directly set internal attr to not call setters...
  486. vm._netvm = netvm
  487. if netvm:
  488. netvm.connected_vms[vm.qid] = vm
  489. def load_globals(self, element):
  490. default_template = element.get("default_template")
  491. self.default_template_qid = int(default_template) \
  492. if default_template.lower() != "none" else None
  493. default_netvm = element.get("default_netvm")
  494. if default_netvm is not None:
  495. self.default_netvm_qid = int(default_netvm) \
  496. if default_netvm != "None" else None
  497. #assert self.default_netvm_qid is not None
  498. default_fw_netvm = element.get("default_fw_netvm")
  499. if default_fw_netvm is not None:
  500. self.default_fw_netvm_qid = int(default_fw_netvm) \
  501. if default_fw_netvm != "None" else None
  502. #assert self.default_netvm_qid is not None
  503. updatevm = element.get("updatevm")
  504. if updatevm is not None:
  505. self.updatevm_qid = int(updatevm) \
  506. if updatevm != "None" else None
  507. #assert self.default_netvm_qid is not None
  508. clockvm = element.get("clockvm")
  509. if clockvm is not None:
  510. self.clockvm_qid = int(clockvm) \
  511. if clockvm != "None" else None
  512. self.default_kernel = element.get("default_kernel")
  513. def _check_global(self, attr, default):
  514. qid = getattr(self, attr)
  515. if qid is None:
  516. return
  517. try:
  518. self[qid]
  519. except KeyError:
  520. setattr(self, attr, default)
  521. def check_globals(self):
  522. '''Ensure that all referenced qids are present in the collection'''
  523. self._check_global('default_template_qid', None)
  524. self._check_global('default_fw_netvm_qid', None)
  525. self._check_global('default_netvm_qid', self.default_fw_netvm_qid)
  526. self._check_global('updatevm_qid', self.default_netvm_qid)
  527. self._check_global('clockvm_qid', self.default_netvm_qid)
  528. def load(self):
  529. self.log.debug('load()')
  530. self.clear()
  531. try:
  532. self.qubes_store_file.seek(0)
  533. tree = lxml.etree.parse(self.qubes_store_file)
  534. except (EnvironmentError,
  535. xml.parsers.expat.ExpatError) as err:
  536. print("{0}: import error: {1}".format(
  537. os.path.basename(sys.argv[0]), err))
  538. return False
  539. self.load_globals(tree.getroot())
  540. for (vm_class_name, vm_class) in sorted(QubesVmClasses.items(),
  541. key=lambda _x: _x[1].load_order):
  542. vms_of_class = tree.findall(vm_class_name)
  543. # first non-template based, then template based
  544. sorted_vms_of_class = sorted(vms_of_class, key= \
  545. lambda x: str(x.get('template_qid')).lower() != "none")
  546. for element in sorted_vms_of_class:
  547. try:
  548. vm = vm_class(xml_element=element, collection=self)
  549. self[vm.qid] = vm
  550. except (ValueError, LookupError) as err:
  551. print("{0}: import error ({1}): {2}".format(
  552. os.path.basename(sys.argv[0]), vm_class_name, err))
  553. raise
  554. return False
  555. # After importing all VMs, set netvm references, in the same order
  556. for (vm_class_name, vm_class) in sorted(QubesVmClasses.items(),
  557. key=lambda _x: _x[1].load_order):
  558. for element in tree.findall(vm_class_name):
  559. try:
  560. self.set_netvm_dependency(element)
  561. except (ValueError, LookupError) as err:
  562. print("{0}: import error2 ({}): {}".format(
  563. os.path.basename(sys.argv[0]), vm_class_name, err))
  564. return False
  565. self.check_globals()
  566. # if there was no clockvm entry in qubes.xml, try to determine default:
  567. # root of default NetVM chain
  568. if tree.getroot().get("clockvm") is None:
  569. if self.default_netvm_qid is not None:
  570. clockvm = self[self.default_netvm_qid]
  571. # Find root of netvm chain
  572. while clockvm.netvm is not None:
  573. clockvm = clockvm.netvm
  574. self.clockvm_qid = clockvm.qid
  575. # Disable ntpd in ClockVM - to not conflict with ntpdate (both are
  576. # using 123/udp port)
  577. if self.clockvm_qid is not None:
  578. self[self.clockvm_qid].services['ntpd'] = False
  579. # Add dom0 if wasn't present in qubes.xml
  580. if not 0 in self.keys():
  581. dom0vm = QubesAdminVm (collection=self)
  582. self[dom0vm.qid] = dom0vm
  583. return True
  584. def pop(self, qid):
  585. self.log.debug('pop({})'.format(qid))
  586. if self.default_netvm_qid == qid:
  587. self.default_netvm_qid = None
  588. if self.default_fw_netvm_qid == qid:
  589. self.default_fw_netvm_qid = None
  590. if self.clockvm_qid == qid:
  591. self.clockvm_qid = None
  592. if self.updatevm_qid == qid:
  593. self.updatevm_qid = None
  594. if self.default_template_qid == qid:
  595. self.default_template_qid = None
  596. return super(QubesVmCollection, self).pop(qid)
  597. class QubesDaemonPidfile(object):
  598. def __init__(self, name):
  599. self.name = name
  600. self.path = "/var/run/qubes/" + name + ".pid"
  601. def create_pidfile(self):
  602. f = open (self.path, 'w')
  603. f.write(str(os.getpid()))
  604. f.close()
  605. def pidfile_exists(self):
  606. return os.path.exists(self.path)
  607. def read_pid(self):
  608. f = open (self.path)
  609. pid = f.read ().strip()
  610. f.close()
  611. return int(pid)
  612. def pidfile_is_stale(self):
  613. if not self.pidfile_exists():
  614. return False
  615. # check if the pid file is valid...
  616. proc_path = "/proc/" + str(self.read_pid()) + "/cmdline"
  617. if not os.path.exists (proc_path):
  618. print >> sys.stderr, \
  619. "Path {0} doesn't exist, assuming stale pidfile.".\
  620. format(proc_path)
  621. return True
  622. return False # It's a good pidfile
  623. def remove_pidfile(self):
  624. os.remove (self.path)
  625. def __enter__ (self):
  626. # assumes the pidfile doesn't exist -- you should ensure it before opening the context
  627. self.create_pidfile()
  628. def __exit__ (self, exc_type, exc_val, exc_tb):
  629. self.remove_pidfile()
  630. return False
  631. ### Initialization code
  632. defaults["appvm_label"] = QubesVmLabels["red"]
  633. defaults["template_label"] = QubesVmLabels["black"]
  634. defaults["servicevm_label"] = QubesVmLabels["red"]
  635. QubesVmClasses = {}
  636. modules_dir = os.path.join(os.path.dirname(__file__), 'modules')
  637. for module_file in sorted(os.listdir(modules_dir)):
  638. if not module_file.endswith(".py") or module_file == "__init__.py":
  639. continue
  640. __import__('qubes.modules.%s' % module_file[:-3])
  641. try:
  642. import qubes.settings
  643. qubes.settings.apply(system_path, vm_files, defaults)
  644. except ImportError:
  645. pass
  646. for path_key in system_path.keys():
  647. if not os.path.isabs(system_path[path_key]):
  648. system_path[path_key] = os.path.join(
  649. system_path['qubes_base_dir'], system_path[path_key])
  650. # vim:sw=4:et: