qubes.py 28 KB

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