diff --git a/.gitignore b/.gitignore index 4d22dcb6..e2440642 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ rpm/ *.pyc +*.pyo +*~ +*.o diff --git a/Makefile b/Makefile index 01085b28..b38c5f5c 100644 --- a/Makefile +++ b/Makefile @@ -5,20 +5,26 @@ help: @echo "make update-repo-testing -- same, but to -testing repo" rpms: + rpmbuild --define "_rpmdir $(RPMS_DIR)" -bb rpm_spec/core-commonvm.spec rpmbuild --define "_rpmdir $(RPMS_DIR)" -bb rpm_spec/core-appvm.spec rpmbuild --define "_rpmdir $(RPMS_DIR)" -bb rpm_spec/core-netvm.spec + rpmbuild --define "_rpmdir $(RPMS_DIR)" -bb rpm_spec/core-proxyvm.spec rpmbuild --define "_rpmdir $(RPMS_DIR)" -bb rpm_spec/core-dom0.spec rpm --addsign $(RPMS_DIR)/x86_64/*.rpm update-repo: ln -f $(RPMS_DIR)/x86_64/qubes-core-dom0-*.rpm ../yum/r1/dom0/rpm/ ln -f $(RPMS_DIR)/x86_64/qubes-core-appvm-*.rpm ../yum/r1/appvm/rpm/ + ln -f $(RPMS_DIR)/x86_64/qubes-core-commonvm-*.rpm ../yum/r1/netvm/rpm/ ln -f $(RPMS_DIR)/x86_64/qubes-core-netvm-*.rpm ../yum/r1/netvm/rpm/ + ln -f $(RPMS_DIR)/x86_64/qubes-core-proxyvm-*.rpm ../yum/r1/netvm/rpm/ update-repo-testing: ln -f $(RPMS_DIR)/x86_64/qubes-core-dom0-*.rpm ../yum/r1-testing/dom0/rpm/ ln -f $(RPMS_DIR)/x86_64/qubes-core-appvm-*.rpm ../yum/r1-testing/appvm/rpm/ + ln -f $(RPMS_DIR)/x86_64/qubes-core-commonvm-*.rpm ../yum/r1-testing/netvm/rpm/ ln -f $(RPMS_DIR)/x86_64/qubes-core-netvm-*.rpm ../yum/r1-testing/netvm/rpm/ + ln -f $(RPMS_DIR)/x86_64/qubes-core-proxyvm-*.rpm ../yum/r1-testing/netvm/rpm/ diff --git a/appvm/.gitignore b/appvm/.gitignore new file mode 100644 index 00000000..23680333 --- /dev/null +++ b/appvm/.gitignore @@ -0,0 +1,4 @@ +qubes_add_pendrive_script +qubes_penctl +qvm-open-in-dvm +xenstore-watch diff --git a/appvm/Makefile b/appvm/Makefile index 6bb1dea6..0ef375a7 100644 --- a/appvm/Makefile +++ b/appvm/Makefile @@ -1,11 +1,14 @@ CC=gcc CFLAGS=-Wall -all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm +all: qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm xenstore-watch qubes_penctl: qubes_penctl.o $(CC) -o qubes_penctl qubes_penctl.o -lxenstore qubes_add_pendrive_script: qubes_add_pendrive_script.o $(CC) -o qubes_add_pendrive_script qubes_add_pendrive_script.o -lxenstore qvm-open-in-dvm: qvm-open-in-dvm.o $(CC) -o qvm-open-in-dvm qvm-open-in-dvm.o -lxenstore +xenstore-watch: xenstore-watch.o + $(CC) -o xenstore-watch xenstore-watch.o -lxenstore + clean: - rm -f qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm *.o *~ + rm -f qubes_penctl qubes_add_pendrive_script qvm-open-in-dvm xenstore-watch *.o *~ diff --git a/appvm/qubes_core_appvm b/appvm/qubes_core_appvm new file mode 100755 index 00000000..ed250dd5 --- /dev/null +++ b/appvm/qubes_core_appvm @@ -0,0 +1,82 @@ +#!/bin/sh +# +# chkconfig: 345 85 85 +# description: Executes Qubes core scripts at AppVM boot +# +# Source function library. +. /etc/rc.d/init.d/functions + +possibly_run_save_script() +{ + ENCODED_SCRIPT=$(xenstore-read qubes_save_script) + if [ -z "$ENCODED_SCRIPT" ] ; then return ; fi + echo $ENCODED_SCRIPT|perl -e 'use MIME::Base64 qw(decode_base64); local($/) = undef;print decode_base64()' >/tmp/qubes_save_script + chmod 755 /tmp/qubes_save_script + Xorg -config /etc/X11/xorg-preload-apps.conf :0 & + sleep 2 + DISPLAY=:0 su - user -c /tmp/qubes_save_script + killall Xorg +} + +start() +{ + if ! [ -x /usr/bin/xenstore-read ] ; then + echo "ERROR: /usr/bin/xenstore-read not found!" + exit 1 + fi + + type=$(/usr/bin/xenstore-read qubes_vm_type) + if [ "$type" != "AppVM" -a "$type" != "DisposableVM" ]; then + # This script runs only on AppVMs + return 0 + fi + + # Start AppVM specific services + /sbin/service cups start + + echo -n $"Executing Qubes Core scripts for AppVM:" + + if xenstore-read qubes_save_request 2>/dev/null ; then + ln -sf /home_volatile /home + possibly_run_save_script + touch /etc/this_is_dvm + dmesg -c >/dev/null + free | grep Mem: | + (read a b c d ; xenstore-write device/qubes_used_mem $c) + # we're still running in DispVM template + echo "Waiting for save/restore..." + # ... wait until qubes_restore.c (in Dom0) recreates VM-specific keys + while ! xenstore-read qubes_restore_complete 2>/dev/null ; do + usleep 10 + done + echo Back to life. + fi + + MEM_CHANGE_THRESHOLD_KB=30000 + MEMINFO_DELAY_USEC=100000 + /usr/lib/qubes/meminfo-writer $MEM_CHANGE_THRESHOLD_KB $MEMINFO_DELAY_USEC & + + success + echo "" + return 0 +} + +stop() +{ + return 0 +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + *) + echo $"Usage: $0 {start|stop}" + exit 3 + ;; +esac + +exit $RETVAL diff --git a/appvm/xenstore-watch.c b/appvm/xenstore-watch.c new file mode 100644 index 00000000..497ed294 --- /dev/null +++ b/appvm/xenstore-watch.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include +main(int argc, char **argv) +{ + struct xs_handle *xs; + unsigned int count; + char **vec; + char dummy; + if (argc != 2) { + fprintf(stderr, "usage: %s xenstore_path\n", argv[0]); + exit(1); + } + xs = xs_domain_open(); + if (!xs) { + perror("xs_domain_open"); + exit(1); + } + if (!xs_watch(xs, argv[1], &dummy)) { + perror("xs_watch"); + exit(1); + } + vec = xs_read_watch(xs, &count); + free(vec); + vec = xs_read_watch(xs, &count); + free(vec); +} diff --git a/common/.gitignore b/common/.gitignore new file mode 100644 index 00000000..03034a95 --- /dev/null +++ b/common/.gitignore @@ -0,0 +1 @@ +meminfo-writer diff --git a/common/block-snapshot b/common/block-snapshot new file mode 100755 index 00000000..a668935c --- /dev/null +++ b/common/block-snapshot @@ -0,0 +1,200 @@ +#!/bin/bash + +# Usage: block-snapshot add|remove img-file cow-file +# +# This creates dm-snapshot device on given arguments + +dir=$(dirname "$0") +. "$dir/block-common.sh" + +get_dev() { + dev=$1 + + if [ -L "$dev" ]; then + dev=$(readlink -f "$dev") || fatal "$dev link does not exist." + fi + + if [ -f "$dev" ]; then + file=$dev + + inode=$(stat -c '%i' "$file") + devnum=$(stat -c '%D' "$file") + if [ -z "$inode" ] || [ -z "$devnum" ] + then + release_lock "block" + fatal "Unable to lookup $file: dev: $devnum inode: $inode" + fi + + dev_list=$(losetup -a | grep ' \[0*'${devnum}'\]:'${inode} | cut -d : -f 1) + for loopdev in $dev_list; do + # found existing loop to this file + echo $loopdev + return + done + + + # assign new loop device + loopdev=$(losetup -f 2>/dev/null || find_free_loopback_dev) + if [ "$loopdev" = '' ] + then + release_lock "block" + fatal 'Failed to find an unused loop device' + fi + + do_or_die losetup "$loopdev" "$file" + echo $loopdev + else + test -e "$dev" || fatal "$dev does not exist." + test -b "$dev" || fatal "$dev is not a block device nor file." + fi +} + +get_dm_snapshot_name() { + base=$1 + cow=$2 + + echo snapshot-$(stat -c '%D:%i' "$base")-$(stat -c '%D:%i' "$cow") +} + +create_dm_snapshot() { + local base_dev cow_dev base_sz + + dm_devname=$1 + base=$2 + cow=$3 + + if [ ! -e /dev/mapper/$dm_devname ]; then + # prepare new snapshot device + base_dev=$(get_dev $base) + cow_dev=$(get_dev $cow) + base_sz=$(blockdev --getsz $base_dev) + do_or_die dmsetup create $dm_devname --table "0 $base_sz snapshot $base_dev $cow_dev P 256" + fi + +} + +create_dm_snapshot_origin() { + local base_dev base_sz + + dm_devname=$1 + base=$2 + + if [ ! -e /dev/mapper/$dm_devname ]; then + # prepare new snapshot-origin device + base_dev=$(get_dev $base) + base_sz=$(blockdev --getsz $base_dev) + do_or_die dmsetup create $dm_devname --table "0 $base_sz snapshot-origin $base_dev" + fi +} + +t=$(xenstore_read_default "$XENBUS_PATH/type" 'MISSING') + + +case "$command" in + add) + case $t in + snapshot|origin) + p=$(xenstore_read "$XENBUS_PATH/params") + base=${p/:*/} + cow=${p/*:/} + + if [ -L "$base" ]; then + base=$(readlink -f "$base") || fatal "$base link does not exist." + fi + + if [ -L "$cow" ]; then + cow=$(readlink -f "$cow") || fatal "$cow link does not exist." + fi + + # first ensure that snapshot device exists (to write somewhere changes from snapshot-origin) + dm_devname=$(get_dm_snapshot_name "$base" "$cow") + + claim_lock "block" + + # prepare snapshot device + create_dm_snapshot $dm_devname "$base" "$cow" + + if [ "$t" == "snapshot" ]; then + #that's all for snapshot, store name of prepared device + xenstore_write "$XENBUS_PATH/node" "/dev/mapper/$dm_devname" + write_dev /dev/mapper/$dm_devname + elif [ "$t" == "origin" ]; then + # for origin - prepare snapshot-origin device and store its name + dm_devname=origin-$(stat -c '%D:%i' "$base") + create_dm_snapshot_origin $dm_devname "$base" + xenstore_write "$XENBUS_PATH/node" "/dev/mapper/$dm_devname" + write_dev /dev/mapper/$dm_devname + fi + + release_lock "block" + exit 0 + ;; + esac + ;; + remove) + case $t in + snapshot|origin) + node=$(xenstore_read "$XENBUS_PATH/node") + + if [ -z "$node" ]; then + fatal "No device node to remove" + fi + + if [ ! -e "$node" ]; then + fatal "Device $node does not exists" + fi + + claim_lock "block" + + use_count=$(dmsetup info $node|grep Open|awk '{print $3}') + + # do not remove snapshot if snapshot origin is still present + if [ "${node/snapshot/}" != "$node" -a -e "/dev/mapper/origin-$(echo $node|cut -d- -f2)" ]; then + use_count=1 + fi + + if [ "$use_count" -gt 0 ]; then + log info "Device $node still in use - not removing" + release_lock "block" + exit 0 + fi + + # get list of used (loop) devices + deps="$(dmsetup deps $node | cut -d: -f2 | sed -e 's#(7, \([0-9]\+\))#/dev/loop\1#g')" + + # if this is origin + if [ "${node/origin/}" != "$node" ]; then + # remove unused snapshots + for snap in /dev/mapper/snapshot-$(echo $node|cut -d- -f2)-*; do + use_count=$(dmsetup info $snap|grep Open|awk '{print $3}') + if [ "$use_count" -eq 0 ]; then + # unused snapshot - remove it + deps="$deps $(dmsetup deps $snap | cut -d: -f2 | sed -e 's#(7, \([0-9]\+\))#/dev/loop\1#g')" + log debug "Removing $snap" + dmsetup remove $snap + fi + done + fi + + if [ -e $node ]; then + log debug "Removing $node" + dmsetup remove $node + fi + + # try to free loop devices + for dev in $deps; do + if [ -b "$dev" ]; then + log debug "Removing $dev" + losetup -d $dev || true 2> /dev/null + fi + done + + release_lock "block" + + exit 0 + ;; + esac + ;; +esac + +# vim:sw=2:et: diff --git a/appvm/fstab b/common/fstab similarity index 100% rename from appvm/fstab rename to common/fstab diff --git a/appvm/qubes_core b/common/qubes_core similarity index 61% rename from appvm/qubes_core rename to common/qubes_core index 714727a2..26fe514d 100755 --- a/appvm/qubes_core +++ b/common/qubes_core @@ -6,18 +6,6 @@ # Source function library. . /etc/rc.d/init.d/functions -possibly_run_save_script() -{ - ENCODED_SCRIPT=$(xenstore-read qubes_save_script) - if [ -z "$ENCODED_SCRIPT" ] ; then return ; fi - echo $ENCODED_SCRIPT|perl -e 'use MIME::Base64 qw(decode_base64); local($/) = undef;print decode_base64()' >/tmp/qubes_save_script - chmod 755 /tmp/qubes_save_script - Xorg -config /etc/X11/xorg-preload-apps.conf :0 & - sleep 2 - DISPLAY=:0 su - user -c /tmp/qubes_save_script - killall Xorg -} - start() { echo -n $"Executing Qubes Core scripts:" @@ -26,21 +14,6 @@ start() echo "ERROR: /usr/bin/xenstore-read not found!" exit 1 fi - if xenstore-read qubes_save_request 2>/dev/null ; then - ln -sf /home_volatile /home - possibly_run_save_script - touch /etc/this_is_dvm - dmesg -c >/dev/null - free | grep Mem: | - (read a b c d ; xenstore-write device/qubes_used_mem $c) - # we're still running in DispVM template - echo "Waiting for save/restore..." - # ... wait until qubes_restore.c (in Dom0) recreates VM-specific keys - while ! xenstore-read qubes_restore_complete 2>/dev/null ; do - usleep 10 - done - echo Back to life. - fi name=$(/usr/bin/xenstore-read name) if ! [ -f /etc/this_is_dvm ] ; then @@ -81,10 +54,6 @@ start() fi fi - MEM_CHANGE_THRESHOLD_KB=30000 - MEMINFO_DELAY_USEC=100000 - /usr/lib/qubes/meminfo-writer $MEM_CHANGE_THRESHOLD_KB $MEMINFO_DELAY_USEC & - [ -x /rw/config/rc.local ] && /rw/config/rc.local success echo "" diff --git a/dom0/aux-tools/reset_vm_configs.py b/dom0/aux-tools/reset_vm_configs.py index 6930a1fa..faaf54b3 100755 --- a/dom0/aux-tools/reset_vm_configs.py +++ b/dom0/aux-tools/reset_vm_configs.py @@ -14,7 +14,7 @@ def main(): if tvm is None: print 'Template', templ, 'does not exist' sys.exit(1) - if not tvm.is_templete(): + if not tvm.is_template(): print templ, 'is not a template' sys.exit(1) for vm in qvm_collection.values(): diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 55ab2d6b..4556c3e7 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -29,6 +29,7 @@ import xml.parsers.expat import fcntl import re import shutil +from datetime import datetime from qmemman_client import QMemmanClient # Do not use XenAPI or create/read any VM files @@ -48,9 +49,9 @@ qubes_guid_path = "/usr/bin/qubes_guid" qubes_base_dir = "/var/lib/qubes" -qubes_appvms_dir = qubes_base_dir + "/appvms" -qubes_templates_dir = qubes_base_dir + "/vm-templates" -qubes_servicevms_dir = qubes_base_dir + "/servicevms" +qubes_appvms_dir = qubes_base_dir + "/appvms" +qubes_templates_dir = qubes_base_dir + "/vm-templates" +qubes_servicevms_dir = qubes_base_dir + "/servicevms" qubes_store_filename = qubes_base_dir + "/qubes.xml" qubes_max_qid = 254*254 @@ -65,11 +66,12 @@ default_appvms_conf_file = "appvm-template.conf" default_templatevm_conf_template = "templatevm.conf" # needed for TemplateVM cloning default_appmenus_templates_subdir = "apps.templates" default_kernels_subdir = "kernels" +default_firewall_conf_file = "firewall.xml" # do not allow to start a new AppVM if Dom0 mem was to be less than this dom0_min_memory = 700*1024*1024 -# We need this global reference, as each instance of QubesVM +# We need this global reference, as each instance of QubesVm # must be able to ask Dom0 VM about how much memory it currently has... dom0_vm = None @@ -81,11 +83,6 @@ qubes_appmenu_remove_cmd = "/usr/lib/qubes/remove_appvm_appmenus.sh" # as "should be good for everyone" swap_cow_sz = 1024*1024*1024 -VM_TEMPLATE = 'TempleteVM' -VM_APPVM = 'AppVM' -VM_NETVM = 'NetVM' -VM_DISPOSABLEVM = 'DisposableVM' - class XendSession(object): def __init__(self): self.get_xend_session_old_api() @@ -109,7 +106,7 @@ class XendSession(object): if not dry_run: xend_session = XendSession() - + class QubesException (Exception) : pass @@ -170,7 +167,7 @@ class QubesVm(object): Note that qid is not the same as Xen's domid! """ - def __init__(self, qid, name, type, + def __init__(self, qid, name, dir_path, conf_file = None, uses_default_netvm = True, netvm_vm = None, @@ -195,11 +192,10 @@ class QubesVm(object): else: self.conf_file = dir_path + "/" + conf_file - self.__type = type self.uses_default_netvm = uses_default_netvm self.netvm_vm = netvm_vm - # We use it in remove from disk to avoid removing rpm files (for templates and netvms) + # We use it in remove from disk to avoid removing rpm files (for templates) self.installed_by_rpm = installed_by_rpm self.updateable = updateable @@ -208,7 +204,10 @@ class QubesVm(object): self.icon_path = self.dir_path + "/icon.png" else: self.icon_path = None - + + # PCI devices - used only by NetVM + self.pcidevs = "" + if not dry_run and xend_session.session is not None: self.refresh_xend_session() @@ -216,10 +215,6 @@ class QubesVm(object): def qid(self): return self.__qid - @property - def type(self): - return self.__type - @property def ip(self): if self.netvm_vm is not None: @@ -254,7 +249,7 @@ class QubesVm(object): def is_networked(self): if self.is_netvm(): return True - + if self.netvm_vm is not None: return True else: @@ -269,29 +264,20 @@ class QubesVm(object): # We can always downgrade a VM to non-updateable... self.updateable = False - def is_templete(self): - if self.type == VM_TEMPLATE: - return True - else: - return False + def is_template(self): + return isinstance(self, QubesTemplateVm) def is_appvm(self): - if self.type == VM_APPVM: - return True - else: - return False + return isinstance(self, QubesAppVm) def is_netvm(self): - if self.type == VM_NETVM: - return True - else: - return False + return isinstance(self, QubesNetVm) + + def is_proxyvm(self): + return isinstance(self, QubesProxyVm) def is_disposablevm(self): - if self.type == VM_DISPOSABLEVM: - return True - else: - return False + return isinstance(self, QubesDisposableVm) def add_to_xen_storage(self): if dry_run: @@ -394,7 +380,7 @@ class QubesVm(object): for cpu in cpus_util: cpu_total_load += cpus_util[cpu] cpu_total_load /= len(cpus_util) - p = 100*cpu_total_load + p = 100*cpu_total_load if p > 100: p = 100 return p @@ -444,7 +430,7 @@ class QubesVm(object): def get_private_img_sz(self): if not os.path.exists(self.private_img): return 0 - + return os.path.getsize(self.private_img) def create_xenstore_entries(self, xid): @@ -478,8 +464,8 @@ class QubesVm(object): "/usr/bin/xenstore-write", "/local/domain/{0}/qubes_netvm_network".format(xid), self.network]) - - elif self.netvm_vm is not None: + + if self.netvm_vm is not None: retcode = subprocess.check_call ([ "/usr/bin/xenstore-write", "/local/domain/{0}/qubes_ip".format(xid), @@ -488,19 +474,17 @@ class QubesVm(object): retcode = subprocess.check_call ([ "/usr/bin/xenstore-write", "/local/domain/{0}/qubes_netmask".format(xid), - self.netmask]) + self.netvm_vm.netmask]) retcode = subprocess.check_call ([ "/usr/bin/xenstore-write", "/local/domain/{0}/qubes_gateway".format(xid), - self.gateway]) + self.netvm_vm.gateway]) retcode = subprocess.check_call ([ "/usr/bin/xenstore-write", "/local/domain/{0}/qubes_secondary_dns".format(xid), - self.secondary_dns]) - else: - pass + self.netvm_vm.secondary_dns]) def get_total_xen_memory(self): @@ -524,8 +508,8 @@ class QubesVm(object): if verbose: print "--> Loading the VM (type = {0})...".format(self.type) - if not self.is_netvm(): - total_mem_mb = self.get_total_xen_memory()/1024/1024 + if not self.is_netvm(): + total_mem_mb = self.get_total_xen_memory()/1024/1024 xend_session.xend_server.xend.domain.maxmem_set(self.name, total_mem_mb) mem_required = self.get_mem_dynamic_max() @@ -541,14 +525,14 @@ class QubesVm(object): xend_session.session.xenapi.VM.start (self.session_uuid, True) # Starting a VM paused qmemman_client.close() # let qmemman_daemon resume balancing - + xid = int (xend_session.session.xenapi.VM.get_domid (self.session_uuid)) if verbose: print "--> Setting Xen Store info for the VM..." self.create_xenstore_entries(xid) - if not self.is_netvm() and self.netvm_vm is not None: + if self.netvm_vm is not None: assert self.netvm_vm is not None if verbose: print "--> Attaching to the network backend (netvm={0})...".format(self.netvm_vm.name) @@ -559,9 +543,7 @@ class QubesVm(object): xm_cmdline = ["/usr/sbin/xm", "network-attach", self.name, "script=vif-route-qubes", "ip="+actual_ip] if self.netvm_vm.qid != 0: if not self.netvm_vm.is_running(): - print "ERROR: NetVM not running, please start it first" - self.force_shutdown() - raise QubesException ("NetVM not running") + self.netvm_vm.start() retcode = subprocess.call (xm_cmdline + ["backend={0}".format(self.netvm_vm.name)]) if retcode != 0: self.force_shutdown() @@ -573,9 +555,20 @@ class QubesVm(object): self.force_shutdown() raise OSError ("ERROR: Cannot attach to network backend!") + qvm_collection = QubesVmCollection() + qvm_collection.lock_db_for_reading() + qvm_collection.load() + qvm_collection.unlock_db() + + if verbose: + print "--> Updating FirewallVMs rules..." + for vm in qvm_collection.values(): + if vm.is_proxyvm(): + vm.write_iptables_xenstore_entry() + if verbose: print "--> Starting the VM..." - xend_session.session.xenapi.VM.unpause (self.session_uuid) + xend_session.session.xenapi.VM.unpause (self.session_uuid) # perhaps we should move it before unpause and fork? if debug_console: @@ -606,7 +599,7 @@ class QubesVm(object): class QubesTemplateVm(QubesVm): """ - A class that represents an TemplateVM. A child of QubesVM. + A class that represents an TemplateVM. A child of QubesVm. """ def __init__(self, **kwargs): @@ -620,17 +613,18 @@ class QubesTemplateVm(QubesVm): private_img = kwargs.pop("private_img") if "private_img" in kwargs else None appvms_conf_file = kwargs.pop("appvms_conf_file") if "appvms_conf_file" in kwargs else None - super(QubesTemplateVm, self).__init__(type=VM_TEMPLATE, label = default_template_label, **kwargs) + super(QubesTemplateVm, self).__init__(label = default_template_label, **kwargs) dir_path = kwargs["dir_path"] - # TempleteVM doesn't use root-cow image if root_img is not None and os.path.isabs(root_img): self.root_img = root_img else: self.root_img = dir_path + "/" + ( root_img if root_img is not None else default_root_img) + self.rootcow_img = dir_path + "/" + default_rootcow_img + if private_img is not None and os.path.isabs(private_img): self.private_img = private_img else: @@ -648,6 +642,10 @@ class QubesTemplateVm(QubesVm): self.appmenus_templates_dir = self.dir_path + "/" + default_appmenus_templates_subdir self.appvms = QubesVmCollection() + @property + def type(self): + return "TemplateVM" + def set_updateable(self): if self.is_updateable(): return @@ -657,7 +655,7 @@ class QubesTemplateVm(QubesVm): for appvm in self.appvms.values(): if appvm.is_updateable(): raise QubesException("One of the AppVMs ('{0}')is also 'updateable'\ - -- cannot make the TempleteVM {'{1}'} 'nonupdatable'".\ + -- cannot make the TemplateVM {'{1}'} 'nonupdatable'".\ format (appvm.name, self.name)) self.updateable = True @@ -711,6 +709,14 @@ class QubesTemplateVm(QubesVm): format(src_template_vm.root_img, self.root_img) # We prefer to use Linux's cp, because it nicely handles sparse files retcode = subprocess.call (["cp", src_template_vm.root_img, self.root_img]) + if retcode != 0: + raise IOError ("Error while copying {0} to {1}".\ + format(src_template_vm.root_img, self.root_img)) + if verbose: + print "--> Copying the template's root COW image:\n{0} ==>\n{1}".\ + format(src_template_vm.rootcow_img, self.rootcow_img) + # We prefer to use Linux's cp, because it nicely handles sparse files + retcode = subprocess.call (["cp", src_template_vm.rootcow_img, self.rootcow_img]) if retcode != 0: raise IOError ("Error while copying {0} to {1}".\ format(src_template_vm.root_img, self.root_img)) @@ -723,15 +729,15 @@ class QubesTemplateVm(QubesVm): print "--> Copying the template's appvm templates dir:\n{0} ==>\n{1}".\ format(src_template_vm.appmenus_templates_dir, self.appmenus_templates_dir) shutil.copytree (src_template_vm.appmenus_templates_dir, self.appmenus_templates_dir) - - + + def get_disk_utilization_root_img(self): return self.get_disk_usage(self.root_img) def get_root_img_sz(self): if not os.path.exists(self.root_img): return 0 - + return os.path.getsize(self.root_img) def verify_files(self): @@ -768,10 +774,10 @@ class QubesTemplateVm(QubesVm): raise QubesException ( "VM's kernels directory does not exist: {0}".\ format(self.kernels_dir)) - + return True - def start(self, debug_console = False, verbose = False): + def start(self, debug_console = False, verbose = False, preparing_dvm=False): if dry_run: return @@ -779,13 +785,29 @@ class QubesTemplateVm(QubesVm): if not self.is_updateable(): raise QubesException ("Cannot start Template VM that is marked \"nonupdatable\"") - # First ensure that none of our appvms is running: - for appvm in self.appvms.values(): - if appvm.is_running(): - raise QubesException ("Cannot start TemplateVM when one of its AppVMs is running!") + # TODO?: check if none of running appvms are outdated return super(QubesTemplateVm, self).start(debug_console=debug_console, verbose=verbose) + def commit_changes (self): + + assert not self.is_running(), "Attempt to commit changes on running Template VM!" + + print "--> Commiting template updates... COW: {0}...".format (self.rootcow_img) + + if dry_run: + return + if os.path.exists (self.rootcow_img): + os.remove (self.rootcow_img) + + + f_cow = open (self.rootcow_img, "w") + f_root = open (self.root_img, "r") + f_root.seek(0, os.SEEK_END) + f_cow.truncate (f_root.tell()) # make empty sparse file of the same size as root.img + f_cow.close () + f_root.close() + def create_xml_element(self): element = xml.etree.ElementTree.Element( "QubesTemplateVm", @@ -795,6 +817,7 @@ class QubesTemplateVm(QubesVm): conf_file=self.conf_file, appvms_conf_file=self.appvms_conf_file, root_img=self.root_img, + rootcow_img=self.rootcow_img, private_img=self.private_img, uses_default_netvm=str(self.uses_default_netvm), netvm_qid=str(self.netvm_vm.qid) if self.netvm_vm is not None else "none", @@ -803,59 +826,113 @@ class QubesTemplateVm(QubesVm): ) return element -class QubesServiceVm(QubesVm): +class QubesCowVm(QubesVm): """ - A class that represents a ServiceVM, e.g. NetVM. A child of QubesVM. + A class that represent a VM based on some template, i.e. doesn't have own root.img """ - def __init__(self, **kwargs): - + def __init__(self, **kwargs): if "dir_path" not in kwargs or kwargs["dir_path"] is None: - kwargs["dir_path"] = qubes_servicevms_dir + "/" + kwargs["name"] + kwargs["dir_path"] = qubes_appvms_dir + "/" + kwargs["name"] - root_img = kwargs.pop("root_img") if "root_img" in kwargs else None - private_img = kwargs.pop("private_img") if "private_img" in kwargs else None + if "updateable" not in kwargs or kwargs["updateable"] is None: + kwargs["updateable"] = False - kwargs["updateable"] = True - super(QubesServiceVm, self).__init__(**kwargs) + private_img = kwargs.pop("private_img") + template_vm = kwargs.pop("template_vm") + super(QubesCowVm, self).__init__(**kwargs) + qid = kwargs["qid"] dir_path = kwargs["dir_path"] - assert dir_path is not None + # Dirty hack for QubesDom0NetVm... + if not isinstance(self, QubesDom0NetVm): + assert template_vm is not None, "Missing template_vm for template based VM!" + if not template_vm.is_template(): + print "ERROR: template_qid={0} doesn't point to a valid TemplateVM".\ + format(template_vm.qid) + return False - if root_img is not None and os.path.isabs(root_img): - self.root_img = root_img - else: - self.root_img = dir_path + "/" + ( - root_img if root_img is not None else default_root_img) + template_vm.appvms[qid] = self + self.template_vm = template_vm + # template based VM doesn't have its own root_img, it uses the one provided by the TemplateVM if private_img is not None and os.path.isabs(private_img): self.private_img = private_img else: self.private_img = dir_path + "/" + ( private_img if private_img is not None else default_private_img) + self.rootcow_img = dir_path + "/" + default_rootcow_img + self.swapcow_img = dir_path + "/" + default_swapcow_img + def set_updateable(self): if self.is_updateable(): return - # ServiceVMs are standalone, we can always make it updateable - # In fact there is no point in making it unpdatebale... - # So, this is just for completncess + assert not self.is_running() - self.updateable = True + # Check if the TemaplteVM is *non* updatable... + if not self.template_vm.is_updateable(): + self.updateable = True + self.reset_cow_storage() + self.reset_swap_cow_storage() + else: + # Temaplate VM is Updatable itself --> can't make the AppVM updateable too + # as this would cause COW-backed storage incoherency + raise QubesException ("TemaplteVM is updateable: cannot make the template based VM '{0}' updateable".format(self.name)) + + def create_config_file(self): + conf_template = open (self.template_vm.appvms_conf_file, "r") + if os.path.isfile(self.conf_file): + shutil.copy(self.conf_file, self.conf_file + ".backup") + conf_appvm = open(self.conf_file, "w") + rx_vmname = re.compile (r"%VMNAME%") + rx_vmdir = re.compile (r"%VMDIR%") + rx_template = re.compile (r"%TEMPLATEDIR%") + rx_pcidevs = re.compile (r"%PCIDEVS%") + + for line in conf_template: + line = rx_vmname.sub (self.name, line) + line = rx_vmdir.sub (self.dir_path, line) + line = rx_template.sub (self.template_vm.dir_path, line) + line = rx_pcidevs.sub (self.pcidevs, line) + conf_appvm.write(line) + + conf_template.close() + conf_appvm.close() + + def create_on_disk(self, verbose): + if dry_run: + return + + if verbose: + print "--> Creating directory: {0}".format(self.dir_path) + os.mkdir (self.dir_path) + + if verbose: + print "--> Creating the VM config file: {0}".format(self.conf_file) + + self.create_config_file() + + template_priv = self.template_vm.private_img + if verbose: + print "--> Copying the template's private image: {0}".\ + format(template_priv) + + # We prefer to use Linux's cp, because it nicely handles sparse files + retcode = subprocess.call (["cp", template_priv, self.private_img]) + if retcode != 0: + raise IOError ("Error while copying {0} to {1}".\ + format(template_priv, self.private_img)) def get_disk_utilization_root_img(self): - return self.get_disk_usage(self.root_img) + return 0 def get_root_img_sz(self): - if not os.path.exists(self.root_img): - return 0 - - return os.path.getsize(self.root_img) + return 0 def verify_files(self): if dry_run: return - if not os.path.exists (self.dir_path): raise QubesException ( "VM directory doesn't exist: {0}".\ @@ -866,19 +943,65 @@ class QubesServiceVm(QubesVm): "VM config file doesn't exist: {0}".\ format(self.conf_file)) - if not os.path.exists (self.root_img): + if not os.path.exists (self.private_img): raise QubesException ( - "VM root image file doesn't exist: {0}".\ - format(self.root_img)) - + "VM private image file doesn't exist: {0}".\ + format(self.private_img)) return True - def create_xml_element(self): - raise NotImplementedError + def start(self, debug_console = False, verbose = False, preparing_dvm = False): + if dry_run: + return -class QubesNetVm(QubesServiceVm): + if self.is_running(): + raise QubesException("VM is already running!") + + if not self.is_updateable(): + self.reset_cow_storage() + + self.reset_swap_cow_storage() + + return super(QubesCowVm, self).start(debug_console=debug_console, verbose=verbose, preparing_dvm=preparing_dvm) + + def reset_cow_storage (self): + + print "--> Resetting the COW storage: {0}...".format (self.rootcow_img) + + if dry_run: + return + # this is probbaly not needed, as open (..., "w") should remove the previous file + if os.path.exists (self.rootcow_img): + os.remove (self.rootcow_img) + + + f_cow = open (self.rootcow_img, "w") + f_root = open (self.template_vm.root_img, "r") + f_root.seek(0, os.SEEK_END) + f_cow.truncate (f_root.tell()) # make empty sparse file of the same size as root.img + f_cow.close () + f_root.close() + + def reset_swap_cow_storage (self): + print "--> Resetting the swap COW storage: {0}...".format (self.swapcow_img) + if os.path.exists (self.swapcow_img): + os.remove (self.swapcow_img) + + f_swap_cow = open (self.swapcow_img, "w") + f_swap_cow.truncate (swap_cow_sz) + f_swap_cow.close() + + + def remove_from_disk(self): + if dry_run: + return + + + subprocess.check_call ([qubes_appmenu_remove_cmd, self.name]) + shutil.rmtree (self.dir_path) + +class QubesNetVm(QubesCowVm): """ - A class that represents a NetVM. A child of QubesServiceVM. + A class that represents a NetVM. A child of QubesCowVM. """ def __init__(self, **kwargs): netid = kwargs.pop("netid") @@ -889,9 +1012,17 @@ class QubesNetVm(QubesServiceVm): self.__gateway = self.netprefix + "0.1" self.__secondary_dns = self.netprefix + "255.254" + if "dir_path" not in kwargs or kwargs["dir_path"] is None: + kwargs["dir_path"] = qubes_servicevms_dir + "/" + kwargs["name"] + self.__external_ip_allowed_xids = set() + if "label" not in kwargs or kwargs["label"] is None: kwargs["label"] = default_servicevm_label - super(QubesNetVm, self).__init__(type=VM_NETVM, installed_by_rpm=True, **kwargs) + super(QubesNetVm, self).__init__(installed_by_rpm=True, **kwargs) + + @property + def type(self): + return "NetVM" @property def gateway(self): @@ -911,10 +1042,50 @@ class QubesNetVm(QubesServiceVm): def get_ip_for_vm(self, qid): hi = qid / 253 - lo = qid % 253 + 2 + lo = qid % 253 + 2 assert hi >= 0 and hi <= 254 and lo >= 2 and lo <= 254, "Wrong IP address for VM" return self.netprefix + "{0}.{1}".format(hi,lo) + def create_xenstore_entries(self, xid): + if dry_run: + return + + super(QubesNetVm, self).create_xenstore_entries(xid) + retcode = subprocess.check_call ([ + "/usr/bin/xenstore-write", + "/local/domain/{0}/qubes_netvm_external_ip".format(xid), + ""]) + self.update_external_ip_permissions(xid) + + def update_external_ip_permissions(self, xid = -1): + if xid < 0: + xid = self.get_xid() + if xid < 0: + return + + command = [ + "/usr/bin/xenstore-chmod", + "/local/domain/{0}/qubes_netvm_external_ip".format(xid) + ] + + command.append("r{0}".format(xid,xid)) + command.append("w{0}".format(xid,xid)) + + for id in self.__external_ip_allowed_xids: + command.append("r{0}".format(id)) + + return subprocess.check_call(command) + + def add_external_ip_permission(self, xid): + if int(xid) < 0: + return + self.__external_ip_allowed_xids.add(int(xid)) + self.update_external_ip_permissions() + + def remove_external_ip_permission(self, xid): + self.__external_ip_allowed_xids.discard(int(xid)) + self.update_external_ip_permissions() + def create_xml_element(self): element = xml.etree.ElementTree.Element( "QubesNetVm", @@ -923,7 +1094,152 @@ class QubesNetVm(QubesServiceVm): name=self.name, dir_path=self.dir_path, conf_file=self.conf_file, - root_img=self.root_img, + template_qid=str(self.template_vm.qid), + updateable=str(self.updateable), + private_img=self.private_img, + installed_by_rpm=str(self.installed_by_rpm), + ) + return element + +class QubesProxyVm(QubesNetVm): + """ + A class that represents a ProxyVM, ex FirewallVM. A child of QubesNetVM. + """ + def __init__(self, **kwargs): + super(QubesProxyVm, self).__init__(uses_default_netvm=False, **kwargs) + self.rules_applied = None + + @property + def type(self): + return "ProxyVM" + + def start(self, debug_console = False, verbose = False, preparing_dvm = False): + if dry_run: + return + retcode = super(QubesFirewallVm, self).start(debug_console=debug_console, verbose=verbose, preparing_dvm=preparing_dvm) + self.netvm_vm.add_external_ip_permission(self.get_xid()) + self.write_netvm_domid_entry() + return retcode + + def force_shutdown(self): + if dry_run: + return + self.netvm_vm.remove_external_ip_permission(self.get_xid()) + super(QubesFirewallVm, self).force_shutdown() + + def create_xenstore_entries(self, xid): + if dry_run: + return + + super(QubesProxyVm, self).create_xenstore_entries(xid) + retcode = subprocess.check_call ([ + "/usr/bin/xenstore-write", + "/local/domain/{0}/qubes_iptables_error".format(xid), + ""]) + retcode = subprocess.check_call ([ + "/usr/bin/xenstore-chmod", + "/local/domain/{0}/qubes_iptables_error".format(xid), + "r{0}".format(xid), "w{0}".format(xid)]) + self.write_iptables_xenstore_entry() + + def write_netvm_domid_entry(self, xid = -1): + if xid < 0: + xid = self.get_xid() + + return subprocess.check_call ([ + "/usr/bin/xenstore-write", "--", + "/local/domain/{0}/qubes_netvm_domid".format(xid), + "{0}".format(self.netvm_vm.get_xid())]) + + def write_iptables_xenstore_entry(self): + iptables = "# Generated by Qubes Core on {0}\n".format(datetime.now().ctime()) + iptables += "*filter\n" + iptables += ":INPUT DROP [0:0]\n" + iptables += ":FORWARD DROP [0:0]\n" + iptables += ":OUTPUT ACCEPT [0:0]\n" + + # Strict INPUT rules + iptables += "-A INPUT -i vif+ -p udp -m udp --dport 68 -j DROP\n" + iptables += "-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT\n" + iptables += "-A INPUT -p icmp -j ACCEPT\n" + iptables += "-A INPUT -i lo -j ACCEPT\n" + iptables += "-A INPUT -j REJECT --reject-with icmp-host-prohibited\n" + + # Allow dom0 networking + iptables += "-A FORWARD -i vif0.0 -j ACCEPT\n" + + qvm_collection = QubesVmCollection() + qvm_collection.lock_db_for_reading() + qvm_collection.load() + qvm_collection.unlock_db() + + vms = [vm for vm in qvm_collection.values() if vm.is_appvm()] + for vm in vms: + conf = vm.get_firewall_conf() + + xid = vm.get_xid() + if xid < 0: # VM not active ATM + continue + + iptables += "# '{0}' VM:\n".format(vm.name) + iptables += "-A FORWARD ! -s {0}/32 -i vif{1}.0 -j DROP\n".format(vm.ip, xid) + + + accept_action = "ACCEPT" + reject_action = "REJECT --reject-with icmp-host-prohibited" + + if conf["allow"]: + rules_action = accept_action + default_action = reject_action + iptables += "-A FORWARD -i vif{0}.0 -p icmp -j ACCEPT\n".format(xid) + else: + rules_action = reject_action + default_action = accept_action + + for rule in conf["rules"]: + iptables += "-A FORWARD -i vif{0}.0 -d {1}".format(xid, rule["address"]) + if rule["netmask"] != 32: + iptables += "/{0}".format(rule["netmask"]) + + if rule["portBegin"] is not None and rule["portBegin"] > 0: + iptables += " -p tcp --dport {0}".format(rule["portBegin"]) + if rule["portEnd"] is not None and rule["portEnd"] > rule["portBegin"]: + iptables += ":{0}".format(rule["portEnd"]) + + iptables += " -j {0}\n".format(rules_action) + + if conf["allowDns"]: + # PREROUTING does DNAT to NetVM DNSes, so we need self.netvm_vm. properties + iptables += "-A FORWARD -i vif{0}.0 -p udp -d {1} --dport 53 -j ACCEPT\n".format(xid,self.netvm_vm.gateway) + iptables += "-A FORWARD -i vif{0}.0 -p udp -d {1} --dport 53 -j ACCEPT\n".format(xid,self.netvm_vm.secondary_dns) + + iptables += "-A FORWARD -i vif{0}.0 -j {1}\n".format(xid, default_action) + + iptables += "#End of VM rules\n" + iptables += "-A FORWARD -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT\n" + iptables += "-A FORWARD -j DROP\n" + + iptables += "COMMIT" + + self.write_netvm_domid_entry() + + self.rules_applied = None + return subprocess.check_call ([ + "/usr/bin/xenstore-write", + "/local/domain/{0}/qubes_iptables".format(self.get_xid()), + iptables]) + + def create_xml_element(self): + element = xml.etree.ElementTree.Element( + "QubesProxyVm", + qid=str(self.qid), + netid=str(self.netid), + name=self.name, + dir_path=self.dir_path, + conf_file=self.conf_file, + template_qid=str(self.template_vm.qid), + updateable=str(self.updateable), + netvm_qid=str(self.netvm_vm.qid) if self.netvm_vm is not None else "none", private_img=self.private_img, installed_by_rpm=str(self.installed_by_rpm), ) @@ -932,8 +1248,9 @@ class QubesNetVm(QubesServiceVm): class QubesDom0NetVm(QubesNetVm): def __init__(self): super(QubesDom0NetVm, self).__init__(qid=0, name="dom0", netid=0, - dir_path=None, root_img = None, + dir_path=None, private_img = None, + template_vm = None, label = default_template_label) if not dry_run and xend_session.session is not None: self.session_hosts = xend_session.session.xenapi.host.get_all() @@ -952,7 +1269,7 @@ class QubesDom0NetVm(QubesNetVm): for cpu in self.session_cpus: cpu_total_load += xend_session.session.xenapi.host_cpu.get_utilisation(cpu) cpu_total_load /= len(self.session_cpus) - p = 100*cpu_total_load + p = 100*cpu_total_load if p > 100: p = 100 return p @@ -1004,24 +1321,28 @@ class QubesDom0NetVm(QubesNetVm): class QubesDisposableVm(QubesVm): """ - A class that represents an DisposableVM. A child of QubesVM. + A class that represents an DisposableVM. A child of QubesVm. """ def __init__(self, **kwargs): template_vm = kwargs.pop("template_vm") - super(QubesDisposableVm, self).__init__(type=VM_DISPOSABLEVM, dir_path=None, **kwargs) + super(QubesDisposableVm, self).__init__(dir_path=None, **kwargs) qid = kwargs["qid"] assert template_vm is not None, "Missing template_vm for DisposableVM!" - if not template_vm.is_templete(): - print "ERROR: template_qid={0} doesn't point to a valid TempleteVM".\ + if not template_vm.is_template(): + print "ERROR: template_qid={0} doesn't point to a valid TemplateVM".\ format(new_vm.template_vm.qid) return False self.template_vm = template_vm template_vm.appvms[qid] = self + @property + def type(self): + return "DisposableVM" + def create_xml_element(self): element = xml.etree.ElementTree.Element( "QubesDisposableVm", @@ -1035,103 +1356,33 @@ class QubesDisposableVm(QubesVm): return True -class QubesAppVm(QubesVm): +class QubesAppVm(QubesCowVm): """ - A class that represents an AppVM. A child of QubesVM. + A class that represents an AppVM. A child of QubesVm. """ def __init__(self, **kwargs): - if "dir_path" not in kwargs or kwargs["dir_path"] is None: - kwargs["dir_path"] = qubes_appvms_dir + "/" + kwargs["name"] + super(QubesAppVm, self).__init__(**kwargs) + dir_path = self.dir_path - if "updateable" not in kwargs or kwargs["updateable"] is None: - kwargs["updateable"] = False + if "firewall_conf" not in kwargs or kwargs["firewall_conf"] is None: + kwargs["firewall_conf"] = dir_path + "/" + default_firewall_conf_file - private_img = kwargs.pop("private_img") - template_vm = kwargs.pop("template_vm") - - - super(QubesAppVm, self).__init__(type=VM_APPVM, **kwargs) - qid = kwargs["qid"] - dir_path = kwargs["dir_path"] - - assert template_vm is not None, "Missing template_vm for AppVM!" - if not template_vm.is_templete(): - print "ERROR: template_qid={0} doesn't point to a valid TempleteVM".\ - format(new_vm.template_vm.qid) - return False - - self.template_vm = template_vm - template_vm.appvms[qid] = self - - # AppVM doesn't have its own root_img, it uses the one provided by the TemplateVM - if private_img is not None and os.path.isabs(private_img): - self.private_img = private_img - else: - self.private_img = dir_path + "/" + ( - private_img if private_img is not None else default_private_img) - - self.rootcow_img = dir_path + "/" + default_rootcow_img - self.swapcow_img = dir_path + "/" + default_swapcow_img + self.firewall_conf = kwargs["firewall_conf"] + @property + def type(self): + return "AppVM" def set_updateable(self): - if self.is_updateable(): - return - assert not self.is_running() - # Check if the TemaplteVM is *non* updatable... - if not self.template_vm.is_updateable(): - self.updateable = True - self.reset_cow_storage() - self.reset_swap_cow_storage() - else: - # Temaplate VM is Updatable itself --> can't make the AppVM updateable too - # as this would cause COW-backed storage incoherency - raise QubesException ("TemaplteVM is updateable: cannot make the AppVM '{0}' updateable".format(self.name)) - - def create_config_file(self): - conf_template = open (self.template_vm.appvms_conf_file, "r") - if os.path.isfile(self.conf_file): - shutil.copy(self.conf_file, self.conf_file + ".backup") - conf_appvm = open(self.conf_file, "w") - rx_vmname = re.compile (r"%VMNAME%") - rx_vmdir = re.compile (r"%VMDIR%") - rx_template = re.compile (r"%TEMPLATEDIR%") - - for line in conf_template: - line = rx_vmname.sub (self.name, line) - line = rx_vmdir.sub (self.dir_path, line) - line = rx_template.sub (self.template_vm.dir_path, line) - conf_appvm.write(line) - - conf_template.close() - conf_appvm.close() + super(QubesAppVm, self).set_updateable() def create_on_disk(self, verbose): if dry_run: return - - if verbose: - print "--> Creating directory: {0}".format(self.dir_path) - os.mkdir (self.dir_path) - - if verbose: - print "--> Creating the VM config file: {0}".format(self.conf_file) - - self.create_config_file() - - template_priv = self.template_vm.private_img - if verbose: - print "--> Copying the template's private image: {0}".\ - format(template_priv) - - # We prefer to use Linux's cp, because it nicely handles sparse files - retcode = subprocess.call (["cp", template_priv, self.private_img]) - if retcode != 0: - raise IOError ("Error while copying {0} to {1}".\ - format(template_priv, self.private_img)) + super(QubesAppVm, self).create_on_disk(verbose) if verbose: print "--> Creating icon symlink: {0} -> {1}".format(self.icon_path, self.label.icon_path) @@ -1142,34 +1393,86 @@ class QubesAppVm(QubesVm): def create_appmenus(self, verbose): subprocess.check_call ([qubes_appmenu_create_cmd, self.template_vm.appmenus_templates_dir, self.name]) - def get_disk_utilization_root_img(self): - return 0 + def write_firewall_conf(self, conf): + root = xml.etree.ElementTree.Element( + "QubesFirwallRules", + policy = "allow" if conf["allow"] else "deny", + dns = "allow" if conf["allowDns"] else "deny" + ) - def get_root_img_sz(self): - return 0 + for rule in conf["rules"]: + element = xml.etree.ElementTree.Element( + "rule", + address=rule["address"], + port=str(rule["portBegin"]), + ) + if rule["netmask"] is not None and rule["netmask"] != 32: + element.set("netmask", str(rule["netmask"])) + if rule["portEnd"] is not None: + element.set("toport", str(rule["portEnd"])) + root.append(element) + tree = xml.etree.ElementTree.ElementTree(root) - def verify_files(self): - if dry_run: - return + try: + f = open(self.firewall_conf, 'a') # create the file if not exist + f.close() + with open(self.firewall_conf, 'w') as f: + fcntl.lockf(f, fcntl.LOCK_EX) + tree.write(f, "UTF-8") + fcntl.lockf(f, fcntl.LOCK_UN) + f.close() + except EnvironmentError as err: + print "{0}: save error: {1}".format( + os.path.basename(sys.argv[0]), err) + return False - if not os.path.exists (self.dir_path): - raise QubesException ( - "VM directory doesn't exist: {0}".\ - format(self.dir_path)) - - if not os.path.exists (self.conf_file): - raise QubesException ( - "VM config file doesn't exist: {0}".\ - format(self.conf_file)) - - if not os.path.exists (self.private_img): - raise QubesException ( - "VM private image file doesn't exist: {0}".\ - format(self.private_img)) return True + def get_firewall_conf(self): + conf = { "rules": list() } + + try: + tree = xml.etree.ElementTree.parse(self.firewall_conf) + root = tree.getroot() + + conf["allow"] = (root.get("policy") == "allow") + conf["allowDns"] = (root.get("dns") == "allow") + + for element in root: + rule = {} + attr_list = ("address", "netmask", "port", "toport") + + for attribute in attr_list: + rule[attribute] = element.get(attribute) + + if rule["netmask"] is not None: + rule["netmask"] = int(rule["netmask"]) + else: + rule["netmask"] = 32 + + rule["portBegin"] = int(rule["port"]) + + if rule["toport"] is not None: + rule["portEnd"] = int(rule["toport"]) + else: + rule["portEnd"] = None + + del(rule["port"]) + del(rule["toport"]) + + conf["rules"].append(rule) + + except EnvironmentError as err: + return conf + except (xml.parsers.expat.ExpatError, + ValueError, LookupError) as err: + print("{0}: load error: {1}".format( + os.path.basename(sys.argv[0]), err)) + return None + + return conf def create_xml_element(self): element = xml.etree.ElementTree.Element( @@ -1191,56 +1494,10 @@ class QubesAppVm(QubesVm): if dry_run: return - if self.is_running(): - raise QubesException("VM is already running!") - - # First ensure that our template is *not* running: - if self.template_vm.is_running(): - raise QubesException ("Cannot start AppVM when its template is running!") - - if not self.is_updateable(): - self.reset_cow_storage() self.reset_swap_cow_storage() return super(QubesAppVm, self).start(debug_console=debug_console, verbose=verbose, preparing_dvm=preparing_dvm) - def reset_cow_storage (self): - - print "--> Resetting the COW storage: {0}...".format (self.rootcow_img) - - if dry_run: - return - # this is probbaly not needed, as open (..., "w") should remove the previous file - if os.path.exists (self.rootcow_img): - os.remove (self.rootcow_img) - - - f_cow = open (self.rootcow_img, "w") - f_root = open (self.template_vm.root_img, "r") - f_root.seek(0, os.SEEK_END) - f_cow.truncate (f_root.tell()) # make empty sparse file of the same size as root.img - f_cow.close () - f_root.close() - - def reset_swap_cow_storage (self): - print "--> Resetting the swap COW storage: {0}...".format (self.swapcow_img) - if os.path.exists (self.swapcow_img): - os.remove (self.swapcow_img) - - f_swap_cow = open (self.swapcow_img, "w") - f_swap_cow.truncate (swap_cow_sz) - f_swap_cow.close() - - def remove_from_disk(self): - if dry_run: - return - - - subprocess.check_call ([qubes_appmenu_remove_cmd, self.name]) - shutil.rmtree (self.dir_path) - - - class QubesVmCollection(dict): """ A collection of Qubes VMs indexed by Qubes id (qid) @@ -1249,6 +1506,7 @@ class QubesVmCollection(dict): def __init__(self, store_filename=qubes_store_filename): super(QubesVmCollection, self).__init__() self.default_netvm_qid = None + self.default_fw_netvm_qid = None self.default_template_qid = None self.qubes_store_filename = store_filename @@ -1309,7 +1567,7 @@ class QubesVmCollection(dict): installed_by_rpm = True): qid = self.get_new_unused_qid() - vm = QubesTemplateVm (qid=qid, name=name, + vm = QubesTemplateVm (qid=qid, name=name, dir_path=dir_path, conf_file=conf_file, root_img=root_img, private_img=private_img, installed_by_rpm=installed_by_rpm, @@ -1333,16 +1591,37 @@ class QubesVmCollection(dict): return vm - def add_new_netvm(self, name, + def add_new_netvm(self, name, template_vm, dir_path = None, conf_file = None, - root_img = None): + private_img = None): qid = self.get_new_unused_qid() netid = self.get_new_unused_netid() - vm = QubesNetVm (qid=qid, name=name, + vm = QubesNetVm (qid=qid, name=name, template_vm=template_vm, netid=netid, - dir_path=dir_path, conf_file=conf_file, - root_img=root_img) + private_img=private_img, + dir_path=dir_path, conf_file=conf_file) + + if not self.verify_new_vm (vm): + assert False, "Wrong VM description!" + self[vm.qid]=vm + + if self.default_fw_netvm_qid is None: + self.set_default_fw_netvm_vm(vm) + + return vm + + def add_new_proxyvm(self, name, template_vm, + dir_path = None, conf_file = None, + private_img = None): + + qid = self.get_new_unused_qid() + netid = self.get_new_unused_netid() + vm = QubesProxyVm (qid=qid, name=name, template_vm=template_vm, + netid=netid, + private_img=private_img, + dir_path=dir_path, conf_file=conf_file, + netvm_vm = self.get_default_fw_netvm_vm()) if not self.verify_new_vm (vm): assert False, "Wrong VM description!" @@ -1354,7 +1633,7 @@ class QubesVmCollection(dict): return vm def set_default_template_vm(self, vm): - assert vm.is_templete(), "VM {0} is not a TempleteVM!".format(vm.name) + assert vm.is_template(), "VM {0} is not a TemplateVM!".format(vm.name) self.default_template_qid = vm.qid def get_default_template_vm(self): @@ -1364,7 +1643,7 @@ class QubesVmCollection(dict): return self[self.default_template_qid] def set_default_netvm_vm(self, vm): - assert vm.is_netvm(), "VM {0} is not a NetVM!".format(vm.name) + assert vm.is_netvm(), "VM {0} does not provide network!".format(vm.name) self.default_netvm_qid = vm.qid def get_default_netvm_vm(self): @@ -1373,6 +1652,16 @@ class QubesVmCollection(dict): else: return self[self.default_netvm_qid] + def set_default_fw_netvm_vm(self, vm): + assert vm.is_netvm(), "VM {0} does not provide network!".format(vm.name) + self.default_fw_netvm_qid = vm.qid + + def get_default_fw_netvm_vm(self): + if self.default_fw_netvm_qid is None: + return None + else: + return self[self.default_fw_netvm_qid] + def get_vm_by_name(self, name): for vm in self.values(): if (vm.name == name): @@ -1387,7 +1676,7 @@ class QubesVmCollection(dict): vms = set([vm for vm in self.values() if (vm.is_appvm() and vm.template_vm.qid == template_qid)]) return vms - + def verify_new_vm(self, new_vm): # Verify that qid is unique @@ -1454,7 +1743,10 @@ class QubesVmCollection(dict): if self.default_template_qid is not None else "None", default_netvm=str(self.default_netvm_qid) \ - if self.default_netvm_qid is not None else "None" + if self.default_netvm_qid is not None else "None", + + default_fw_netvm=str(self.default_fw_netvm_qid) \ + if self.default_fw_netvm_qid is not None else "None" ) for vm in self.values(): @@ -1464,14 +1756,14 @@ class QubesVmCollection(dict): tree = xml.etree.ElementTree.ElementTree(root) try: - + # We need to manually truncate the file, as we open the # file as "r+" in the lock_db_for_writing() function self.qubes_store_file.seek (0, os.SEEK_SET) self.qubes_store_file.truncate() tree.write(self.qubes_store_file, "UTF-8") except EnvironmentError as err: - print("{0}: import error: {1}".format( + print("{0}: export error: {1}".format( os.path.basename(sys.argv[0]), err)) return False return True @@ -1495,43 +1787,24 @@ class QubesVmCollection(dict): return False element = tree.getroot() - default_template = element.get("default_template") + default_template = element.get("default_template") self.default_template_qid = int(default_template) \ if default_template != "None" else None - default_netvm = element.get("default_netvm") + default_netvm = element.get("default_netvm") if default_netvm is not None: self.default_netvm_qid = int(default_netvm) \ if default_netvm != "None" else None #assert self.default_netvm_qid is not None - # Read in the NetVMs first, because a reference to NetVM - # is needed to create all other VMs - for element in tree.findall("QubesNetVm"): - try: - kwargs = {} - attr_list = ("qid", "netid", "name", "dir_path", "conf_file", - "private_img", "root_img", - ) + default_fw_netvm = element.get("default_netvm") + if default_fw_netvm is not None: + self.default_fw_netvm_qid = int(default_fw_netvm) \ + if default_fw_netvm != "None" else None + #assert self.default_netvm_qid is not None - for attribute in attr_list: - kwargs[attribute] = element.get(attribute) - - kwargs["qid"] = int(kwargs["qid"]) - kwargs["netid"] = int(kwargs["netid"]) - - vm = QubesNetVm(**kwargs) - self[vm.qid] = vm - - except (ValueError, LookupError) as err: - print("{0}: import error (QubesNetVM) {1}".format( - os.path.basename(sys.argv[0]), err)) - return False - - - self.default_template_qid - # Then, read in the TemplateVMs, because a reference to templete VM - # is needed to create each AppVM + # Then, read in the TemplateVMs, because a reference to template VM + # is needed to create each AppVM for element in tree.findall("QubesTemplateVm"): try: @@ -1539,7 +1812,7 @@ class QubesVmCollection(dict): attr_list = ("qid", "name", "dir_path", "conf_file", "appvms_conf_file", "private_img", "root_img", "installed_by_rpm", "updateable", - "uses_default_netvm", "netvm_qid") + "uses_default_netvm") for attribute in attr_list: kwargs[attribute] = element.get(attribute) @@ -1549,11 +1822,97 @@ class QubesVmCollection(dict): if kwargs["updateable"] is not None: kwargs["updateable"] = True if kwargs["updateable"] == "True" else False + vm = QubesTemplateVm(**kwargs) + + self[vm.qid] = vm + except (ValueError, LookupError) as err: + print("{0}: import error (QubesTemplateVm): {1}".format( + os.path.basename(sys.argv[0]), err)) + return False + + # Read in the NetVMs first, because a reference to NetVM + # is needed to create all other VMs + for element in tree.findall("QubesNetVm"): + try: + kwargs = {} + attr_list = ("qid", "netid", "name", "dir_path", "conf_file", + "private_img", "template_qid", "updateable", + ) + + for attribute in attr_list: + kwargs[attribute] = element.get(attribute) + + kwargs["qid"] = int(kwargs["qid"]) + kwargs["template_qid"] = int(kwargs["template_qid"]) + if kwargs["updateable"] is not None: + kwargs["updateable"] = True if kwargs["updateable"] == "True" else False + + template_vm = self[kwargs.pop("template_qid")] + if template_vm is None: + print "ERROR: NetVM '{0}' uses unkown template qid='{1}'!".\ + format(kwargs["name"], kwargs["template_qid"]) + + kwargs["template_vm"] = template_vm + kwargs["netid"] = int(kwargs["netid"]) + + vm = QubesNetVm(**kwargs) + self[vm.qid] = vm + + except (ValueError, LookupError) as err: + print("{0}: import error (QubesNetVM) {1}".format( + os.path.basename(sys.argv[0]), err)) + return False + + # Next read in the ProxyVMs, because they may be referenced + # by other VMs + for element in tree.findall("QubesProxyVm"): + try: + kwargs = {} + attr_list = ("qid", "netid", "name", "dir_path", "conf_file", "updateable", + "private_img", "template_qid") + + for attribute in attr_list: + kwargs[attribute] = element.get(attribute) + + kwargs["qid"] = int(kwargs["qid"]) + kwargs["template_qid"] = int(kwargs["template_qid"]) + if kwargs["updateable"] is not None: + kwargs["updateable"] = True if kwargs["updateable"] == "True" else False + + template_vm = self[kwargs.pop("template_qid")] + if template_vm is None: + print "ERROR: ProxyVM '{0}' uses unkown template qid='{1}'!".\ + format(kwargs["name"], kwargs["template_qid"]) + + kwargs["template_vm"] = template_vm + kwargs["netid"] = int(kwargs["netid"]) + + vm = QubesProxyVm(**kwargs) + self[vm.qid] = vm + + except (ValueError, LookupError) as err: + print("{0}: import error (QubesProxyVM) {1}".format( + os.path.basename(sys.argv[0]), err)) + return False + + # After importing all NetVMs and ProxyVMs, set netvm references + # 1. For TemplateVMs + for element in tree.findall("QubesTemplateVm"): + try: + + kwargs = {} + attr_list = ("qid", "uses_default_netvm", "netvm_qid") + + for attribute in attr_list: + kwargs[attribute] = element.get(attribute) + + vm = self[int(kwargs["qid"])] + if "uses_default_netvm" not in kwargs: - kwargs["uses_default_netvm"] = True + vm.uses_default_netvm = True else: - kwargs["uses_default_netvm"] = True if kwargs["uses_default_netvm"] == "True" else False - if kwargs["uses_default_netvm"] is True: + vm.uses_default_netvm = True if kwargs["uses_default_netvm"] == "True" else False + if vm.uses_default_netvm is True: netvm_vm = self.get_default_netvm_vm() kwargs.pop("netvm_qid") else: @@ -1567,16 +1926,41 @@ class QubesVmCollection(dict): else: netvm_vm = self[netvm_qid] - kwargs["netvm_vm"] = netvm_vm - - vm = QubesTemplateVm(**kwargs) + vm.netvm_vm = netvm_vm - self[vm.qid] = vm except (ValueError, LookupError) as err: print("{0}: import error (QubesTemplateVm): {1}".format( os.path.basename(sys.argv[0]), err)) return False + # 2. For PoxyVMs + for element in tree.findall("QubesProxyVm"): + try: + kwargs = {} + attr_list = ("qid", "netvm_qid") + + for attribute in attr_list: + kwargs[attribute] = element.get(attribute) + + vm = self[int(kwargs["qid"])] + + if kwargs["netvm_qid"] == "none" or kwargs["netvm_qid"] is None: + netvm_vm = None + kwargs.pop("netvm_qid") + else: + netvm_qid = int(kwargs.pop("netvm_qid")) + if netvm_qid not in self: + netvm_vm = None + else: + netvm_vm = self[netvm_qid] + + vm.netvm_vm = netvm_vm + + except (ValueError, LookupError) as err: + print("{0}: import error (QubesProxyVM) {1}".format( + os.path.basename(sys.argv[0]), err)) + return False + # Finally, read in the AppVMs for element in tree.findall("QubesAppVm"): try: @@ -1620,7 +2004,7 @@ class QubesVmCollection(dict): netvm_vm = self[netvm_qid] kwargs["netvm_vm"] = netvm_vm - + if kwargs["label"] is not None: if kwargs["label"] not in QubesVmLabels: print "ERROR: incorrect label for VM '{0}'".format(kwargs["name"]) @@ -1656,8 +2040,8 @@ class QubesVmCollection(dict): format(kwargs["name"], kwargs["template_qid"]) kwargs["template_vm"] = template_vm - kwargs["netvm_vm"] = self.get_default_netvm_vm() - + kwargs["netvm_vm"] = self.get_default_netvm_vm() + if kwargs["label"] is not None: if kwargs["label"] not in QubesVmLabels: print "ERROR: incorrect label for VM '{0}'".format(kwargs["name"]) @@ -1728,3 +2112,4 @@ class QubesDaemonPidfile(object): self.remove_pidfile() +# vim:sw=4:et: diff --git a/dom0/qvm-tools/qvm-add-netvm b/dom0/qvm-tools/qvm-add-netvm deleted file mode 100755 index f5d3cde4..00000000 --- a/dom0/qvm-tools/qvm-add-netvm +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/python2.6 -# -# The Qubes OS Project, http://www.qubes-os.org -# -# Copyright (C) 2010 Joanna Rutkowska -# -# 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 subprocess -import shutil -import re - -from qubes.qubes import QubesVmCollection -from qubes.qubes import QubesException - -def find_net_devices(): - p = subprocess.Popen (["lspci", "-mm", "-n"], stdout=subprocess.PIPE) - result = p.communicate() - retcode = p.returncode - if (retcode != 0): - print "ERROR when executing lspci!" - raise IOError - - net_devices = set() - rx_netdev = re.compile (r"^([0-9][0-9]:[0-9][0-9].[0-9]) \"02") - 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 - net_devices.add (dev_bdf) - - return net_devices - -def main(): - usage = "usage: %prog [options] " - 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)") - - - (options, args) = parser.parse_args () - if (len (args) != 1): - parser.error ("You must specify a NetVM name!") - netvmname = args[0] - - qvm_collection = QubesVmCollection() - qvm_collection.lock_db_for_writing() - qvm_collection.load() - - if qvm_collection.get_vm_by_name(netvmname) is not None: - print "ERROR: A VM with the name '{0}' already exists in the system.".format(netvmname) - exit(1) - - vm = qvm_collection.add_new_netvm(netvmname, - conf_file=options.conf_file, - dir_path=options.dir_path) - - try: - vm.verify_files() - except QubesException as err: - print "ERROR: {0}".format(err) - qvm_collection.pop(vm.qid) - exit (1) - - - net_devices = find_net_devices() - print "Found the following net devices in your system:" - dev_str = '' - for dev in net_devices: - print "--> {0}".format(dev) - dev_str += '"{0}", '.format(dev) - - print "Assigning them to the netvm '{0}'".format(netvmname) - rx_pcidevs = re.compile (r"%NETVMPCIDEVS%") - conf_template = open (vm.conf_file, "r") - conf_vm = open(vm.conf_file + ".processed", "w") - - for line in conf_template: - line = rx_pcidevs.sub(dev_str, line) - conf_vm.write(line) - - conf_template.close() - conf_vm.close() - - shutil.move (vm.conf_file + ".processed", vm.conf_file) - - - try: - pass - vm.add_to_xen_storage() - - except (IOError, OSError) as err: - print "ERROR: {0}".format(err) - qvm_collection.pop(vm.qid) - exit (1) - - qvm_collection.save() - qvm_collection.unlock_db() - -main() diff --git a/dom0/qvm-tools/qvm-backup b/dom0/qvm-tools/qvm-backup index 8dbcdc4f..923a5803 100755 --- a/dom0/qvm-tools/qvm-backup +++ b/dom0/qvm-tools/qvm-backup @@ -144,7 +144,7 @@ def main(): print s - template_vms_worth_backingup = [ vm for vm in vms_list if (vm.is_templete() and not vm.installed_by_rpm)] + template_vms_worth_backingup = [ vm for vm in vms_list if (vm.is_template() and not vm.installed_by_rpm)] if len (template_vms_worth_backingup): for vm in template_vms_worth_backingup: vm_sz = vm.get_disk_utilization() diff --git a/dom0/qvm-tools/qvm-backup-restore b/dom0/qvm-tools/qvm-backup-restore index 43e15379..37fa128e 100755 --- a/dom0/qvm-tools/qvm-backup-restore +++ b/dom0/qvm-tools/qvm-backup-restore @@ -47,18 +47,18 @@ fields = { "name": {"func": "('=>' if backup_collection.get_default_template_vm() is not None\ and vm.qid == backup_collection.get_default_template_vm().qid else '')\ - + ('[' if vm.is_templete() else '')\ + + ('[' if vm.is_template() else '')\ + ('{' if vm.is_netvm() else '')\ + vm.name \ - + (']' if vm.is_templete() else '')\ + + (']' if vm.is_template() else '')\ + ('}' if vm.is_netvm() else '')"}, - "type": {"func": "'Tpl' if vm.is_templete() else \ + "type": {"func": "'Tpl' if vm.is_template() else \ (' Net' if vm.is_netvm() else 'App')"}, "updbl" : {"func": "'Yes' if vm.is_updateable() else ''"}, - "template": {"func": "'n/a' if vm.is_templete() or vm.is_netvm() else\ + "template": {"func": "'n/a' if vm.is_template() or vm.is_netvm() else\ backup_collection[vm.template_vm.qid].name"}, "netvm": {"func": "'n/a' if vm.is_netvm() else\ @@ -196,11 +196,11 @@ def main(): template_vm_on_host = host_collection.get_vm_by_name (templatevm_name) # No template on the host? - if not ((template_vm_on_host is not None) and template_vm_on_host.is_templete): + if not ((template_vm_on_host is not None) and template_vm_on_host.is_template): # Maybe the (custom) template is in the backup? template_vm_on_backup = backup_collection.get_vm_by_name (templatevm_name) - if not ((template_vm_on_backup is not None) and template_vm_on_backup.is_templete): + if not ((template_vm_on_backup is not None) and template_vm_on_backup.is_template): s += " <-- No matching template on the host or in the backup found!" there_are_missing_templates = True good_to_go = False if not (options.ignore_missing) else True @@ -287,13 +287,13 @@ def main(): if vm.is_updateable(): restore_vm_file (backup_dir, vm.rootcow_img) - elif vm.is_templete(): + elif vm.is_template(): restore_vm_dir (backup_dir, vm.dir_path, qubes_templates_dir); else: print "ERROR: VM '{0}', type='{1}': unsupported VM type!".format(vm.name, vm.type) # Add templates... - for vm in [ vm for vm in vms_to_restore if vm.is_templete()]: + for vm in [ vm for vm in vms_to_restore if vm.is_template()]: print "-> Adding Template VM {0}...".format(vm.name) updateable = vm.updateable vm = host_collection.add_new_templatevm(vm.name, diff --git a/dom0/qvm-tools/qvm-create b/dom0/qvm-tools/qvm-create index 9ff9c6c0..aa567b98 100755 --- a/dom0/qvm-tools/qvm-create +++ b/dom0/qvm-tools/qvm-create @@ -24,6 +24,26 @@ from qubes.qubes import QubesVmCollection from qubes.qubes import QubesVmLabels from optparse import OptionParser; import subprocess +import re + +def find_net_devices(): + p = subprocess.Popen (["lspci", "-mm", "-n"], stdout=subprocess.PIPE) + result = p.communicate() + retcode = p.returncode + if (retcode != 0): + print "ERROR when executing lspci!" + raise IOError + + net_devices = set() + rx_netdev = re.compile (r"^([0-9][0-9]:[0-9][0-9].[0-9]) \"02") + 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 + net_devices.add (dev_bdf) + + return net_devices def main(): @@ -33,6 +53,10 @@ def main(): help="Specify the TemplateVM to use") parser.add_option ("-l", "--label", dest="label", help="Specify the label to use for the new VM (e.g. red, yellow, green, ...)") + parser.add_option ("-p", "--proxy", action="store_true", dest="proxyvm", default=False, + help="Create ProxyVM") + parser.add_option ("-n", "--net", action="store_true", dest="netvm", default=False, + help="Create NetVM") parser.add_option ("-q", "--quiet", action="store_false", dest="verbose", default=True) (options, args) = parser.parse_args () @@ -40,20 +64,24 @@ def main(): parser.error ("You must specify VM name!") vmname = args[0] - if options.label is None: - print "You must choose a label for the new VM by passing the --label option." - print "Possible values are:" - for l in QubesVmLabels.values(): - print "* {0}".format(l.name) - exit (1) - - if options.label not in QubesVmLabels: - print "Wrong label name, supported values are the following:" - for l in QubesVmLabels.values(): - print "* {0}".format(l.name) - exit (1) - label = QubesVmLabels[options.label] + if options.netvm and options.proxyvm: + parser.error ("You must specify at most one of --proxy and --net") + + label = None + if not options.proxyvm and not options.netvm: + if options.label is None: + print "You must choose a label for the new VM by passing the --label option." + print "Possible values are:" + for l in QubesVmLabels.values(): + print "* {0}".format(l.name) + exit (1) + if options.label not in QubesVmLabels: + print "Wrong label name, supported values are the following:" + for l in QubesVmLabels.values(): + print "* {0}".format(l.name) + exit (1) + label = QubesVmLabels[options.label] qvm_collection = QubesVmCollection() qvm_collection.lock_db_for_writing() @@ -68,7 +96,7 @@ def main(): if template_vm is None: print "There is no (Templete)VM with the name '{0}'".format(options.template) exit (1) - if not template_vm.is_templete(): + if not template_vm.is_template(): print "VM '{0}' is not a TemplateVM".format(options.template) exit (1) if (options.verbose): @@ -83,7 +111,24 @@ def main(): if (options.verbose): print "--> Using default TemplateVM: {0}".format(template_vm.name) - vm = qvm_collection.add_new_appvm(vmname, template_vm, label = label) + vm = None + if options.netvm: + vm = qvm_collection.add_new_netvm(vmname, template_vm) + + net_devices = find_net_devices() + print "Found the following net devices in your system:" + dev_str = '' + for dev in net_devices: + print "--> {0}".format(dev) + dev_str += '"{0}", '.format(dev) + + print "Assigning them to the netvm '{0}'".format(vmname) + vm.pcidevs = dev_str + + elif options.proxyvm: + vm = qvm_collection.add_new_proxyvm(vmname, template_vm) + else: + vm = qvm_collection.add_new_appvm(vmname, template_vm, label = label) try: vm.create_on_disk(verbose=options.verbose) vm.add_to_xen_storage() diff --git a/dom0/qvm-tools/qvm-ls b/dom0/qvm-tools/qvm-ls index 395a3426..14d3e95f 100755 --- a/dom0/qvm-tools/qvm-ls +++ b/dom0/qvm-tools/qvm-ls @@ -30,23 +30,25 @@ fields = { "name": {"func": "('=>' if qvm_collection.get_default_template_vm() is not None\ and vm.qid == qvm_collection.get_default_template_vm().qid else '')\ - + ('[' if vm.is_templete() else '')\ + + ('[' if vm.is_template() else '')\ + ('<' if vm.is_disposablevm() else '')\ + ('{' if vm.is_netvm() else '')\ + vm.name \ - + (']' if vm.is_templete() else '')\ + + (']' if vm.is_template() else '')\ + ('>' if vm.is_disposablevm() else '')\ + ('}' if vm.is_netvm() else '')"}, - "type": {"func": "'Tpl' if vm.is_templete() else \ - (' Net' if vm.is_netvm() else '')"}, + "type": {"func": "'Tpl' if vm.is_template() else \ + ('Proxy' if vm.is_proxyvm() else \ + (' Net' if vm.is_netvm() else ''))"}, "updbl" : {"func": "'Yes' if vm.is_updateable() else ''"}, - "template": {"func": "'n/a' if vm.is_templete() or vm.is_netvm() else\ - qvm_collection[vm.template_vm.qid].name"}, + "template": {"func": "'n/a' if vm.is_template() else\ + ('None' if vm.template_vm is None else\ + qvm_collection[vm.template_vm.qid].name)"}, - "netvm": {"func": "'n/a' if vm.is_netvm() else\ + "netvm": {"func": "'n/a' if vm.is_netvm() and not vm.is_proxyvm() else\ ('*' if vm.uses_default_netvm else '') +\ qvm_collection[vm.netvm_vm.qid].name\ if vm.netvm_vm is not None else '-'"}, @@ -141,7 +143,7 @@ def main(): # Now, the template, and all its AppVMs... for tvm in vms_list: - if tvm.is_templete(): + if tvm.is_template(): vms_to_display.append (tvm) for vm in vms_list: if (vm.is_appvm() or vm.is_disposablevm()) and vm.template_vm.qid == tvm.qid: diff --git a/dom0/qvm-tools/qvm-prefs b/dom0/qvm-tools/qvm-prefs index f2c77367..743de112 100755 --- a/dom0/qvm-tools/qvm-prefs +++ b/dom0/qvm-tools/qvm-prefs @@ -42,6 +42,8 @@ def do_list(vm): print fmt.format ("config", vm.conf_file) if not vm.is_appvm(): print fmt.format ("root img", vm.root_img) + if vm.is_template(): + print fmt.format ("root COW img", vm.rootcow_img) if vm.is_appvm(): print fmt.format ("root img", vm.template_vm.root_img) print fmt.format ("root COW img", vm.rootcow_img) @@ -113,7 +115,7 @@ def set_updateable(vms, vm, args): print "If you want to make this AppVM updateable, you must first make the Template VM nonupdateable." return False - if vm.is_templete(): + if vm.is_template(): # Make sure that all the AppVMs are non-updateable... for appvm in vm.appvms.values(): if appvm.is_updateable(): diff --git a/dom0/qvm-tools/qvm-remove b/dom0/qvm-tools/qvm-remove index 06e39af5..50beaf65 100755 --- a/dom0/qvm-tools/qvm-remove +++ b/dom0/qvm-tools/qvm-remove @@ -42,7 +42,7 @@ def main(): print "A VM with the name '{0}' does not exist in the system.".format(vmname) exit(1) - if vm.is_templete(): + if vm.is_template(): dependent_vms = qvm_collection.get_vms_based_on(vm.qid) if len(dependent_vms) > 0: print "The following AppVMs use '{0}' as a template:".format(vmname) diff --git a/dom0/qvm-tools/qvm-set-default-template b/dom0/qvm-tools/qvm-set-default-template index 429d34ad..98003bc0 100755 --- a/dom0/qvm-tools/qvm-set-default-template +++ b/dom0/qvm-tools/qvm-set-default-template @@ -39,7 +39,7 @@ def main(): print "A VM with the name '{0}' does not exist in the system.".format(vmname) exit(1) - if not vm.is_templete(): + if not vm.is_template(): print "VM '{0}' is not a TemplateVM".format(vmname) exit (1) diff --git a/dom0/qvm-tools/qvm-template-commit b/dom0/qvm-tools/qvm-template-commit new file mode 100755 index 00000000..a82249d0 --- /dev/null +++ b/dom0/qvm-tools/qvm-template-commit @@ -0,0 +1,67 @@ +#!/usr/bin/python2.6 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2011 Marek Marczykowski +# +# 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 + +qubes_guid_path = "/usr/bin/qubes_guid" + +def main(): + usage = "usage: %prog [options] " + parser = OptionParser (usage) + + (options, args) = parser.parse_args () + if (len (args) != 1): + parser.error ("You must specify VM name!") + vmname = args[0] + + 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 "A VM with the name '{0}' does not exist in the system.".format(vmname) + exit(1) + + if not vm.is_template(): + print "A VM '{0}' is not template.".format(vmname) + exit(1) + + if vm.is_running(): + print "You must stop VM first." + exit(1) + + try: + vm.verify_files() + vm.commit_changes() + except (IOError, OSError, QubesException) as err: + print "ERROR: {0}".format(err) + exit (1) + + exit (0) + + +main() diff --git a/dom0/restore/.gitignore b/dom0/restore/.gitignore new file mode 100644 index 00000000..0535ab8a --- /dev/null +++ b/dom0/restore/.gitignore @@ -0,0 +1,3 @@ +qubes_restore +xenfreepages +xenstore-watch diff --git a/netvm/30-qubes_external_ip b/netvm/30-qubes_external_ip new file mode 100755 index 00000000..66ae5269 --- /dev/null +++ b/netvm/30-qubes_external_ip @@ -0,0 +1,8 @@ +#!/bin/sh +if [ x$2 == xup ]; then + INET=$(/sbin/ip addr show dev $1 | /bin/grep inet) + /usr/bin/xenstore-write qubes_netvm_external_ip "$INET" +fi +if [ x$2 == xdown ]; then + /usr/bin/xenstore-write qubes_netvm_external_ip "" +fi diff --git a/netvm/qubes_core b/netvm/qubes_core deleted file mode 100755 index dbfaad7a..00000000 --- a/netvm/qubes_core +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/sh -# -# chkconfig: 345 90 90 -# description: Executes Qubes core scripts at VM boot -# -# Source function library. -. /etc/rc.d/init.d/functions - -start() -{ - echo -n $"Executing Qubes Core scripts NetVM:" - - if ! [ -x /usr/bin/xenstore-read ] ; then - echo "ERROR: /usr/bin/xenstore-read not found!" - exit 1 - fi - - name=$(/usr/bin/xenstore-read name) - hostname $name - - # Setup gateway for all the VMs this netVM is serviceing... - modprobe netbk - gateway=$(/usr/bin/xenstore-read qubes_netvm_gateway) - netmask=$(/usr/bin/xenstore-read qubes_netvm_netmask) - network=$(/usr/bin/xenstore-read qubes_netvm_network) - secondary_dns=$(/usr/bin/xenstore-read qubes_netvm_secondary_dns) - echo "NS1=$gateway" > /var/run/qubes/qubes_ns - echo "NS2=$secondary_dns" >> /var/run/qubes/qubes_ns - /usr/lib/qubes/qubes_setup_dnat_to_ns - echo "1" > /proc/sys/net/ipv4/ip_forward - success - echo "" - return 0 -} - -stop() -{ - return 0 -} - -case "$1" in - start) - start - ;; - stop) - stop - ;; - *) - echo $"Usage: $0 {start|stop}" - exit 3 - ;; -esac - -exit $RETVAL diff --git a/netvm/qubes_core_netvm b/netvm/qubes_core_netvm new file mode 100755 index 00000000..dd713fce --- /dev/null +++ b/netvm/qubes_core_netvm @@ -0,0 +1,58 @@ +#!/bin/sh +# +# chkconfig: 345 90 90 +# description: Executes Qubes core scripts at NetVM boot +# +# Source function library. +. /etc/rc.d/init.d/functions + +start() +{ + if ! [ -x /usr/bin/xenstore-read ] ; then + echo "ERROR: /usr/bin/xenstore-read not found!" + exit 1 + fi + + type=$(/usr/bin/xenstore-read qubes_vm_type) + if [ "$type" == "NetVM" ]; then + /sbin/service NetworkManager start + fi + + echo -n $"Executing Qubes Core scripts NetVM:" + + # Setup gateway for all the VMs this netVM is serviceing... + network=$(/usr/bin/xenstore-read qubes_netvm_network 2>/dev/null) + if [ "x$network" != "x" ]; then + gateway=$(/usr/bin/xenstore-read qubes_netvm_gateway) + netmask=$(/usr/bin/xenstore-read qubes_netvm_netmask) + secondary_dns=$(/usr/bin/xenstore-read qubes_netvm_secondary_dns) + modprobe netbk + echo "NS1=$gateway" > /var/run/qubes/qubes_ns + echo "NS2=$secondary_dns" >> /var/run/qubes/qubes_ns + /usr/lib/qubes/qubes_setup_dnat_to_ns + echo "1" > /proc/sys/net/ipv4/ip_forward + fi + success + echo "" + return 0 +} + +stop() +{ + return 0 +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + *) + echo $"Usage: $0 {start|stop}" + exit 3 + ;; +esac + +exit $RETVAL diff --git a/proxyvm/bin/qubes_firewall b/proxyvm/bin/qubes_firewall new file mode 100755 index 00000000..6f1cc267 --- /dev/null +++ b/proxyvm/bin/qubes_firewall @@ -0,0 +1,33 @@ +#!/bin/bash +set -e + +PIDFILE=/var/run/qubes/qubes_firewall.pid +XENSTORE_IPTABLES=qubes_iptables +XENSTORE_ERROR=qubes_iptables_error +OLD_RULES="" + +# PIDfile handling +[[ -e $PIDFILE ]] && kill -s 0 $(<$PIDFILE) 2>/dev/null && exit 0 +echo $$ >$PIDFILE + +trap 'exit 0' SIGTERM + +while true; do + RULES=$(/usr/bin/xenstore-read $XENSTORE_IPTABLES) + + if [[ "$RULES" != "$OLD_RULES" ]]; then + IPTABLES_SAVE=$(/sbin/iptables-save | sed '/^\*filter/,/^COMMIT/d') + OUT=`echo -e "$RULES\n$IPTABLES_SAVE" | /sbin/iptables-restore 2>&1 || :` + /usr/bin/xenstore-write $XENSTORE_ERROR "$OUT" + + if [[ -z "$OUT" ]]; then + # If OK save it for later + /sbin/service iptables save >/dev/null + fi + + OLD_RULES="$RULES" + fi + + # Wait for changes in xenstore file + /usr/bin/xenstore-watch $XENSTORE_IPTABLES +done diff --git a/proxyvm/bin/qubes_netwatcher b/proxyvm/bin/qubes_netwatcher new file mode 100755 index 00000000..8f9d2e9c --- /dev/null +++ b/proxyvm/bin/qubes_netwatcher @@ -0,0 +1,27 @@ +#!/bin/bash +set -e + +PIDFILE=/var/run/qubes/qubes_netwatcher.pid +CURR_NETCFG="" + +# PIDfile handling +[[ -e $PIDFILE ]] && kill -s 0 $(<$PIDFILE) 2>/dev/null && exit 0 +echo $$ >$PIDFILE + +trap 'exit 0' SIGTERM + +while true; do + NET_DOMID=$(/usr/bin/xenstore-read qubes_netvm_domid) + if [[ -n "$NET_DOMID" ]] && [[ $NET_DOMID -gt 0 ]]; then + NETCFG=$(/usr/bin/xenstore-read /local/domain/$NET_DOMID/qubes_netvm_external_ip) + if [[ "$NETCFG" != "$CURR_NETCFG" ]]; then + /sbin/service qubes_firewall stop + /sbin/service qubes_firewall start + CURR_NETCFG="$NETCFG" + fi + + /usr/bin/xenstore-watch /local/domain/$NET_DOMID/qubes_netvm_external_ip + else + /usr/bin/xenstore-watch qubes_netvm_domid + fi +done diff --git a/proxyvm/init.d/qubes_firewall b/proxyvm/init.d/qubes_firewall new file mode 100755 index 00000000..2d1218ec --- /dev/null +++ b/proxyvm/init.d/qubes_firewall @@ -0,0 +1,48 @@ +#!/bin/sh +# +# chkconfig: 345 91 91 +# description: Starts Qubes Firewall monitor +# +# Source function library. +. /etc/rc.d/init.d/functions + +PIDFILE=/var/run/qubes/qubes_firewall.pid + +start() +{ + type=$(/usr/bin/xenstore-read qubes_vm_type) + if [ "$type" == "ProxyVM" ]; then + echo -n $"Starting Qubes Firewall monitor:" + /sbin/ethtool -K eth0 sg off + /usr/sbin/qubes_firewall & + success + echo "" + fi + return 0 +} + +stop() +{ + type=$(/usr/bin/xenstore-read qubes_vm_type) + if [ "$type" == "ProxyVM" ]; then + echo -n "Stopping Qubes Firewall monitor:" + kill -9 $(cat $PIDFILE) 2>/dev/null && success || failure + echo "" + fi + return 0 +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + *) + echo $"Usage: $0 {start|stop}" + exit 3 + ;; +esac + +exit $RETVAL diff --git a/proxyvm/init.d/qubes_netwatcher b/proxyvm/init.d/qubes_netwatcher new file mode 100755 index 00000000..e8d9af46 --- /dev/null +++ b/proxyvm/init.d/qubes_netwatcher @@ -0,0 +1,48 @@ +#!/bin/sh +# +# chkconfig: 345 92 92 +# description: Starts Qubes Network monitor +# +# Source function library. +. /etc/rc.d/init.d/functions + +PIDFILE=/var/run/qubes/qubes_netwatcher.pid + +start() +{ + type=$(/usr/bin/xenstore-read qubes_vm_type) + if [ "$type" == "ProxyVM" ]; then + echo -n $"Starting Qubes Network monitor:" + /sbin/ethtool -K eth0 sg off + /usr/sbin/qubes_netwatcher & + success + echo "" + fi + return 0 +} + +stop() +{ + type=$(/usr/bin/xenstore-read qubes_vm_type) + if [ "$type" == "ProxyVM" ]; then + echo -n "Stopping Qubes Network monitor:" + kill -9 $(cat $PIDFILE) 2>/dev/null && success || failure + echo "" + fi + return 0 +} + +case "$1" in + start) + start + ;; + stop) + stop + ;; + *) + echo $"Usage: $0 {start|stop}" + exit 3 + ;; +esac + +exit $RETVAL diff --git a/rpm_spec/core-appvm.spec b/rpm_spec/core-appvm.spec index c9a009d1..91fd13c6 100644 --- a/rpm_spec/core-appvm.spec +++ b/rpm_spec/core-appvm.spec @@ -34,6 +34,9 @@ URL: http://www.qubes-os.org Requires: /usr/bin/xenstore-read Requires: fedora-release = 13 Requires: /usr/bin/mimeopen +Requires: qubes-core-commonvm +BuildRequires: gcc +BuildRequires: xen-devel Provides: qubes-core-vm %define _builddir %(pwd)/appvm @@ -53,9 +56,6 @@ fi adduser --create-home user mkdir -p $RPM_BUILD_ROOT/var/lib/qubes -if [ -e $RPM_BUILD_ROOT/etc/fstab ] ; then -mv $RPM_BUILD_ROOT/etc/fstab $RPM_BUILD_ROOT/var/lib/qubes/fstab.orig -fi %build make clean all @@ -63,10 +63,8 @@ make -C ../common %install -mkdir -p $RPM_BUILD_ROOT/etc -cp fstab $RPM_BUILD_ROOT/etc/fstab mkdir -p $RPM_BUILD_ROOT/etc/init.d -cp qubes_core $RPM_BUILD_ROOT/etc/init.d/ +cp qubes_core_appvm $RPM_BUILD_ROOT/etc/init.d/ mkdir -p $RPM_BUILD_ROOT/var/lib/qubes mkdir -p $RPM_BUILD_ROOT/usr/bin cp qubes_timestamp qvm-copy-to-vm qvm-open-in-dvm $RPM_BUILD_ROOT/usr/bin @@ -78,17 +76,9 @@ mkdir -p $RPM_BUILD_ROOT/%{kde_service_dir} cp qvm-copy.desktop qvm-dvm.desktop $RPM_BUILD_ROOT/%{kde_service_dir} mkdir -p $RPM_BUILD_ROOT/etc/udev/rules.d cp qubes.rules $RPM_BUILD_ROOT/etc/udev/rules.d -mkdir -p $RPM_BUILD_ROOT/etc/sysconfig -cp iptables $RPM_BUILD_ROOT/etc/sysconfig/ mkdir -p $RPM_BUILD_ROOT/mnt/incoming mkdir -p $RPM_BUILD_ROOT/mnt/outgoing mkdir -p $RPM_BUILD_ROOT/mnt/removable -mkdir -p $RPM_BUILD_ROOT/etc/yum.repos.d -cp ../appvm/qubes.repo $RPM_BUILD_ROOT/etc/yum.repos.d -mkdir -p $RPM_BUILD_ROOT/sbin -cp ../common/qubes_serial_login $RPM_BUILD_ROOT/sbin -mkdir -p $RPM_BUILD_ROOT/etc -cp ../common/serial.conf $RPM_BUILD_ROOT/var/lib/qubes/ mkdir -p $RPM_BUILD_ROOT/etc/X11 cp xorg-preload-apps.conf $RPM_BUILD_ROOT/etc/X11 @@ -96,91 +86,22 @@ cp xorg-preload-apps.conf $RPM_BUILD_ROOT/etc/X11 mkdir -p $RPM_BUILD_ROOT/home_volatile/user chown 500:500 $RPM_BUILD_ROOT/home_volatile/user -%triggerin -- initscripts -cp /var/lib/qubes/serial.conf /etc/init/serial.conf - %post +chkconfig --add qubes_core_appvm || echo "WARNING: Cannot add service qubes_core!" +chkconfig qubes_core_appvm on || echo "WARNING: Cannot enable service qubes_core!" + if [ "$1" != 1 ] ; then # do this whole %post thing only when updating for the first time... exit 0 fi -usermod -L root usermod -L user -if ! [ -f /var/lib/qubes/serial.orig ] ; then - cp /etc/init/serial.conf /var/lib/qubes/serial.orig -fi - -#echo "--> Disabling SELinux..." -sed -e s/^SELINUX=.*$/SELINUX=disabled/ /etc/selinux/config.processed -mv /etc/selinux/config.processed /etc/selinux/config -setenforce 0 2>/dev/null - -#echo "--> Turning off unnecessary services..." -# FIXME: perhaps there is more elegant way to do this? -for f in /etc/init.d/* -do - srv=`basename $f` - [ $srv = 'functions' ] && continue - [ $srv = 'killall' ] && continue - [ $srv = 'halt' ] && continue - [ $srv = 'single' ] && continue - chkconfig $srv off -done - -#echo "--> Enabling essential services..." -chkconfig rsyslog on -chkconfig haldaemon on -chkconfig messagebus on -chkconfig cups on -chkconfig iptables on -chkconfig --add qubes_core || echo "WARNING: Cannot add service qubes_core!" -chkconfig qubes_core on || echo "WARNING: Cannot enable service qubes_core!" - - -# TODO: make this not display the silly message about security context... -sed -i s/^id:.:initdefault:/id:3:initdefault:/ /etc/inittab - -# Remove most of the udev scripts to speed up the VM boot time -# Just leave the xen* scripts, that are needed if this VM was -# ever used as a net backend (e.g. as a VPN domain in the future) -#echo "--> Removing unnecessary udev scripts..." -mkdir -p /var/lib/qubes/removed-udev-scripts -for f in /etc/udev/rules.d/* -do - if [ $(basename $f) == "xen-backend.rules" ] ; then - continue - fi - - if [ $(basename $f) == "xend.rules" ] ; then - continue - fi - - if [ $(basename $f) == "qubes.rules" ] ; then - continue - fi - - if [ $(basename $f) == "90-hal.rules" ] ; then - continue - fi - - - mv $f /var/lib/qubes/removed-udev-scripts/ -done -mkdir -p /rw -#rm -f /etc/mtab -#echo "--> Removing HWADDR setting from /etc/sysconfig/network-scripts/ifcfg-eth0" -#mv /etc/sysconfig/network-scripts/ifcfg-eth0 /etc/sysconfig/network-scripts/ifcfg-eth0.orig -#grep -v HWADDR /etc/sysconfig/network-scripts/ifcfg-eth0.orig > /etc/sysconfig/network-scripts/ifcfg-eth0 %preun if [ "$1" = 0 ] ; then # no more packages left - chkconfig qubes_core off - mv /var/lib/qubes/fstab.orig /etc/fstab - mv /var/lib/qubes/removed-udev-scripts/* /etc/udev/rules.d/ - mv /var/lib/qubes/serial.orig /etc/init/serial.conf + chkconfig qubes_core_appvm off fi %clean @@ -188,8 +109,7 @@ rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) -/etc/fstab -/etc/init.d/qubes_core +/etc/init.d/qubes_core_appvm /usr/bin/qvm-copy-to-vm /usr/lib/qubes/qvm-copy-to-vm.kde %attr(4755,root,root) /usr/bin/qvm-open-in-dvm @@ -200,13 +120,9 @@ rm -rf $RPM_BUILD_ROOT %attr(4755,root,root) /usr/lib/qubes/qubes_penctl /usr/lib/qubes/qubes_add_pendrive_script /etc/udev/rules.d/qubes.rules -/etc/sysconfig/iptables -/var/lib/qubes %dir /mnt/incoming %dir /mnt/outgoing %dir /mnt/removable -/etc/yum.repos.d/qubes.repo -/sbin/qubes_serial_login /usr/bin/qubes_timestamp %dir /home_volatile %attr(700,user,user) /home_volatile/user diff --git a/rpm_spec/core-commonvm.spec b/rpm_spec/core-commonvm.spec new file mode 100644 index 00000000..6705c28b --- /dev/null +++ b/rpm_spec/core-commonvm.spec @@ -0,0 +1,166 @@ +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2010 Joanna Rutkowska +# 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. +# +# + +%{!?version: %define version %(cat version_vm)} + +Name: qubes-core-commonvm +Version: %{version} +Release: 1 +Summary: The Qubes core files for any VM + +Group: Qubes +Vendor: Invisible Things Lab +License: GPL +URL: http://www.qubes-os.org +Requires: /usr/bin/xenstore-read +Requires: fedora-release = 13 + +%define _builddir %(pwd)/common + +%description +The Qubes core files for installation inside a Qubes VM. + +%pre + +if [ "$1" != 1 ] ; then +# do this whole %pre thing only when updating for the first time... +exit 0 +fi + +mkdir -p $RPM_BUILD_ROOT/var/lib/qubes +if [ -e $RPM_BUILD_ROOT/etc/fstab ] ; then +mv $RPM_BUILD_ROOT/etc/fstab $RPM_BUILD_ROOT/var/lib/qubes/fstab.orig +fi + +%install + +mkdir -p $RPM_BUILD_ROOT/etc +cp fstab $RPM_BUILD_ROOT/etc/fstab +mkdir -p $RPM_BUILD_ROOT/etc/init.d +cp qubes_core $RPM_BUILD_ROOT/etc/init.d/ +mkdir -p $RPM_BUILD_ROOT/var/lib/qubes +mkdir -p $RPM_BUILD_ROOT/etc/sysconfig +cp iptables $RPM_BUILD_ROOT/etc/sysconfig/ +mkdir -p $RPM_BUILD_ROOT/etc/yum.repos.d +cp ../appvm/qubes.repo $RPM_BUILD_ROOT/etc/yum.repos.d +mkdir -p $RPM_BUILD_ROOT/sbin +cp ../common/qubes_serial_login $RPM_BUILD_ROOT/sbin +mkdir -p $RPM_BUILD_ROOT/etc +cp ../common/serial.conf $RPM_BUILD_ROOT/var/lib/qubes/ + +%triggerin -- initscripts +cp /var/lib/qubes/serial.conf /etc/init/serial.conf + +%post + +if [ "$1" != 1 ] ; then +# do this whole %post thing only when updating for the first time... +exit 0 +fi + +usermod -L root +if ! [ -f /var/lib/qubes/serial.orig ] ; then + cp /etc/init/serial.conf /var/lib/qubes/serial.orig +fi + +#echo "--> Disabling SELinux..." +sed -e s/^SELINUX=.*$/SELINUX=disabled/ /etc/selinux/config.processed +mv /etc/selinux/config.processed /etc/selinux/config +setenforce 0 2>/dev/null + +#echo "--> Turning off unnecessary services..." +# FIXME: perhaps there is more elegant way to do this? +for f in /etc/init.d/* +do + srv=`basename $f` + [ $srv = 'functions' ] && continue + [ $srv = 'killall' ] && continue + [ $srv = 'halt' ] && continue + [ $srv = 'single' ] && continue + [ $srv = 'qubes_gui' ] && continue + chkconfig $srv off +done + +#echo "--> Enabling essential services..." +chkconfig rsyslog on +chkconfig haldaemon on +chkconfig messagebus on +chkconfig iptables on +chkconfig --add qubes_core || echo "WARNING: Cannot add service qubes_core!" +chkconfig qubes_core on || echo "WARNING: Cannot enable service qubes_core!" + + +# TODO: make this not display the silly message about security context... +sed -i s/^id:.:initdefault:/id:3:initdefault:/ /etc/inittab + +# Remove most of the udev scripts to speed up the VM boot time +# Just leave the xen* scripts, that are needed if this VM was +# ever used as a net backend (e.g. as a VPN domain in the future) +#echo "--> Removing unnecessary udev scripts..." +mkdir -p /var/lib/qubes/removed-udev-scripts +for f in /etc/udev/rules.d/* +do + if [ $(basename $f) == "xen-backend.rules" ] ; then + continue + fi + + if [ $(basename $f) == "xend.rules" ] ; then + continue + fi + + if [ $(basename $f) == "qubes.rules" ] ; then + continue + fi + + if [ $(basename $f) == "90-hal.rules" ] ; then + continue + fi + + + mv $f /var/lib/qubes/removed-udev-scripts/ +done +mkdir -p /rw +#rm -f /etc/mtab +#echo "--> Removing HWADDR setting from /etc/sysconfig/network-scripts/ifcfg-eth0" +#mv /etc/sysconfig/network-scripts/ifcfg-eth0 /etc/sysconfig/network-scripts/ifcfg-eth0.orig +#grep -v HWADDR /etc/sysconfig/network-scripts/ifcfg-eth0.orig > /etc/sysconfig/network-scripts/ifcfg-eth0 + +%preun +if [ "$1" = 0 ] ; then + # no more packages left + chkconfig qubes_core off + mv /var/lib/qubes/fstab.orig /etc/fstab + mv /var/lib/qubes/removed-udev-scripts/* /etc/udev/rules.d/ + mv /var/lib/qubes/serial.orig /etc/init/serial.conf +fi + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root,-) +/etc/fstab +/etc/init.d/qubes_core +/etc/sysconfig/iptables +/var/lib/qubes +/etc/yum.repos.d/qubes.repo +/sbin/qubes_serial_login diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index 9256125b..e770b66c 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -36,8 +36,9 @@ Group: Qubes Vendor: Invisible Things Lab License: GPL URL: http://www.qubes-os.org +BuildRequires: xen-devel Requires: python, xen-runtime, pciutils, python-inotify, python-daemon, kernel-qubes-dom0 -Conflicts: qubes-gui-dom0 < 1.1.13 +Conflicts: qubes-gui-dom0 < 1.1.13 Requires: NetworkManager >= 0.8.1-1 %define _builddir %(pwd)/dom0 @@ -65,6 +66,8 @@ cp pendrive_swapper/qfilexchgd $RPM_BUILD_ROOT/usr/bin mkdir -p $RPM_BUILD_ROOT/etc/xen/scripts cp restore/block.qubes $RPM_BUILD_ROOT/etc/xen/scripts cp ../common/vif-route-qubes $RPM_BUILD_ROOT/etc/xen/scripts +cp ../common/block-snapshot $RPM_BUILD_ROOT/etc/xen/scripts +ln -s block-snapshot $RPM_BUILD_ROOT/etc/xen/scripts/block-origin mkdir -p $RPM_BUILD_ROOT%{python_sitearch}/qubes cp qvm-core/qubes.py $RPM_BUILD_ROOT%{python_sitearch}/qubes @@ -94,6 +97,7 @@ cp restore/qubes_prepare_saved_domain.sh $RPM_BUILD_ROOT/usr/lib/qubes mkdir -p $RPM_BUILD_ROOT/var/lib/qubes mkdir -p $RPM_BUILD_ROOT/var/lib/qubes/vm-templates mkdir -p $RPM_BUILD_ROOT/var/lib/qubes/appvms +mkdir -p $RPM_BUILD_ROOT/var/lib/qubes/servicevms mkdir -p $RPM_BUILD_ROOT/var/lib/qubes/backup mkdir -p $RPM_BUILD_ROOT/var/lib/qubes/dvmdata @@ -124,10 +128,16 @@ mkdir -p $RPM_BUILD_ROOT/var/run/qubes %post +# Create NetworkManager configuration if we do not have it +if ! [ -e /etc/NetworkManager/NetworkManager.conf ]; then +echo '[main]' > /etc/NetworkManager/NetworkManager.conf +echo 'plugins = keyfile' >> /etc/NetworkManager/NetworkManager.conf +echo '[keyfile]' >> /etc/NetworkManager/NetworkManager.conf +fi /usr/lib/qubes/qubes_fix_nm_conf.sh if [ -e /etc/yum.repos.d/qubes-r1-dom0.repo ]; then -# we want the user to use the repo that comes with qubes-code-dom0 packages instead +# we want the user to use the repo that comes with qubes-core-dom0 packages instead rm -f /etc/yum.repos.d/qubes-r1-dom0.repo fi @@ -136,15 +146,16 @@ fi #exit 0 #fi -# TODO: This is only temporary, until we will have our own installer -for f in /etc/init.d/* -do - srv=`basename $f` - [ $srv = 'functions' ] && continue - [ $srv = 'killall' ] && continue - [ $srv = 'halt' ] && continue - chkconfig $srv off -done +## TODO: This is only temporary, until we will have our own installer +#for f in /etc/init.d/* +#do +# srv=`basename $f` +# [ $srv = 'functions' ] && continue +# [ $srv = 'killall' ] && continue +# [ $srv = 'halt' ] && continue +# [ $srv = 'single' ] && continue +# chkconfig $srv off +#done chkconfig iptables on chkconfig NetworkManager on @@ -165,6 +176,20 @@ chkconfig qubes_core on || echo "WARNING: Cannot enable service qubes_core!" chkconfig qubes_netvm on || echo "WARNING: Cannot enable service qubes_netvm!" chkconfig qubes_setupdvm on || echo "WARNING: Cannot enable service qubes_setupdvm!" +HAD_SYSCONFIG_NETWORK=yes +if ! [ -e /etc/sysconfig/network ]; then + HAD_SYSCONFIG_NETWORK=no + # supplant empty one so NetworkManager init script does not complain + touch /etc/sysconfig/network +fi + +# Load evtchn module - xenstored needs it +modprobe evtchn + +# Now launch xend - we will need it for subsequent steps +service xenstored start +service xend start + if ! [ -e /var/lib/qubes/qubes.xml ]; then # echo "Initializing Qubes DB..." umask 007; sg qubes -c qvm-init-storage @@ -173,13 +198,16 @@ for i in /usr/share/qubes/icons/*.png ; do xdg-icon-resource install --novendor --size 48 $i done -/etc/init.d/qubes_core start +service qubes_core start NETVM=$(qvm-get-default-netvm) if [ "X"$NETVM = "X""dom0" ] ; then - /etc/init.d/qubes_netvm start + service qubes_netvm start fi +if [ "x"$HAD_SYSCONFIG_NETWORK = "xno" ]; then + rm -f /etc/sysconfig/network +fi %clean rm -rf $RPM_BUILD_ROOT @@ -258,6 +286,7 @@ fi %attr(770,root,qubes) %dir /var/lib/qubes %attr(770,root,qubes) %dir /var/lib/qubes/vm-templates %attr(770,root,qubes) %dir /var/lib/qubes/appvms +%attr(770,root,qubes) %dir /var/lib/qubes/servicevms %attr(770,root,qubes) %dir /var/lib/qubes/backup %attr(770,root,qubes) %dir /var/lib/qubes/dvmdata %dir /usr/share/qubes/icons/*.png @@ -271,10 +300,11 @@ fi /usr/lib64/pm-utils/sleep.d/01qubes-swap-pci-devs /usr/lib64/pm-utils/sleep.d/02qubes-pause-vms /usr/bin/xenstore-watch -/usr/bin/qvm-create-default-dvm /usr/lib/qubes/qubes_restore /usr/lib/qubes/qubes_prepare_saved_domain.sh /etc/xen/scripts/block.qubes +/etc/xen/scripts/block-snapshot +/etc/xen/scripts/block-origin /etc/xen/scripts/vif-route-qubes %attr(4750,root,qubes) /usr/lib/qubes/xenfreepages %attr(2770,root,qubes) %dir /var/log/qubes diff --git a/rpm_spec/core-netvm.spec b/rpm_spec/core-netvm.spec index 83d88c29..346c548e 100644 --- a/rpm_spec/core-netvm.spec +++ b/rpm_spec/core-netvm.spec @@ -34,6 +34,7 @@ URL: http://www.qubes-os.org Requires: /usr/bin/xenstore-read Requires: fedora-release = 13 Requires: NetworkManager >= 0.8.1-1 +Requires: qubes-core-commonvm Provides: qubes-core-vm %define _builddir %(pwd)/netvm @@ -43,22 +44,13 @@ The Qubes core files for installation inside a Qubes NetVM. %pre -mkdir -p $RPM_BUILD_ROOT/var/lib/qubes -if [ -e $RPM_BUILD_ROOT/etc/fstab ] ; then -mv $RPM_BUILD_ROOT/etc/fstab $RPM_BUILD_ROOT/var/lib/qubes/fstab.orig -fi - - %build %install -mkdir -p $RPM_BUILD_ROOT/etc/sysconfig -cp ../common/iptables $RPM_BUILD_ROOT/etc/sysconfig mkdir -p $RPM_BUILD_ROOT/etc -cp fstab $RPM_BUILD_ROOT/etc/fstab mkdir -p $RPM_BUILD_ROOT/etc/init.d -cp qubes_core $RPM_BUILD_ROOT/etc/init.d/ +cp qubes_core_netvm $RPM_BUILD_ROOT/etc/init.d/ mkdir -p $RPM_BUILD_ROOT/var/lib/qubes mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes cp ../common/qubes_setup_dnat_to_ns $RPM_BUILD_ROOT/usr/lib/qubes @@ -67,103 +59,22 @@ mkdir -p $RPM_BUILD_ROOT/etc/dhclient.d ln -s /usr/lib/qubes/qubes_setup_dnat_to_ns $RPM_BUILD_ROOT/etc/dhclient.d/qubes_setup_dnat_to_ns.sh mkdir -p $RPM_BUILD_ROOT/etc/NetworkManager/dispatcher.d/ cp ../common/qubes_nmhook $RPM_BUILD_ROOT/etc/NetworkManager/dispatcher.d/ -mkdir -p $RPM_BUILD_ROOT/etc/yum.repos.d -cp ../netvm/qubes.repo $RPM_BUILD_ROOT/etc/yum.repos.d -mkdir -p $RPM_BUILD_ROOT/sbin -cp ../common/qubes_serial_login $RPM_BUILD_ROOT/sbin -mkdir -p $RPM_BUILD_ROOT/etc -cp ../common/serial.conf $RPM_BUILD_ROOT/var/lib/qubes/ +cp ../netvm/30-qubes_external_ip $RPM_BUILD_ROOT/etc/NetworkManager/dispatcher.d/ mkdir -p $RPM_BUILD_ROOT/var/run/qubes mkdir -p $RPM_BUILD_ROOT/etc/xen/scripts cp ../common/vif-route-qubes $RPM_BUILD_ROOT/etc/xen/scripts -%triggerin -- initscripts -cp /var/lib/qubes/serial.conf /etc/init/serial.conf - %post /usr/lib/qubes/qubes_fix_nm_conf.sh -if [ "$1" != 1 ] ; then -# do this whole %post thing only when updating for the first time... -exit 0 -fi - -sed 's/^net.ipv4.ip_forward.*/net.ipv4.ip_forward = 1/' -i /etc/sysctl.conf -usermod -L root -if ! [ -f /var/lib/qubes/serial.orig ] ; then - cp /etc/init/serial.conf /var/lib/qubes/serial.orig -fi - -#echo "--> Disabling SELinux..." -sed -e s/^SELINUX=.*$/SELINUX=disabled/ /etc/selinux/config.processed -mv /etc/selinux/config.processed /etc/selinux/config -setenforce 0 2>/dev/null - -#echo "--> Turning off unnecessary services..." -# FIXME: perhaps there is more elegant way to do this? -for f in /etc/init.d/* -do - srv=`basename $f` - [ $srv = 'functions' ] && continue - [ $srv = 'killall' ] && continue - [ $srv = 'halt' ] && continue - [ $srv = 'single' ] && continue - chkconfig $srv off -done - -#echo "--> Enabling essential services..." -chkconfig iptables on -chkconfig rsyslog on -chkconfig haldaemon on -chkconfig messagebus on -chkconfig NetworkManager on -chkconfig --add qubes_core || echo "WARNING: Cannot add service qubes_core!" -chkconfig qubes_core on || echo "WARNING: Cannot enable service qubes_core!" - - -# TODO: make this not display the silly message about security context... -sed -i s/^id:.:initdefault:/id:3:initdefault:/ /etc/inittab - -# Remove most of the udev scripts to speed up the VM boot time -# Just leave the xen* scripts, that are needed if this VM was -# ever used as a net backend (e.g. as a VPN domain in the future) -#echo "--> Removing unnecessary udev scripts..." -mkdir -p /var/lib/qubes/removed-udev-scripts -for f in /etc/udev/rules.d/* -do - if [ $(basename $f) == "xen-backend.rules" ] ; then - continue - fi - - if [ $(basename $f) == "xend.rules" ] ; then - continue - fi - - if [ $(basename $f) == "qubes.rules" ] ; then - continue - fi - - if [ $(basename $f) == "90-hal.rules" ] ; then - continue - fi - - - mv $f /var/lib/qubes/removed-udev-scripts/ -done - -#rm -f /etc/mtab -#echo "--> Removing HWADDR setting from /etc/sysconfig/network-scripts/ifcfg-eth0" -#mv /etc/sysconfig/network-scripts/ifcfg-eth0 /etc/sysconfig/network-scripts/ifcfg-eth0.orig -#grep -v HWADDR /etc/sysconfig/network-scripts/ifcfg-eth0.orig > /etc/sysconfig/network-scripts/ifcfg-eth0 +chkconfig --add qubes_core_netvm || echo "WARNING: Cannot add service qubes_core!" +chkconfig qubes_core_netvm on || echo "WARNING: Cannot enable service qubes_core!" %preun if [ "$1" = 0 ] ; then # no more packages left - chkconfig qubes_core off - mv /var/lib/qubes/fstab.orig /etc/fstab - mv /var/lib/qubes/removed-udev-scripts/* /etc/udev/rules.d/ - mv /var/lib/qubes/serial.orig /etc/init/serial.conf + chkconfig qubes_core_netvm off fi %clean @@ -171,15 +82,11 @@ rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,-) -/etc/fstab -/etc/sysconfig/iptables -/etc/init.d/qubes_core -/var/lib/qubes +/etc/init.d/qubes_core_netvm /usr/lib/qubes/qubes_setup_dnat_to_ns /usr/lib/qubes/qubes_fix_nm_conf.sh /etc/dhclient.d/qubes_setup_dnat_to_ns.sh /etc/NetworkManager/dispatcher.d/qubes_nmhook -/etc/yum.repos.d/qubes.repo -/sbin/qubes_serial_login +/etc/NetworkManager/dispatcher.d/30-qubes_external_ip /etc/xen/scripts/vif-route-qubes %dir /var/run/qubes diff --git a/rpm_spec/core-proxyvm.spec b/rpm_spec/core-proxyvm.spec new file mode 100644 index 00000000..52bc17f8 --- /dev/null +++ b/rpm_spec/core-proxyvm.spec @@ -0,0 +1,80 @@ +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2010 Joanna Rutkowska +# 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. +# +# + +%{!?version: %define version %(cat version_vm)} + +Name: qubes-core-proxyvm +Version: %{version} +Release: 1 +Summary: The Qubes core files for NetVM + +Group: Qubes +Vendor: Invisible Things Lab +License: GPL +URL: http://www.qubes-os.org +Requires: /usr/bin/xenstore-read +Requires: /sbin/ethtool +Requires: fedora-release = 13 +Requires: qubes-core-netvm + +%define _builddir %(pwd)/proxyvm + +%description +The Qubes core files for installation inside a Qubes ProxyVM in addition to NetVM scripts. + +%pre + +%build + +%install + +mkdir -p $RPM_BUILD_ROOT/etc/init.d +cp init.d/qubes_firewall $RPM_BUILD_ROOT/etc/init.d/ +cp init.d/qubes_netwatcher $RPM_BUILD_ROOT/etc/init.d/ +mkdir -p $RPM_BUILD_ROOT/usr/sbin +cp bin/qubes_firewall $RPM_BUILD_ROOT/usr/sbin/ +cp bin/qubes_netwatcher $RPM_BUILD_ROOT/usr/sbin/ + +%post + +chkconfig --add qubes_firewall || echo "WARNING: Cannot add service qubes_core!" +chkconfig qubes_firewall on || echo "WARNING: Cannot enable service qubes_core!" + +chkconfig --add qubes_netwatcher || echo "WARNING: Cannot add service qubes_core!" +chkconfig qubes_netwatcher on || echo "WARNING: Cannot enable service qubes_core!" + +%preun +if [ "$1" = 0 ] ; then + # no more packages left + chkconfig qubes_firewall off + chkconfig qubes_netwatcher off +fi + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root,-) +/etc/init.d/qubes_firewall +/etc/init.d/qubes_netwatcher +/usr/sbin/qubes_firewall +/usr/sbin/qubes_netwatcher