#!/usr/bin/python2 # # The Qubes OS Project, http://www.qubes-os.org # # Copyright (C) 2010 Rafal Wojtczuk # # 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 fcntl import shutil import time from qubes.qubes import QubesVmCollection from qubes.qubes import QubesException from qubes.qubes import QubesDaemonPidfile from qubes.qubes import QubesDispVmLabels from qubes.qmemman_client import QMemmanClient from qubes.notify import tray_notify,tray_notify_error,tray_notify_init current_savefile = '/var/run/qubes/current-savefile' current_dvm_conf = '/var/run/qubes/current-dvm.conf' class QfileDaemonDvm: def __init__(self, name): self.name = name def do_get_dvm(self): qmemman_client = QMemmanClient() if not qmemman_client.request_memory(400*1024*1024): qmemman_client.close() errmsg = 'Not enough memory to create DVM. ' errmsg +='Terminate some appVM and retry.' if os.path.exists('/usr/bin/kdialog'): subprocess.call(['/usr/bin/kdialog', '--sorry', errmsg]) else: subprocess.call(['/usr/bin/zenity', '--warning', '--text', errmsg]) return None tray_notify("Starting new DispVM...", "red") qvm_collection = QubesVmCollection() qvm_collection.lock_db_for_writing() qvm_collection.load() vm = qvm_collection.get_vm_by_name(self.name) if vm is None: sys.stderr.write( 'Domain ' + self.name + ' does not exist ?') qvm_collection.unlock_db() qmemman_client.close() 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]] print >>sys.stderr, "time=%s, starting qubes-restore" % (str(time.time())) retcode = subprocess.call(['/usr/lib/qubes/qubes-restore', current_savefile, current_dvm_conf, '-u', str(vm.default_user), '-c', label.color, '-i', label.icon_path, '-l', str(label.index)]) qmemman_client.close() if retcode != 0: tray_notify_error('DisposableVM creation failed, see qubes-restore.log') qvm_collection.unlock_db() return None f = open('/var/run/qubes/dispVM.xid', 'r'); disp_xid = f.readline().rstrip('\n') disp_name = f.readline().rstrip('\n') disptempl = f.readline().rstrip('\n') f.close() print >>sys.stderr, "time=%s, adding to qubes.xml" % (str(time.time())) vm_disptempl = qvm_collection.get_vm_by_name(disptempl); if vm_disptempl is None: sys.stderr.write( 'Domain ' + disptempl + ' does not exist ?') qvm_collection.unlock_db() return None dispid=int(disp_name[4:]) dispvm=qvm_collection.add_new_disposablevm(disp_name, vm_disptempl.template, label=label, dispid=dispid, netvm=vm_disptempl.netvm) # By default inherit firewall rules from calling VM if os.path.exists(vm.firewall_conf): disp_firewall_conf = '/var/run/qubes/%s-firewall.xml' % disp_name shutil.copy(vm.firewall_conf, disp_firewall_conf) dispvm.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] qvm_collection.save() 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_xenstore_entry() return disp_name def dvm_setup_ok(self): 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]) == 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 >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) lockf = open("/var/run/qubes/qfile-daemon-dvm.lock", 'a') fcntl.fcntl(lockf, fcntl.F_SETFD, fcntl.FD_CLOEXEC) fcntl.flock(lockf, fcntl.LOCK_EX) dispname = qfile.get_dvm() lockf.close() if dispname is not None: print >>sys.stderr, "time=%s, starting VM process" % (str(time.time())) subprocess.call(['/usr/lib/qubes/qrexec-client', '-d', dispname, user+':exec /usr/lib/qubes/qubes-rpc-multiplexer ' + exec_index + " " + src_vmname]) subprocess.call(['/usr/sbin/xl', 'destroy', dispname]) qfile.remove_disposable_from_qdb(dispname) main()