Remove core2 code
This all either have been migrated to core3, or is not needed anymore. There is still qvm-tools directory with a few tools that needs to be migrated, or installed as is.
This commit is contained in:
parent
bb4dc91ee8
commit
8992e71f85
7
Makefile
7
Makefile
@ -40,16 +40,10 @@ rpms-dom0:
|
||||
$(RPMS_DIR)/x86_64/qubes-core-dom0-$(VERSION)*.rpm \
|
||||
$(RPMS_DIR)/noarch/qubes-core-dom0-doc-$(VERSION)*rpm
|
||||
|
||||
clean:
|
||||
make -C dispvm clean
|
||||
|
||||
all:
|
||||
$(PYTHON) setup.py build
|
||||
# make all -C tests
|
||||
# Currently supported only on xen
|
||||
ifeq ($(BACKEND_VMM),xen)
|
||||
make all -C dispvm
|
||||
endif
|
||||
|
||||
install:
|
||||
ifeq ($(OS),Linux)
|
||||
@ -67,7 +61,6 @@ ifeq ($(BACKEND_VMM),xen)
|
||||
# Currently supported only on xen
|
||||
cp etc/qmemman.conf $(DESTDIR)/etc/qubes/
|
||||
endif
|
||||
$(MAKE) install -C dispvm
|
||||
mkdir -p $(DESTDIR)/etc/qubes-rpc/policy
|
||||
mkdir -p $(DESTDIR)/usr/libexec/qubes
|
||||
cp qubes-rpc-policy/qubes.FeaturesRequest.policy $(DESTDIR)/etc/qubes-rpc/policy/qubes.FeaturesRequest
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,77 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import (
|
||||
absolute_import,
|
||||
division,
|
||||
print_function,
|
||||
unicode_literals,
|
||||
)
|
||||
|
||||
from qubes.qubes import (
|
||||
register_qubes_vm_class,
|
||||
QubesException,
|
||||
QubesVm,
|
||||
)
|
||||
from time import sleep
|
||||
|
||||
|
||||
class QubesResizableVm(QubesVm):
|
||||
|
||||
def resize_root_img(self, size, allow_start=False):
|
||||
if self.template:
|
||||
raise QubesException("Cannot resize root.img of template-based VM"
|
||||
". Resize the root.img of the template "
|
||||
"instead.")
|
||||
|
||||
if self.is_running():
|
||||
raise QubesException("Cannot resize root.img of running VM")
|
||||
|
||||
if size < self.get_root_img_sz():
|
||||
raise QubesException(
|
||||
"For your own safety shringing of root.img is disabled. If "
|
||||
"you really know what you are doing, use 'truncate' manually.")
|
||||
|
||||
f_root = open(self.root_img, "a+b")
|
||||
f_root.truncate(size)
|
||||
f_root.close()
|
||||
|
||||
|
||||
class QubesResizableVmWithResize2fs(QubesResizableVm):
|
||||
|
||||
def resize_root_img(self, size, allow_start=False):
|
||||
super(QubesResizableVmWithResize2fs, self).\
|
||||
resize_root_img(size, allow_start=allow_start)
|
||||
if not allow_start:
|
||||
raise QubesException("VM start required to complete the "
|
||||
"operation, but not allowed. Either run the "
|
||||
"operation again allowing VM start this "
|
||||
"time, or run resize2fs in the VM manually.")
|
||||
self.start(start_guid=False)
|
||||
self.run("resize2fs /dev/mapper/dmroot", user="root", wait=True,
|
||||
gui=False)
|
||||
self.shutdown()
|
||||
while self.is_running():
|
||||
sleep(1)
|
||||
|
||||
|
||||
register_qubes_vm_class(QubesResizableVm)
|
||||
register_qubes_vm_class(QubesResizableVmWithResize2fs)
|
@ -1,54 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
||||
# Copyright (C) 2013 Marek Marczykowski <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
import os.path
|
||||
|
||||
from qubes.qubes import (
|
||||
register_qubes_vm_class,
|
||||
system_path,
|
||||
QubesResizableVmWithResize2fs,
|
||||
QubesVmLabel,
|
||||
)
|
||||
|
||||
|
||||
class QubesAppVm(QubesResizableVmWithResize2fs):
|
||||
"""
|
||||
A class that represents an AppVM. A child of QubesVm.
|
||||
"""
|
||||
def get_attrs_config(self):
|
||||
attrs_config = super(QubesAppVm, self).get_attrs_config()
|
||||
attrs_config['dir_path']['func'] = \
|
||||
lambda value: value if value is not None else \
|
||||
os.path.join(system_path["qubes_appvms_dir"], self.name)
|
||||
|
||||
return attrs_config
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
return "AppVM"
|
||||
|
||||
def is_appvm(self):
|
||||
return True
|
||||
|
||||
register_qubes_vm_class(QubesAppVm)
|
@ -1,248 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
||||
# Copyright (C) 2013 Marek Marczykowski <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
import os
|
||||
import sys
|
||||
import libvirt
|
||||
import time
|
||||
from qubes.qubes import QubesVm,QubesVmLabel,register_qubes_vm_class, \
|
||||
QubesException
|
||||
from qubes.qubes import QubesDispVmLabels
|
||||
from qubes.qubes import dry_run,vmm
|
||||
import grp
|
||||
|
||||
qmemman_present = False
|
||||
try:
|
||||
from qubes.qmemman_client import QMemmanClient
|
||||
qmemman_present = True
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
DISPID_STATE_FILE = '/var/run/qubes/dispid'
|
||||
GUID_SHMID_FILE = ['/var/run/qubes/shm.id', '/var/run/shm.id']
|
||||
|
||||
class QubesDisposableVm(QubesVm):
|
||||
"""
|
||||
A class that represents an DisposableVM. A child of QubesVm.
|
||||
"""
|
||||
|
||||
# In which order load this VM type from qubes.xml
|
||||
load_order = 120
|
||||
|
||||
|
||||
def _assign_new_dispid(self):
|
||||
# This method in called while lock on qubes.xml is held, so no need for
|
||||
# additional lock
|
||||
if os.path.exists(DISPID_STATE_FILE):
|
||||
f = open(DISPID_STATE_FILE, 'r+')
|
||||
dispid = int(f.read())
|
||||
f.seek(0)
|
||||
f.truncate(0)
|
||||
f.write(str(dispid+1))
|
||||
f.close()
|
||||
else:
|
||||
dispid = 1
|
||||
f = open(DISPID_STATE_FILE, 'w')
|
||||
f.write(str(dispid+1))
|
||||
f.close()
|
||||
os.chown(DISPID_STATE_FILE, -1, grp.getgrnam('qubes').gr_gid)
|
||||
os.chmod(DISPID_STATE_FILE, 0664)
|
||||
return dispid
|
||||
|
||||
def get_attrs_config(self):
|
||||
attrs_config = super(QubesDisposableVm, self).get_attrs_config()
|
||||
|
||||
attrs_config['name']['func'] = \
|
||||
lambda x: "disp%d" % self.dispid if x is None else x
|
||||
|
||||
# New attributes
|
||||
attrs_config['dispid'] = {
|
||||
'func': lambda x: (self._assign_new_dispid() if x is None
|
||||
else int(x)),
|
||||
'save': lambda: str(self.dispid),
|
||||
# needs to be set before name
|
||||
'order': 0
|
||||
}
|
||||
attrs_config['include_in_backups']['func'] = lambda x: False
|
||||
attrs_config['disp_savefile'] = {
|
||||
'default': '/var/run/qubes/current-savefile',
|
||||
'save': lambda: str(self.disp_savefile) }
|
||||
|
||||
return attrs_config
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
||||
disp_template = None
|
||||
if 'disp_template' in kwargs.keys():
|
||||
disp_template = kwargs['disp_template']
|
||||
kwargs['template'] = disp_template.template
|
||||
kwargs['dir_path'] = disp_template.dir_path
|
||||
kwargs['kernel'] = disp_template.kernel
|
||||
kwargs['uses_default_kernel'] = disp_template.uses_default_kernel
|
||||
kwargs['kernelopts'] = disp_template.kernelopts
|
||||
kwargs['uses_default_kernelopts'] = \
|
||||
disp_template.uses_default_kernelopts
|
||||
super(QubesDisposableVm, self).__init__(**kwargs)
|
||||
|
||||
assert self.template is not None, "Missing template for DisposableVM!"
|
||||
|
||||
if disp_template:
|
||||
self.clone_attrs(disp_template)
|
||||
|
||||
# Use DispVM icon with the same color
|
||||
if self._label:
|
||||
self._label = QubesDispVmLabels[self._label.name]
|
||||
self.icon_path = self._label.icon_path
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
return "DisposableVM"
|
||||
|
||||
def is_disposablevm(self):
|
||||
return True
|
||||
|
||||
@property
|
||||
def ip(self):
|
||||
if self.netvm is not None:
|
||||
return self.netvm.get_ip_for_dispvm(self.dispid)
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_clone_attrs(self):
|
||||
attrs = super(QubesDisposableVm, self).get_clone_attrs()
|
||||
attrs.remove('_label')
|
||||
return attrs
|
||||
|
||||
def do_not_use_get_xml_attrs(self):
|
||||
# Minimal set - do not inherit rest of attributes
|
||||
attrs = {}
|
||||
attrs["qid"] = str(self.qid)
|
||||
attrs["name"] = self.name
|
||||
attrs["dispid"] = str(self.dispid)
|
||||
attrs["template_qid"] = str(self.template.qid)
|
||||
attrs["label"] = self.label.name
|
||||
attrs["firewall_conf"] = self.relative_path(self.firewall_conf)
|
||||
attrs["netvm_qid"] = str(self.netvm.qid) if self.netvm is not None else "none"
|
||||
return attrs
|
||||
|
||||
def verify_files(self):
|
||||
return True
|
||||
|
||||
def get_config_params(self):
|
||||
attrs = super(QubesDisposableVm, self).get_config_params()
|
||||
attrs['privatedev'] = ''
|
||||
return attrs
|
||||
|
||||
def create_qubesdb_entries(self):
|
||||
super(QubesDisposableVm, self).create_qubesdb_entries()
|
||||
|
||||
self.qdb.write("/qubes-vm-persistence", "none")
|
||||
self.qdb.write('/qubes-restore-complete', '1')
|
||||
|
||||
def start(self, verbose = False, **kwargs):
|
||||
self.log.debug('start()')
|
||||
if dry_run:
|
||||
return
|
||||
|
||||
# Intentionally not used is_running(): eliminate also "Paused", "Crashed", "Halting"
|
||||
if self.get_power_state() != "Halted":
|
||||
raise QubesException ("VM is already running!")
|
||||
|
||||
if self.netvm is not None:
|
||||
if self.netvm.qid != 0:
|
||||
if not self.netvm.is_running():
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Starting NetVM {0}...".\
|
||||
format(self.netvm.name)
|
||||
self.netvm.start(verbose=verbose, **kwargs)
|
||||
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Loading the VM (type = {0})...".format(self.type)
|
||||
|
||||
print >>sys.stderr, "time=%s, creating config file" % (str(time.time()))
|
||||
# refresh config file
|
||||
domain_config = self.create_config_file()
|
||||
|
||||
qmemman_client = self.request_memory()
|
||||
|
||||
# dispvm cannot have PCI devices
|
||||
assert (len(self.pcidevs) == 0), "DispVM cannot have PCI devices"
|
||||
|
||||
print >>sys.stderr, "time=%s, calling restore" % (str(time.time()))
|
||||
vmm.libvirt_conn.restoreFlags(self.disp_savefile,
|
||||
domain_config, libvirt.VIR_DOMAIN_SAVE_PAUSED)
|
||||
|
||||
print >>sys.stderr, "time=%s, done" % (str(time.time()))
|
||||
self._libvirt_domain = None
|
||||
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Starting Qubes DB..."
|
||||
self.start_qubesdb()
|
||||
|
||||
self.services['qubes-dvm'] = True
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Setting Qubes DB info for the VM..."
|
||||
self.create_qubesdb_entries()
|
||||
print >>sys.stderr, "time=%s, done qubesdb" % (str(time.time()))
|
||||
|
||||
# fire hooks
|
||||
for hook in self.hooks_start:
|
||||
hook(self, verbose = verbose, **kwargs)
|
||||
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Starting the VM..."
|
||||
self.libvirt_domain.resume()
|
||||
print >>sys.stderr, "time=%s, resumed" % (str(time.time()))
|
||||
|
||||
# close() is not really needed, because the descriptor is close-on-exec
|
||||
# anyway, the reason to postpone close() is that possibly xl is not done
|
||||
# constructing the domain after its main process exits
|
||||
# so we close() when we know the domain is up
|
||||
# the successful unpause is some indicator of it
|
||||
if qmemman_present:
|
||||
qmemman_client.close()
|
||||
|
||||
if kwargs.get('start_guid', True) and \
|
||||
any(os.path.exists(x) for x in GUID_SHMID_FILE):
|
||||
self.start_guid(verbose=verbose, before_qrexec=True,
|
||||
notify_function=kwargs.get('notify_function', None))
|
||||
|
||||
self.start_qrexec_daemon(verbose=verbose,
|
||||
notify_function=kwargs.get('notify_function', None))
|
||||
print >>sys.stderr, "time=%s, qrexec done" % (str(time.time()))
|
||||
|
||||
if kwargs.get('start_guid', True) and \
|
||||
any(os.path.exists(x) for x in GUID_SHMID_FILE):
|
||||
self.start_guid(verbose=verbose,
|
||||
notify_function=kwargs.get('notify_function', None))
|
||||
print >>sys.stderr, "time=%s, guid done" % (str(time.time()))
|
||||
|
||||
return self.xid
|
||||
|
||||
def remove_from_disk(self):
|
||||
# nothing to remove
|
||||
pass
|
||||
|
||||
# register classes
|
||||
register_qubes_vm_class(QubesDisposableVm)
|
@ -1,283 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
||||
# Copyright (C) 2013 Marek Marczykowski <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import shutil
|
||||
from xml.etree import ElementTree
|
||||
|
||||
from qubes.qubes import (
|
||||
dry_run,
|
||||
defaults,
|
||||
register_qubes_vm_class,
|
||||
system_path,
|
||||
vmm,
|
||||
QubesException,
|
||||
QubesResizableVm,
|
||||
)
|
||||
|
||||
|
||||
system_path["config_template_hvm"] = '/usr/share/qubes/vm-template-hvm.xml'
|
||||
|
||||
defaults["hvm_disk_size"] = 20*1024*1024*1024
|
||||
defaults["hvm_private_img_size"] = 2*1024*1024*1024
|
||||
defaults["hvm_memory"] = 512
|
||||
|
||||
|
||||
class QubesHVm(QubesResizableVm):
|
||||
"""
|
||||
A class that represents an HVM. A child of QubesVm.
|
||||
"""
|
||||
|
||||
# FIXME: logically should inherit after QubesAppVm, but none of its methods
|
||||
# are useful for HVM
|
||||
|
||||
def get_attrs_config(self):
|
||||
attrs = super(QubesHVm, self).get_attrs_config()
|
||||
attrs.pop('kernel')
|
||||
attrs.pop('kernels_dir')
|
||||
attrs.pop('kernelopts')
|
||||
attrs.pop('uses_default_kernel')
|
||||
attrs.pop('uses_default_kernelopts')
|
||||
attrs['dir_path']['func'] = lambda value: value if value is not None \
|
||||
else os.path.join(system_path["qubes_appvms_dir"], self.name)
|
||||
attrs['config_file_template']['func'] = \
|
||||
lambda x: system_path["config_template_hvm"]
|
||||
attrs['drive'] = { 'attr': '_drive',
|
||||
'save': lambda: str(self.drive) }
|
||||
# Remove this two lines when HVM will get qmemman support
|
||||
attrs['maxmem'].pop('save')
|
||||
attrs['maxmem']['func'] = lambda x: self.memory
|
||||
attrs['timezone'] = { 'default': 'localtime',
|
||||
'save': lambda: str(self.timezone) }
|
||||
attrs['qrexec_installed'] = { 'default': False,
|
||||
'attr': '_qrexec_installed',
|
||||
'save': lambda: str(self._qrexec_installed) }
|
||||
attrs['guiagent_installed'] = { 'default' : False,
|
||||
'attr': '_guiagent_installed',
|
||||
'save': lambda: str(self._guiagent_installed) }
|
||||
attrs['seamless_gui_mode'] = { 'default': False,
|
||||
'attr': '_seamless_gui_mode',
|
||||
'save': lambda: str(self._seamless_gui_mode) }
|
||||
attrs['services']['default'] = "{'meminfo-writer': False}"
|
||||
|
||||
return attrs
|
||||
|
||||
@classmethod
|
||||
def is_template_compatible(cls, template):
|
||||
if template and (not template.is_template() or template.type != "TemplateHVM"):
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_clone_attrs(self):
|
||||
attrs = super(QubesHVm, self).get_clone_attrs()
|
||||
attrs.remove('kernel')
|
||||
attrs.remove('uses_default_kernel')
|
||||
attrs.remove('kernelopts')
|
||||
attrs.remove('uses_default_kernelopts')
|
||||
attrs += [ 'timezone' ]
|
||||
attrs += [ 'qrexec_installed' ]
|
||||
attrs += [ 'guiagent_installed' ]
|
||||
return attrs
|
||||
|
||||
@property
|
||||
def seamless_gui_mode(self):
|
||||
if not self.guiagent_installed:
|
||||
return False
|
||||
return self._seamless_gui_mode
|
||||
|
||||
@seamless_gui_mode.setter
|
||||
def seamless_gui_mode(self, value):
|
||||
if self._seamless_gui_mode == value:
|
||||
return
|
||||
if not self.guiagent_installed and value:
|
||||
raise ValueError("Seamless GUI mode requires GUI agent installed")
|
||||
|
||||
self._seamless_gui_mode = value
|
||||
if self.is_running():
|
||||
self.send_gui_mode()
|
||||
|
||||
@property
|
||||
def drive(self):
|
||||
return self._drive
|
||||
|
||||
@drive.setter
|
||||
def drive(self, value):
|
||||
if value is None:
|
||||
self._drive = None
|
||||
return
|
||||
|
||||
# strip type for a moment
|
||||
drv_type = "cdrom"
|
||||
if value.startswith("hd:") or value.startswith("cdrom:"):
|
||||
(drv_type, unused, value) = value.partition(":")
|
||||
drv_type = drv_type.lower()
|
||||
|
||||
# sanity check
|
||||
if drv_type not in ['hd', 'cdrom']:
|
||||
raise QubesException("Unsupported drive type: %s" % type)
|
||||
|
||||
if value.count(":") == 0:
|
||||
value = "dom0:" + value
|
||||
if value.count(":/") == 0:
|
||||
# FIXME: when Windows backend will be supported, improve this
|
||||
raise QubesException("Drive path must be absolute")
|
||||
|
||||
self._drive = drv_type + ":" + value
|
||||
|
||||
def create_on_disk(self, verbose, source_template = None):
|
||||
self.log.debug('create_on_disk(source_template={!r})'.format(
|
||||
source_template))
|
||||
if dry_run:
|
||||
return
|
||||
|
||||
if source_template is None:
|
||||
source_template = self.template
|
||||
|
||||
# create empty disk
|
||||
self.storage.private_img_size = defaults["hvm_private_img_size"]
|
||||
self.storage.root_img_size = defaults["hvm_disk_size"]
|
||||
self.storage.create_on_disk(verbose, source_template)
|
||||
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Creating icon symlink: {0} -> {1}".format(self.icon_path, self.label.icon_path)
|
||||
|
||||
try:
|
||||
if hasattr(os, "symlink"):
|
||||
os.symlink (self.label.icon_path, self.icon_path)
|
||||
else:
|
||||
shutil.copy(self.label.icon_path, self.icon_path)
|
||||
except Exception as e:
|
||||
print >> sys.stderr, "WARNING: Failed to set VM icon: %s" % str(e)
|
||||
|
||||
# Make sure that we have UUID allocated
|
||||
self._update_libvirt_domain()
|
||||
|
||||
# fire hooks
|
||||
for hook in self.hooks_create_on_disk:
|
||||
hook(self, verbose, source_template=source_template)
|
||||
|
||||
def get_private_img_sz(self):
|
||||
if not os.path.exists(self.private_img):
|
||||
return 0
|
||||
|
||||
return os.path.getsize(self.private_img)
|
||||
|
||||
def resize_private_img(self, size):
|
||||
assert size >= self.get_private_img_sz(), "Cannot shrink private.img"
|
||||
|
||||
if self.is_running():
|
||||
raise NotImplementedError("Online resize of HVM's private.img not implemented, shutdown the VM first")
|
||||
|
||||
self.storage.resize_private_img(size)
|
||||
|
||||
def run(self, command, **kwargs):
|
||||
if self.qrexec_installed:
|
||||
if 'gui' in kwargs and kwargs['gui']==False:
|
||||
command = "nogui:" + command
|
||||
return super(QubesHVm, self).run(command, **kwargs)
|
||||
else:
|
||||
raise QubesException("Needs qrexec agent installed in VM to use this function. See also qvm-prefs.")
|
||||
|
||||
@property
|
||||
def stubdom_xid(self):
|
||||
if self.xid < 0:
|
||||
return -1
|
||||
|
||||
if vmm.xs is None:
|
||||
return -1
|
||||
|
||||
stubdom_xid_str = vmm.xs.read('', '/local/domain/%d/image/device-model-domid' % self.xid)
|
||||
if stubdom_xid_str is not None:
|
||||
return int(stubdom_xid_str)
|
||||
else:
|
||||
return -1
|
||||
|
||||
def validate_drive_path(self, drive):
|
||||
drive_type, drive_domain, drive_path = drive.split(':', 2)
|
||||
if drive_domain == 'dom0':
|
||||
if not os.path.exists(drive_path):
|
||||
raise QubesException("Invalid drive path '{}'".format(
|
||||
drive_path))
|
||||
|
||||
def start(self, *args, **kwargs):
|
||||
if self.drive:
|
||||
self.validate_drive_path(self.drive)
|
||||
# make it available to storage.prepare_for_vm_startup, which is
|
||||
# called before actually building VM libvirt configuration
|
||||
self.storage.drive = self.drive
|
||||
|
||||
if self.template and self.template.is_running():
|
||||
raise QubesException("Cannot start the HVM while its template is running")
|
||||
try:
|
||||
if 'mem_required' not in kwargs:
|
||||
# Reserve 44MB for stubdomain
|
||||
kwargs['mem_required'] = (self.memory + 44) * 1024 * 1024
|
||||
return super(QubesHVm, self).start(*args, **kwargs)
|
||||
except QubesException as e:
|
||||
capabilities = vmm.libvirt_conn.getCapabilities()
|
||||
tree = ElementTree.fromstring(capabilities)
|
||||
os_types = tree.findall('./guest/os_type')
|
||||
if 'hvm' not in map(lambda x: x.text, os_types):
|
||||
raise QubesException("Cannot start HVM without VT-x/AMD-v enabled")
|
||||
else:
|
||||
raise
|
||||
|
||||
def _cleanup_zombie_domains(self):
|
||||
super(QubesHVm, self)._cleanup_zombie_domains()
|
||||
if not self.is_running():
|
||||
xc_stubdom = self.get_xc_dominfo(name=self.name+'-dm')
|
||||
if xc_stubdom is not None:
|
||||
if xc_stubdom['paused'] == 1:
|
||||
subprocess.call(['xl', 'destroy', str(xc_stubdom['domid'])])
|
||||
if xc_stubdom['dying'] == 1:
|
||||
# GUID still running?
|
||||
guid_pidfile = \
|
||||
'/var/run/qubes/guid-running.%d' % xc_stubdom['domid']
|
||||
if os.path.exists(guid_pidfile):
|
||||
guid_pid = open(guid_pidfile).read().strip()
|
||||
os.kill(int(guid_pid), 15)
|
||||
|
||||
def is_guid_running(self):
|
||||
# If user force the guiagent, is_guid_running will mimic a standard QubesVM
|
||||
if self.guiagent_installed:
|
||||
return super(QubesHVm, self).is_guid_running()
|
||||
else:
|
||||
xid = self.stubdom_xid
|
||||
if xid < 0:
|
||||
return False
|
||||
if not os.path.exists('/var/run/qubes/guid-running.%d' % xid):
|
||||
return False
|
||||
return True
|
||||
|
||||
def is_fully_usable(self):
|
||||
# Running gui-daemon implies also VM running
|
||||
if not self.is_guid_running():
|
||||
return False
|
||||
if self.qrexec_installed and not self.is_qrexec_running():
|
||||
return False
|
||||
return True
|
@ -1,110 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
||||
# Copyright (C) 2013 Marek Marczykowski <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import subprocess
|
||||
import stat
|
||||
import sys
|
||||
import re
|
||||
|
||||
from qubes.qubes import QubesHVm,register_qubes_vm_class,dry_run,vmm
|
||||
from qubes.qubes import QubesException,QubesVmCollection
|
||||
from qubes.qubes import system_path,defaults
|
||||
|
||||
class QubesTemplateHVm(QubesHVm):
|
||||
"""
|
||||
A class that represents an HVM template. A child of QubesHVm.
|
||||
"""
|
||||
|
||||
# In which order load this VM type from qubes.xml
|
||||
load_order = 50
|
||||
|
||||
def get_attrs_config(self):
|
||||
attrs_config = super(QubesTemplateHVm, self).get_attrs_config()
|
||||
attrs_config['dir_path']['func'] = \
|
||||
lambda value: value if value is not None else \
|
||||
os.path.join(system_path["qubes_templates_dir"], self.name)
|
||||
attrs_config['label']['default'] = defaults["template_label"]
|
||||
return attrs_config
|
||||
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
||||
super(QubesTemplateHVm, self).__init__(**kwargs)
|
||||
|
||||
self.appvms = QubesVmCollection()
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
return "TemplateHVM"
|
||||
|
||||
@property
|
||||
def updateable(self):
|
||||
return True
|
||||
|
||||
def is_template(self):
|
||||
return True
|
||||
|
||||
def is_appvm(self):
|
||||
return False
|
||||
|
||||
@property
|
||||
def rootcow_img(self):
|
||||
return self.storage.rootcow_img
|
||||
|
||||
@classmethod
|
||||
def is_template_compatible(cls, template):
|
||||
if template is None:
|
||||
return True
|
||||
return False
|
||||
|
||||
def resize_root_img(self, size):
|
||||
for vm in self.appvms.values():
|
||||
if vm.is_running():
|
||||
raise QubesException("Cannot resize root.img while any VM "
|
||||
"based on this tempate is running")
|
||||
return super(QubesTemplateHVm, self).resize_root_img(size)
|
||||
|
||||
def start(self, *args, **kwargs):
|
||||
for vm in self.appvms.values():
|
||||
if vm.is_running():
|
||||
raise QubesException("Cannot start HVM template while VMs based on it are running")
|
||||
return super(QubesTemplateHVm, self).start(*args, **kwargs)
|
||||
|
||||
def commit_changes (self, verbose = False):
|
||||
self.log.debug('commit_changes()')
|
||||
|
||||
if not vmm.offline_mode:
|
||||
assert not self.is_running(), "Attempt to commit changes on running Template VM!"
|
||||
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Commiting template updates... COW: {0}...".format (self.rootcow_img)
|
||||
|
||||
if dry_run:
|
||||
return
|
||||
|
||||
self.storage.commit_template_changes()
|
||||
|
||||
register_qubes_vm_class(QubesTemplateHVm)
|
@ -1,15 +0,0 @@
|
||||
PYTHON_QUBESMODPATH = $(PYTHON_SITEPATH)/qubes/modules
|
||||
|
||||
all:
|
||||
python -m compileall .
|
||||
python -O -m compileall .
|
||||
|
||||
install:
|
||||
ifndef PYTHON_SITEPATH
|
||||
$(error PYTHON_SITEPATH not defined)
|
||||
endif
|
||||
mkdir -p $(DESTDIR)$(PYTHON_QUBESMODPATH)
|
||||
cp 0*.py $(DESTDIR)$(PYTHON_QUBESMODPATH)
|
||||
cp 0*.py[co] $(DESTDIR)$(PYTHON_QUBESMODPATH)
|
||||
cp __init__.py $(DESTDIR)$(PYTHON_QUBESMODPATH)
|
||||
cp __init__.py[co] $(DESTDIR)$(PYTHON_QUBESMODPATH)
|
@ -1,5 +0,0 @@
|
||||
This directory contains Qubes core modules. It will be loaded in
|
||||
lexicographical order, use numeric prefix to force load ordering.
|
||||
|
||||
0* - Qubes base modules
|
||||
00* - Qubes core VM classes
|
1
core/.gitignore
vendored
1
core/.gitignore
vendored
@ -1 +0,0 @@
|
||||
*.pyo
|
@ -1,31 +0,0 @@
|
||||
OS ?= Linux
|
||||
|
||||
PYTHON_QUBESPATH = $(PYTHON_SITEPATH)/qubes
|
||||
SETTINGS_SUFFIX = $(BACKEND_VMM)-$(OS)
|
||||
|
||||
all:
|
||||
python -m compileall .
|
||||
python -O -m compileall .
|
||||
make -C storage all
|
||||
|
||||
install:
|
||||
ifndef PYTHON_SITEPATH
|
||||
$(error PYTHON_SITEPATH not defined)
|
||||
endif
|
||||
mkdir -p $(DESTDIR)$(PYTHON_QUBESPATH)
|
||||
cp qubes.py $(DESTDIR)$(PYTHON_QUBESPATH)
|
||||
cp qubes.py[co] $(DESTDIR)$(PYTHON_QUBESPATH)
|
||||
cp qubesutils.py $(DESTDIR)$(PYTHON_QUBESPATH)
|
||||
cp qubesutils.py[co] $(DESTDIR)$(PYTHON_QUBESPATH)
|
||||
cp guihelpers.py $(DESTDIR)$(PYTHON_QUBESPATH)
|
||||
cp guihelpers.py[co] $(DESTDIR)$(PYTHON_QUBESPATH)
|
||||
cp backup.py $(DESTDIR)$(PYTHON_QUBESPATH)
|
||||
cp backup.py[co] $(DESTDIR)$(PYTHON_QUBESPATH)
|
||||
ifneq ($(BACKEND_VMM),)
|
||||
if [ -r settings-$(SETTINGS_SUFFIX).py ]; then \
|
||||
cp settings-$(SETTINGS_SUFFIX).py $(DESTDIR)$(PYTHON_QUBESPATH)/settings.py && \
|
||||
cp settings-$(SETTINGS_SUFFIX).pyc $(DESTDIR)$(PYTHON_QUBESPATH)/settings.pyc && \
|
||||
cp settings-$(SETTINGS_SUFFIX).pyo $(DESTDIR)$(PYTHON_QUBESPATH)/settings.pyo; \
|
||||
fi
|
||||
endif
|
||||
make -C storage install
|
@ -1,57 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2011 Marek Marczykowski <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
import sys
|
||||
from optparse import OptionParser
|
||||
from PyQt4.QtGui import QApplication,QMessageBox
|
||||
|
||||
app = None
|
||||
system_bus = None
|
||||
|
||||
def prepare_app():
|
||||
global app
|
||||
app = QApplication(sys.argv)
|
||||
app.setOrganizationName("The Qubes Project")
|
||||
app.setOrganizationDomain("http://qubes-os.org")
|
||||
app.setApplicationName("Qubes")
|
||||
|
||||
def ask(text, title="Question", yestoall=False):
|
||||
global app
|
||||
if app is None:
|
||||
prepare_app()
|
||||
|
||||
buttons = QMessageBox.Yes | QMessageBox.No
|
||||
if yestoall:
|
||||
buttons |= QMessageBox.YesToAll
|
||||
|
||||
reply = QMessageBox.question(None, title, text, buttons, defaultButton=QMessageBox.Yes)
|
||||
if reply == QMessageBox.Yes:
|
||||
return 0
|
||||
elif reply == QMessageBox.No:
|
||||
return 1
|
||||
elif reply == QMessageBox.YesToAll:
|
||||
return 2
|
||||
else:
|
||||
#?!
|
||||
return 127
|
||||
|
@ -1 +0,0 @@
|
||||
../core-modules
|
187
core/qubes.py
187
core/qubes.py
@ -1,187 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
qubes_base_dir = "/var/lib/qubes"
|
||||
system_path = {
|
||||
'qubes_guid_path': '/usr/bin/qubes-guid',
|
||||
'qrexec_daemon_path': '/usr/lib/qubes/qrexec-daemon',
|
||||
'qrexec_client_path': '/usr/lib/qubes/qrexec-client',
|
||||
'qubesdb_daemon_path': '/usr/sbin/qubesdb-daemon',
|
||||
|
||||
'qubes_base_dir': qubes_base_dir,
|
||||
|
||||
# Relative to qubes_base_dir
|
||||
'qubes_appvms_dir': 'appvms',
|
||||
'qubes_templates_dir': 'vm-templates',
|
||||
'qubes_servicevms_dir': 'servicevms',
|
||||
'qubes_store_filename': 'qubes.xml',
|
||||
'qubes_kernels_base_dir': 'vm-kernels',
|
||||
|
||||
# qubes_icon_dir is obsolete
|
||||
# use QIcon.fromTheme() where applicable
|
||||
'qubes_icon_dir': '/usr/share/icons/hicolor/128x128/devices',
|
||||
|
||||
'qrexec_policy_dir': '/etc/qubes-rpc/policy',
|
||||
|
||||
'config_template_pv': '/usr/share/qubes/vm-template.xml',
|
||||
|
||||
'qubes_pciback_cmd': '/usr/lib/qubes/unbind-pci-device.sh',
|
||||
'prepare_volatile_img_cmd': '/usr/lib/qubes/prepare-volatile-img.sh',
|
||||
}
|
||||
|
||||
vm_files = {
|
||||
'root_img': 'root.img',
|
||||
'rootcow_img': 'root-cow.img',
|
||||
'volatile_img': 'volatile.img',
|
||||
'private_img': 'private.img',
|
||||
'kernels_subdir': 'kernels',
|
||||
'firewall_conf': 'firewall.xml',
|
||||
'whitelisted_appmenus': 'whitelisted-appmenus.list',
|
||||
'updates_stat_file': 'updates.stat',
|
||||
}
|
||||
|
||||
defaults = {
|
||||
'libvirt_uri': 'xen:///',
|
||||
'memory': 400,
|
||||
'kernelopts': "nopat",
|
||||
'kernelopts_pcidevs': "nopat iommu=soft swiotlb=8192",
|
||||
|
||||
'dom0_update_check_interval': 6*3600,
|
||||
|
||||
'private_img_size': 2*1024*1024*1024,
|
||||
'root_img_size': 10*1024*1024*1024,
|
||||
|
||||
'storage_class': None,
|
||||
|
||||
# how long (in sec) to wait for VMs to shutdown,
|
||||
# before killing them (when used qvm-run with --wait option),
|
||||
'shutdown_counter_max': 60,
|
||||
|
||||
'vm_default_netmask': "255.255.255.0",
|
||||
|
||||
# Set later
|
||||
'appvm_label': None,
|
||||
'template_label': None,
|
||||
'servicevm_label': None,
|
||||
}
|
||||
|
||||
qubes_max_qid = 254
|
||||
qubes_max_netid = 254
|
||||
|
||||
##########################################
|
||||
|
||||
def register_qubes_vm_class(vm_class):
|
||||
QubesVmClasses[vm_class.__name__] = vm_class
|
||||
# register class as local for this module - to make it easy to import from
|
||||
# other modules
|
||||
setattr(sys.modules[__name__], vm_class.__name__, vm_class)
|
||||
|
||||
|
||||
class QubesDaemonPidfile(object):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.path = "/var/run/qubes/" + name + ".pid"
|
||||
|
||||
def create_pidfile(self):
|
||||
f = open (self.path, 'w')
|
||||
f.write(str(os.getpid()))
|
||||
f.close()
|
||||
|
||||
def pidfile_exists(self):
|
||||
return os.path.exists(self.path)
|
||||
|
||||
def read_pid(self):
|
||||
f = open (self.path)
|
||||
pid = f.read ().strip()
|
||||
f.close()
|
||||
return int(pid)
|
||||
|
||||
def pidfile_is_stale(self):
|
||||
if not self.pidfile_exists():
|
||||
return False
|
||||
|
||||
# check if the pid file is valid...
|
||||
proc_path = "/proc/" + str(self.read_pid()) + "/cmdline"
|
||||
if not os.path.exists (proc_path):
|
||||
print >> sys.stderr, \
|
||||
"Path {0} doesn't exist, assuming stale pidfile.".\
|
||||
format(proc_path)
|
||||
return True
|
||||
|
||||
return False # It's a good pidfile
|
||||
|
||||
def remove_pidfile(self):
|
||||
os.remove (self.path)
|
||||
|
||||
def __enter__ (self):
|
||||
# assumes the pidfile doesn't exist -- you should ensure it before opening the context
|
||||
self.create_pidfile()
|
||||
|
||||
def __exit__ (self, exc_type, exc_val, exc_tb):
|
||||
self.remove_pidfile()
|
||||
return False
|
||||
|
||||
### Initialization code
|
||||
|
||||
# Globally defined lables
|
||||
QubesVmLabels = {
|
||||
"red": QubesVmLabel(1, "0xcc0000", "red" ),
|
||||
"orange": QubesVmLabel(2, "0xf57900", "orange" ),
|
||||
"yellow": QubesVmLabel(3, "0xedd400", "yellow" ),
|
||||
"green": QubesVmLabel(4, "0x73d216", "green" ),
|
||||
"gray": QubesVmLabel(5, "0x555753", "gray" ),
|
||||
"blue": QubesVmLabel(6, "0x3465a4", "blue" ),
|
||||
"purple": QubesVmLabel(7, "0x75507b", "purple" ),
|
||||
"black": QubesVmLabel(8, "0x000000", "black" ),
|
||||
}
|
||||
|
||||
QubesDispVmLabels = {
|
||||
k: QubesVmLabel(index=v.index, color=v.color, name=v.name, dispvm=True)
|
||||
for k, v in QubesVmLabels.iteritems()
|
||||
}
|
||||
|
||||
defaults["appvm_label"] = QubesVmLabels["red"]
|
||||
defaults["template_label"] = QubesVmLabels["black"]
|
||||
defaults["servicevm_label"] = QubesVmLabels["red"]
|
||||
|
||||
|
||||
QubesVmClasses = {}
|
||||
modules_dir = os.path.join(os.path.dirname(__file__), 'modules')
|
||||
for module_file in sorted(os.listdir(modules_dir)):
|
||||
if not module_file.endswith(".py") or module_file == "__init__.py":
|
||||
continue
|
||||
__import__('qubes.modules.%s' % module_file[:-3])
|
||||
|
||||
try:
|
||||
import qubes.settings
|
||||
qubes.settings.apply(system_path, vm_files, defaults)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
for path_key in system_path.keys():
|
||||
if not os.path.isabs(system_path[path_key]):
|
||||
system_path[path_key] = os.path.join(
|
||||
system_path['qubes_base_dir'], system_path[path_key])
|
||||
|
||||
# vim:sw=4:et:
|
@ -1,844 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2011 Marek Marczykowski <marmarek@invisiblethingslab.com>
|
||||
# Copyright (C) 2014 Wojciech Porczyk <wojciech@porczyk.eu>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import string
|
||||
import errno
|
||||
from lxml import etree
|
||||
from lxml.etree import ElementTree, SubElement, Element
|
||||
|
||||
from qubes.qubes import QubesException
|
||||
from qubes.qubes import vmm,defaults
|
||||
from qubes.qubes import system_path,vm_files
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import re
|
||||
import time
|
||||
import stat
|
||||
import libvirt
|
||||
from qubes.qdb import QubesDB,Error,DisconnectedError
|
||||
|
||||
import xen.lowlevel.xc
|
||||
import xen.lowlevel.xs
|
||||
|
||||
# all frontends, prefer xvdi
|
||||
# TODO: get this from libvirt driver?
|
||||
AVAILABLE_FRONTENDS = ['xvd'+c for c in
|
||||
string.lowercase[8:]+string.lowercase[:8]]
|
||||
|
||||
class USBProxyNotInstalled(QubesException):
|
||||
pass
|
||||
|
||||
def mbytes_to_kmg(size):
|
||||
if size > 1024:
|
||||
return "%d GiB" % (size/1024)
|
||||
else:
|
||||
return "%d MiB" % size
|
||||
|
||||
def kbytes_to_kmg(size):
|
||||
if size > 1024:
|
||||
return mbytes_to_kmg(size/1024)
|
||||
else:
|
||||
return "%d KiB" % size
|
||||
|
||||
def bytes_to_kmg(size):
|
||||
if size > 1024:
|
||||
return kbytes_to_kmg(size/1024)
|
||||
else:
|
||||
return "%d B" % size
|
||||
|
||||
def size_to_human (size):
|
||||
"""Humane readable size, with 1/10 precission"""
|
||||
if size < 1024:
|
||||
return str (size);
|
||||
elif size < 1024*1024:
|
||||
return str(round(size/1024.0,1)) + ' KiB'
|
||||
elif size < 1024*1024*1024:
|
||||
return str(round(size/(1024.0*1024),1)) + ' MiB'
|
||||
else:
|
||||
return str(round(size/(1024.0*1024*1024),1)) + ' GiB'
|
||||
|
||||
def print_stdout(text):
|
||||
print (text)
|
||||
|
||||
def print_stderr(text):
|
||||
print >> sys.stderr, (text)
|
||||
|
||||
###### Block devices ########
|
||||
|
||||
def block_devid_to_name(devid):
|
||||
major = devid / 256
|
||||
minor = devid % 256
|
||||
|
||||
dev_class = ""
|
||||
if major == 202:
|
||||
dev_class = "xvd"
|
||||
elif major == 8:
|
||||
dev_class = "sd"
|
||||
else:
|
||||
raise QubesException("Unknown device class %d" % major)
|
||||
|
||||
if minor % 16 == 0:
|
||||
return "%s%c" % (dev_class, ord('a')+minor/16)
|
||||
else:
|
||||
return "%s%c%d" % (dev_class, ord('a')+minor/16, minor%16)
|
||||
|
||||
def block_name_to_majorminor(name):
|
||||
# check if it is already devid
|
||||
if isinstance(name, int):
|
||||
return (name / 256, name % 256)
|
||||
if name.isdigit():
|
||||
return (int(name) / 256, int(name) % 256)
|
||||
|
||||
if os.path.exists('/dev/%s' % name):
|
||||
blk_info = os.stat(os.path.realpath('/dev/%s' % name))
|
||||
if stat.S_ISBLK(blk_info.st_mode):
|
||||
return (blk_info.st_rdev / 256, blk_info.st_rdev % 256)
|
||||
|
||||
major = 0
|
||||
minor = 0
|
||||
dXpY_style = False
|
||||
disk = True
|
||||
|
||||
if name.startswith("xvd"):
|
||||
major = 202
|
||||
elif name.startswith("sd"):
|
||||
major = 8
|
||||
elif name.startswith("mmcblk"):
|
||||
dXpY_style = True
|
||||
major = 179
|
||||
elif name.startswith("scd"):
|
||||
disk = False
|
||||
major = 11
|
||||
elif name.startswith("sr"):
|
||||
disk = False
|
||||
major = 11
|
||||
elif name.startswith("loop"):
|
||||
dXpY_style = True
|
||||
disk = False
|
||||
major = 7
|
||||
elif name.startswith("md"):
|
||||
dXpY_style = True
|
||||
major = 9
|
||||
elif name.startswith("dm-"):
|
||||
disk = False
|
||||
major = 253
|
||||
else:
|
||||
# Unknown device
|
||||
return (0, 0)
|
||||
|
||||
if not dXpY_style:
|
||||
name_match = re.match(r"^([a-z]+)([a-z-])([0-9]*)$", name)
|
||||
else:
|
||||
name_match = re.match(r"^([a-z]+)([0-9]*)(?:p([0-9]+))?$", name)
|
||||
if not name_match:
|
||||
raise QubesException("Invalid device name: %s" % name)
|
||||
|
||||
if disk:
|
||||
if dXpY_style:
|
||||
minor = int(name_match.group(2))*8
|
||||
else:
|
||||
minor = (ord(name_match.group(2))-ord('a')) * 16
|
||||
else:
|
||||
minor = 0
|
||||
if name_match.group(3):
|
||||
minor += int(name_match.group(3))
|
||||
|
||||
return (major, minor)
|
||||
|
||||
|
||||
def block_name_to_devid(name):
|
||||
# check if it is already devid
|
||||
if isinstance(name, int):
|
||||
return name
|
||||
if name.isdigit():
|
||||
return int(name)
|
||||
|
||||
(major, minor) = block_name_to_majorminor(name)
|
||||
return major << 8 | minor
|
||||
|
||||
def block_find_unused_frontend(vm = None):
|
||||
assert vm is not None
|
||||
assert vm.is_running()
|
||||
|
||||
xml = vm.libvirt_domain.XMLDesc()
|
||||
parsed_xml = etree.fromstring(xml)
|
||||
used = [target.get('dev', None) for target in
|
||||
parsed_xml.xpath("//domain/devices/disk/target")]
|
||||
for dev in AVAILABLE_FRONTENDS:
|
||||
if dev not in used:
|
||||
return dev
|
||||
return None
|
||||
|
||||
def block_list_vm(vm, system_disks = False):
|
||||
name_re = re.compile(r"^[a-z0-9-]{1,12}$")
|
||||
device_re = re.compile(r"^[a-z0-9/-]{1,64}$")
|
||||
# FIXME: any better idea of desc_re?
|
||||
desc_re = re.compile(r"^.{1,255}$")
|
||||
mode_re = re.compile(r"^[rw]$")
|
||||
|
||||
assert vm is not None
|
||||
|
||||
if not vm.is_running():
|
||||
return []
|
||||
|
||||
devices_list = {}
|
||||
|
||||
try:
|
||||
untrusted_devices = vm.qdb.multiread('/qubes-block-devices/')
|
||||
except Error:
|
||||
vm.refresh()
|
||||
return {}
|
||||
|
||||
def get_dev_item(dev, item):
|
||||
return untrusted_devices.get(
|
||||
'/qubes-block-devices/%s/%s' % (dev, item),
|
||||
None)
|
||||
|
||||
untrusted_devices_names = list(set(map(lambda x: x.split("/")[2],
|
||||
untrusted_devices.keys())))
|
||||
for untrusted_dev_name in untrusted_devices_names:
|
||||
if name_re.match(untrusted_dev_name):
|
||||
dev_name = untrusted_dev_name
|
||||
untrusted_device_size = get_dev_item(dev_name, 'size')
|
||||
untrusted_device_desc = get_dev_item(dev_name, 'desc')
|
||||
untrusted_device_mode = get_dev_item(dev_name, 'mode')
|
||||
untrusted_device_device = get_dev_item(dev_name, 'device')
|
||||
if untrusted_device_desc is None or untrusted_device_mode is None\
|
||||
or untrusted_device_size is None:
|
||||
print >>sys.stderr, "Missing field in %s device parameters" %\
|
||||
dev_name
|
||||
continue
|
||||
if untrusted_device_device is None:
|
||||
untrusted_device_device = '/dev/' + dev_name
|
||||
if not device_re.match(untrusted_device_device):
|
||||
print >> sys.stderr, "Invalid %s device path in VM '%s'" % (
|
||||
dev_name, vm.name)
|
||||
continue
|
||||
device_device = untrusted_device_device
|
||||
if not untrusted_device_size.isdigit():
|
||||
print >> sys.stderr, "Invalid %s device size in VM '%s'" % (
|
||||
dev_name, vm.name)
|
||||
continue
|
||||
device_size = int(untrusted_device_size)
|
||||
if not desc_re.match(untrusted_device_desc):
|
||||
print >> sys.stderr, "Invalid %s device desc in VM '%s'" % (
|
||||
dev_name, vm.name)
|
||||
continue
|
||||
device_desc = untrusted_device_desc
|
||||
if not mode_re.match(untrusted_device_mode):
|
||||
print >> sys.stderr, "Invalid %s device mode in VM '%s'" % (
|
||||
dev_name, vm.name)
|
||||
continue
|
||||
device_mode = untrusted_device_mode
|
||||
|
||||
if not system_disks:
|
||||
if vm.qid == 0 and device_desc.startswith(system_path[
|
||||
"qubes_base_dir"]):
|
||||
continue
|
||||
|
||||
visible_name = "%s:%s" % (vm.name, dev_name)
|
||||
devices_list[visible_name] = {
|
||||
"name": visible_name,
|
||||
"vm": vm.name,
|
||||
"device": device_device,
|
||||
"size": device_size,
|
||||
"desc": device_desc,
|
||||
"mode": device_mode
|
||||
}
|
||||
|
||||
return devices_list
|
||||
|
||||
def block_list(qvmc = None, vm = None, system_disks = False):
|
||||
if vm is not None:
|
||||
if not vm.is_running():
|
||||
return []
|
||||
else:
|
||||
vm_list = [ vm ]
|
||||
else:
|
||||
if qvmc is None:
|
||||
raise QubesException("You must pass either qvm or vm argument")
|
||||
vm_list = qvmc.values()
|
||||
|
||||
devices_list = {}
|
||||
for vm in vm_list:
|
||||
devices_list.update(block_list_vm(vm, system_disks))
|
||||
return devices_list
|
||||
|
||||
def block_check_attached(qvmc, device):
|
||||
"""
|
||||
|
||||
@type qvmc: QubesVmCollection
|
||||
"""
|
||||
if qvmc is None:
|
||||
# TODO: ValueError
|
||||
raise QubesException("You need to pass qvmc argument")
|
||||
|
||||
for vm in qvmc.values():
|
||||
if vm.qid == 0:
|
||||
# Connecting devices to dom0 not supported
|
||||
continue
|
||||
if not vm.is_running():
|
||||
continue
|
||||
try:
|
||||
libvirt_domain = vm.libvirt_domain
|
||||
if libvirt_domain:
|
||||
xml = libvirt_domain.XMLDesc()
|
||||
else:
|
||||
xml = None
|
||||
except libvirt.libvirtError:
|
||||
if vmm.libvirt_conn.virConnGetLastError()[0] == libvirt.VIR_ERR_NO_DOMAIN:
|
||||
xml = None
|
||||
else:
|
||||
raise
|
||||
if xml:
|
||||
parsed_xml = etree.fromstring(xml)
|
||||
disks = parsed_xml.xpath("//domain/devices/disk")
|
||||
for disk in disks:
|
||||
backend_name = 'dom0'
|
||||
if disk.find('backenddomain') is not None:
|
||||
backend_name = disk.find('backenddomain').get('name')
|
||||
source = disk.find('source')
|
||||
if disk.get('type') == 'file':
|
||||
path = source.get('file')
|
||||
elif disk.get('type') == 'block':
|
||||
path = source.get('dev')
|
||||
else:
|
||||
# TODO: logger
|
||||
print >>sys.stderr, "Unknown disk type '%s' attached to " \
|
||||
"VM '%s'" % (source.get('type'),
|
||||
vm.name)
|
||||
continue
|
||||
if backend_name == device['vm'] and (path == device['device']
|
||||
or not path.startswith('/dev/') and path == device[
|
||||
'desc']):
|
||||
return {
|
||||
"frontend": disk.find('target').get('dev'),
|
||||
"vm": vm}
|
||||
return None
|
||||
|
||||
def device_attach_check(vm, backend_vm, device, frontend, mode):
|
||||
""" Checks all the parameters, dies on errors """
|
||||
if not vm.is_running():
|
||||
raise QubesException("VM %s not running" % vm.name)
|
||||
|
||||
if not backend_vm.is_running():
|
||||
raise QubesException("VM %s not running" % backend_vm.name)
|
||||
|
||||
if device['mode'] == 'r' and mode == 'w':
|
||||
raise QubesException("Cannot attach read-only device in read-write "
|
||||
"mode")
|
||||
|
||||
def block_attach(qvmc, vm, device, frontend=None, mode="w", auto_detach=False, wait=True):
|
||||
backend_vm = qvmc.get_vm_by_name(device['vm'])
|
||||
device_attach_check(vm, backend_vm, device, frontend, mode)
|
||||
if frontend is None:
|
||||
frontend = block_find_unused_frontend(vm)
|
||||
if frontend is None:
|
||||
raise QubesException("No unused frontend found")
|
||||
else:
|
||||
# Check if any device attached at this frontend
|
||||
xml = vm.libvirt_domain.XMLDesc()
|
||||
parsed_xml = etree.fromstring(xml)
|
||||
disks = parsed_xml.xpath("//domain/devices/disk/target[@dev='%s']" %
|
||||
frontend)
|
||||
if len(disks):
|
||||
raise QubesException("Frontend %s busy in VM %s, detach it first" % (frontend, vm.name))
|
||||
|
||||
# Check if this device is attached to some domain
|
||||
attached_vm = block_check_attached(qvmc, device)
|
||||
if attached_vm:
|
||||
if auto_detach:
|
||||
block_detach(attached_vm['vm'], attached_vm['frontend'])
|
||||
else:
|
||||
raise QubesException("Device %s from %s already connected to VM "
|
||||
"%s as %s" % (device['device'],
|
||||
backend_vm.name, attached_vm['vm'], attached_vm['frontend']))
|
||||
|
||||
disk = Element("disk")
|
||||
disk.set('type', 'block')
|
||||
disk.set('device', 'disk')
|
||||
SubElement(disk, 'driver').set('name', 'phy')
|
||||
SubElement(disk, 'source').set('dev', device['device'])
|
||||
SubElement(disk, 'target').set('dev', frontend)
|
||||
if backend_vm.qid != 0:
|
||||
SubElement(disk, 'backenddomain').set('name', device['vm'])
|
||||
if mode == "r":
|
||||
SubElement(disk, 'readonly')
|
||||
vm.libvirt_domain.attachDevice(etree.tostring(disk, encoding='utf-8'))
|
||||
try:
|
||||
# trigger watches to update device status
|
||||
# FIXME: this should be removed once libvirt will report such
|
||||
# events itself
|
||||
vm.qdb.write('/qubes-block-devices', '')
|
||||
except Error:
|
||||
pass
|
||||
|
||||
def block_detach(vm, frontend = "xvdi"):
|
||||
|
||||
xml = vm.libvirt_domain.XMLDesc()
|
||||
parsed_xml = etree.fromstring(xml)
|
||||
attached = parsed_xml.xpath("//domain/devices/disk")
|
||||
for disk in attached:
|
||||
if frontend is not None and disk.find('target').get('dev') != frontend:
|
||||
# Not the device we are looking for
|
||||
continue
|
||||
if frontend is None:
|
||||
# ignore system disks
|
||||
if disk.find('domain') == None and \
|
||||
disk.find('source').get('dev').startswith(system_path[
|
||||
"qubes_base_dir"]):
|
||||
continue
|
||||
vm.libvirt_domain.detachDevice(etree.tostring(disk, encoding='utf-8'))
|
||||
try:
|
||||
# trigger watches to update device status
|
||||
# FIXME: this should be removed once libvirt will report such
|
||||
# events itself
|
||||
vm.qdb.write('/qubes-block-devices', '')
|
||||
except Error:
|
||||
pass
|
||||
|
||||
def block_detach_all(vm):
|
||||
""" Detach all non-system devices"""
|
||||
|
||||
block_detach(vm, None)
|
||||
|
||||
####### USB devices ######
|
||||
|
||||
usb_ver_re = re.compile(r"^(1|2)$")
|
||||
usb_device_re = re.compile(r"^[0-9]+-[0-9]+(_[0-9]+)?$")
|
||||
usb_port_re = re.compile(r"^$|^[0-9]+-[0-9]+(\.[0-9]+)?$")
|
||||
usb_desc_re = re.compile(r"^[ -~]{1,255}$")
|
||||
# should match valid VM name
|
||||
usb_connected_to_re = re.compile(r"^[a-zA-Z][a-zA-Z0-9_.-]*$")
|
||||
|
||||
def usb_decode_device_from_qdb(qdb_encoded_device):
|
||||
""" recover actual device name (xenstore doesn't allow dot in key names, so it was translated to underscore) """
|
||||
return qdb_encoded_device.replace('_', '.')
|
||||
|
||||
def usb_encode_device_for_qdb(device):
|
||||
""" encode actual device name (xenstore doesn't allow dot in key names, so translated it into underscore) """
|
||||
return device.replace('.', '_')
|
||||
|
||||
def usb_list_vm(qvmc, vm):
|
||||
if not vm.is_running():
|
||||
return {}
|
||||
|
||||
try:
|
||||
untrusted_devices = vm.qdb.multiread('/qubes-usb-devices/')
|
||||
except Error:
|
||||
vm.refresh()
|
||||
return {}
|
||||
|
||||
def get_dev_item(dev, item):
|
||||
return untrusted_devices.get(
|
||||
'/qubes-usb-devices/%s/%s' % (dev, item),
|
||||
None)
|
||||
|
||||
devices = {}
|
||||
|
||||
untrusted_devices_names = list(set(map(lambda x: x.split("/")[2],
|
||||
untrusted_devices.keys())))
|
||||
for untrusted_dev_name in untrusted_devices_names:
|
||||
if usb_device_re.match(untrusted_dev_name):
|
||||
dev_name = untrusted_dev_name
|
||||
untrusted_device_desc = get_dev_item(dev_name, 'desc')
|
||||
if not usb_desc_re.match(untrusted_device_desc):
|
||||
print >> sys.stderr, "Invalid %s device desc in VM '%s'" % (
|
||||
dev_name, vm.name)
|
||||
continue
|
||||
device_desc = untrusted_device_desc
|
||||
|
||||
untrusted_connected_to = get_dev_item(dev_name, 'connected-to')
|
||||
if untrusted_connected_to:
|
||||
if not usb_connected_to_re.match(untrusted_connected_to):
|
||||
print >>sys.stderr, \
|
||||
"Invalid %s device 'connected-to' in VM '%s'" % (
|
||||
dev_name, vm.name)
|
||||
continue
|
||||
connected_to = qvmc.get_vm_by_name(untrusted_connected_to)
|
||||
if connected_to is None:
|
||||
print >>sys.stderr, \
|
||||
"Device {} appears to be connected to {}, " \
|
||||
"but such VM doesn't exist".format(
|
||||
dev_name, untrusted_connected_to)
|
||||
else:
|
||||
connected_to = None
|
||||
|
||||
device = usb_decode_device_from_qdb(dev_name)
|
||||
|
||||
full_name = vm.name + ':' + device
|
||||
|
||||
devices[full_name] = {
|
||||
'vm': vm,
|
||||
'device': device,
|
||||
'qdb_path': '/qubes-usb-devices/' + dev_name,
|
||||
'name': full_name,
|
||||
'desc': device_desc,
|
||||
'connected-to': connected_to,
|
||||
}
|
||||
return devices
|
||||
|
||||
|
||||
def usb_list(qvmc, vm=None):
|
||||
"""
|
||||
Returns a dictionary of USB devices (for PVUSB backends running in all VM).
|
||||
The dictionary is keyed by 'name' (see below), each element is a dictionary itself:
|
||||
vm = backend domain object
|
||||
device = device ID
|
||||
name = <backend-vm>:<device>
|
||||
desc = description
|
||||
"""
|
||||
if vm is not None:
|
||||
if not vm.is_running():
|
||||
return {}
|
||||
else:
|
||||
vm_list = [vm]
|
||||
else:
|
||||
vm_list = qvmc.values()
|
||||
|
||||
devices_list = {}
|
||||
for vm in vm_list:
|
||||
devices_list.update(usb_list_vm(qvmc, vm))
|
||||
return devices_list
|
||||
|
||||
def usb_check_attached(qvmc, device):
|
||||
"""Reread device attachment status"""
|
||||
vm = device['vm']
|
||||
untrusted_connected_to = vm.qdb.read(
|
||||
'{}/connected-to'.format(device['qdb_path']))
|
||||
if untrusted_connected_to:
|
||||
if not usb_connected_to_re.match(untrusted_connected_to):
|
||||
raise QubesException(
|
||||
"Invalid %s device 'connected-to' in VM '%s'" % (
|
||||
device['device'], vm.name))
|
||||
connected_to = qvmc.get_vm_by_name(untrusted_connected_to)
|
||||
if connected_to is None:
|
||||
print >>sys.stderr, \
|
||||
"Device {} appears to be connected to {}, " \
|
||||
"but such VM doesn't exist".format(
|
||||
device['device'], untrusted_connected_to)
|
||||
else:
|
||||
connected_to = None
|
||||
return connected_to
|
||||
|
||||
def usb_attach(qvmc, vm, device, auto_detach=False, wait=True):
|
||||
if not vm.is_running():
|
||||
raise QubesException("VM {} not running".format(vm.name))
|
||||
|
||||
if not device['vm'].is_running():
|
||||
raise QubesException("VM {} not running".format(device['vm'].name))
|
||||
|
||||
connected_to = usb_check_attached(qvmc, device)
|
||||
if connected_to:
|
||||
if auto_detach:
|
||||
usb_detach(qvmc, device)
|
||||
else:
|
||||
raise QubesException("Device {} already connected, to {}".format(
|
||||
device['name'], connected_to
|
||||
))
|
||||
|
||||
# set qrexec policy to allow this device
|
||||
policy_line = '{} {} allow\n'.format(vm.name, device['vm'].name)
|
||||
policy_path = '/etc/qubes-rpc/policy/qubes.USB+{}'.format(device['device'])
|
||||
policy_exists = os.path.exists(policy_path)
|
||||
if not policy_exists:
|
||||
try:
|
||||
fd = os.open(policy_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY)
|
||||
with os.fdopen(fd, 'w') as f:
|
||||
f.write(policy_line)
|
||||
except OSError as e:
|
||||
if e.errno == errno.EEXIST:
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
with open(policy_path, 'r+') as f:
|
||||
policy = f.readlines()
|
||||
policy.insert(0, policy_line)
|
||||
f.truncate(0)
|
||||
f.seek(0)
|
||||
f.write(''.join(policy))
|
||||
try:
|
||||
# and actual attach
|
||||
p = vm.run_service('qubes.USBAttach', passio_popen=True, user='root')
|
||||
(stdout, stderr) = p.communicate(
|
||||
'{} {}\n'.format(device['vm'].name, device['device']))
|
||||
if p.returncode == 127:
|
||||
raise USBProxyNotInstalled(
|
||||
"qubes-usb-proxy not installed in the VM")
|
||||
elif p.returncode != 0:
|
||||
# TODO: sanitize and include stdout
|
||||
sanitized_stderr = ''.join([c for c in stderr if ord(c) >= 0x20])
|
||||
raise QubesException('Device attach failed: {}'.format(
|
||||
sanitized_stderr))
|
||||
finally:
|
||||
# FIXME: there is a race condition here - some other process might
|
||||
# modify the file in the meantime. This may result in unexpected
|
||||
# denials, but will not allow too much
|
||||
if not policy_exists:
|
||||
os.unlink(policy_path)
|
||||
else:
|
||||
with open(policy_path, 'r+') as f:
|
||||
policy = f.readlines()
|
||||
policy.remove('{} {} allow\n'.format(vm.name, device['vm'].name))
|
||||
f.truncate(0)
|
||||
f.seek(0)
|
||||
f.write(''.join(policy))
|
||||
|
||||
def usb_detach(qvmc, vm, device):
|
||||
connected_to = usb_check_attached(qvmc, device)
|
||||
# detect race conditions; there is still race here, but much smaller
|
||||
if connected_to is None or connected_to.qid != vm.qid:
|
||||
raise QubesException(
|
||||
"Device {} not connected to VM {}".format(
|
||||
device['name'], vm.name))
|
||||
|
||||
p = device['vm'].run_service('qubes.USBDetach', passio_popen=True,
|
||||
user='root')
|
||||
(stdout, stderr) = p.communicate(
|
||||
'{}\n'.format(device['device']))
|
||||
if p.returncode != 0:
|
||||
# TODO: sanitize and include stdout
|
||||
raise QubesException('Device detach failed')
|
||||
|
||||
def usb_detach_all(qvmc, vm):
|
||||
for dev in usb_list(qvmc).values():
|
||||
connected_to = dev['connected-to']
|
||||
if connected_to is not None and connected_to.qid == vm.qid:
|
||||
usb_detach(qvmc, connected_to, dev)
|
||||
|
||||
####### QubesWatch ######
|
||||
|
||||
def only_in_first_list(l1, l2):
|
||||
ret=[]
|
||||
for i in l1:
|
||||
if not i in l2:
|
||||
ret.append(i)
|
||||
return ret
|
||||
|
||||
class QubesWatch(object):
|
||||
def __init__(self):
|
||||
self._qdb = {}
|
||||
self._qdb_events = {}
|
||||
self.block_callback = None
|
||||
self.meminfo_callback = None
|
||||
self.domain_callback = None
|
||||
libvirt.virEventRegisterDefaultImpl()
|
||||
# open new libvirt connection because above
|
||||
# virEventRegisterDefaultImpl is in practice effective only for new
|
||||
# connections
|
||||
self.libvirt_conn = libvirt.open(defaults['libvirt_uri'])
|
||||
self.libvirt_conn.domainEventRegisterAny(
|
||||
None,
|
||||
libvirt.VIR_DOMAIN_EVENT_ID_LIFECYCLE,
|
||||
self._domain_list_changed, None)
|
||||
self.libvirt_conn.domainEventRegisterAny(
|
||||
None,
|
||||
libvirt.VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED,
|
||||
self._device_removed, None)
|
||||
# TODO: device attach libvirt event
|
||||
for vm in vmm.libvirt_conn.listAllDomains():
|
||||
try:
|
||||
if vm.isActive():
|
||||
self._register_watches(vm)
|
||||
except libvirt.libvirtError as e:
|
||||
# this will happen if we loose a race with another tool,
|
||||
# which can just remove the domain
|
||||
if e.get_error_code() == libvirt.VIR_ERR_NO_DOMAIN:
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
# and for dom0
|
||||
self._register_watches(None)
|
||||
|
||||
def _qdb_handler(self, watch, fd, events, domain_name):
|
||||
try:
|
||||
path = self._qdb[domain_name].read_watch()
|
||||
except DisconnectedError:
|
||||
libvirt.virEventRemoveHandle(watch)
|
||||
del(self._qdb_events[domain_name])
|
||||
self._qdb[domain_name].close()
|
||||
del(self._qdb[domain_name])
|
||||
return
|
||||
if path.startswith('/qubes-block-devices'):
|
||||
if self.block_callback is not None:
|
||||
self.block_callback(domain_name)
|
||||
|
||||
|
||||
def setup_block_watch(self, callback):
|
||||
self.block_callback = callback
|
||||
|
||||
def setup_meminfo_watch(self, callback):
|
||||
raise NotImplementedError
|
||||
|
||||
def setup_domain_watch(self, callback):
|
||||
self.domain_callback = callback
|
||||
|
||||
def get_meminfo_key(self, xid):
|
||||
return '/local/domain/%s/memory/meminfo' % xid
|
||||
|
||||
def _register_watches(self, libvirt_domain):
|
||||
if libvirt_domain and libvirt_domain.ID() == 0:
|
||||
# don't use libvirt object for dom0, to always have the same
|
||||
# hardcoded "dom0" name
|
||||
libvirt_domain = None
|
||||
if libvirt_domain:
|
||||
name = libvirt_domain.name()
|
||||
if name in self._qdb:
|
||||
return
|
||||
if not libvirt_domain.isActive():
|
||||
return
|
||||
# open separate connection to Qubes DB:
|
||||
# 1. to not confuse pull() with responses to real commands sent from
|
||||
# other threads (like read, write etc) with watch events
|
||||
# 2. to not think whether QubesDB is thread-safe (it isn't)
|
||||
try:
|
||||
self._qdb[name] = QubesDB(name)
|
||||
except Error as e:
|
||||
if e.args[0] != 2:
|
||||
raise
|
||||
libvirt.virEventAddTimeout(500, self._retry_register_watches,
|
||||
libvirt_domain)
|
||||
return
|
||||
else:
|
||||
name = "dom0"
|
||||
if name in self._qdb:
|
||||
return
|
||||
self._qdb[name] = QubesDB(name)
|
||||
try:
|
||||
self._qdb[name].watch('/qubes-block-devices')
|
||||
except Error as e:
|
||||
if e.args[0] == 102: # Connection reset by peer
|
||||
# QubesDB daemon not running - most likely we've connected to
|
||||
# stale daemon which just exited; retry later
|
||||
libvirt.virEventAddTimeout(500, self._retry_register_watches,
|
||||
libvirt_domain)
|
||||
return
|
||||
self._qdb_events[name] = libvirt.virEventAddHandle(
|
||||
self._qdb[name].watch_fd(),
|
||||
libvirt.VIR_EVENT_HANDLE_READABLE,
|
||||
self._qdb_handler, name)
|
||||
|
||||
def _retry_register_watches(self, timer, libvirt_domain):
|
||||
libvirt.virEventRemoveTimeout(timer)
|
||||
self._register_watches(libvirt_domain)
|
||||
|
||||
def _unregister_watches(self, libvirt_domain):
|
||||
if libvirt_domain and libvirt_domain.ID() == 0:
|
||||
name = "dom0"
|
||||
else:
|
||||
name = libvirt_domain.name()
|
||||
if name in self._qdb_events:
|
||||
libvirt.virEventRemoveHandle(self._qdb_events[name])
|
||||
del(self._qdb_events[name])
|
||||
if name in self._qdb:
|
||||
self._qdb[name].close()
|
||||
del(self._qdb[name])
|
||||
|
||||
def _domain_list_changed(self, conn, domain, event, reason, param):
|
||||
# use VIR_DOMAIN_EVENT_RESUMED instead of VIR_DOMAIN_EVENT_STARTED to
|
||||
# make sure that qubesdb daemon is already running
|
||||
if event == libvirt.VIR_DOMAIN_EVENT_RESUMED:
|
||||
self._register_watches(domain)
|
||||
elif event == libvirt.VIR_DOMAIN_EVENT_STOPPED:
|
||||
self._unregister_watches(domain)
|
||||
else:
|
||||
# ignore other events for now
|
||||
return None
|
||||
if self.domain_callback:
|
||||
self.domain_callback(name=domain.name(), uuid=domain.UUID())
|
||||
|
||||
def _device_removed(self, conn, domain, device, param):
|
||||
if self.block_callback is not None:
|
||||
self.block_callback(domain.name())
|
||||
|
||||
def watch_loop(self):
|
||||
while True:
|
||||
libvirt.virEventRunDefaultImpl()
|
||||
|
||||
##### updates check #####
|
||||
|
||||
#
|
||||
# XXX this whole section is a new global property
|
||||
# TODO make event handlers
|
||||
#
|
||||
|
||||
UPDATES_DOM0_DISABLE_FLAG='/var/lib/qubes/updates/disable-updates'
|
||||
UPDATES_DEFAULT_VM_DISABLE_FLAG=\
|
||||
'/var/lib/qubes/updates/vm-default-disable-updates'
|
||||
|
||||
def updates_vms_toggle(qvm_collection, value):
|
||||
# Flag for new VMs
|
||||
if value:
|
||||
if os.path.exists(UPDATES_DEFAULT_VM_DISABLE_FLAG):
|
||||
os.unlink(UPDATES_DEFAULT_VM_DISABLE_FLAG)
|
||||
else:
|
||||
open(UPDATES_DEFAULT_VM_DISABLE_FLAG, "w").close()
|
||||
|
||||
# Change for existing VMs
|
||||
for vm in qvm_collection.values():
|
||||
if vm.qid == 0:
|
||||
continue
|
||||
if value:
|
||||
vm.services.pop('qubes-update-check', None)
|
||||
if vm.is_running():
|
||||
try:
|
||||
vm.run("systemctl start qubes-update-check.timer",
|
||||
user="root")
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
vm.services['qubes-update-check'] = False
|
||||
if vm.is_running():
|
||||
try:
|
||||
vm.run("systemctl stop qubes-update-check.timer",
|
||||
user="root")
|
||||
except:
|
||||
pass
|
||||
def updates_dom0_toggle(qvm_collection, value):
|
||||
if value:
|
||||
if os.path.exists(UPDATES_DOM0_DISABLE_FLAG):
|
||||
os.unlink(UPDATES_DOM0_DISABLE_FLAG)
|
||||
else:
|
||||
open(UPDATES_DOM0_DISABLE_FLAG, "w").close()
|
||||
|
||||
def updates_dom0_status(qvm_collection):
|
||||
return not os.path.exists(UPDATES_DOM0_DISABLE_FLAG)
|
||||
|
||||
def updates_vms_status(qvm_collection):
|
||||
# default value:
|
||||
status = not os.path.exists(UPDATES_DEFAULT_VM_DISABLE_FLAG)
|
||||
# check if all the VMs uses the default value
|
||||
for vm in qvm_collection.values():
|
||||
if vm.qid == 0:
|
||||
continue
|
||||
if vm.services.get('qubes-update-check', True) != status:
|
||||
# "mixed"
|
||||
return None
|
||||
return status
|
||||
|
||||
# vim:sw=4:et:
|
@ -1,11 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from qubes.storage.xen import XenStorage, XenPool
|
||||
|
||||
|
||||
def apply(system_path, vm_files, defaults):
|
||||
defaults['storage_class'] = XenStorage
|
||||
defaults['pool_drivers'] = {'xen': XenPool}
|
||||
defaults['pool_config'] = {'dir_path': '/var/lib/qubes/'}
|
@ -1,24 +0,0 @@
|
||||
OS ?= Linux
|
||||
|
||||
SYSCONFDIR ?= /etc
|
||||
PYTHON_QUBESPATH = $(PYTHON_SITEPATH)/qubes
|
||||
|
||||
all:
|
||||
python -m compileall .
|
||||
python -O -m compileall .
|
||||
|
||||
install:
|
||||
ifndef PYTHON_SITEPATH
|
||||
$(error PYTHON_SITEPATH not defined)
|
||||
endif
|
||||
mkdir -p $(DESTDIR)$(PYTHON_QUBESPATH)/storage
|
||||
cp __init__.py $(DESTDIR)$(PYTHON_QUBESPATH)/storage
|
||||
cp __init__.py[co] $(DESTDIR)$(PYTHON_QUBESPATH)/storage
|
||||
mkdir -p $(DESTDIR)$(SYSCONFDIR)/qubes
|
||||
cp storage.conf $(DESTDIR)$(SYSCONFDIR)/qubes/
|
||||
ifneq ($(BACKEND_VMM),)
|
||||
if [ -r $(BACKEND_VMM).py ]; then \
|
||||
cp $(BACKEND_VMM).py $(DESTDIR)$(PYTHON_QUBESPATH)/storage && \
|
||||
cp $(BACKEND_VMM).py[co] $(DESTDIR)$(PYTHON_QUBESPATH)/storage; \
|
||||
fi
|
||||
endif
|
@ -1,48 +0,0 @@
|
||||
#!/usr/bin/python -O
|
||||
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2014 Wojciech Porczyk <wojciech@porczyk.eu>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
import subprocess
|
||||
import unittest
|
||||
|
||||
import qubes.qubesutils
|
||||
|
||||
|
||||
class TestCaseFunctionsAndConstants(unittest.TestCase):
|
||||
def check_output_int(self, cmd):
|
||||
return int(subprocess.check_output(cmd).strip().split(None, 1)[0])
|
||||
|
||||
def test_00_BLKSIZE(self):
|
||||
# this may fail on systems without st_blocks
|
||||
self.assertEqual(qubes.qubesutils.BLKSIZE, self.check_output_int(['stat', '-c%B', '.']))
|
||||
|
||||
def test_01_get_size_one(self):
|
||||
# this may fail on systems without st_blocks
|
||||
self.assertEqual(qubes.qubesutils.get_disk_usage_one(os.stat('.')),
|
||||
self.check_output_int(['stat', '-c%b', '.']) * qubes.qubesutils.BLKSIZE)
|
||||
|
||||
def test_02_get_size(self):
|
||||
self.assertEqual(qubes.qubesutils.get_disk_usage('.'),
|
||||
self.check_output_int(['du', '-s', '--block-size=1', '.']))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
2
dispvm/.gitignore
vendored
2
dispvm/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
qubes_restore
|
||||
xenstore-watch
|
@ -1,21 +0,0 @@
|
||||
UNITDIR ?= /usr/lib/systemd/system
|
||||
|
||||
all:
|
||||
true
|
||||
|
||||
clean:
|
||||
true
|
||||
|
||||
install:
|
||||
mkdir -p $(DESTDIR)/etc/xen/scripts
|
||||
cp block.qubes $(DESTDIR)/etc/xen/scripts
|
||||
mkdir -p $(DESTDIR)/usr/bin $(DESTDIR)/usr/lib/qubes
|
||||
cp qubes-prepare-saved-domain.sh $(DESTDIR)/usr/lib/qubes
|
||||
cp qubes-update-dispvm-savefile-with-progress.sh $(DESTDIR)/usr/lib/qubes
|
||||
cp qfile-daemon-dvm $(DESTDIR)/usr/lib/qubes
|
||||
mkdir -p $(DESTDIR)$(UNITDIR)
|
||||
cp startup-dvm.sh $(DESTDIR)/usr/lib/qubes
|
||||
cp qubes-setupdvm.service $(DESTDIR)$(UNITDIR)
|
||||
|
||||
|
||||
|
@ -1,61 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
HOTPLUG_STORE="/var/run/xen-hotplug/${XENBUS_PATH//\//-}"
|
||||
|
||||
hd_arr[10]=a
|
||||
hd_arr[11]=b
|
||||
hd_arr[12]=c
|
||||
hd_arr[13]=d
|
||||
hd_arr[14]=e
|
||||
hd_arr[15]=f
|
||||
|
||||
hexdigit()
|
||||
{
|
||||
if [ $1 -lt 10 ] ; then
|
||||
RET=$1
|
||||
else
|
||||
RET=${hd_arr[$1]}
|
||||
fi
|
||||
}
|
||||
|
||||
hexnumber()
|
||||
{
|
||||
hexdigit $(($1/16))
|
||||
ret2=$RET
|
||||
hexdigit $(($1%16))
|
||||
HEXNUMBER="$ret2"$RET
|
||||
}
|
||||
|
||||
|
||||
process()
|
||||
{
|
||||
if ! [ "x""$1" = "xfile" ] ; then
|
||||
exec flock /var/run/qubes/hotplug-block /etc/xen/scripts/block $ORIG_ARGS
|
||||
fi
|
||||
while true ; do
|
||||
dev=$(losetup -f --show $2)
|
||||
if [ -n "$dev" ] ; then break ; fi
|
||||
done
|
||||
hexnumber ${dev:9:70}
|
||||
xenstore-write "$XENBUS_PATH/node" "$dev" \
|
||||
"$XENBUS_PATH/physical-device" "7:"$HEXNUMBER \
|
||||
"$XENBUS_PATH/hotplug-status" connected
|
||||
echo "$dev" > "$HOTPLUG_STORE-node"
|
||||
echo "file" > "$HOTPLUG_STORE-type"
|
||||
}
|
||||
|
||||
#exec 2>>/tmp/block.$$
|
||||
#set -x
|
||||
export PATH="/sbin:/bin:/usr/bin:/usr/sbin:$PATH"
|
||||
|
||||
XENBUS_PATH="${XENBUS_PATH:?}"
|
||||
if ! [ "$1" = "add" ] || ! [ -f /var/run/qubes/fast-block-attach ] ; then
|
||||
script=$(xenstore-read "$XENBUS_PATH/script")
|
||||
exec flock /var/run/qubes/hotplug-block $script "$@"
|
||||
fi
|
||||
|
||||
ORIG_ARGS="$@"
|
||||
|
||||
vars=$(xenstore-read "$XENBUS_PATH/type" "$XENBUS_PATH/params")
|
||||
process $vars
|
||||
exit 0
|
@ -1,200 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# coding=utf-8
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
|
||||
# Copyright (C) 2013-2015 Marek Marczykowski-Górecki
|
||||
# <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import shutil
|
||||
import time
|
||||
|
||||
from qubes.qubes import QubesVmCollection, QubesException
|
||||
from qubes.qubes import QubesDispVmLabels
|
||||
from qubes.notify import tray_notify, tray_notify_error, tray_notify_init
|
||||
|
||||
|
||||
current_savefile = '/var/run/qubes/current-savefile'
|
||||
current_savefile_vmdir = '/var/lib/qubes/dvmdata/vmdir'
|
||||
|
||||
|
||||
class QfileDaemonDvm:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
@staticmethod
|
||||
def get_disp_templ():
|
||||
vmdir = os.readlink(current_savefile_vmdir)
|
||||
return vmdir.split('/')[-1]
|
||||
|
||||
def do_get_dvm(self):
|
||||
tray_notify("Starting new DispVM...", "red")
|
||||
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_writing()
|
||||
try:
|
||||
|
||||
tar_process = subprocess.Popen(
|
||||
['bsdtar', '-C', current_savefile_vmdir,
|
||||
'-xSUf', os.path.join(current_savefile_vmdir, 'saved-cows.tar')])
|
||||
|
||||
qvm_collection.load()
|
||||
print >>sys.stderr, "time=%s, collection loaded" % (str(time.time()))
|
||||
|
||||
vm = qvm_collection.get_vm_by_name(self.name)
|
||||
if vm is None:
|
||||
sys.stderr.write('Domain ' + self.name + ' does not exist ?')
|
||||
return None
|
||||
label = vm.label
|
||||
if len(sys.argv) > 4 and len(sys.argv[4]) > 0:
|
||||
assert sys.argv[4] in QubesDispVmLabels.keys(), "Invalid label"
|
||||
label = QubesDispVmLabels[sys.argv[4]]
|
||||
disp_templ = self.get_disp_templ()
|
||||
vm_disptempl = qvm_collection.get_vm_by_name(disp_templ)
|
||||
if vm_disptempl is None:
|
||||
sys.stderr.write('Domain ' + disp_templ + ' does not exist ?')
|
||||
return None
|
||||
dispvm = qvm_collection.add_new_vm('QubesDisposableVm',
|
||||
disp_template=vm_disptempl,
|
||||
label=label)
|
||||
print >>sys.stderr, "time=%s, VM created" % (str(time.time()))
|
||||
# By default inherit firewall rules from calling VM
|
||||
disp_firewall_conf = '/var/run/qubes/%s-firewall.xml' % dispvm.name
|
||||
dispvm.firewall_conf = disp_firewall_conf
|
||||
if os.path.exists(vm.firewall_conf):
|
||||
shutil.copy(vm.firewall_conf, disp_firewall_conf)
|
||||
elif vm.qid == 0 and os.path.exists(vm_disptempl.firewall_conf):
|
||||
# for DispVM called from dom0, copy use rules from DispVM template
|
||||
shutil.copy(vm_disptempl.firewall_conf, disp_firewall_conf)
|
||||
if len(sys.argv) > 5 and len(sys.argv[5]) > 0:
|
||||
assert os.path.exists(sys.argv[5]), "Invalid firewall.conf location"
|
||||
dispvm.firewall_conf = sys.argv[5]
|
||||
if vm.qid != 0:
|
||||
dispvm.uses_default_netvm = False
|
||||
# netvm can be changed before restore,
|
||||
# but cannot be enabled/disabled
|
||||
if (dispvm.netvm is None) == (vm.dispvm_netvm is None):
|
||||
dispvm.netvm = vm.dispvm_netvm
|
||||
# Wait for tar to finish
|
||||
if tar_process.wait() != 0:
|
||||
sys.stderr.write('Failed to unpack saved-cows.tar')
|
||||
return None
|
||||
print >>sys.stderr, "time=%s, VM starting" % (str(time.time()))
|
||||
try:
|
||||
dispvm.start()
|
||||
except (MemoryError, QubesException) as e:
|
||||
tray_notify_error(str(e))
|
||||
raise
|
||||
if vm.qid != 0:
|
||||
# if need to enable/disable netvm, do it while DispVM is alive
|
||||
if (dispvm.netvm is None) != (vm.dispvm_netvm is None):
|
||||
dispvm.netvm = vm.dispvm_netvm
|
||||
print >>sys.stderr, "time=%s, VM started" % (str(time.time()))
|
||||
qvm_collection.save()
|
||||
finally:
|
||||
qvm_collection.unlock_db()
|
||||
# Reload firewall rules
|
||||
print >>sys.stderr, "time=%s, reloading firewall" % (str(time.time()))
|
||||
for vm in qvm_collection.values():
|
||||
if vm.is_proxyvm() and vm.is_running():
|
||||
vm.write_iptables_qubesdb_entry()
|
||||
|
||||
return dispvm
|
||||
|
||||
@staticmethod
|
||||
def dvm_setup_ok():
|
||||
dvmdata_dir = '/var/lib/qubes/dvmdata/'
|
||||
if not os.path.isfile(current_savefile):
|
||||
return False
|
||||
if not os.path.isfile(dvmdata_dir+'default-savefile') or \
|
||||
not os.path.isfile(dvmdata_dir+'savefile-root'):
|
||||
return False
|
||||
dvm_mtime = os.stat(current_savefile).st_mtime
|
||||
root_mtime = os.stat(dvmdata_dir+'savefile-root').st_mtime
|
||||
if dvm_mtime < root_mtime:
|
||||
template_name = os.path.basename(
|
||||
os.path.dirname(os.readlink(dvmdata_dir+'savefile-root')))
|
||||
if subprocess.call(["xl", "domid", template_name],
|
||||
stdout=open(os.devnull, "w")) == 0:
|
||||
tray_notify("For optimum performance, you should not "
|
||||
"start DispVM when its template is running.", "red")
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_dvm(self):
|
||||
if not self.dvm_setup_ok():
|
||||
if os.system("/usr/lib/qubes/"
|
||||
"qubes-update-dispvm-savefile-with-progress.sh"
|
||||
" >/dev/null </dev/null") != 0:
|
||||
tray_notify_error("DVM savefile creation failed")
|
||||
return None
|
||||
return self.do_get_dvm()
|
||||
|
||||
@staticmethod
|
||||
def finish_disposable(name):
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_writing()
|
||||
qvm_collection.load()
|
||||
vm = qvm_collection.get_vm_by_name(name)
|
||||
if vm is None:
|
||||
qvm_collection.unlock_db()
|
||||
return False
|
||||
|
||||
try:
|
||||
vm.force_shutdown()
|
||||
except QubesException:
|
||||
# VM already destroyed
|
||||
pass
|
||||
qvm_collection.pop(vm.qid)
|
||||
qvm_collection.save()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
|
||||
def main():
|
||||
exec_index = sys.argv[1]
|
||||
if exec_index == "FINISH":
|
||||
QfileDaemonDvm.finish_disposable(sys.argv[2])
|
||||
return
|
||||
|
||||
src_vmname = sys.argv[2]
|
||||
user = sys.argv[3]
|
||||
# accessed directly by get_dvm()
|
||||
# sys.argv[4] - override label
|
||||
# sys.argv[5] - override firewall
|
||||
|
||||
print >>sys.stderr, "time=%s, qfile-daemon-dvm init" % (str(time.time()))
|
||||
tray_notify_init()
|
||||
print >>sys.stderr, "time=%s, creating DispVM" % (str(time.time()))
|
||||
qfile = QfileDaemonDvm(src_vmname)
|
||||
dispvm = qfile.get_dvm()
|
||||
if dispvm is not None:
|
||||
if exec_index == "LAUNCH":
|
||||
print dispvm.name
|
||||
return
|
||||
|
||||
print >>sys.stderr, "time=%s, starting VM process" % (str(time.time()))
|
||||
subprocess.call(['/usr/lib/qubes/qrexec-client', '-d', dispvm.name,
|
||||
user+':exec /usr/lib/qubes/qubes-rpc-multiplexer ' +
|
||||
exec_index + " " + src_vmname])
|
||||
QfileDaemonDvm.finish_disposable(dispvm.name)
|
||||
|
||||
main()
|
@ -1,86 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -o pipefail
|
||||
|
||||
get_encoded_script()
|
||||
{
|
||||
ENCODED_SCRIPT=`
|
||||
if [ "$1" == "vm-default" ]; then
|
||||
echo /usr/lib/qubes/dispvm-prerun.sh
|
||||
else
|
||||
cat "$1"
|
||||
fi | base64 -w0` || exit 1
|
||||
}
|
||||
|
||||
if [ $# != 2 -a $# != 3 ] ; then
|
||||
echo "usage: $0 domainname savefile_to_be_created [preload script]" >&2
|
||||
exit 1
|
||||
fi
|
||||
export PATH=$PATH:/sbin:/usr/sbin
|
||||
if [ $# = 3 ] ; then
|
||||
get_encoded_script $3
|
||||
fi
|
||||
VMDIR=/var/lib/qubes/appvms/$1
|
||||
if ! [ -d $VMDIR ] ; then
|
||||
echo "$VMDIR does not exist ?" >&2
|
||||
exit 1
|
||||
fi
|
||||
if ! qvm-start $1 --dvm ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ID=`virsh -c xen:/// domid $1`
|
||||
echo "Waiting for DVM $1 ..." >&2
|
||||
if [ -n "$ENCODED_SCRIPT" ] ; then
|
||||
qubesdb-write -d $1 /qubes-save-script "$ENCODED_SCRIPT"
|
||||
fi
|
||||
#set -x
|
||||
qubesdb-write -d $1 /qubes-save-request 1
|
||||
qubesdb-watch -d $1 /qubes-used-mem
|
||||
qubesdb-read -d $1 /qubes-gateway | \
|
||||
cut -d . -f 3 | tr -d "\n" > $VMDIR/netvm-id.txt
|
||||
kill `cat /var/run/qubes/guid-running.$ID`
|
||||
# FIXME: get connection URI from core scripts
|
||||
virsh -c xen:/// detach-disk $1 xvdb
|
||||
MEM=$(qubesdb-read -d $1 /qubes-used-mem | grep '^[0-9]\+$' | head -n 1)
|
||||
echo "DVM boot complete, memory used=$MEM. Saving image..." >&2
|
||||
QMEMMAN_STOP=/var/run/qubes/do-not-membalance
|
||||
touch $QMEMMAN_STOP
|
||||
virsh -c xen:/// setmem $1 $MEM
|
||||
# Add some safety margin
|
||||
virsh -c xen:/// setmaxmem $1 $[ $MEM + 1024 ]
|
||||
# Stop qubesdb daemon now, so VM can restart it later
|
||||
kill `cat /var/run/qubes/qubesdb.$1.pid`
|
||||
sleep 1
|
||||
touch $2
|
||||
if ! virsh -c xen:/// save $1 $2; then
|
||||
rm -f $QMEMMAN_STOP
|
||||
qvm-kill $1
|
||||
exit 1
|
||||
fi
|
||||
rm -f $QMEMMAN_STOP
|
||||
# Do not allow smaller allocation than 400MB. If that small number comes from
|
||||
# an error, it would prevent further savefile regeneration (because VM would
|
||||
# not start with too little memory). Also 'maxmem' depends on 'memory', so
|
||||
# 400MB is sane compromise.
|
||||
if [ "$MEM" -lt 409600 ]; then
|
||||
qvm-prefs -s $1 memory 400
|
||||
else
|
||||
qvm-prefs -s $1 memory $[ $MEM / 1024 ]
|
||||
fi
|
||||
ln -snf $VMDIR /var/lib/qubes/dvmdata/vmdir
|
||||
cd $VMDIR
|
||||
fstype=`df --output=fstype $VMDIR | tail -n 1`
|
||||
if [ "$fstype" = "tmpfs" ]; then
|
||||
# bsdtar doesn't work on tmpfs because FS_IOC_FIEMAP ioctl isn't supported
|
||||
# there
|
||||
tar -cSf saved-cows.tar volatile.img || exit 1
|
||||
else
|
||||
errors=`bsdtar -cSf saved-cows.tar volatile.img 2>&1`
|
||||
if [ -n "$errors" ]; then
|
||||
echo "Failed to create saved-cows.tar: $errors" >&2
|
||||
rm -f saved-cows.tar
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
echo "DVM savefile created successfully."
|
@ -1,12 +0,0 @@
|
||||
[Unit]
|
||||
Description=Qubes DispVM startup setup
|
||||
After=qubes-core.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/lib/qubes/startup-dvm.sh
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
# Cover legacy init.d script
|
||||
Alias=qubes_setupdvm.service
|
@ -1,23 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
line1="<b>Please wait (up to 120s) while the DispVM savefile is being updated.</b>"
|
||||
line2="<i><small>This only happens when you have updated the template.</small></i>"
|
||||
line3="<i><small>Next time will be much faster.</small></i>"
|
||||
|
||||
if [ -n "$KDE_FULL_SESSION" ]; then
|
||||
br="<br/>"
|
||||
else
|
||||
br="
|
||||
"
|
||||
fi
|
||||
notify-send --icon=/usr/share/qubes/icons/qubes.png --expire-time=120000 \
|
||||
"Updating default DispVM savefile" "$line1$br$line2$br$line3"
|
||||
|
||||
ret=0
|
||||
|
||||
rm -f /var/run/qubes/qvm-create-default-dvm.stdout
|
||||
if ! qvm-create-default-dvm --used-template --default-script >/var/run/qubes/qvm-create-default-dvm.stdout </dev/null ; then
|
||||
ret=1
|
||||
fi
|
||||
|
||||
exit $ret
|
@ -1,22 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Setup DispVM things at Qubes system startup
|
||||
|
||||
printf "\x00\x00\x00\x00" > /var/run/qubes/dispVM.seq
|
||||
chown root:qubes /var/run/qubes/dispVM.seq
|
||||
chmod 660 /var/run/qubes/dispVM.seq
|
||||
DEFAULT=/var/lib/qubes/dvmdata/default-savefile
|
||||
# setup DispVM files only when they exists
|
||||
if [ -r $DEFAULT ]; then
|
||||
if [ -f /var/lib/qubes/dvmdata/dont-use-shm ] ; then
|
||||
ln -s $DEFAULT /var/run/qubes/current-savefile
|
||||
else
|
||||
mkdir -m 770 /dev/shm/qubes
|
||||
chown root.qubes /dev/shm/qubes
|
||||
cp -a $(readlink $DEFAULT) /dev/shm/qubes/current-savefile
|
||||
chown root.qubes /dev/shm/qubes/current-savefile
|
||||
chmod 660 /dev/shm/qubes/current-savefile
|
||||
ln -s /dev/shm/qubes/current-savefile /var/run/qubes/current-savefile
|
||||
fi
|
||||
fi
|
||||
|
@ -7,7 +7,6 @@ install:
|
||||
cp block-snapshot $(DESTDIR)/etc/xen/scripts
|
||||
ln -s block-snapshot $(DESTDIR)/etc/xen/scripts/block-origin
|
||||
install -d $(DESTDIR)/etc/xdg/autostart
|
||||
install -m 0644 qubes-guid.desktop $(DESTDIR)/etc/xdg/autostart/
|
||||
install -m 0644 qrexec-policy-agent.desktop $(DESTDIR)/etc/xdg/autostart/
|
||||
install -m 0644 -D tmpfiles-qubes.conf $(DESTDIR)/usr/lib/tmpfiles.d/qubes.conf
|
||||
install -d $(DESTDIR)/etc/dbus-1/system.d
|
||||
|
@ -1,7 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Name=Qubes Guid
|
||||
Comment=Starts Dom0 GUI daemon for Qubes VMs
|
||||
Icon=qubes
|
||||
Exec=qvm-run --all true
|
||||
Terminal=false
|
||||
Type=Application
|
@ -1,161 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2012 Marek Marczykowski <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
from qubes.qubes import QubesVmCollection
|
||||
from qubes.qubes import QubesHost
|
||||
from qubes.qubes import system_path
|
||||
from optparse import OptionParser
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
from qubes.qubes import vmm
|
||||
|
||||
|
||||
def handle_vm(vms, label, new_value = None):
|
||||
functions = { # label: [ getter, setter ],
|
||||
'default-netvm': [ 'get_default_netvm', 'set_default_netvm' ],
|
||||
'default-fw-netvm': [ 'get_default_fw_netvm', 'set_default_fw_netvm' ],
|
||||
'default-template': [ 'get_default_template', 'set_default_template' ],
|
||||
'clockvm': [ 'get_clockvm_vm', 'set_clockvm_vm' ],
|
||||
'updatevm': [ 'get_updatevm_vm', 'set_updatevm_vm' ],
|
||||
}
|
||||
assert label in functions.keys()
|
||||
|
||||
if new_value:
|
||||
if new_value == "none":
|
||||
try:
|
||||
vms.__getattribute__(functions[label][1])(None)
|
||||
except Exception as e:
|
||||
print >> sys.stderr, "ERROR: {0}".format(str(e))
|
||||
exit(1)
|
||||
else:
|
||||
vm = vms.get_vm_by_name (new_value)
|
||||
if vm is None:
|
||||
print >> sys.stderr, "A VM with the name '{0}' does not exist in the system.".format(new_value)
|
||||
exit(1)
|
||||
try:
|
||||
vms.__getattribute__(functions[label][1])(vm)
|
||||
except Exception as e:
|
||||
print >> sys.stderr, "ERROR: {0}".format(str(e))
|
||||
exit(1)
|
||||
else:
|
||||
vm = vms.__getattribute__(functions[label][0])()
|
||||
if vm is not None:
|
||||
return vm.name
|
||||
else:
|
||||
return ""
|
||||
|
||||
def handle_kernel(vms, label, new_value = None):
|
||||
if new_value is not None:
|
||||
if not os.path.exists(os.path.join(system_path["qubes_kernels_base_dir"], new_value)):
|
||||
print >> sys.stderr, "Kernel version {0} not installed.".format(new_value)
|
||||
print >> sys.stderr, "Available versions:"
|
||||
for k in os.listdir(system_path["qubes_kernels_base_dir"]):
|
||||
print >> sys.stderr, " -", k
|
||||
exit(1)
|
||||
vms.set_default_kernel(new_value)
|
||||
else:
|
||||
return vms.get_default_kernel()
|
||||
|
||||
preferences = {
|
||||
"default-netvm": handle_vm,
|
||||
"default-fw-netvm": handle_vm,
|
||||
"default-template": handle_vm,
|
||||
"clockvm": handle_vm,
|
||||
"updatevm": handle_vm,
|
||||
"default-kernel": handle_kernel,
|
||||
}
|
||||
|
||||
def do_list(vms):
|
||||
label_width = 18
|
||||
fmt="{{0:<{0}}}: {{1}}".format(label_width)
|
||||
for pref in sorted(preferences.items()):
|
||||
print fmt.format (pref[0], pref[1](vms, pref[0]))
|
||||
|
||||
def main():
|
||||
usage = "usage: %prog [-l]\n"\
|
||||
"usage: %prog [-g] <property>\n"\
|
||||
"usage: %prog [-s] <property> <new-value>\n"\
|
||||
"List/set various global properties."
|
||||
|
||||
parser = OptionParser (usage)
|
||||
parser.add_option ("-l", "--list", action="store_true", dest="do_list", default=False)
|
||||
parser.add_option ("-s", "--set", action="store_true", dest="do_set", default=False)
|
||||
parser.add_option ("-g", "--get", action="store_true", dest="do_get", default=False)
|
||||
|
||||
(options, args) = parser.parse_args ()
|
||||
|
||||
if options.do_list + options.do_set + options.do_get > 1:
|
||||
print >> sys.stderr, "You can provide only one action at once!"
|
||||
exit (1)
|
||||
|
||||
# Select action based on args count:
|
||||
if not options.do_list and not options.do_get and not options.do_set:
|
||||
if (len (args) < 1):
|
||||
options.do_list = True
|
||||
elif (len (args) == 1):
|
||||
options.do_get = True
|
||||
else:
|
||||
options.do_set = True
|
||||
|
||||
vmm.offline_mode = True
|
||||
|
||||
if options.do_set:
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_writing()
|
||||
qvm_collection.load()
|
||||
else:
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_reading()
|
||||
qvm_collection.load()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
if options.do_set:
|
||||
if len (args) < 2 or args[0] not in preferences.keys():
|
||||
print >> sys.stderr, "You must specify the property and the new value you wish to set..."
|
||||
print >> sys.stderr, "Available properties:"
|
||||
for p in sorted(preferences.keys()):
|
||||
print >> sys.stderr, "--> '{0}'".format(p)
|
||||
exit (1)
|
||||
|
||||
pref = args[0]
|
||||
new_value = args[1]
|
||||
preferences[pref](qvm_collection, pref, new_value)
|
||||
qvm_collection.save()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
elif options.do_get:
|
||||
if len (args) < 1 or args[0] not in preferences.keys():
|
||||
print >> sys.stderr, "You must specify the property you wish to get..."
|
||||
print >> sys.stderr, "Available properties:"
|
||||
for p in sorted(preferences.keys()):
|
||||
print >> sys.stderr, "--> '{0}'".format(p)
|
||||
exit (1)
|
||||
pref = args[0]
|
||||
print preferences[pref](qvm_collection, pref)
|
||||
|
||||
else:
|
||||
# do_list
|
||||
do_list(qvm_collection)
|
||||
|
||||
main()
|
@ -1,85 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2014 Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
from optparse import OptionParser
|
||||
import optparse
|
||||
|
||||
import os
|
||||
import sys
|
||||
from qubes.qubes import QubesVmCollection
|
||||
from qubes.qubesutils import updates_vms_toggle,updates_dom0_toggle,\
|
||||
updates_dom0_status,updates_vms_status
|
||||
from qubes.qubes import vmm
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
usage = "%prog enable|disable|status\n"\
|
||||
" Enable or disable globally checking for updates (both dom0 and VM)"
|
||||
parser = OptionParser (usage)
|
||||
parser.add_option("--offline-mode", dest="offline_mode",
|
||||
action="store_true", default=False,
|
||||
help=optparse.SUPPRESS_HELP)
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if len(args) < 1:
|
||||
parser.error("You must provide an action")
|
||||
|
||||
action = args[0]
|
||||
if action not in ['enable', 'disable', 'status']:
|
||||
parser.error("Invalid action")
|
||||
|
||||
if options.offline_mode:
|
||||
vmm.offline_mode = True
|
||||
|
||||
qvm_collection = QubesVmCollection()
|
||||
if action == 'status':
|
||||
qvm_collection.lock_db_for_reading()
|
||||
else:
|
||||
qvm_collection.lock_db_for_writing()
|
||||
qvm_collection.load()
|
||||
if action == 'enable':
|
||||
updates_dom0_toggle(qvm_collection, True)
|
||||
updates_vms_toggle(qvm_collection, True)
|
||||
elif action == 'disable':
|
||||
updates_dom0_toggle(qvm_collection, False)
|
||||
updates_vms_toggle(qvm_collection, False)
|
||||
else:
|
||||
if updates_dom0_status(qvm_collection):
|
||||
print "dom0: enabled"
|
||||
else:
|
||||
print "dom0: disabled"
|
||||
status_vms = updates_vms_status(qvm_collection)
|
||||
if status_vms is None:
|
||||
print "vms: mixed"
|
||||
elif status_vms:
|
||||
print "vms: enabled"
|
||||
else:
|
||||
print "vms: disabled"
|
||||
|
||||
if action != 'status':
|
||||
qvm_collection.save()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,86 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
from qubes.qubes import QubesVmCollection
|
||||
from qubes.qubes import QubesException
|
||||
from optparse import OptionParser;
|
||||
import sys
|
||||
import os
|
||||
|
||||
def main():
|
||||
usage = "usage: %prog [options] <appvm-name> <vm-template-name>\n\n"\
|
||||
"Adds an already installed appvm to the Qubes DB\n"\
|
||||
"WARNING: Noramlly you would not need this command,\n"\
|
||||
"and you would use qvm-create instead!"
|
||||
|
||||
parser = OptionParser (usage)
|
||||
parser.add_option ("-p", "--path", dest="dir_path",
|
||||
help="Specify path to the template directory")
|
||||
parser.add_option ("-c", "--conf", dest="conf_file",
|
||||
help="Specify the Xen VM .conf file to use\
|
||||
(relative to the template dir path)")
|
||||
parser.add_option ("--force-root", action="store_true", dest="force_root", default=False,
|
||||
help="Force to run, even with root privileges")
|
||||
|
||||
(options, args) = parser.parse_args ()
|
||||
if (len (args) != 2):
|
||||
parser.error ("You must specify at least the AppVM and TemplateVM names!")
|
||||
vmname = args[0]
|
||||
templatename = args[1]
|
||||
|
||||
if hasattr(os, "geteuid") and os.geteuid() == 0:
|
||||
if not options.force_root:
|
||||
print >> sys.stderr, "*** Running this tool as root is strongly discouraged, this will lead you in permissions problems."
|
||||
print >> sys.stderr, "Retry as unprivileged user."
|
||||
print >> sys.stderr, "... or use --force-root to continue anyway."
|
||||
exit(1)
|
||||
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_writing()
|
||||
qvm_collection.load()
|
||||
|
||||
if qvm_collection.get_vm_by_name(vmname) is not None:
|
||||
print >> sys.stderr, "ERROR: A VM with the name '{0}' already exists in the system.".format(vmname)
|
||||
exit(1)
|
||||
|
||||
template = qvm_collection.get_vm_by_name(templatename)
|
||||
if template is None:
|
||||
print >> sys.stderr, "ERROR: A Template VM with the name '{0}' does not exist in the system.".format(templatename)
|
||||
exit(1)
|
||||
|
||||
|
||||
vm = qvm_collection.add_new_vm("QubesAppVm", name=vmname, template=template,
|
||||
conf_file=options.conf_file,
|
||||
dir_path=options.dir_path)
|
||||
|
||||
try:
|
||||
vm.verify_files()
|
||||
except QubesException as err:
|
||||
print >> sys.stderr, "ERROR: {0}".format(err)
|
||||
qvm_collection.pop(vm.qid)
|
||||
exit (1)
|
||||
|
||||
qvm_collection.save()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
main()
|
@ -1,83 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
from qubes.qubes import QubesVmCollection,vmm
|
||||
from qubes.qubes import QubesException
|
||||
from optparse import OptionParser;
|
||||
import sys
|
||||
import os
|
||||
|
||||
def main():
|
||||
usage = "usage: %prog [options] <vm-template-name>\n"\
|
||||
"Adds an already installed template to the Qubes DB"
|
||||
|
||||
parser = OptionParser (usage)
|
||||
parser.add_option ("-p", "--path", dest="dir_path",
|
||||
help="Specify path to the template directory")
|
||||
parser.add_option ("-c", "--conf", dest="conf_file",
|
||||
help="Specify the Xen VM .conf file to use\
|
||||
(relative to the template dir path)")
|
||||
|
||||
parser.add_option ("--rpm", action="store_true", dest="installed_by_rpm",
|
||||
help="Template files have been installed by RPM", default=False)
|
||||
parser.add_option ("--force-root", action="store_true", dest="force_root", default=False,
|
||||
help="Force to run, even with root privileges")
|
||||
|
||||
|
||||
(options, args) = parser.parse_args ()
|
||||
if (len (args) != 1):
|
||||
parser.error ("You must specify at least the TemplateVM name!")
|
||||
vmname = args[0]
|
||||
|
||||
if hasattr(os, "geteuid") and os.geteuid() == 0:
|
||||
if not options.force_root and not options.installed_by_rpm:
|
||||
print >> sys.stderr, "*** Running this tool as root is strongly discouraged, this will lead you in permissions problems."
|
||||
print >> sys.stderr, "Retry as unprivileged user."
|
||||
print >> sys.stderr, "... or use --force-root to continue anyway."
|
||||
exit(1)
|
||||
|
||||
vmm.offline_mode = True
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_writing()
|
||||
qvm_collection.load()
|
||||
|
||||
if qvm_collection.get_vm_by_name(vmname) is not None:
|
||||
print >> sys.stderr, "ERROR: A VM with the name '{0}' already exists in the system.".format(vmname)
|
||||
exit(1)
|
||||
|
||||
vm = qvm_collection.add_new_vm("QubesTemplateVm", name=vmname,
|
||||
conf_file=options.conf_file,
|
||||
dir_path=options.dir_path,
|
||||
installed_by_rpm=options.installed_by_rpm)
|
||||
|
||||
try:
|
||||
vm.verify_files()
|
||||
except QubesException as err:
|
||||
print >> sys.stderr, "ERROR: {0}".format(err)
|
||||
qvm_collection.pop(vm.qid)
|
||||
exit (1)
|
||||
|
||||
qvm_collection.save()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
main()
|
@ -1,218 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
from qubes.qubes import QubesVmCollection
|
||||
from qubes.qubes import QubesException
|
||||
from qubes.backup import backup_prepare, backup_do
|
||||
from qubes.qubesutils import size_to_human
|
||||
from optparse import OptionParser
|
||||
import qubes.backup
|
||||
import os
|
||||
import sys
|
||||
import getpass
|
||||
from locale import getpreferredencoding
|
||||
|
||||
def print_progress(progress):
|
||||
print >> sys.stderr, "\r-> Backing up files: {0}%...".format (progress),
|
||||
|
||||
def main():
|
||||
usage = "usage: %prog [options] <backup-dir-path> [vms-to-be-included ...]"
|
||||
parser = OptionParser (usage)
|
||||
|
||||
parser.add_option ("-x", "--exclude", action="append",
|
||||
dest="exclude_list", default=[],
|
||||
help="Exclude the specified VM from the backup (may be "
|
||||
"repeated)")
|
||||
parser.add_option ("--force-root", action="store_true", dest="force_root", default=False,
|
||||
help="Force to run with root privileges")
|
||||
parser.add_option ("-d", "--dest-vm", action="store", dest="appvm",
|
||||
help="Specify the destination VM to which the backup "
|
||||
"will be sent (implies -e)")
|
||||
parser.add_option ("-e", "--encrypt", action="store_true", dest="encrypt", default=False,
|
||||
help="Encrypt the backup")
|
||||
parser.add_option ("--no-encrypt", action="store_true",
|
||||
dest="no_encrypt", default=False,
|
||||
help="Skip encryption even if sending the backup to a "
|
||||
"VM")
|
||||
parser.add_option ("-p", "--passphrase-file", action="store",
|
||||
dest="pass_file", default=None,
|
||||
help="Read passphrase from a file, or use '-' to read "
|
||||
"from stdin")
|
||||
parser.add_option ("-E", "--enc-algo", action="store",
|
||||
dest="crypto_algorithm", default=None,
|
||||
help="Specify a non-default encryption algorithm. For a "
|
||||
"list of supported algorithms, execute 'openssl "
|
||||
"list-cipher-algorithms' (implies -e)")
|
||||
parser.add_option ("-H", "--hmac-algo", action="store",
|
||||
dest="hmac_algorithm", default=None,
|
||||
help="Specify a non-default HMAC algorithm. For a list "
|
||||
"of supported algorithms, execute 'openssl "
|
||||
"list-message-digest-algorithms'")
|
||||
parser.add_option ("-z", "--compress", action="store_true", dest="compress", default=False,
|
||||
help="Compress the backup")
|
||||
parser.add_option ("-Z", "--compress-filter", action="store",
|
||||
dest="compress_filter", default=False,
|
||||
help="Specify a non-default compression filter program "
|
||||
"(default: gzip)")
|
||||
parser.add_option("--tmpdir", action="store", dest="tmpdir", default=None,
|
||||
help="Specify a temporary directory (if you have at least "
|
||||
"1GB free RAM in dom0, use of /tmp is advised) ("
|
||||
"default: /var/tmp)")
|
||||
parser.add_option ("--debug", action="store_true", dest="debug",
|
||||
default=False, help="Enable (a lot of) debug output")
|
||||
|
||||
(options, args) = parser.parse_args ()
|
||||
|
||||
if (len (args) < 1):
|
||||
print >> sys.stderr, "You must specify the target backup directory "\
|
||||
" (e.g. /mnt/backup)."
|
||||
print >> sys.stderr, "qvm-backup will create a subdirectory there for "\
|
||||
" each individual backup."
|
||||
exit (0)
|
||||
|
||||
base_backup_dir = args[0]
|
||||
|
||||
if hasattr(os, "geteuid") and os.geteuid() == 0:
|
||||
if not options.force_root:
|
||||
print >> sys.stderr, "*** Running this tool as root is strongly "\
|
||||
"discouraged. This will lead to permissions "\
|
||||
"problems."
|
||||
print >> sys.stderr, "Retry as an unprivileged user, or use "\
|
||||
"--force-root to continue anyway."
|
||||
exit(1)
|
||||
|
||||
# Only for locking
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_reading()
|
||||
qvm_collection.load()
|
||||
|
||||
vms = None
|
||||
if (len (args) > 1):
|
||||
vms = [qvm_collection.get_vm_by_name(vmname) for vmname in args[1:]]
|
||||
|
||||
if options.appvm:
|
||||
options.exclude_list.append(options.appvm)
|
||||
|
||||
if options.appvm or options.crypto_algorithm:
|
||||
options.encrypt = True
|
||||
if options.no_encrypt:
|
||||
options.encrypt = False
|
||||
if options.debug:
|
||||
qubes.backup.BACKUP_DEBUG = True
|
||||
files_to_backup = None
|
||||
try:
|
||||
files_to_backup = backup_prepare(
|
||||
vms_list=vms,
|
||||
exclude_list=options.exclude_list,
|
||||
hide_vm_names=options.encrypt)
|
||||
except QubesException as e:
|
||||
print >>sys.stderr, "ERROR: %s" % str(e)
|
||||
exit(1)
|
||||
|
||||
total_backup_sz = reduce(lambda size, file: size+file["size"],
|
||||
files_to_backup, 0)
|
||||
|
||||
if not options.appvm:
|
||||
appvm = None
|
||||
|
||||
if os.path.isdir(base_backup_dir):
|
||||
stat = os.statvfs(base_backup_dir)
|
||||
else:
|
||||
stat = os.statvfs(os.path.dirname(base_backup_dir))
|
||||
backup_fs_free_sz = stat.f_bsize * stat.f_bavail
|
||||
print
|
||||
if (total_backup_sz > backup_fs_free_sz):
|
||||
print >>sys.stderr, "ERROR: Not enough space available on the "\
|
||||
"backup filesystem!"
|
||||
exit(1)
|
||||
|
||||
print "-> Available space: {0}".format(size_to_human(backup_fs_free_sz))
|
||||
else:
|
||||
appvm = qvm_collection.get_vm_by_name(options.appvm)
|
||||
if appvm is None:
|
||||
print >>sys.stderr, "ERROR: VM {0} does not exist!".format(options.appvm)
|
||||
exit(1)
|
||||
|
||||
stat = os.statvfs('/var/tmp')
|
||||
backup_fs_free_sz = stat.f_bsize * stat.f_bavail
|
||||
print
|
||||
if (backup_fs_free_sz < 1000000000):
|
||||
print >>sys.stderr, "ERROR: Not enough space available " \
|
||||
"on the local filesystem (1GB required for temporary files)!"
|
||||
exit(1)
|
||||
|
||||
if not appvm.is_running():
|
||||
appvm.start(verbose=True)
|
||||
|
||||
if options.appvm:
|
||||
print >>sys.stderr, ("NOTE: VM {} will be excluded because it is "
|
||||
"the backup destination.").format(options.appvm)
|
||||
options.exclude_list.append(options.appvm)
|
||||
|
||||
if not options.encrypt:
|
||||
print >>sys.stderr, "WARNING: The backup will NOT be encrypted!"
|
||||
|
||||
if options.pass_file is not None:
|
||||
f = open(options.pass_file) if options.pass_file != "-" else sys.stdin
|
||||
passphrase = f.readline().rstrip()
|
||||
if f is not sys.stdin:
|
||||
f.close()
|
||||
|
||||
else:
|
||||
if raw_input("Do you want to proceed? [y/N] ").upper() != "Y":
|
||||
exit(0)
|
||||
|
||||
s = ("Please enter the passphrase that will be used to {}verify "
|
||||
"the backup: ").format('encrypt and ' if options.encrypt else '')
|
||||
passphrase = getpass.getpass(s)
|
||||
|
||||
if getpass.getpass("Enter again for verification: ") != passphrase:
|
||||
print >>sys.stderr, "ERROR: Passphrase mismatch!"
|
||||
exit(1)
|
||||
|
||||
encoding = sys.stdin.encoding or getpreferredencoding()
|
||||
passphrase = passphrase.decode(encoding)
|
||||
|
||||
kwargs = {}
|
||||
if options.hmac_algorithm:
|
||||
kwargs['hmac_algorithm'] = options.hmac_algorithm
|
||||
if options.crypto_algorithm:
|
||||
kwargs['crypto_algorithm'] = options.crypto_algorithm
|
||||
if options.tmpdir:
|
||||
kwargs['tmpdir'] = options.tmpdir
|
||||
|
||||
try:
|
||||
backup_do(base_backup_dir, files_to_backup, passphrase,
|
||||
progress_callback=print_progress,
|
||||
encrypted=options.encrypt,
|
||||
compressed=options.compress_filter or options.compress,
|
||||
appvm=appvm, **kwargs)
|
||||
except QubesException as e:
|
||||
print >>sys.stderr, "ERROR: %s" % str(e)
|
||||
exit(1)
|
||||
|
||||
print
|
||||
print "-> Backup completed."
|
||||
|
||||
qvm_collection.unlock_db()
|
||||
main()
|
@ -1,313 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
from multiprocessing import Event
|
||||
|
||||
from qubes.qubes import QubesVmCollection
|
||||
from qubes.qubes import QubesException
|
||||
from qubes.backup import backup_restore_header
|
||||
from qubes.backup import backup_restore_prepare
|
||||
from qubes.backup import backup_restore_print_summary
|
||||
from qubes.backup import backup_restore_do
|
||||
import qubes.backup
|
||||
import sys
|
||||
from optparse import OptionParser
|
||||
from locale import getpreferredencoding
|
||||
|
||||
import os
|
||||
import sys
|
||||
import getpass
|
||||
|
||||
def main():
|
||||
usage = "usage: %prog [options] <backup-dir> [vms-to-be-restored ...]"
|
||||
parser = OptionParser (usage)
|
||||
|
||||
parser.add_option ("--verify-only", action="store_true",
|
||||
dest="verify_only", default=False,
|
||||
help="Verify backup integrity without restoring any "
|
||||
"data")
|
||||
|
||||
parser.add_option ("--skip-broken", action="store_true", dest="skip_broken", default=False,
|
||||
help="Do not restore VMs that have missing TemplateVMs "
|
||||
"or NetVMs")
|
||||
|
||||
parser.add_option ("--ignore-missing", action="store_true", dest="ignore_missing", default=False,
|
||||
help="Restore VMs even if their associated TemplateVMs "
|
||||
"and NetVMs are missing")
|
||||
|
||||
parser.add_option ("--skip-conflicting", action="store_true", dest="skip_conflicting", default=False,
|
||||
help="Do not restore VMs that are already present on "
|
||||
"the host")
|
||||
|
||||
parser.add_option ("--rename-conflicting", action="store_true",
|
||||
dest="rename_conflicting", default=False,
|
||||
help="Restore VMs that are already present on the host "
|
||||
"under different names")
|
||||
|
||||
parser.add_option ("--force-root", action="store_true", dest="force_root", default=False,
|
||||
help="Force to run with root privileges")
|
||||
|
||||
parser.add_option ("--replace-template", action="append", dest="replace_template", default=[],
|
||||
help="Restore VMs using another TemplateVM; syntax: "
|
||||
"old-template-name:new-template-name (may be "
|
||||
"repeated)")
|
||||
|
||||
parser.add_option ("-x", "--exclude", action="append", dest="exclude", default=[],
|
||||
help="Skip restore of specified VM (may be repeated)")
|
||||
|
||||
parser.add_option ("--skip-dom0-home", action="store_false", dest="dom0_home", default=True,
|
||||
help="Do not restore dom0 user home directory")
|
||||
|
||||
parser.add_option ("--ignore-username-mismatch", action="store_true", dest="ignore_username_mismatch", default=False,
|
||||
help="Ignore dom0 username mismatch when restoring home "
|
||||
"directory")
|
||||
|
||||
parser.add_option ("-d", "--dest-vm", action="store", dest="appvm",
|
||||
help="Specify VM containing the backup to be restored")
|
||||
|
||||
parser.add_option ("-e", "--encrypted", action="store_true", dest="decrypt", default=False,
|
||||
help="The backup is encrypted")
|
||||
|
||||
parser.add_option ("-p", "--passphrase-file", action="store",
|
||||
dest="pass_file", default=None,
|
||||
help="Read passphrase from file, or use '-' to read from stdin")
|
||||
|
||||
parser.add_option ("-z", "--compressed", action="store_true", dest="compressed", default=False,
|
||||
help="The backup is compressed")
|
||||
|
||||
parser.add_option ("--debug", action="store_true", dest="debug",
|
||||
default=False, help="Enable (a lot of) debug output")
|
||||
|
||||
(options, args) = parser.parse_args ()
|
||||
|
||||
if (len (args) < 1):
|
||||
print >> sys.stderr, "You must specify the backup directory "\
|
||||
"(e.g. /mnt/backup/qubes-2010-12-01-235959)"
|
||||
exit (0)
|
||||
|
||||
backup_dir = args[0]
|
||||
vmlist = args[1:]
|
||||
|
||||
#if not os.path.exists (backup_dir):
|
||||
# print >> sys.stderr, "The backup directory doesn't exist!"
|
||||
# exit(1)
|
||||
|
||||
host_collection = QubesVmCollection()
|
||||
host_collection.lock_db_for_writing()
|
||||
host_collection.load()
|
||||
|
||||
restore_options = {}
|
||||
if options.ignore_missing:
|
||||
restore_options['use-default-template'] = True
|
||||
restore_options['use-default-netvm'] = True
|
||||
if options.replace_template:
|
||||
restore_options['replace-template'] = options.replace_template
|
||||
if options.rename_conflicting:
|
||||
restore_options['rename-conflicting'] = True
|
||||
if not options.dom0_home:
|
||||
restore_options['dom0-home'] = False
|
||||
if options.ignore_username_mismatch:
|
||||
restore_options['ignore-username-mismatch'] = True
|
||||
if options.exclude:
|
||||
restore_options['exclude'] = options.exclude
|
||||
if options.verify_only:
|
||||
restore_options['verify-only'] = True
|
||||
if options.debug:
|
||||
qubes.backup.BACKUP_DEBUG = True
|
||||
|
||||
appvm = None
|
||||
if options.appvm is not None:
|
||||
appvm = host_collection.get_vm_by_name(options.appvm)
|
||||
if appvm is None:
|
||||
print >>sys.stderr, "ERROR: VM {0} does not exist".format(options.appvm)
|
||||
exit(1)
|
||||
|
||||
if options.pass_file is not None:
|
||||
f = open(options.pass_file) if options.pass_file != "-" else sys.stdin
|
||||
passphrase = f.readline().rstrip()
|
||||
if f is not sys.stdin:
|
||||
f.close()
|
||||
else:
|
||||
passphrase = getpass.getpass("Please enter the passphrase to verify "
|
||||
"and (if encrypted) decrypt the backup: ")
|
||||
|
||||
encoding = sys.stdin.encoding or getpreferredencoding()
|
||||
passphrase = passphrase.decode(encoding)
|
||||
|
||||
print >> sys.stderr, "Checking backup content..."
|
||||
|
||||
error_detected = Event()
|
||||
def error_callback(message):
|
||||
error_detected.set()
|
||||
print >> sys.stderr, message
|
||||
|
||||
restore_info = None
|
||||
try:
|
||||
restore_info = backup_restore_prepare(
|
||||
backup_dir,
|
||||
passphrase=passphrase,
|
||||
options=restore_options,
|
||||
host_collection=host_collection,
|
||||
encrypted=options.decrypt,
|
||||
compressed=options.compressed,
|
||||
appvm=appvm,
|
||||
error_callback=error_callback)
|
||||
except QubesException as e:
|
||||
print >> sys.stderr, "ERROR: %s" % str(e)
|
||||
exit(1)
|
||||
|
||||
if len(vmlist) > 0:
|
||||
for vm in restore_info.keys():
|
||||
if vm.startswith('$'):
|
||||
continue
|
||||
if not vm in vmlist:
|
||||
restore_info.pop(vm)
|
||||
|
||||
backup_restore_print_summary(restore_info)
|
||||
|
||||
there_are_conflicting_vms = False
|
||||
there_are_missing_templates = False
|
||||
there_are_missing_netvms = False
|
||||
dom0_username_mismatch = False
|
||||
|
||||
for vm_info in restore_info.values():
|
||||
if 'excluded' in vm_info and vm_info['excluded']:
|
||||
continue
|
||||
if 'missing-template' in vm_info.keys():
|
||||
there_are_missing_templates = True
|
||||
if 'missing-netvm' in vm_info.keys():
|
||||
there_are_missing_netvms = True
|
||||
if 'already-exists' in vm_info.keys():
|
||||
there_are_conflicting_vms = True
|
||||
if 'username-mismatch' in vm_info.keys():
|
||||
dom0_username_mismatch = True
|
||||
|
||||
print
|
||||
|
||||
if hasattr(os, "geteuid") and os.geteuid() == 0:
|
||||
print >> sys.stderr, "*** Running this tool as root is strongly "\
|
||||
"discouraged. This will lead to permissions "\
|
||||
"problems."
|
||||
if options.force_root:
|
||||
print >> sys.stderr, "Continuing as commanded. You have been "\
|
||||
"warned."
|
||||
else:
|
||||
print >> sys.stderr, "Retry as an unprivileged user, or use "\
|
||||
"--force-root to continue anyway."
|
||||
exit(1)
|
||||
|
||||
if there_are_conflicting_vms:
|
||||
print >> sys.stderr, "*** There are VMs with conflicting names on the "\
|
||||
"host! ***"
|
||||
if options.skip_conflicting:
|
||||
print >> sys.stderr, "Those VMs will not be restored. The host "\
|
||||
"VMs will NOT be overwritten."
|
||||
else:
|
||||
print >> sys.stderr, "Remove VMs with conflicting names from the "\
|
||||
"host before proceeding."
|
||||
print >> sys.stderr, "Or use --skip-conflicting to restore only "\
|
||||
"those VMs that do not exist on the host."
|
||||
print >> sys.stderr, "Or use --rename-conflicting to restore " \
|
||||
"those VMs under modified names (with "\
|
||||
"numbers at the end)."
|
||||
exit (1)
|
||||
|
||||
print "The above VMs will be copied and added to your system."
|
||||
print "Exisiting VMs will NOT be removed."
|
||||
|
||||
if there_are_missing_templates:
|
||||
print >> sys.stderr, "*** One or more TemplateVMs are missing on the"\
|
||||
"host! ***"
|
||||
if not (options.skip_broken or options.ignore_missing):
|
||||
print >> sys.stderr, "Install them before proceeding with the "\
|
||||
"restore."
|
||||
print >> sys.stderr, "Or pass: --skip-broken or --ignore-missing."
|
||||
exit (1)
|
||||
elif options.skip_broken:
|
||||
print >> sys.stderr, "Skipping broken entries: VMs that depend on "\
|
||||
"missing TemplateVMs will NOT be restored."
|
||||
elif options.ignore_missing:
|
||||
print >> sys.stderr, "Ignoring missing entries: VMs that depend "\
|
||||
"on missing TemplateVMs will NOT be restored."
|
||||
else:
|
||||
print >> sys.stderr, "INTERNAL ERROR! Please report this to the "\
|
||||
"Qubes OS team!"
|
||||
exit (1)
|
||||
|
||||
if there_are_missing_netvms:
|
||||
print >> sys.stderr, "*** One or more NetVMs are missing on the "\
|
||||
"host! ***"
|
||||
if not (options.skip_broken or options.ignore_missing):
|
||||
print >> sys.stderr, "Install them before proceeding with the "\
|
||||
"restore."
|
||||
print >> sys.stderr, "Or pass: --skip-broken or --ignore-missing."
|
||||
exit (1)
|
||||
elif options.skip_broken:
|
||||
print >> sys.stderr, "Skipping broken entries: VMs that depend on "\
|
||||
"missing NetVMs will NOT be restored."
|
||||
elif options.ignore_missing:
|
||||
print >> sys.stderr, "Ignoring missing entries: VMs that depend "\
|
||||
"on missing NetVMs will NOT be restored."
|
||||
else:
|
||||
print >> sys.stderr, "INTERNAL ERROR! Please report this to the "\
|
||||
"Qubes OS team!"
|
||||
exit (1)
|
||||
|
||||
if 'dom0' in restore_info.keys() and options.dom0_home:
|
||||
if dom0_username_mismatch:
|
||||
print >> sys.stderr, "*** Dom0 username mismatch! This can break "\
|
||||
"some settings! ***"
|
||||
if not options.ignore_username_mismatch:
|
||||
print >> sys.stderr, "Skip restoring the dom0 home directory "\
|
||||
"(--skip-dom0-home), or pass "\
|
||||
"--ignore-username-mismatch to continue "\
|
||||
"anyway."
|
||||
exit(1)
|
||||
else:
|
||||
print >> sys.stderr, "Continuing as directed."
|
||||
print >> sys.stderr, "NOTE: Before restoring the dom0 home directory, "\
|
||||
"a new directory named "\
|
||||
"'home-pre-restore-<current-time>' will be "\
|
||||
"created inside the dom0 home directory. If any "\
|
||||
"restored files conflict with existing files, "\
|
||||
"the existing files will be moved to this new "\
|
||||
"directory."
|
||||
|
||||
if options.pass_file is None:
|
||||
if raw_input("Do you want to proceed? [y/N] ").upper() != "Y":
|
||||
exit(0)
|
||||
|
||||
try:
|
||||
backup_restore_do(restore_info,
|
||||
host_collection=host_collection,
|
||||
error_callback=error_callback)
|
||||
except QubesException as e:
|
||||
print >> sys.stderr, "ERROR: %s" % str(e)
|
||||
|
||||
host_collection.unlock_db()
|
||||
|
||||
if error_detected.is_set():
|
||||
print "-> Completed with errors!"
|
||||
exit(1)
|
||||
else:
|
||||
print "-> Done."
|
||||
main()
|
@ -1,152 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Marek Marczykowski <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
from qubes.qubes import QubesVmCollection, QubesException
|
||||
from qubes.qubesutils import block_list,block_attach,block_detach,block_detach_all,block_check_attached
|
||||
from qubes.qubesutils import kbytes_to_kmg, bytes_to_kmg
|
||||
from optparse import OptionParser
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
|
||||
def main():
|
||||
usage = "usage: %prog -l [options]\n"\
|
||||
"usage: %prog -a [options] <vm-name> <device-vm-name>:<device>\n"\
|
||||
"usage: %prog -A [options] <vm-name> <file-vm-name>:<file>\n"\
|
||||
"usage: %prog -d [options] <device-vm-name>:<device>\n"\
|
||||
"usage: %prog -d [options] <vm-name>\n"\
|
||||
"List/set VM block devices."
|
||||
|
||||
parser = OptionParser (usage)
|
||||
parser.add_option ("-l", "--list", action="store_true", dest="do_list", default=False)
|
||||
parser.add_option ("-A", "--attach-file", action="store_true", dest="do_file_attach", default=False,
|
||||
help="Attach specified file instead of physical device")
|
||||
parser.add_option ("-a", "--attach", action="store_true", dest="do_attach", default=False)
|
||||
parser.add_option ("-d", "--detach", action="store_true", dest="do_detach", default=False)
|
||||
parser.add_option ("-f", "--frontend", dest="frontend",
|
||||
help="Specify device name at destination VM [default: xvdi]")
|
||||
parser.add_option ("--ro", dest="ro", action="store_true", default=False,
|
||||
help="Force read-only mode")
|
||||
parser.add_option ("--no-auto-detach", dest="auto_detach", action="store_false", default=True,
|
||||
help="Fail when device already connected to other VM")
|
||||
parser.add_option ("--show-system-disks", dest="system_disks", action="store_true", default=False,
|
||||
help="List also system disks")
|
||||
parser.add_option ("--force-root", action="store_true", dest="force_root", default=False,
|
||||
help="Force to run, even with root privileges")
|
||||
|
||||
(options, args) = parser.parse_args ()
|
||||
|
||||
if hasattr(os, "geteuid") and os.geteuid() == 0:
|
||||
if not options.force_root:
|
||||
print >> sys.stderr, "*** Running this tool as root is strongly discouraged, this will lead you in permissions problems."
|
||||
print >> sys.stderr, "Retry as unprivileged user."
|
||||
print >> sys.stderr, "... or use --force-root to continue anyway."
|
||||
exit(1)
|
||||
|
||||
if options.do_file_attach:
|
||||
options.do_attach = True
|
||||
|
||||
if options.do_list + options.do_attach + options.do_detach > 1:
|
||||
print >> sys.stderr, "Only one of -l -a/-A -d is allowed!"
|
||||
exit (1)
|
||||
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_reading()
|
||||
qvm_collection.load()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
if options.do_attach:
|
||||
if len(args) != 2:
|
||||
parser.error ("You must provide vm name and device!")
|
||||
vm = qvm_collection.get_vm_by_name(args[0])
|
||||
if vm is None:
|
||||
parser.error ("Invalid VM name: %s" % args[0])
|
||||
# FIXME: here we assume that device is always in form "domain:dev", which can be changed in the future
|
||||
if args[1].find(":") < 0:
|
||||
parser.error ("Invalid device syntax (missing VM name): %s" % args[1])
|
||||
if options.do_file_attach:
|
||||
dev = {}
|
||||
(dev['vm'], dev['device']) = args[1].split(":")
|
||||
dev['desc'] = dev['device']
|
||||
dev['mode'] = 'w'
|
||||
else:
|
||||
dev_list = block_list(qvm_collection)
|
||||
if not args[1] in dev_list.keys():
|
||||
parser.error ("Invalid device name: %s" % args[1])
|
||||
dev = dev_list[args[1]]
|
||||
kwargs = {}
|
||||
if options.frontend:
|
||||
kwargs['frontend'] = options.frontend
|
||||
if options.ro:
|
||||
kwargs['mode'] = "r"
|
||||
else:
|
||||
kwargs['mode'] = dev['mode']
|
||||
kwargs['auto_detach'] = options.auto_detach
|
||||
try:
|
||||
block_attach(qvm_collection, vm, dev, **kwargs)
|
||||
except QubesException as e:
|
||||
print >> sys.stderr, "ERROR: %s" % str(e)
|
||||
sys.exit(1)
|
||||
elif options.do_detach:
|
||||
if (len (args) < 1):
|
||||
parser.error ("You must provide device or vm name!")
|
||||
if len(args) > 1:
|
||||
parser.error ("Too many parameters")
|
||||
# Check if provided name is VM
|
||||
vm = qvm_collection.get_vm_by_name(args[0])
|
||||
if vm is not None:
|
||||
kwargs = {}
|
||||
if options.frontend:
|
||||
kwargs['frontend'] = options.frontend
|
||||
block_detach(vm, **kwargs)
|
||||
else:
|
||||
block_detach_all(vm)
|
||||
else:
|
||||
# Maybe device?
|
||||
dev_list = block_list(qvm_collection)
|
||||
if not args[0] in dev_list.keys():
|
||||
parser.error ("Invalid VM or device name: %s" % args[0])
|
||||
dev = dev_list[args[0]]
|
||||
attached_to = block_check_attached(qvm_collection, dev)
|
||||
if attached_to is None:
|
||||
print >> sys.stderr, "WARNING: Device not connected to any VM"
|
||||
exit(0)
|
||||
block_detach(attached_to['vm'], attached_to['frontend'])
|
||||
else:
|
||||
# do_list
|
||||
if len(args) > 0:
|
||||
parser.error ("Too many parameters")
|
||||
kwargs = {}
|
||||
kwargs['qvmc'] = qvm_collection
|
||||
kwargs['system_disks'] = options.system_disks
|
||||
for dev in block_list(**kwargs).values():
|
||||
attached_to = block_check_attached(qvm_collection, dev)
|
||||
attached_to_str = ""
|
||||
if attached_to:
|
||||
attached_to_str = " (attached to '%s' as '%s')" % (
|
||||
attached_to['vm'].name, attached_to['frontend'])
|
||||
size_str = bytes_to_kmg(dev['size'])
|
||||
print "%s\t%s %s%s" % (dev['name'], dev['desc'], size_str, attached_to_str)
|
||||
exit (0)
|
||||
|
||||
main()
|
@ -1,81 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2011 Marek Marczykowski <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
from qubes.qubes import QubesVmCollection,QubesException
|
||||
from optparse import OptionParser
|
||||
import sys
|
||||
import time
|
||||
|
||||
def main():
|
||||
usage = """usage: %prog [options] <vm-name>\n
|
||||
Specify no state options to check if VM exists"""
|
||||
parser = OptionParser (usage)
|
||||
|
||||
parser.add_option ("-q", "--quiet", action="store_false", dest="verbose", default=True)
|
||||
parser.add_option ("--running", action="store_true", dest="running", default=False,
|
||||
help="Determine if VM is running")
|
||||
parser.add_option ("--paused", action="store_true", dest="paused", default=False,
|
||||
help="Determine if VM is paused")
|
||||
parser.add_option ("--template", action="store_true", dest="template", default=False,
|
||||
help="Determine if VM is a template")
|
||||
|
||||
(options, args) = parser.parse_args ()
|
||||
if (len (args) != 1):
|
||||
parser.error ("You must specify VM name!")
|
||||
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_reading()
|
||||
qvm_collection.load()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
vmname = args[0]
|
||||
vm = qvm_collection.get_vm_by_name(vmname)
|
||||
if vm is None:
|
||||
if options.verbose:
|
||||
print >> sys.stderr, "A VM with the name '{0}' does not exist in the system!".format(vmname)
|
||||
exit(1)
|
||||
|
||||
elif options.running:
|
||||
vm_state = not vm.is_running()
|
||||
if options.verbose:
|
||||
print >> sys.stderr, "A VM with the name {0} is {1}running.".format(vmname, "not " * vm_state)
|
||||
exit(vm_state)
|
||||
|
||||
elif options.paused:
|
||||
vm_state = not vm.is_paused()
|
||||
if options.verbose:
|
||||
print >> sys.stderr, "A VM with the name {0} is {1}paused.".format(vmname, "not " * vm_state)
|
||||
exit(vm_state)
|
||||
|
||||
elif options.template:
|
||||
vm_state = not vm.is_template()
|
||||
if options.verbose:
|
||||
print >> sys.stderr, "A VM with the name {0} is {1}a template.".format(vmname, "not " * vm_state)
|
||||
exit(vm_state)
|
||||
|
||||
else:
|
||||
if options.verbose:
|
||||
print >> sys.stderr, "A VM with the name '{0}' does exist.".format(vmname)
|
||||
exit(0)
|
||||
|
||||
main()
|
@ -1,105 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
# USA.
|
||||
|
||||
import os
|
||||
import sys
|
||||
from optparse import OptionParser
|
||||
|
||||
from qubes.qubes import QubesVmCollection
|
||||
|
||||
|
||||
def main():
|
||||
usage = "usage: %prog [options] <src-name> <new-name>\n"\
|
||||
"Clones an existing VM by copying all its disk files"
|
||||
|
||||
parser = OptionParser(usage)
|
||||
parser.add_option("-q", "--quiet", action="store_false", dest="verbose",
|
||||
default=True)
|
||||
parser.add_option("-p", "--path", dest="dir_path",
|
||||
help="Specify path to the template directory")
|
||||
parser.add_option("--force-root", action="store_true", dest="force_root",
|
||||
default=False,
|
||||
help="Force to run, even with root privileges")
|
||||
parser.add_option("-P", "--pool", dest="pool_name",
|
||||
help="Specify in to which storage pool to clone")
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
if (len(args) != 2):
|
||||
parser.error(
|
||||
"You must specify at least the src and dst TemplateVM names!")
|
||||
srcname = args[0]
|
||||
dstname = args[1]
|
||||
|
||||
if hasattr(os, "geteuid") and os.geteuid() == 0:
|
||||
if not options.force_root:
|
||||
print >> sys.stderr, "*** Running this tool as root is" + \
|
||||
" strongly discouraged, this will lead you in permissions" + \
|
||||
"problems."
|
||||
print >> sys.stderr, "Retry as unprivileged user."
|
||||
print >> sys.stderr, "... or use --force-root to continue anyway."
|
||||
exit(1)
|
||||
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_writing()
|
||||
qvm_collection.load()
|
||||
|
||||
src_vm = qvm_collection.get_vm_by_name(srcname)
|
||||
if src_vm is None:
|
||||
print >> sys.stderr, \
|
||||
"ERROR: A VM with the name '{0}' does not exist in the system." \
|
||||
.format(srcname)
|
||||
exit(1)
|
||||
|
||||
if options.pool_name is None:
|
||||
pool_name = src_vm.pool_name
|
||||
else:
|
||||
pool_name = options.pool_name
|
||||
|
||||
if qvm_collection.get_vm_by_name(dstname) is not None:
|
||||
print >> sys.stderr, \
|
||||
"ERROR: A VM with the name '{0}' already exists in the system." \
|
||||
.format(dstname)
|
||||
exit(1)
|
||||
|
||||
if src_vm.is_disposablevm():
|
||||
print >> sys.stderr, "ERROR: Clone not supported for this type of VM"
|
||||
exit(1)
|
||||
|
||||
dst_vm = qvm_collection.add_new_vm(src_vm.__class__.__name__,
|
||||
name=dstname, template=src_vm.template,
|
||||
pool_name=pool_name,
|
||||
dir_path=options.dir_path,
|
||||
installed_by_rpm=False)
|
||||
|
||||
try:
|
||||
dst_vm.clone_attrs(src_vm)
|
||||
dst_vm.clone_disk_files(src_vm=src_vm, verbose=options.verbose)
|
||||
except (IOError, OSError) as err:
|
||||
print >> sys.stderr, "ERROR: {0}".format(err)
|
||||
qvm_collection.pop(dst_vm.qid)
|
||||
dst_vm.remove_from_disk()
|
||||
exit(1)
|
||||
|
||||
qvm_collection.save()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
main()
|
@ -1,72 +0,0 @@
|
||||
#!/bin/sh
|
||||
if [ $# != 1 -a $# != 2 -o $1 == "--help" -o $1 == "-h" ] ; then
|
||||
echo 'Usage: qvm-create-default-dvm templatename|--default-template|--used-template [script-name|--default-script]'
|
||||
exit 1
|
||||
fi
|
||||
export ROOT=/var/lib/qubes/dvmdata/savefile-root
|
||||
TEMPLATENAME=$1
|
||||
if [ "$TEMPLATENAME" = --used-template ] ; then
|
||||
if [ -e $ROOT ] ; then
|
||||
TEMPLATENAME=$(readlink $ROOT | sed -e 's/.root.img//' -e 's/.*\///')
|
||||
else
|
||||
TEMPLATENAME=--default-template
|
||||
fi
|
||||
fi
|
||||
if [ "$TEMPLATENAME" = --default-template ] ; then
|
||||
TEMPLATENAME=$(qubes-prefs --get default-template)
|
||||
if [ "X"$TEMPLATENAME = "X" ] ; then
|
||||
echo No default template ?
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$2" -o "X""$2" = "X""--default-script" ] ; then
|
||||
SCRIPTNAME="vm-default"
|
||||
else
|
||||
SCRIPTNAME=$2
|
||||
fi
|
||||
|
||||
if ! [ -d "/var/lib/qubes/vm-templates/$TEMPLATENAME" ] ; then
|
||||
echo /var/lib/qubes/vm-templates/$TEMPLATENAME is not a directory
|
||||
exit 1
|
||||
fi
|
||||
DVMTMPL="$TEMPLATENAME"-dvm
|
||||
DVMTMPLDIR="/var/lib/qubes/appvms/$DVMTMPL"
|
||||
if ! [ -d "$DVMTMPLDIR" ] ; then
|
||||
# unfortunately, currently there are reliability issues with save of a domain
|
||||
# with multiple CPUs and/or more than 4000M RAM
|
||||
if ! qvm-create --force-root --vcpus=1 --internal -t "$TEMPLATENAME" -l gray "$DVMTMPL" ; then exit 1 ; fi
|
||||
MAXMEM=`qvm-prefs --force-root $DVMTMPL|grep ^maxmem|awk '{print $3}'`
|
||||
if [ "$MAXMEM" -ge 4000 ]; then
|
||||
qvm-prefs --force-root -s $DVMTMPL maxmem 4000
|
||||
fi
|
||||
fi
|
||||
if ! /usr/lib/qubes/qubes-prepare-saved-domain.sh \
|
||||
"$DVMTMPL" "/var/lib/qubes/appvms/$DVMTMPL/dvm-savefile" $SCRIPTNAME ; then
|
||||
exit 1
|
||||
fi
|
||||
DEFAULT=/var/lib/qubes/dvmdata/default-savefile
|
||||
CURRENT=/var/run/qubes/current-savefile
|
||||
SHMDIR=/dev/shm/qubes
|
||||
SHMCOPY=$SHMDIR/current-savefile
|
||||
rm -f $ROOT $DEFAULT $CURRENT
|
||||
ln -s "/var/lib/qubes/appvms/$DVMTMPL/dvm-savefile" $DEFAULT
|
||||
ln -s "/var/lib/qubes/vm-templates/$TEMPLATENAME/root.img" $ROOT
|
||||
if [ -f /var/lib/qubes/dvmdata/dont-use-shm ] ; then
|
||||
ln -s $DEFAULT $CURRENT
|
||||
else
|
||||
mkdir -m 770 $SHMDIR 2>/dev/null
|
||||
chgrp qubes $SHMDIR 2>/dev/null
|
||||
rm -f $SHMCOPY
|
||||
cp $DEFAULT $SHMCOPY || exit 1
|
||||
chgrp qubes $SHMCOPY
|
||||
chmod 660 $SHMCOPY
|
||||
ln -s $SHMCOPY $CURRENT
|
||||
fi
|
||||
|
||||
if [ $(whoami) = "root" ] ; then
|
||||
chgrp qubes "$DVMTMPLDIR" "$DVMTMPLDIR"/*
|
||||
chmod 660 "$DVMTMPLDIR"/*
|
||||
chmod 770 "$DVMTMPLDIR"
|
||||
fi
|
||||
|
@ -1,321 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2012 Marek Marczykowski <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
import datetime
|
||||
|
||||
from qubes.qubes import QubesVmCollection
|
||||
from optparse import OptionParser;
|
||||
import subprocess
|
||||
import sys
|
||||
import re
|
||||
import os
|
||||
import socket
|
||||
|
||||
def parse_rule(args):
|
||||
if len(args) < 2:
|
||||
print >>sys.stderr, "ERROR: Rule must have at least address and protocol"
|
||||
return None
|
||||
|
||||
address = args[0]
|
||||
netmask = 32
|
||||
proto = args[1]
|
||||
port = args[2] if len(args) > 2 else None
|
||||
port_end = None
|
||||
|
||||
unmask = address.split("/", 1)
|
||||
if len(unmask) == 2:
|
||||
address = unmask[0]
|
||||
netmask = unmask[1]
|
||||
if netmask.isdigit():
|
||||
if re.match("^([0-9]{1,3}\.){3}[0-9]{1,3}$", address) is None:
|
||||
print >>sys.stderr, "ERROR: Only IP is allowed when specyfying netmask"
|
||||
return None
|
||||
if netmask != "":
|
||||
netmask = int(unmask[1])
|
||||
if netmask < 0 or netmask > 32:
|
||||
print >>sys.stderr, "ERROR: Invalid netmask"
|
||||
return None
|
||||
else:
|
||||
print >>sys.stderr, "ERROR: Invalid netmask"
|
||||
return None
|
||||
|
||||
if address[-1:] == ".":
|
||||
address = address[:-1]
|
||||
|
||||
allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
|
||||
if not all(allowed.match(x) for x in address.split(".")):
|
||||
print >>sys.stderr, "ERROR: Invalid hostname"
|
||||
return None
|
||||
|
||||
proto_split = proto.split('/', 1)
|
||||
if len(proto_split) == 2:
|
||||
proto = proto_split[0]
|
||||
port = proto_split[1]
|
||||
|
||||
if proto not in ['tcp', 'udp', 'any']:
|
||||
print >>sys.stderr, "ERROR: Protocol must be one of: 'tcp', 'udp', 'any'"
|
||||
return None
|
||||
|
||||
if proto != "any" and port is None:
|
||||
print >>sys.stderr, "ERROR: Port required for protocol %s" % args[1]
|
||||
return None
|
||||
|
||||
if port is not None:
|
||||
port_range = port.split('-', 1)
|
||||
if len(port_range) == 2:
|
||||
port = port_range[0]
|
||||
port_end = port_range[1]
|
||||
|
||||
if port.isdigit():
|
||||
port = int(port)
|
||||
else:
|
||||
try:
|
||||
port = socket.getservbyname(port)
|
||||
except socket.error:
|
||||
print >>sys.stderr, "ERROR: Invalid port/service name '%s'" % port
|
||||
return None
|
||||
|
||||
if port_end is not None and not port_end.isdigit():
|
||||
print >>sys.stderr, "ERROR: Invalid port '%s'" % port_end
|
||||
return None
|
||||
|
||||
if port_end is not None:
|
||||
port_end = int(port_end)
|
||||
|
||||
rule = {}
|
||||
rule['address'] = address
|
||||
rule['netmask'] = netmask
|
||||
rule['proto'] = proto
|
||||
rule['portBegin'] = port
|
||||
rule['portEnd'] = port_end
|
||||
return rule
|
||||
|
||||
def list_rules(rules, numeric=False):
|
||||
fields = [ "num", "address", "proto", "port(s)" ]
|
||||
|
||||
rules_to_display = list()
|
||||
counter = 1
|
||||
for rule in rules:
|
||||
parsed_rule = {
|
||||
'num': "{0:>2}".format(counter),
|
||||
'address': rule['address'] + ('/' + str(rule['netmask']) if rule['netmask'] < 32 else ""),
|
||||
'proto': rule['proto'],
|
||||
'port(s)': '',
|
||||
}
|
||||
if rule['proto'] in ['tcp', 'udp']:
|
||||
parsed_rule['port(s)'] = str(rule['portBegin']) + \
|
||||
('-' + str(rule['portEnd']) if rule['portEnd'] is not None else '')
|
||||
if not numeric and rule['portBegin'] is not None and rule['portEnd'] is None:
|
||||
try:
|
||||
parsed_rule['port(s)'] = str(socket.getservbyport(rule['portBegin']))
|
||||
except socket.error:
|
||||
pass
|
||||
|
||||
if 'expire' in rule:
|
||||
parsed_rule['expire'] = str(datetime.datetime.fromtimestamp(rule[
|
||||
'expire']))
|
||||
|
||||
rules_to_display.append(parsed_rule)
|
||||
counter += 1
|
||||
|
||||
fields_width = {}
|
||||
for f in fields:
|
||||
fields_width[f] = len(f)
|
||||
for r in rules_to_display:
|
||||
if len(r[f]) > fields_width[f]:
|
||||
fields_width[f] = len(r[f])
|
||||
|
||||
# Display the header
|
||||
s = ""
|
||||
for f in fields:
|
||||
fmt="{{0:-^{0}}}-+".format(fields_width[f] + 1)
|
||||
s += fmt.format('-')
|
||||
print s
|
||||
|
||||
s = ""
|
||||
for f in fields:
|
||||
fmt=" {{0:^{0}}} |".format(fields_width[f])
|
||||
s += fmt.format(f)
|
||||
print s
|
||||
|
||||
s = ""
|
||||
for f in fields:
|
||||
fmt="{{0:-^{0}}}-+".format(fields_width[f] + 1)
|
||||
s += fmt.format('-')
|
||||
print s
|
||||
|
||||
# And the content
|
||||
for r in rules_to_display:
|
||||
s = ""
|
||||
for f in fields:
|
||||
fmt=" {{0:<{0}}} |".format(fields_width[f])
|
||||
s += fmt.format(r[f])
|
||||
if 'expire' in r:
|
||||
s += " <-- expires at %s" % r['expire']
|
||||
print s
|
||||
|
||||
def display_firewall(conf, numeric=False):
|
||||
print "Firewall policy: %s" % (
|
||||
"ALLOW all traffic except" if conf['allow'] else "DENY all traffic except")
|
||||
print "ICMP: %s" % ("ALLOW" if conf['allowIcmp'] else 'DENY')
|
||||
print "DNS: %s" % ("ALLOW" if conf['allowDns'] else 'DENY')
|
||||
print "Qubes yum proxy: %s" % ("ALLOW" if conf['allowYumProxy'] else 'DENY')
|
||||
list_rules(conf['rules'], numeric)
|
||||
|
||||
def add_rule(conf, args):
|
||||
rule = parse_rule(args)
|
||||
if rule is None:
|
||||
return False
|
||||
|
||||
conf['rules'].append(rule)
|
||||
return True
|
||||
|
||||
def del_rule(conf, args):
|
||||
if len(args) == 1 and args[0].isdigit():
|
||||
rulenum = int(args[0])
|
||||
if rulenum < 1 or rulenum > len(conf['rules']):
|
||||
print >>sys.stderr, "ERROR: Rule number out of range"
|
||||
return False
|
||||
conf['rules'].pop(rulenum-1)
|
||||
else:
|
||||
rule = parse_rule(args)
|
||||
#print "PARSED: %s" % str(rule)
|
||||
#print "ALL: %s" % str(conf['rules'])
|
||||
if rule is None:
|
||||
return False
|
||||
try:
|
||||
conf['rules'].remove(rule)
|
||||
except ValueError:
|
||||
print >>sys.stderr, "ERROR: Rule not found"
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def allow_deny_value(s):
|
||||
value = None
|
||||
if s == "allow":
|
||||
value = True
|
||||
elif s == "deny":
|
||||
value = False
|
||||
else:
|
||||
print >>sys.stderr, 'ERROR: Only "allow" or "deny" allowed'
|
||||
exit(1)
|
||||
return value
|
||||
|
||||
def main():
|
||||
usage = "usage: %prog [-n] <vm-name> [action] [rule spec]\n"
|
||||
usage += " rule specification can be one of:\n"
|
||||
usage += " address|hostname[/netmask] tcp|udp port[-port]\n"
|
||||
usage += " address|hostname[/netmask] tcp|udp service_name\n"
|
||||
usage += " address|hostname[/netmask] any\n"
|
||||
parser = OptionParser (usage)
|
||||
parser.add_option ("-l", "--list", dest="do_list", action="store_true", default=True,
|
||||
help="List firewall settings (default action)")
|
||||
parser.add_option ("-a", "--add", dest="do_add", action="store_true", default=False,
|
||||
help="Add rule")
|
||||
parser.add_option ("-d", "--del", dest="do_del", action="store_true", default=False,
|
||||
help="Remove rule (given by number or by rule spec)")
|
||||
parser.add_option ("-P", "--policy", dest="set_policy", action="store", default=None,
|
||||
help="Set firewall policy (allow/deny)")
|
||||
parser.add_option ("-i", "--icmp", dest="set_icmp", action="store", default=None,
|
||||
help="Set ICMP access (allow/deny)")
|
||||
parser.add_option ("-D", "--dns", dest="set_dns", action="store", default=None,
|
||||
help="Set DNS access (allow/deny)")
|
||||
parser.add_option ("-Y", "--yum-proxy", dest="set_yum_proxy", action="store", default=None,
|
||||
help="Set access to Qubes yum proxy (allow/deny)")
|
||||
parser.add_option ("-r", "--reload", dest="reload", action="store_true",
|
||||
default=False, help="Reload firewall (implied by any "
|
||||
"change action")
|
||||
|
||||
parser.add_option ("-n", "--numeric", dest="numeric", action="store_true", default=False,
|
||||
help="Display port numbers instead of services (makes sense only with --list)")
|
||||
parser.add_option ("--force-root", action="store_true", dest="force_root", default=False,
|
||||
help="Force to run, even with root privileges")
|
||||
|
||||
(options, args) = parser.parse_args ()
|
||||
if (len (args) < 1):
|
||||
parser.error ("You must specify VM name!")
|
||||
vmname = args[0]
|
||||
args = args[1:]
|
||||
|
||||
if hasattr(os, "geteuid") and os.geteuid() == 0:
|
||||
if not options.force_root:
|
||||
print >> sys.stderr, "*** Running this tool as root is strongly discouraged, this will lead you in permissions problems."
|
||||
print >> sys.stderr, "Retry as unprivileged user."
|
||||
print >> sys.stderr, "... or use --force-root to continue anyway."
|
||||
exit(1)
|
||||
|
||||
if options.do_add or options.do_del or options.set_policy or \
|
||||
options.set_icmp or options.set_dns or options.set_yum_proxy:
|
||||
options.do_list = False
|
||||
qvm_collection = QubesVmCollection()
|
||||
if options.do_list:
|
||||
qvm_collection.lock_db_for_reading()
|
||||
qvm_collection.load()
|
||||
qvm_collection.unlock_db()
|
||||
else:
|
||||
qvm_collection.lock_db_for_writing()
|
||||
qvm_collection.load()
|
||||
|
||||
vm = qvm_collection.get_vm_by_name(vmname)
|
||||
if vm is None:
|
||||
print >> sys.stderr, "A VM with the name '{0}' does not exist in the system.".format(vmname)
|
||||
exit(1)
|
||||
|
||||
changed = False
|
||||
conf = vm.get_firewall_conf()
|
||||
|
||||
if options.set_policy:
|
||||
conf['allow'] = allow_deny_value(options.set_policy)
|
||||
changed = True
|
||||
if options.set_icmp:
|
||||
conf['allowIcmp'] = allow_deny_value(options.set_icmp)
|
||||
changed = True
|
||||
if options.set_dns:
|
||||
conf['allowDns'] = allow_deny_value(options.set_dns)
|
||||
changed = True
|
||||
if options.set_yum_proxy:
|
||||
conf['allowYumProxy'] = allow_deny_value(options.set_yum_proxy)
|
||||
changed = True
|
||||
|
||||
if options.do_add:
|
||||
changed = add_rule(conf, args)
|
||||
elif options.do_del:
|
||||
changed = del_rule(conf, args)
|
||||
elif options.do_list and not options.reload:
|
||||
if not vm.has_firewall():
|
||||
print "INFO: This VM has no firewall rules set, below defaults are listed"
|
||||
display_firewall(conf, options.numeric)
|
||||
|
||||
if changed:
|
||||
vm.write_firewall_conf(conf)
|
||||
qvm_collection.save()
|
||||
if changed or options.reload:
|
||||
if vm.is_running():
|
||||
if vm.netvm is not None and vm.netvm.is_proxyvm():
|
||||
vm.netvm.write_iptables_qubesdb_entry()
|
||||
|
||||
if not options.do_list:
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
|
||||
main()
|
@ -1,66 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2011 Marek Marczykowski <marmarek@mimuw.edu.pl>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
from qubes.qubes import QubesVmCollection
|
||||
from qubes.qubes import QubesException
|
||||
from qubes.qubesutils import parse_size
|
||||
from optparse import OptionParser
|
||||
import subprocess
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
qvm_run_path = "/usr/bin/qvm-run"
|
||||
|
||||
def main():
|
||||
usage = "usage: %prog <vm-name> <size>"
|
||||
parser = OptionParser (usage)
|
||||
|
||||
(options, args) = parser.parse_args ()
|
||||
if (len (args) != 2):
|
||||
parser.error ("You must specify VM name and new size!")
|
||||
vmname = args[0]
|
||||
size = args[1]
|
||||
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_reading()
|
||||
qvm_collection.load()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
vm = qvm_collection.get_vm_by_name(vmname)
|
||||
if vm is None:
|
||||
print >> sys.stderr, "A VM with the name '{0}' does not exist in the system.".format(vmname)
|
||||
exit(1)
|
||||
|
||||
size_bytes = parse_size(size)
|
||||
|
||||
try:
|
||||
vm.resize_private_img(size_bytes)
|
||||
except (IOError, OSError, QubesException) as err:
|
||||
print >> sys.stderr, "ERROR: {0}".format(err)
|
||||
exit (1)
|
||||
|
||||
exit (0)
|
||||
|
||||
|
||||
main()
|
@ -1,72 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2014 Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
from qubes.qubes import QubesVmCollection
|
||||
from qubes.qubes import QubesException
|
||||
from qubes.qubesutils import parse_size
|
||||
from optparse import OptionParser
|
||||
import subprocess
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
def main():
|
||||
usage = "usage: %prog <vm-name> <size>"
|
||||
parser = OptionParser (usage)
|
||||
|
||||
parser.add_option("--allow-start", action="store_true",
|
||||
dest="allow_start", default=False,
|
||||
help="Allow VM to be started to complete the operation")
|
||||
|
||||
(options, args) = parser.parse_args ()
|
||||
if (len (args) != 2):
|
||||
parser.error ("You must specify VM name and new size!")
|
||||
vmname = args[0]
|
||||
size = args[1]
|
||||
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_reading()
|
||||
qvm_collection.load()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
vm = qvm_collection.get_vm_by_name(vmname)
|
||||
if vm is None:
|
||||
print >> sys.stderr, "A VM with the name '{0}' does not exist in the system.".format(vmname)
|
||||
exit(1)
|
||||
|
||||
size_bytes = parse_size(size)
|
||||
|
||||
if not hasattr(vm, 'resize_root_img'):
|
||||
print >> sys.stderr, "Operation not supported for this VM type"
|
||||
exit(1)
|
||||
|
||||
try:
|
||||
vm.resize_root_img(size_bytes, allow_start=options.allow_start)
|
||||
except (IOError, OSError, QubesException) as err:
|
||||
print >> sys.stderr, "ERROR: {0}".format(err)
|
||||
exit (1)
|
||||
|
||||
exit (0)
|
||||
|
||||
|
||||
main()
|
@ -1,35 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
from qubes.qubes import QubesVmCollection,vmm
|
||||
import sys
|
||||
|
||||
def main():
|
||||
vmm.offline_mode = True
|
||||
qvm_collection = QubesVmCollection()
|
||||
if qvm_collection.check_if_storage_exists():
|
||||
print >> sys.stderr, "Storage exists, not overwriting."
|
||||
exit(1)
|
||||
qvm_collection.create_empty_storage()
|
||||
|
||||
main()
|
@ -1,141 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Marek Marczykowski <marmarek@mimuw.edu.pl>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
from qubes.qubes import QubesVmCollection
|
||||
from optparse import OptionParser
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
from qubes.qubes import vmm
|
||||
import re
|
||||
|
||||
|
||||
def find_devices_of_class(klass):
|
||||
p = subprocess.Popen(["/sbin/lspci", "-mm", "-n"], stdout=subprocess.PIPE)
|
||||
result = p.communicate()
|
||||
retcode = p.returncode
|
||||
if retcode != 0:
|
||||
print "ERROR when executing lspci!"
|
||||
raise IOError
|
||||
|
||||
rx_netdev = re.compile(r"^([0-9a-f]{2}:[0-9a-f]{2}.[0-9a-f]) \"" +
|
||||
klass)
|
||||
for dev in str(result[0]).splitlines():
|
||||
match = rx_netdev.match(dev)
|
||||
if match is not None:
|
||||
dev_bdf = match.group(1)
|
||||
assert dev_bdf is not None
|
||||
yield dev_bdf
|
||||
|
||||
|
||||
def main():
|
||||
usage = "usage: %prog -l [options] <vm-name>\n"\
|
||||
"usage: %prog -a [options] <vm-name> <device>\n"\
|
||||
"usage: %prog -d [options] <vm-name> <device>\n"\
|
||||
"List/set VM PCI devices."
|
||||
|
||||
parser = OptionParser (usage)
|
||||
parser.add_option ("-l", "--list", action="store_true", dest="do_list", default=False)
|
||||
parser.add_option ("-a", "--add", action="store_true", dest="do_add", default=False)
|
||||
parser.add_option ("-d", "--delete", action="store_true", dest="do_delete", default=False)
|
||||
parser.add_option("-C", "--add-class", action="store_true",
|
||||
dest="do_add_class", default=False,
|
||||
help="Add all devices of given class (net, usb)")
|
||||
parser.add_option ("--offline-mode", dest="offline_mode",
|
||||
action="store_true", default=False,
|
||||
help="Offline mode")
|
||||
|
||||
(options, args) = parser.parse_args ()
|
||||
if (len (args) < 1):
|
||||
parser.error ("You must provide at least the vmname!")
|
||||
|
||||
vmname = args[0]
|
||||
|
||||
if options.do_list + options.do_add + options.do_delete + \
|
||||
options.do_add_class > 1:
|
||||
print >> sys.stderr, "Only one of -l -a -d -C is allowed!"
|
||||
exit(1)
|
||||
|
||||
if options.offline_mode:
|
||||
vmm.offline_mode = True
|
||||
|
||||
if options.do_add or options.do_delete or options.do_add_class:
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_writing()
|
||||
qvm_collection.load()
|
||||
else:
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_reading()
|
||||
qvm_collection.load()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
vm = qvm_collection.get_vm_by_name(vmname)
|
||||
if vm is None or vm.qid not in qvm_collection:
|
||||
print >> sys.stderr, "A VM with the name '{0}' does not exist in the system.".format(vmname)
|
||||
exit(1)
|
||||
|
||||
if options.do_add:
|
||||
if len (args) < 2:
|
||||
print >> sys.stderr, "You must specify the PCI device to add"
|
||||
exit (1)
|
||||
|
||||
pci = args[1]
|
||||
vm.pci_add(pci)
|
||||
qvm_collection.save()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
elif options.do_add_class:
|
||||
if len(args) < 2:
|
||||
print >> sys.stderr, "You must specify the PCI device class to add"
|
||||
exit(1)
|
||||
|
||||
klass = args[1]
|
||||
|
||||
if klass == 'net':
|
||||
devs = find_devices_of_class("02")
|
||||
elif klass == 'usb':
|
||||
devs = find_devices_of_class("0c03")
|
||||
else:
|
||||
print >> sys.stderr, "Supported classes: net, usb"
|
||||
exit(1)
|
||||
|
||||
for dev in devs:
|
||||
vm.pci_add(dev)
|
||||
qvm_collection.save()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
elif options.do_delete:
|
||||
if len (args) < 2:
|
||||
print >> sys.stderr, "You must specify the PCI device to delete"
|
||||
exit (1)
|
||||
|
||||
pci = args[1]
|
||||
vm.pci_remove(pci)
|
||||
qvm_collection.save()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
else:
|
||||
# do_list
|
||||
print str(vm.pcidevs)
|
||||
|
||||
main()
|
@ -1,142 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2011 Marek Marczykowski <marmarek@mimuw.edu.pl>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
from qubes.qubes import QubesVmCollection
|
||||
from qubes.qubes import QubesException
|
||||
from optparse import OptionParser
|
||||
import subprocess
|
||||
import os
|
||||
import time
|
||||
import glob
|
||||
import sys
|
||||
|
||||
def main():
|
||||
usage = "usage: %prog [options] <template-name>"
|
||||
parser = OptionParser (usage)
|
||||
parser.add_option ("--force", action="store_true", dest="force", default=False,
|
||||
help="Do not prompt for confirmation")
|
||||
|
||||
(options, args) = parser.parse_args ()
|
||||
if (len (args) != 1):
|
||||
parser.error ("You must specify TemplateVM name!")
|
||||
vmname = args[0]
|
||||
|
||||
if hasattr(os, "geteuid") and os.geteuid() != 0:
|
||||
print >> sys.stderr, "ERROR: This tool must be run as root!"
|
||||
exit(1)
|
||||
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_reading()
|
||||
qvm_collection.load()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
vm = qvm_collection.get_vm_by_name(vmname)
|
||||
if vm is None:
|
||||
print >> sys.stderr, "A VM with the name '{0}' does not exist in the system.".format(vmname)
|
||||
exit(1)
|
||||
|
||||
if not vm.is_template():
|
||||
print >> sys.stderr, "A VM '{0}' is not template.".format(vmname)
|
||||
exit(1)
|
||||
|
||||
if vm.is_running():
|
||||
print >> sys.stderr, "You must stop VM first."
|
||||
exit(1)
|
||||
|
||||
oldcow_img = vm.rootcow_img + '.old'
|
||||
oldcow_stat = os.stat(oldcow_img)
|
||||
oldcow_time_str = time.strftime("%F %T", time.gmtime(oldcow_stat.st_mtime))
|
||||
|
||||
root_stat = os.stat(vm.root_img)
|
||||
old_dmdev = "/dev/mapper/snapshot-{0:x}:{1}-{2:x}:{3}".format(
|
||||
root_stat[2], root_stat[1],
|
||||
oldcow_stat[2], oldcow_stat[1])
|
||||
|
||||
snapshots = glob.glob('/dev/mapper/snapshot-{0:x}:{1}-*'.format(root_stat[2], root_stat[1]))
|
||||
snapshot_present = False
|
||||
for dev in snapshots:
|
||||
if dev == old_dmdev:
|
||||
snapshot_present = True
|
||||
else:
|
||||
print >> sys.stderr, "ERROR: You must shutdown all VMs running system older/newer than last good one."
|
||||
exit(1)
|
||||
|
||||
root_blocks = os.path.getsize(vm.root_img)/512
|
||||
if not snapshot_present:
|
||||
p = subprocess.Popen (["/etc/xen/scripts/block-snapshot", "prepare",
|
||||
"snapshot", "{0}:{1}".format(vm.root_img, oldcow_img)],
|
||||
stdout=subprocess.PIPE)
|
||||
result = p.communicate()
|
||||
if result[0].strip() != old_dmdev:
|
||||
print >> sys.stderr, "ERROR: Cannot create snapshot device ({0} != {1})".format(
|
||||
result[0].strip(), old_dmdev)
|
||||
exit(1)
|
||||
|
||||
|
||||
print "INFO: Reverting template changes done at {0}".format(oldcow_time_str)
|
||||
if not options.force:
|
||||
prompt = raw_input ("Do you want to proceed? [y/N] ")
|
||||
if not (prompt == "y" or prompt == "Y"):
|
||||
exit (0)
|
||||
|
||||
p = subprocess.Popen(["/sbin/dmsetup", "table", old_dmdev], stdout=subprocess.PIPE)
|
||||
result = p.communicate()
|
||||
dm_table = result[0]
|
||||
dm_table_elements = dm_table.split(' ')
|
||||
if dm_table_elements[2] != 'snapshot':
|
||||
print >> sys.stderr, "ERROR: Unexpected device-mapper type ({0}). Template changes reverting already running".format(dm_table_elements[2])
|
||||
exit(1)
|
||||
|
||||
dm_table_elements[2] = 'snapshot-merge'
|
||||
dm_table = ' '.join(dm_table_elements)
|
||||
subprocess.check_call(["/sbin/dmsetup", "reload", old_dmdev, "--table", dm_table])
|
||||
# Reload new table into LIVE slot
|
||||
subprocess.check_call(["/sbin/dmsetup", "suspend", old_dmdev])
|
||||
subprocess.check_call(["/sbin/dmsetup", "resume", old_dmdev])
|
||||
# Wait to snapshot merge completed
|
||||
while True:
|
||||
p = subprocess.Popen(["/sbin/dmsetup", "status", old_dmdev], stdout=subprocess.PIPE)
|
||||
result = p.communicate()
|
||||
status_details = result[0].split(' ')
|
||||
blocks_used = status_details[3].split('/')[0]
|
||||
if int(blocks_used) == int(status_details[4]):
|
||||
break
|
||||
print "\r-> Reverting template changes: {0} of {1} left".format(blocks_used, root_blocks),
|
||||
time.sleep(1)
|
||||
print "\r-> Reverting template changes: done ".format(blocks_used, root_blocks)
|
||||
|
||||
dm_table_elements[2] = 'snapshot'
|
||||
dm_table = ' '.join(dm_table_elements)
|
||||
subprocess.check_call(["/sbin/dmsetup", "reload", old_dmdev, "--table", dm_table])
|
||||
# Reload new table into LIVE slot
|
||||
subprocess.check_call(["/sbin/dmsetup", "suspend", old_dmdev])
|
||||
subprocess.check_call(["/sbin/dmsetup", "resume", old_dmdev])
|
||||
|
||||
subprocess.check_call(["/etc/xen/scripts/block-snapshot", "cleanup",
|
||||
"snapshot", old_dmdev])
|
||||
|
||||
os.rename(oldcow_img, vm.rootcow_img)
|
||||
exit(0)
|
||||
|
||||
|
||||
main()
|
@ -1,98 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2012 Marek Marczykowski <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
from qubes.qubes import QubesVmCollection
|
||||
from optparse import OptionParser;
|
||||
import subprocess
|
||||
import sys
|
||||
import re
|
||||
|
||||
def do_list(vm):
|
||||
max_len = 0
|
||||
for s in vm.services.keys():
|
||||
max_len = max(max_len, len(s))
|
||||
fmt="{{0:<{0}}}: {{1}}".format(max_len)
|
||||
|
||||
for s in vm.services.keys():
|
||||
print fmt.format (s, "Enabled" if vm.services[s] else "Disabled")
|
||||
|
||||
|
||||
def main():
|
||||
usage = "usage: %prog <vm-name> [action] [service]\n"
|
||||
parser = OptionParser (usage)
|
||||
parser.add_option ("-l", "--list", dest="do_list", action="store_true", default=True,
|
||||
help="List services (default action)")
|
||||
parser.add_option ("-e", "--enable", dest="set_enable", action="store_true", default=False,
|
||||
help="Enable service")
|
||||
parser.add_option ("-d", "--disable", dest="set_disable", action="store_true", default=False,
|
||||
help="Disable service")
|
||||
parser.add_option ("-D", "--default", dest="set_default", action="store_true", default=False,
|
||||
help="Reset service to its default state (remove from the list)")
|
||||
|
||||
(options, args) = parser.parse_args ()
|
||||
if (len (args) < 1):
|
||||
parser.error ("You must specify VM name!")
|
||||
vmname = args[0]
|
||||
args = args[1:]
|
||||
|
||||
if options.set_enable or options.set_disable or options.set_default:
|
||||
if (len(args) < 1):
|
||||
parser.error("You must specify service name!")
|
||||
options.do_list = False
|
||||
|
||||
qvm_collection = QubesVmCollection()
|
||||
if options.do_list:
|
||||
qvm_collection.lock_db_for_reading()
|
||||
qvm_collection.load()
|
||||
qvm_collection.unlock_db()
|
||||
else:
|
||||
qvm_collection.lock_db_for_writing()
|
||||
qvm_collection.load()
|
||||
|
||||
vm = qvm_collection.get_vm_by_name(vmname)
|
||||
if vm is None:
|
||||
print >> sys.stderr, "A VM with the name '{0}' does not exist in the system.".format(vmname)
|
||||
exit(1)
|
||||
|
||||
changed = False
|
||||
if options.do_list:
|
||||
do_list(vm)
|
||||
elif options.set_enable:
|
||||
vm.services[args[0]] = True
|
||||
changed = True
|
||||
elif options.set_disable:
|
||||
vm.services[args[0]] = False
|
||||
changed = True
|
||||
elif options.set_default:
|
||||
if vm.services.has_key(args[0]):
|
||||
vm.services.pop(args[0])
|
||||
changed = True
|
||||
|
||||
if changed:
|
||||
qvm_collection.save()
|
||||
|
||||
if not options.do_list:
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
|
||||
main()
|
@ -1,73 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2011 Marek Marczykowski <marmarek@mimuw.edu.pl>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
from qubes.qubes import QubesVmCollection,vmm
|
||||
from qubes.qubes import QubesException
|
||||
from optparse import OptionParser
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
def main():
|
||||
usage = "usage: %prog [options] <vm-name>"
|
||||
parser = OptionParser (usage)
|
||||
parser.add_option ("--offline-mode", dest="offline_mode",
|
||||
action="store_true", default=False,
|
||||
help="Offline mode")
|
||||
|
||||
(options, args) = parser.parse_args ()
|
||||
if (len (args) != 1):
|
||||
parser.error ("You must specify VM name!")
|
||||
vmname = args[0]
|
||||
|
||||
if options.offline_mode:
|
||||
vmm.offline_mode = True
|
||||
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_reading()
|
||||
qvm_collection.load()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
vm = qvm_collection.get_vm_by_name(vmname)
|
||||
if vm is None:
|
||||
print >> sys.stderr, "A VM with the name '{0}' does not exist in the system.".format(vmname)
|
||||
exit(1)
|
||||
|
||||
if not vm.is_template():
|
||||
print >> sys.stderr, "A VM '{0}' is not template.".format(vmname)
|
||||
exit(1)
|
||||
|
||||
if not vmm.offline_mode and vm.is_running():
|
||||
print >> sys.stderr, "You must stop VM first."
|
||||
exit(1)
|
||||
|
||||
try:
|
||||
vm.verify_files()
|
||||
vm.commit_changes()
|
||||
except (IOError, OSError, QubesException) as err:
|
||||
print >> sys.stderr, "ERROR: {0}".format(err)
|
||||
exit (1)
|
||||
|
||||
exit (0)
|
||||
|
||||
|
||||
main()
|
@ -1,171 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2014 Matt McCutchen <matt@mattmccutchen.net>
|
||||
# Copyright (C) 2015 Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from qubes import qubes
|
||||
from qubes import qubesutils
|
||||
|
||||
|
||||
def is_dvm_up_to_date(tmpl, dvm_tmpl):
|
||||
dvm_savefile_path = os.path.join(dvm_tmpl.dir_path, "dvm-savefile")
|
||||
if not os.path.isfile(dvm_savefile_path):
|
||||
return False
|
||||
|
||||
dvm_mtime = os.path.getmtime(dvm_savefile_path)
|
||||
root_mtime = os.path.getmtime(tmpl.root_img)
|
||||
if dvm_mtime < root_mtime:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2:
|
||||
print >> sys.stderr, 'Usage: qvm-trim-template TEMPLATEVM_NAME'
|
||||
sys.exit(1)
|
||||
|
||||
tvm_name = sys.argv[1]
|
||||
|
||||
qvm_collection = qubes.QubesVmCollection()
|
||||
qvm_collection.lock_db_for_writing()
|
||||
qvm_collection.load()
|
||||
|
||||
tvm = qvm_collection.get_vm_by_name(tvm_name)
|
||||
if tvm is None:
|
||||
print >> sys.stderr, 'VM \'{}\' does not exists'.format(tvm_name)
|
||||
sys.exit(1)
|
||||
|
||||
if not tvm.is_template():
|
||||
print >> sys.stderr, '{} is not template'.format(tvm_name)
|
||||
sys.exit(1)
|
||||
|
||||
if tvm.is_running():
|
||||
print >> sys.stderr, 'Please stop the TemplateVM first.'
|
||||
sys.exit(1)
|
||||
|
||||
outdated_children = [c for c in qvm_collection.get_vms_based_on(tvm.qid) if
|
||||
c.is_outdated()]
|
||||
if outdated_children:
|
||||
print >> sys.stderr, 'Please stop (or restart) the following outdated VMs based on the template first:\n%s' % ', '.join(
|
||||
|
||||
c.name for c in outdated_children)
|
||||
sys.exit(1)
|
||||
|
||||
rootcow_old = tvm.rootcow_img + '.old'
|
||||
print 'Disk usage before:'
|
||||
subprocess.check_call(['du', tvm.root_img] + (
|
||||
['--total', rootcow_old] if os.path.exists(rootcow_old) else []))
|
||||
|
||||
# root-cow.img.old is likely to be invalid once we trim root.img, so go ahead and delete it.
|
||||
# (Note, root-cow.img should be logically empty because the TemplateVM is not running.)
|
||||
if os.path.exists(rootcow_old):
|
||||
os.remove(rootcow_old)
|
||||
|
||||
dvm_tmpl = qvm_collection.get_vm_by_name(tvm.name + '-dvm')
|
||||
if dvm_tmpl is None:
|
||||
touch_dvm_savefile = False
|
||||
else:
|
||||
touch_dvm_savefile = is_dvm_up_to_date(tvm, dvm_tmpl)
|
||||
|
||||
print >> sys.stderr, "Creating temporary VM..."
|
||||
trim_vmname = "trim-{}".format(tvm_name[:31 - len('trim-')])
|
||||
fstrim_vm = qvm_collection.get_vm_by_name(trim_vmname)
|
||||
if fstrim_vm is not None:
|
||||
if not fstrim_vm.internal:
|
||||
print >>sys.stderr, \
|
||||
"ERROR: VM '{}' already exists and is not marked as internal. " \
|
||||
"Remove it manually."
|
||||
fstrim_vm.remove_from_disk()
|
||||
qvm_collection.pop(fstrim_vm.qid)
|
||||
fstrim_vm = qvm_collection.add_new_vm(
|
||||
"QubesAppVm",
|
||||
template=tvm,
|
||||
name=trim_vmname,
|
||||
netvm=None,
|
||||
internal=True,
|
||||
)
|
||||
if not fstrim_vm:
|
||||
print >> sys.stderr, "ERROR: Failed to create new VM"
|
||||
sys.exit(1)
|
||||
|
||||
fstrim_vm.create_on_disk()
|
||||
fstrim_vm.start(start_guid=False, verbose=True)
|
||||
|
||||
print >> sys.stderr, "Performing fstrim now..."
|
||||
fstrim_process = fstrim_vm.run("/bin/sh", user="root", passio_popen=True,
|
||||
gui=False)
|
||||
fstrim_process.stdin.write('''
|
||||
until [ -r /dev/xvdi ]; do
|
||||
sleep 1
|
||||
done
|
||||
mkdir /tmp/root
|
||||
mount -o ro /dev/xvdi /tmp/root
|
||||
fstrim -v /tmp/root
|
||||
poweroff
|
||||
''')
|
||||
fstrim_process.stdin.close()
|
||||
|
||||
qubesutils.block_attach(qvm_collection, fstrim_vm,
|
||||
{
|
||||
'vm': 'dom0',
|
||||
'device': tvm.root_img,
|
||||
'mode': 'w',
|
||||
},
|
||||
mode='w',
|
||||
frontend='xvdi')
|
||||
|
||||
# At this point, the trim should run and the vm should shut down by itself and
|
||||
# detach the block device.
|
||||
|
||||
fstrim_process.wait()
|
||||
print >> sys.stderr, "fstrim done, cleaning up..."
|
||||
|
||||
while fstrim_vm.is_running():
|
||||
time.sleep(1)
|
||||
|
||||
fstrim_vm.remove_from_disk()
|
||||
qvm_collection.pop(fstrim_vm.qid)
|
||||
|
||||
# if DispVM template was up to date, keep that state
|
||||
if touch_dvm_savefile:
|
||||
dvm_savefile_path = os.path.join(dvm_tmpl.dir_path, 'dvm-savefile')
|
||||
os.utime(dvm_savefile_path, None)
|
||||
# If this is default DispVM, make sure that tmpfs copy of the file
|
||||
# (if enabled) also has mtime updated
|
||||
if os.stat('/var/lib/qubes/dvmdata/default-savefile').st_ino == \
|
||||
os.stat(dvm_savefile_path).st_ino:
|
||||
os.utime('/var/run/qubes/current-savefile', None)
|
||||
|
||||
# do not save, all changes to qubes.xml should be reversed
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
print 'Disk usage after:'
|
||||
subprocess.check_call(['du', tvm.root_img])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,141 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
# -*- encoding: utf8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Marek Marczykowski <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
from qubes.qubes import QubesVmCollection, QubesException
|
||||
from qubes.qubesutils import usb_list,usb_attach,usb_detach,usb_detach_all,usb_check_attached
|
||||
from optparse import OptionParser
|
||||
import sys
|
||||
import os
|
||||
|
||||
def main():
|
||||
usage = "usage: %prog -l [options]\n"\
|
||||
"usage: %prog -a [options] <vm-name> <device-vm-name>:<device>\n"\
|
||||
"usage: %prog -d [options] <device-vm-name>:<device>\n"\
|
||||
"List/set VM USB devices."
|
||||
# "usage: %prog -d [options] <vm-name>\n"\
|
||||
|
||||
parser = OptionParser (usage)
|
||||
parser.add_option ("-l", "--list", action="store_true", dest="do_list", default=False)
|
||||
parser.add_option ("-a", "--attach", action="store_true", dest="do_attach", default=False)
|
||||
parser.add_option ("-d", "--detach", action="store_true", dest="do_detach", default=False)
|
||||
# parser.add_option ("-f", "--frontend", dest="frontend",
|
||||
# help="Specify device id at destination VM [default: first unused]")
|
||||
parser.add_option ("--no-auto-detach", dest="auto_detach", action="store_false", default=True,
|
||||
help="Fail when device already connected to other VM")
|
||||
parser.add_option ("--force-root", action="store_true", dest="force_root", default=False,
|
||||
help="Force to run, even with root privileges")
|
||||
|
||||
(options, args) = parser.parse_args ()
|
||||
|
||||
if hasattr(os, "geteuid") and os.geteuid() == 0:
|
||||
if not options.force_root:
|
||||
print >> sys.stderr, "*** Running this tool as root is strongly discouraged, this will lead you in permissions problems."
|
||||
print >> sys.stderr, "Retry as unprivileged user."
|
||||
print >> sys.stderr, "... or use --force-root to continue anyway."
|
||||
exit(1)
|
||||
|
||||
if options.do_list + options.do_attach + options.do_detach > 1:
|
||||
print >> sys.stderr, "Only one of -l -a -d is allowed!"
|
||||
exit (1)
|
||||
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_reading()
|
||||
qvm_collection.load()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
if options.do_attach:
|
||||
if (len (args) != 2):
|
||||
parser.error ("You must provide vm name and device!")
|
||||
vm = qvm_collection.get_vm_by_name(args[0])
|
||||
if vm is None:
|
||||
parser.error ("Invalid VM name: %s" % args[0])
|
||||
|
||||
# FIXME: here we assume that device is always in form "domain:dev",
|
||||
# which can be changed in the future
|
||||
if args[1].find(":") < 0:
|
||||
parser.error("Invalid device syntax: %s" % args[1])
|
||||
backend_vm = qvm_collection.get_vm_by_name(args[1].split(":")[0])
|
||||
if backend_vm is None:
|
||||
parser.error("No such VM: {}".format(args[1].split(":")[0]))
|
||||
dev_list = usb_list(qvm_collection, vm=backend_vm)
|
||||
if not args[1] in dev_list.keys():
|
||||
parser.error("Invalid device name: %s" % args[1])
|
||||
dev = dev_list[args[1]]
|
||||
assert backend_vm is not None
|
||||
|
||||
kwargs = {}
|
||||
# if options.frontend:
|
||||
# kwargs['frontend'] = options.frontend
|
||||
kwargs['auto_detach'] = options.auto_detach
|
||||
try:
|
||||
usb_attach(qvm_collection, vm, dev, **kwargs)
|
||||
except QubesException as e:
|
||||
print >> sys.stderr, "ERROR: %s" % str(e)
|
||||
sys.exit(1)
|
||||
elif options.do_detach:
|
||||
if (len (args) < 1):
|
||||
parser.error ("You must provide device or vm name!")
|
||||
if len(args) > 1:
|
||||
parser.error ("Too many parameters")
|
||||
# Check if provided name is VM
|
||||
vm = qvm_collection.get_vm_by_name(args[0])
|
||||
if vm is not None:
|
||||
#kwargs = {}
|
||||
#if options.frontend:
|
||||
# kwargs['frontend'] = options.frontend
|
||||
# usb_detach(vm, **kwargs)
|
||||
#else:
|
||||
usb_detach_all(qvm_collection, vm)
|
||||
else:
|
||||
# Maybe usbvm:device?
|
||||
|
||||
# FIXME: nasty copy-paste from attach code half a page above
|
||||
# FIXME: here we assume that device is always in form "domain:dev",
|
||||
# which can be changed in the future
|
||||
if args[0].find(":") < 0:
|
||||
parser.error("Invalid device syntax: %s" % args[0])
|
||||
backend_vm = qvm_collection.get_vm_by_name(args[0].split(":")[0])
|
||||
if backend_vm is None:
|
||||
parser.error("No such VM: {}".format(args[0].split(":")[0]))
|
||||
dev_list = usb_list(qvm_collection, vm=backend_vm)
|
||||
if not args[0] in dev_list.keys():
|
||||
parser.error("Invalid device name: %s" % args[0])
|
||||
dev = dev_list[args[0]]
|
||||
attached_to = usb_check_attached(qvm_collection, dev)
|
||||
if attached_to is None:
|
||||
print >> sys.stderr, "WARNING: Device not connected to any VM"
|
||||
exit(0)
|
||||
usb_detach(qvm_collection, attached_to, dev)
|
||||
else:
|
||||
if len(args) > 0:
|
||||
parser.error("Too many parameters")
|
||||
# do_list
|
||||
for dev in usb_list(qvm_collection).values():
|
||||
attached_to = dev['connected-to']
|
||||
attached_to_str = ""
|
||||
if attached_to:
|
||||
attached_to_str = " (attached to %s)" % (attached_to.name)
|
||||
print "%s\t%s%s" % (dev['name'], dev['desc'], attached_to_str)
|
||||
exit (0)
|
||||
|
||||
main()
|
@ -148,7 +148,6 @@ fi
|
||||
|
||||
systemctl --no-reload enable qubes-core.service >/dev/null 2>&1
|
||||
systemctl --no-reload enable qubes-netvm.service >/dev/null 2>&1
|
||||
systemctl --no-reload enable qubes-setupdvm.service >/dev/null 2>&1
|
||||
|
||||
# Conflicts with libxl stack, so disable it
|
||||
systemctl --no-reload disable xend.service >/dev/null 2>&1
|
||||
@ -404,20 +403,17 @@ fi
|
||||
|
||||
/usr/lib/qubes/unbind-pci-device.sh
|
||||
/usr/lib/qubes/cleanup-dispvms
|
||||
/usr/lib/qubes/qfile-daemon-dvm*
|
||||
/usr/lib/qubes/block-cleaner-daemon.py*
|
||||
/usr/lib/qubes/vusb-ctl.py*
|
||||
/usr/lib/qubes/xl-qvm-usb-attach.py*
|
||||
/usr/lib/qubes/xl-qvm-usb-detach.py*
|
||||
/usr/lib/qubes/fix-dir-perms.sh
|
||||
/usr/lib/qubes/startup-dvm.sh
|
||||
/usr/lib/qubes/startup-misc.sh
|
||||
/usr/lib/qubes/prepare-volatile-img.sh
|
||||
/usr/libexec/qubes/qubes-notify-tools
|
||||
/usr/libexec/qubes/qubes-notify-updates
|
||||
%{_unitdir}/qubes-block-cleaner.service
|
||||
%{_unitdir}/qubes-core.service
|
||||
%{_unitdir}/qubes-setupdvm.service
|
||||
%{_unitdir}/qubes-netvm.service
|
||||
%{_unitdir}/qubes-qmemman.service
|
||||
%{_unitdir}/qubes-vm@.service
|
||||
@ -435,9 +431,6 @@ fi
|
||||
/usr/share/qubes/templates/libvirt/devices/pci.xml
|
||||
/usr/share/qubes/templates/libvirt/devices/net.xml
|
||||
/usr/lib/tmpfiles.d/qubes.conf
|
||||
/usr/lib/qubes/qubes-prepare-saved-domain.sh
|
||||
/usr/lib/qubes/qubes-update-dispvm-savefile-with-progress.sh
|
||||
/etc/xen/scripts/block.qubes
|
||||
/etc/xen/scripts/block-snapshot
|
||||
/etc/xen/scripts/block-origin
|
||||
/etc/xen/scripts/vif-route-qubes
|
||||
@ -456,7 +449,6 @@ fi
|
||||
/etc/qubes-rpc/qubes.NotifyUpdates
|
||||
%attr(2770,root,qubes) %dir /var/log/qubes
|
||||
%attr(0770,root,qubes) %dir /var/run/qubes
|
||||
/etc/xdg/autostart/qubes-guid.desktop
|
||||
/etc/xdg/autostart/qrexec-policy-agent.desktop
|
||||
|
||||
/usr/share/doc/qubes/relaxng/*.rng
|
||||
|
Loading…
Reference in New Issue
Block a user