qvm-prefs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. #!/usr/bin/python2
  2. # -*- encoding: utf8 -*-
  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 qubes.qubes import QubesVmCollection
  24. from qubes.qubes import QubesVmLabels
  25. from qubes.qubes import QubesHost
  26. from qubes.qubes import system_path
  27. from optparse import OptionParser
  28. import subprocess
  29. import os
  30. import sys
  31. import re
  32. from qubes.qubes import vmm
  33. def do_list(vm):
  34. label_width = 19
  35. fmt="{{0:<{0}}}: {{1}}".format(label_width)
  36. print fmt.format ("name", vm.name)
  37. print fmt.format ("label", vm.label.name)
  38. print fmt.format ("type", vm.type)
  39. if vm.template is not None:
  40. print fmt.format ("template", vm.template.name)
  41. if vm.netvm is not None:
  42. print fmt.format ("netvm", vm.netvm.name)
  43. if vm.qid != 0:
  44. print fmt.format("dispvm_netvm", "%s%s" % (
  45. vm.dispvm_netvm.name if vm.dispvm_netvm
  46. else "none",
  47. " (default)" if vm.uses_default_dispvm_netvm else ""))
  48. print fmt.format ("updateable", vm.updateable)
  49. print fmt.format ("autostart", vm.autostart)
  50. print fmt.format ("installed_by_rpm", vm.installed_by_rpm)
  51. print fmt.format ("include_in_backups", vm.include_in_backups)
  52. print fmt.format ("last_backup", vm.backup_timestamp)
  53. print fmt.format ("dir", vm.dir_path)
  54. print fmt.format ("config", vm.conf_file)
  55. print fmt.format ("pcidevs", vm.pcidevs)
  56. print fmt.format ("pci_strictreset", vm.pci_strictreset)
  57. if vm.template is None:
  58. print fmt.format ("root_img", vm.root_img)
  59. if hasattr(vm, "rootcow_img") and vm.rootcow_img is not None:
  60. print fmt.format ("root_cow_img", vm.rootcow_img)
  61. if vm.template is not None:
  62. print fmt.format ("root_img", vm.template.root_img)
  63. if hasattr(vm, 'volatile_img') and vm.volatile_img is not None:
  64. print fmt.format ("root_volatile_img", vm.volatile_img)
  65. if hasattr(vm, 'private_img') and vm.private_img is not None:
  66. print fmt.format ("private_img", vm.private_img)
  67. print fmt.format ("vcpus", str(vm.vcpus))
  68. print fmt.format ("memory", vm.memory)
  69. if hasattr(vm, 'maxmem'):
  70. print fmt.format ("maxmem", vm.maxmem)
  71. print fmt.format ("MAC", "%s%s" % (vm.mac, " (auto)" if vm._mac is None else ""))
  72. if hasattr(vm, 'kernel'):
  73. if vm.uses_default_kernel:
  74. print fmt.format ("kernel", "%s (default)" % vm.kernel)
  75. else:
  76. print fmt.format ("kernel", vm.kernel)
  77. if hasattr(vm, 'kernelopts'):
  78. if vm.uses_default_kernelopts:
  79. print fmt.format ("kernelopts", "%s (default)" % vm.kernelopts)
  80. else:
  81. print fmt.format ("kernelopts", vm.kernelopts)
  82. if hasattr(vm, 'debug'):
  83. print fmt.format("debug", "on" if vm.debug else "off")
  84. if hasattr(vm, 'default_user'):
  85. print fmt.format("default_user", str(vm.default_user))
  86. if hasattr(vm, 'qrexec_installed'):
  87. print fmt.format("qrexec_installed", str(vm.qrexec_installed))
  88. if hasattr(vm, 'qrexec_timeout'):
  89. print fmt.format("qrexec_timeout", str(vm.qrexec_timeout))
  90. if hasattr(vm, 'guiagent_installed'):
  91. print fmt.format("guiagent_installed", str(vm.guiagent_installed))
  92. if hasattr(vm, 'seamless_gui_mode'):
  93. print fmt.format("seamless_gui_mode", str(vm.seamless_gui_mode))
  94. if hasattr(vm, 'drive'):
  95. print fmt.format("drive", str(vm.drive))
  96. if hasattr(vm, 'timezone'):
  97. print fmt.format("timezone", str(vm.timezone))
  98. print fmt.format ("internal", vm.internal)
  99. def do_get(vms, vm, prop):
  100. if prop == 'config':
  101. prop = 'conf_file'
  102. elif prop == 'dir':
  103. prop = 'dir_path'
  104. elif prop == 'last_backup':
  105. prop = 'backup_timestamp'
  106. if not hasattr(vm, prop):
  107. print >>sys.stderr, "VM '{}' has no attribute '{}'".format(vm.name,
  108. prop)
  109. return
  110. if getattr(vm, prop, None) is None:
  111. # not set or set to None
  112. return
  113. if prop in ['template', 'netvm', 'dispvm_netvm', 'label']:
  114. print getattr(vm, prop).name
  115. else:
  116. print str(getattr(vm, prop))
  117. def set_label(vms, vm, args):
  118. if len (args) != 1:
  119. print >> sys.stderr, "Missing label name argument!"
  120. return False
  121. label = args[0]
  122. if label not in QubesVmLabels:
  123. print >> sys.stderr, "Wrong label name, supported values are the following:"
  124. for l in QubesVmLabels.values():
  125. print >> sys.stderr, "* {0}".format(l.name)
  126. return False
  127. vm.label = QubesVmLabels[label]
  128. return True
  129. def set_memory(vms, vm, args):
  130. if len (args) != 1:
  131. print >> sys.stderr, "Missing memory argument!"
  132. return False
  133. new_memory = int(args[0])
  134. if new_memory <= 0:
  135. print >>sys.stderr, "Memory size must be positive"
  136. return False
  137. qubes_host = QubesHost()
  138. if new_memory > qubes_host.memory_total/1024:
  139. print >> sys.stderr, "This host has only {0} MB of RAM".format(qubes_host.memory_total/1024)
  140. return False
  141. vm.memory = new_memory
  142. return True
  143. def set_maxmem(vms, vm, args):
  144. if len (args) != 1:
  145. print >> sys.stderr, "Missing maxmem argument!"
  146. exit (1)
  147. new_maxmem = int(args[0])
  148. if new_maxmem <= 0:
  149. print >>sys.stderr, "Memory size must be positive"
  150. return False
  151. qubes_host = QubesHost()
  152. if new_maxmem > qubes_host.memory_total/1024:
  153. print >> sys.stderr, "This host has only {0} MB of RAM".format(qubes_host.memory_total/1024)
  154. return False
  155. if new_maxmem < vm.memory:
  156. print >> sys.stderr, "WARNING: new maxmem smaller than memory property - VM will be able to use only 'maxmem' memory amount"
  157. vm.maxmem = new_maxmem
  158. return True
  159. def set_mac(vms, vm, args):
  160. if len (args) != 1:
  161. print >> sys.stderr, "Missing MAC argument!"
  162. return False
  163. if not re.match("[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}|auto", args[0]):
  164. print >> sys.stderr, "Invalid MAC argument!"
  165. print >> sys.stderr, "Possible values:"
  166. print >> sys.stderr, "1) auto"
  167. print >> sys.stderr, "2) MAC in format: XX:XX:XX:XX:XX:XX"
  168. return False
  169. mac = args[0]
  170. if mac == "auto":
  171. mac = None
  172. vm.mac = mac
  173. return True
  174. def set_pcidevs(vms, vm, args):
  175. if len (args) != 1:
  176. print >> sys.stderr, "Missing pcidevs argument!"
  177. return False
  178. if vm.is_running():
  179. print >>sys.stderr, "Cannot modify PCI devices of running VM, " \
  180. "use qvm-pci instead"
  181. return False
  182. vm.pcidevs = list(eval(args[0]))
  183. return True
  184. def set_pci_strictreset(vms, vm, args):
  185. if len (args) != 1:
  186. print >> sys.stderr, "Missing value (True/False)!"
  187. return False
  188. vm.pci_strictreset = bool(eval(args[0].capitalize()))
  189. return True
  190. def set_netvm(vms, vm, args):
  191. if len (args) != 1:
  192. print >> sys.stderr, "Missing netvm name argument!"
  193. print >> sys.stderr, "Possible values:"
  194. print >> sys.stderr, "1) default"
  195. print >> sys.stderr, "2) none"
  196. print >> sys.stderr, "3) <vmaname>"
  197. return
  198. netvm = args[0]
  199. if netvm == "none":
  200. netvm = None
  201. vm.uses_default_netvm = False
  202. elif netvm == "default":
  203. netvm = vms.get_default_netvm()
  204. vm.uses_default_netvm = True
  205. else:
  206. netvm = vms.get_vm_by_name (netvm)
  207. if netvm is None:
  208. print >> sys.stderr, "A VM with the name '{0}' does not exist in the system.".format(netvm)
  209. return False
  210. if not netvm.is_netvm():
  211. print >> sys.stderr, "VM '{0}' is not a NetVM".format(netvm)
  212. return False
  213. vm.uses_default_netvm = False
  214. vm.netvm = netvm
  215. return True
  216. def set_dispvm_netvm(vms, vm, args):
  217. if len (args) != 1:
  218. print >> sys.stderr, "Missing netvm name argument!"
  219. print >> sys.stderr, "Possible values:"
  220. print >> sys.stderr, "1) default (the same as VM own netvm)"
  221. print >> sys.stderr, "2) none"
  222. print >> sys.stderr, "3) <vmaname>"
  223. return
  224. netvm = args[0]
  225. if netvm == "none":
  226. vm.dispvm_netvm = None
  227. vm.uses_default_dispvm_netvm = False
  228. elif netvm == "default":
  229. vm.uses_default_dispvm_netvm = True
  230. else:
  231. netvm = vms.get_vm_by_name (netvm)
  232. if netvm is None:
  233. print >> sys.stderr, "A VM with the name '{0}' does not exist in the system.".format(netvm)
  234. return False
  235. if not netvm.is_netvm():
  236. print >> sys.stderr, "VM '{0}' is not a NetVM".format(netvm)
  237. return False
  238. vm.dispvm_netvm = netvm
  239. vm.uses_default_dispvm_netvm = False
  240. return True
  241. def set_kernel(vms, vm, args):
  242. if len(args) != 1:
  243. print >> sys.stderr, "Missing kernel version argument!"
  244. print >> sys.stderr, "Possible values:"
  245. print >> sys.stderr, "1) default"
  246. print >> sys.stderr, "2) none (kernels subdir in VM)"
  247. print >> sys.stderr, "3) <kernel version>, one of:"
  248. for k in os.listdir(system_path["qubes_kernels_base_dir"]):
  249. print >> sys.stderr, " -", k
  250. return False
  251. kernel = args[0]
  252. if kernel == "default":
  253. vm.kernel = vms.get_default_kernel()
  254. vm.uses_default_kernel = True
  255. elif kernel == "none":
  256. vm.kernel = None
  257. else:
  258. if not os.path.exists(os.path.join(system_path["qubes_kernels_base_dir"], kernel)):
  259. print >> sys.stderr, "Kernel version {0} not installed.".format(kernel)
  260. return False
  261. vm.kernel = kernel
  262. return True
  263. def set_template(vms, vm, args):
  264. if len (args) != 1:
  265. print >> sys.stderr, "Missing template name argument!"
  266. return False
  267. template_name = args[0];
  268. template = vms.get_vm_by_name(template_name)
  269. if template is None or template.qid not in vms:
  270. print >> sys.stderr, "A VM with the name '{0}' does not exist in the system.".format(template_name)
  271. return False
  272. if not template.is_template():
  273. print >> sys.stderr, "VM '{0}' is not a TemplateVM".format(template_name)
  274. return False
  275. print >> sys.stderr, "Setting template for VM '{0}' to '{1}'...".format (vm.name, template_name)
  276. vm.template = template
  277. return True
  278. def set_vcpus(vms, vm, args):
  279. if len (args) != 1:
  280. print >> sys.stderr, "Missing vcpus count argument!"
  281. return False
  282. vcpus = int(args[0])
  283. if vcpus <= 0:
  284. print >> sys.stderr, "A vcpus count must be positive."
  285. return False
  286. qubes_host = QubesHost()
  287. if vcpus > qubes_host.no_cpus:
  288. print >> sys.stderr, "This host has only {0} cpus".format(
  289. qubes_host.no_cpus)
  290. return False
  291. print >> sys.stderr, "Setting vcpus count for VM '{0}' to '{1}'...".format (vm.name, vcpus)
  292. vm.vcpus = vcpus
  293. return True
  294. def set_kernelopts(vms, vm, args):
  295. if len (args) != 1:
  296. print >> sys.stderr, "Missing kernel opts argument!"
  297. print >> sys.stderr, "Possible values:"
  298. print >> sys.stderr, "1) default"
  299. print >> sys.stderr, "2) <opts>"
  300. return False
  301. if args[0] == 'default':
  302. vm.uses_default_kernelopts = True
  303. else:
  304. vm.uses_default_kernelopts = False
  305. vm.kernelopts = args[0]
  306. return True
  307. def set_name(vms, vm, args):
  308. if len (args) != 1:
  309. print >> sys.stderr, "Missing new name!"
  310. return False
  311. if args[0] == vm.name:
  312. return False
  313. vm.set_name(args[0])
  314. return True
  315. def set_drive(vms, vm, args):
  316. if len (args) != 1:
  317. print >> sys.stderr, "Missing new drive content (file/device)!"
  318. return False
  319. if args[0] == '' or args[0].lower() == 'none':
  320. vm.drive = None
  321. else:
  322. vm.drive = args[0]
  323. return True
  324. def set_debug(vms, vm, args):
  325. if len (args) != 1:
  326. print >> sys.stderr, "Missing value (True/False or on/off)!"
  327. return False
  328. if args[0].lower() == "on":
  329. vm.debug = True
  330. elif args[0].lower() == "off":
  331. vm.debug = False
  332. else:
  333. vm.debug = bool(eval(args[0].capitalize()))
  334. return True
  335. def set_default_user(vms, vm, args):
  336. if len (args) != 1:
  337. print >> sys.stderr, "Missing user name!"
  338. return False
  339. vm.default_user = args[0]
  340. return True
  341. def set_include_in_backups(vms, vm, args):
  342. if len (args) != 1:
  343. print >> sys.stderr, "Missing value (True/False)!"
  344. return False
  345. vm.include_in_backups = bool(eval(args[0].capitalize()))
  346. return True
  347. def set_qrexec_installed(vms, vm, args):
  348. if len (args) != 1:
  349. print >> sys.stderr, "Missing value (True/False)!"
  350. return False
  351. vm.qrexec_installed = bool(eval(args[0].capitalize()))
  352. return True
  353. def set_internal(vms, vm, args):
  354. if len (args) != 1:
  355. print >> sys.stderr, "Missing value (True/False)!"
  356. return False
  357. vm.internal = bool(eval(args[0].capitalize()))
  358. return True
  359. def set_guiagent_installed(vms, vm, args):
  360. if len (args) != 1:
  361. print >> sys.stderr, "Missing value (True/False)!"
  362. return False
  363. vm.guiagent_installed = bool(eval(args[0].capitalize()))
  364. return True
  365. def set_seamless_gui_mode(vms, vm, args):
  366. if len(args) != 1:
  367. print >> sys.stderr, "Missing value (true/false)!"
  368. return False
  369. vm.seamless_gui_mode = bool(eval(args[0].capitalize()))
  370. return True
  371. def set_autostart(vms, vm, args):
  372. if len (args) != 1:
  373. print >> sys.stderr, "Missing value (True/False)!"
  374. return False
  375. vm.autostart = bool(eval(args[0].capitalize()))
  376. return True
  377. def set_qrexec_timeout(vms, vm, args):
  378. if len (args) != 1:
  379. print >> sys.stderr, "Missing timeout value (seconds)!"
  380. return False
  381. if int(args[0]) < 0:
  382. print >> sys.stderr, "Negative timeout value not allowed!"
  383. return False
  384. vm.qrexec_timeout = int(args[0])
  385. return True
  386. def set_timezone(vms, vm, args):
  387. if len (args) != 1:
  388. print >> sys.stderr, "Missing value ('localtime' or timeoffset in seconds)!"
  389. return False
  390. try:
  391. int(args[0])
  392. except ValueError:
  393. if args[0].lower() != 'localtime':
  394. print >> sys.stderr, "Invalid timezone value!"
  395. return False
  396. vm.timezone = args[0]
  397. return True
  398. properties = {
  399. "include_in_backups": set_include_in_backups,
  400. "pcidevs": set_pcidevs,
  401. "pci_strictreset": set_pci_strictreset,
  402. "label" : set_label,
  403. "netvm" : set_netvm,
  404. "dispvm_netvm" : set_dispvm_netvm,
  405. "maxmem" : set_maxmem,
  406. "memory" : set_memory,
  407. "kernel" : set_kernel,
  408. "template" : set_template,
  409. "vcpus" : set_vcpus,
  410. "kernelopts": set_kernelopts,
  411. "name": set_name,
  412. "drive": set_drive,
  413. "mac": set_mac,
  414. "debug": set_debug,
  415. "default_user": set_default_user,
  416. "qrexec_installed": set_qrexec_installed,
  417. "guiagent_installed": set_guiagent_installed,
  418. "seamless_gui_mode": set_seamless_gui_mode,
  419. "qrexec_timeout": set_qrexec_timeout,
  420. "timezone": set_timezone,
  421. "internal": set_internal,
  422. "autostart": set_autostart,
  423. }
  424. def do_set(vms, vm, property, args):
  425. if property not in properties.keys():
  426. print >> sys.stderr, "ERROR: Wrong property name: '{0}'".format(property)
  427. return False
  428. if not hasattr(vm, property):
  429. print >> sys.stderr, "ERROR: Property '{0}' not available for this VM".format(property)
  430. return False
  431. try:
  432. return properties[property](vms, vm, args)
  433. except Exception as err:
  434. print >> sys.stderr, "ERROR: %s" % str(err)
  435. return False
  436. def main():
  437. usage = "usage: %prog -l [options] <vm-name>\n"\
  438. "usage: %prog -g [options] <vm-name> <property>\n"\
  439. "usage: %prog -s [options] <vm-name> <property> [...]\n"\
  440. "List/set various per-VM properties."
  441. parser = OptionParser (usage)
  442. parser.add_option("-l", "--list", action="store_true", dest="do_list",
  443. default=False)
  444. parser.add_option("-s", "--set", action="store_true", dest="do_set",
  445. default=False)
  446. parser.add_option ("-g", "--get", action="store_true", dest="do_get",
  447. default=False)
  448. parser.add_option("--force-root", action="store_true", dest="force_root",
  449. default=False,
  450. help="Force to run, even with root privileges")
  451. parser.add_option ("--offline-mode", dest="offline_mode",
  452. action="store_true", default=False,
  453. help="Offline mode")
  454. (options, args) = parser.parse_args ()
  455. if (len (args) < 1):
  456. parser.error ("You must provide at least the vmname!")
  457. vmname = args[0]
  458. if hasattr(os, "geteuid") and os.geteuid() == 0:
  459. if not options.force_root:
  460. print >> sys.stderr, "*** Running this tool as root is strongly discouraged, this will lead you in permissions problems."
  461. print >> sys.stderr, "Retry as unprivileged user."
  462. print >> sys.stderr, "... or use --force-root to continue anyway."
  463. exit(1)
  464. if options.do_list + options.do_set + options.do_get > 1:
  465. print >> sys.stderr, "You can provide at most one of -l, -g and -s at " \
  466. "the same time!"
  467. exit(1)
  468. if options.offline_mode:
  469. vmm.offline_mode = True
  470. if options.do_set:
  471. qvm_collection = QubesVmCollection()
  472. qvm_collection.lock_db_for_writing()
  473. qvm_collection.load()
  474. else:
  475. qvm_collection = QubesVmCollection()
  476. qvm_collection.lock_db_for_reading()
  477. qvm_collection.load()
  478. qvm_collection.unlock_db()
  479. vm = qvm_collection.get_vm_by_name(vmname)
  480. if vm is None or vm.qid not in qvm_collection:
  481. print >> sys.stderr, "A VM with the name '{0}' does not exist in the system.".format(vmname)
  482. exit(1)
  483. if options.do_set:
  484. if len (args) < 2:
  485. print >> sys.stderr, "You must specify the property you wish to set..."
  486. print >> sys.stderr, "Available properties:"
  487. for p in properties.keys():
  488. if hasattr(vm, p):
  489. print >> sys.stderr, "--> '{0}'".format(p)
  490. exit (1)
  491. property = args[1]
  492. if do_set(qvm_collection, vm, property, args[2:]):
  493. qvm_collection.save()
  494. qvm_collection.unlock_db()
  495. else:
  496. qvm_collection.unlock_db()
  497. exit(1)
  498. elif options.do_get or len(args) == 2:
  499. do_get(qvm_collection, vm, args[1])
  500. else:
  501. # do_list
  502. do_list(vm)
  503. main()