Browse Source

Initial public commit.

(c) 2010 Invisible Things Lab

Authors:
=========
Joanna Rutkowska <joanna@invisiblethingslab.com>
Rafal Wojtczuk  <rafal@invisiblethingslab.com>
Joanna Rutkowska 14 years ago
commit
a17989470a
65 changed files with 4889 additions and 0 deletions
  1. 2 0
      .gitignore
  2. 18 0
      Makefile
  3. 16 0
      TODO
  4. 9 0
      appvm/Makefile
  5. 17 0
      appvm/fstab
  6. 12 0
      appvm/iptables
  7. 1 0
      appvm/qubes.rules
  8. 87 0
      appvm/qubes_add_pendrive_script.c
  9. 87 0
      appvm/qubes_core
  10. 71 0
      appvm/qubes_penctl.c
  11. 40 0
      appvm/qvm-copy-to-vm
  12. 42 0
      appvm/qvm-copy-to-vm.kde
  13. 10 0
      appvm/qvm-copy.desktop
  14. 14 0
      dom0/aux-tools/check_and_remove_appmenu.sh
  15. 13 0
      dom0/aux-tools/convert_apptemplate2vm.sh
  16. 11 0
      dom0/aux-tools/convert_dirtemplate2vm.sh
  17. 41 0
      dom0/aux-tools/create_apps_for_appvm.sh
  18. 82 0
      dom0/aux-tools/patch_appvm_initramfs.sh
  19. 12 0
      dom0/aux-tools/remove_appvm_appmenus.sh
  20. 6 0
      dom0/aux-tools/remove_dom0_appmenus.sh
  21. 74 0
      dom0/aux-tools/unbind_all_network_devices
  22. 35 0
      dom0/aux-tools/unbind_pci_device.sh
  23. 85 0
      dom0/clipboard_notifier/qclipd
  24. BIN
      dom0/icons/black.png
  25. BIN
      dom0/icons/blue.png
  26. 10 0
      dom0/icons/credits-crystal-icons
  27. 1 0
      dom0/icons/credits-padlock-icons
  28. BIN
      dom0/icons/gray.png
  29. BIN
      dom0/icons/green.png
  30. BIN
      dom0/icons/netvm.png
  31. BIN
      dom0/icons/orange.png
  32. BIN
      dom0/icons/purple.png
  33. BIN
      dom0/icons/red.png
  34. BIN
      dom0/icons/template.png
  35. BIN
      dom0/icons/yellow.png
  36. 63 0
      dom0/init.d/qubes_core
  37. 102 0
      dom0/init.d/qubes_netvm
  38. 223 0
      dom0/pendrive_swapper/qfilexchgd
  39. 74 0
      dom0/pendrive_swapper/qubes_pencmd
  40. 0 0
      dom0/qvm-core/__init__.py
  41. 1515 0
      dom0/qvm-core/qubes.py
  42. 82 0
      dom0/qvm-tools/qvm-add-appvm
  43. 119 0
      dom0/qvm-tools/qvm-add-netvm
  44. 78 0
      dom0/qvm-tools/qvm-add-template
  45. 75 0
      dom0/qvm-tools/qvm-clone-template
  46. 101 0
      dom0/qvm-tools/qvm-create
  47. 39 0
      dom0/qvm-tools/qvm-get-default-netvm
  48. 32 0
      dom0/qvm-tools/qvm-init-storage
  49. 54 0
      dom0/qvm-tools/qvm-kill
  50. 191 0
      dom0/qvm-tools/qvm-ls
  51. 219 0
      dom0/qvm-tools/qvm-prefs
  52. 101 0
      dom0/qvm-tools/qvm-remove
  53. 227 0
      dom0/qvm-tools/qvm-run
  54. 50 0
      dom0/qvm-tools/qvm-set-default-netvm
  55. 50 0
      dom0/qvm-tools/qvm-set-default-template
  56. 72 0
      dom0/qvm-tools/qvm-start
  57. 14 0
      netvm/fstab
  58. 17 0
      netvm/iptables
  59. 55 0
      netvm/qubes_core
  60. 162 0
      rpm_spec/core-appvm.spec
  61. 156 0
      rpm_spec/core-dom0.spec
  62. 142 0
      rpm_spec/core-netvm.spec
  63. 78 0
      rpm_spec/dom0-cleanup.spec
  64. 1 0
      version_dom0
  65. 1 0
      version_vm

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+rpm/
+*.pyc

+ 18 - 0
Makefile

@@ -0,0 +1,18 @@
+RPMS_DIR=rpm/
+help:
+	@echo "make rpms        -- generate binary rpm packages"
+	@echo "make update_repo -- copy newly generated rpms to qubes yum repo"
+
+rpms:	
+	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-dom0.spec
+	rpmbuild --define "_rpmdir $(RPMS_DIR)" -bb rpm_spec/dom0-cleanup.spec
+	rpm --addsign $(RPMS_DIR)/x86_64/*.rpm
+
+update_repo:
+	ln -f $(RPMS_DIR)/x86_64/*.rpm ../yum/rpm/
+	(if [ -d $(RPMS_DIR)/i686 ] ; then ln -f $(RPMS_DIR)/i686/*.rpm ../yum/rpm/; fi)
+
+clean:
+	(cd appvm && make clean)

+ 16 - 0
TODO

@@ -0,0 +1,16 @@
+* file exchange -- handle correctly files that have spaces in name
+-- qvm-copy-to-vm* do not copy files in the top directory has spaces in the name
+
+* qvm-update-appmenus
+-- let the user install appmenus for (potential) new apps after template update
+-- BUT: potential problem of Dom0 needing to mount the template's fs
+-- but: perhaps we should trust the template and its update process?
+
+* netvm: prevent inter-VM networking
+-- do not allow one AppVM to send any packets to other AppVMs that use the same netvm
+
+* qvm-prefs: allow to grow/shrink AppVM's private.img?
+
+* Dom0 udev scripts: do not load network drivers at all!
+
+* Dom0: detect when running without VT-d enabled and display a warning

+ 9 - 0
appvm/Makefile

@@ -0,0 +1,9 @@
+CC=gcc
+CFLAGS=-Wall
+all:	qubes_penctl qubes_add_pendrive_script
+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
+clean:
+	rm -f qubes_penctl qubes_add_pendrive_script *.o *~

+ 17 - 0
appvm/fstab

@@ -0,0 +1,17 @@
+
+#
+# /etc/fstab
+# Created by anaconda on Thu Dec  3 11:26:49 2009
+#
+# Accessible filesystems, by reference, are maintained under '/dev/disk'
+# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
+#
+/dev/mapper/dmroot /                       ext4 defaults,noatime        1 1
+/dev/mapper/dmswap swap                    swap    defaults        0 0
+/dev/xvdb		/rw			ext4	defaults	0 0
+tmpfs                   /dev/shm                tmpfs   defaults        0 0
+devpts                  /dev/pts                devpts  gid=5,mode=620  0 0
+sysfs                   /sys                    sysfs   defaults        0 0
+proc                    /proc                   proc    defaults        0 0
+/dev/xvdg	/mnt/outgoing	vfat noauto,user,rw 0 0
+/dev/xvdh	/mnt/incoming	vfat noauto,user,rw 0 0

+ 12 - 0
appvm/iptables

@@ -0,0 +1,12 @@
+# Firewall configuration written by system-config-firewall
+# Manual customization of this file is not recommended.
+*filter
+:INPUT ACCEPT [0:0]
+:FORWARD ACCEPT [0:0]
+:OUTPUT ACCEPT [0:0]
+-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
+-A INPUT -p icmp -j ACCEPT
+-A INPUT -i lo -j ACCEPT
+-A INPUT -j REJECT --reject-with icmp-host-prohibited
+-A FORWARD -j REJECT --reject-with icmp-host-prohibited
+COMMIT

+ 1 - 0
appvm/qubes.rules

@@ -0,0 +1 @@
+SUBSYSTEM=="block", KERNEL=="xvdh", ACTION=="add", RUN+="/usr/bin/qubes_add_pendrive_script"

+ 87 - 0
appvm/qubes_add_pendrive_script.c

@@ -0,0 +1,87 @@
+/*
+ * The Qubes OS Project, http://www.qubes-os.org
+ *
+ * Copyright (C) 2010  Rafal Wojtczuk  <rafal@invisiblethingslab.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/inotify.h>
+#include <fcntl.h>
+#include <stdlib.h>
+int parse_events(char *buf, int len)
+{
+	int i = 0;
+	while (i < len) {
+		struct inotify_event *ev = (struct inotify_event *)(buf + i);
+		if ((ev->mask & IN_UNMOUNT) || (ev->mask & IN_IGNORED))
+			return 1;
+		i += sizeof(struct inotify_event) + ev->len;
+	}
+	return 0;
+}
+
+#define BUFLEN 1024
+void wait_for_umount(char *name)
+{
+	char buf[BUFLEN];
+	int fd = inotify_init();
+	int len;
+	int ret = inotify_add_watch(fd, name, IN_ATTRIB);
+	if (ret < 0) {
+		perror("inotify_add_watch");
+		return;
+	}
+	for (;;) {
+		len = read(fd, buf, BUFLEN - 1);
+		if (len <= 0) {
+			perror("read inotify");
+			return;
+		}
+		if (parse_events(buf, len))
+			return;
+	}
+}
+
+void background()
+{
+	int i, fd;
+	for (i = 0; i < 256; i++)
+		close(i);
+	fd = open("/dev/null", O_RDWR);
+	for (i = 0; i <= 2; i++)
+		dup2(fd, i);
+	switch (fork()) {
+	case -1:
+		exit(1);
+	case 0: break;
+	default:
+		exit(0);
+	}
+}
+
+
+#define MOUNTDIR "/mnt/incoming"
+int main()
+{
+	background();
+	if (!system("su - user -c 'mount " MOUNTDIR "'"))
+		wait_for_umount(MOUNTDIR "/.");
+	system("xenstore-write device/qpen umount");
+	return 0;
+}

+ 87 - 0
appvm/qubes_core

@@ -0,0 +1,87 @@
+#!/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:"
+
+	if ! [ -d /rw/home ] ; then
+	    echo
+	    echo "--> Virgin boot of the VM: Linking /home to /rw/home"
+	    mv /home /home.orig
+	    mkdir -p /rw/config
+	    mkdir -p /rw/home
+	    ln -s /rw/home/ /home
+#	    chcon --reference /home.orig /rw/home
+	    cp -a /home.orig/user /home
+	    touch /rw/config/rc.local
+	    rm -fr /home.orig
+	    touch /var/lib/qubes/first_boot_completed
+    else
+	    mv /home /home.tmpl
+	    ln -s /rw/home/ /home
+	fi
+
+
+	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
+
+	vmtype=$(/usr/bin/xenstore-read qubes_vm_type)
+
+	if [ $vmtype == 'NetVM' ] ; then
+	    # Setup gateway for all the VMs this netVM is serviceing...
+	    brctl addbr br0
+	    gateway=$(/usr/bin/xenstore-read qubes_netvm_gateway)
+	    netmask=$(/usr/bin/xenstore-read qubes_netvm_netmask)
+	    network=$(/usr/bin/xenstore-read qubes_netvm_network)
+	    ifconfig br0 $gateway netmask $netmask up
+	    echo "1" > /proc/sys/net/ipv4/ip_forward
+	    dnsmasq
+	    iptables -t nat -A POSTROUTING -s $network/$netmask -j MASQUERADE
+	else
+	    ip=$(/usr/bin/xenstore-read qubes_ip)
+	    netmask=$(/usr/bin/xenstore-read qubes_netmask)
+	    gateway=$(/usr/bin/xenstore-read qubes_gateway)
+        if [ x$ip != x ]; then
+            /sbin/ifconfig eth0 $ip netmask $netmask up
+            /sbin/route add default gw $gateway
+            echo "nameserver $gateway" > /etc/resolv.conf
+        fi
+	fi
+
+	
+	[ -x /rw/config/rc.local ] && /rw/config/rc.local
+	success
+	echo ""
+	return 0
+}
+
+stop()
+{
+	return 0
+}
+
+case "$1" in
+  start)
+	start
+	;;
+  stop)
+	stop
+	;;
+  *)
+	echo $"Usage: $0 {start|stop}"
+	exit 3
+	;;
+esac
+
+exit $RETVAL

+ 71 - 0
appvm/qubes_penctl.c

@@ -0,0 +1,71 @@
+/*
+ * The Qubes OS Project, http://www.qubes-os.org
+ *
+ * Copyright (C) 2010  Rafal Wojtczuk  <rafal@invisiblethingslab.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <xs.h>
+int check_name(unsigned char *s)
+{
+	int c;
+	for (; *s; s++) {
+		c = *s;
+		if (c >= 'a' && c <= 'z')
+			continue;
+		if (c >= 'A' && c <= 'Z')
+			continue;
+		if (c == '_' || c == '-')
+			continue;
+		return 0;
+	}
+	return 1;
+}
+
+int main(int argc, char **argv)
+{
+	char buf[256] = "new";
+	struct xs_handle *xs;
+	xs = xs_domain_open();
+	setuid(getuid());
+	if (!xs) {
+		perror("xs_domain_open");
+		exit(1);
+	}
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s new\n"
+			"%s send vmname\n", argv[0], argv[0]);
+		exit(1);
+	}
+	if (argc > 2) {
+		if (!check_name((unsigned char*)argv[2])) {
+			fprintf(stderr, "invalid vmname %s\n", argv[2]);
+			exit(1);
+		}
+		snprintf(buf, sizeof(buf), "send %s", argv[2]);
+	}
+	if (!xs_write(xs, 0, "device/qpen", buf, strlen(buf))) {
+		perror("xs_write");
+		exit(1);
+	}
+	xs_daemon_close(xs);
+	return 0;
+}

+ 40 - 0
appvm/qvm-copy-to-vm

@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Rafal Wojtczuk  <rafal@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+if [ $# -lt 2 ] ; then
+	echo usage: $0 'vmname file [file]*'
+	exit 1
+fi
+qubes_penctl new || exit 1
+echo -n Waiting for the Qubes virtual pendrive
+while ! [ -e /dev/xvdg ] ; do
+	echo -n .
+	sleep 1
+done
+echo " received"
+mount /mnt/outgoing 
+VMNAME=$1
+shift
+cp -v -a $* /mnt/outgoing
+#sometimes Dolphin lags a bit
+umount /mnt/outgoing || (sleep 1; umount /mnt/outgoing) || exit 1
+qubes_penctl send $VMNAME || exit 1

+ 42 - 0
appvm/qvm-copy-to-vm.kde

@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Rafal Wojtczuk  <rafal@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+FILES="$*"
+VM=$(kdialog -inputbox "Enter the VM name to send $FILE to:")
+if [ X$VM = X ] ; then exit 0 ; fi
+DEST=/mnt/outgoing
+SIZE=$(du -c $FILES | tail -1 | cut -f 1)
+REF=$(kdialog --progressbar "Copy progress")
+qdbus $REF org.freedesktop.DBus.Properties.Set "" maximum $SIZE
+FLAG=$(mktemp)
+(qvm-copy-to-vm $VM $FILES ; rm $FLAG)  &
+while ! grep -q $DEST /proc/mounts && [ -f $FLAG ] ; do
+	sleep 0.1
+done
+while grep -q $DEST /proc/mounts ; do
+	CURRSIZE=$(du -c $DEST | tail -1 | cut -f 1)
+	qdbus $REF  org.freedesktop.DBus.Properties.Set "" value $CURRSIZE
+	sleep 1
+done
+qdbus $REF close
+
+ 

+ 10 - 0
appvm/qvm-copy.desktop

@@ -0,0 +1,10 @@
+[Desktop Entry]
+Actions=QvmCopy;
+Type=Service
+X-KDE-ServiceTypes=KonqPopupMenu/Plugin,inode/directory,all/allfiles
+
+[Desktop Action QvmCopy]
+Exec=/usr/bin/qvm-copy-to-vm.kde %U
+Icon=kget
+Name=Send To VM
+

+ 14 - 0
dom0/aux-tools/check_and_remove_appmenu.sh

@@ -0,0 +1,14 @@
+#!/bin/sh
+if grep -q X-Qubes-VmName $1 ; then
+	exit 0
+fi
+
+if grep -q "Categories=.*\(System\|Settings\)" $1 ; then
+	#echo "Leaving file: $1"
+	exit 0
+fi
+BACKUP_DIR="/var/lib/qubes/backup/removed-apps/"
+mkdir -p $BACKUP_DIR
+#echo "Moving file: $1 to $BACKUP_DIR
+mv $1 $BACKUP_DIR
+

+ 13 - 0
dom0/aux-tools/convert_apptemplate2vm.sh

@@ -0,0 +1,13 @@
+#!/bin/sh
+SRC=$1
+DSTDIR=$2
+VMNAME=$3
+VMDIR=$4
+
+DST=$DSTDIR/$VMNAME-$(basename $SRC)
+
+sed -e "s/%VMNAME%/$VMNAME/" \
+    -e "s %VMDIR% $VMDIR " \
+        <$SRC >$DST
+
+

+ 11 - 0
dom0/aux-tools/convert_dirtemplate2vm.sh

@@ -0,0 +1,11 @@
+#!/bin/sh
+SRC=$1
+DST=$2
+VMNAME=$3
+VMDIR=$4
+
+sed -e "s/%VMNAME%/$VMNAME/" \
+    -e "s %VMDIR% $VMDIR " \
+        <$SRC >$DST
+
+

+ 41 - 0
dom0/aux-tools/create_apps_for_appvm.sh

@@ -0,0 +1,41 @@
+#!/bin/sh
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+SRCDIR=$1
+VMNAME=$2
+VMDIR=/var/lib/qubes/appvms/$VMNAME
+APPSDIR=$VMDIR/apps
+
+if [ $# != 2 ]; then
+    echo "usage: $0 <apps_templates_dir> <vmname>"
+    exit
+fi
+mkdir -p $APPSDIR
+
+echo "--> Converting Appmenu Templates..."
+find $SRCDIR -name "*.desktop" -exec /usr/lib/qubes/convert_apptemplate2vm.sh {} $APPSDIR $VMNAME $VMDIR \;
+
+/usr/lib/qubes/convert_dirtemplate2vm.sh $SRCDIR/qubes-vm.directory.template $APPSDIR/$VMNAME-vm.directory $VMNAME $VMDIR
+
+echo "--> Adding Apps to the Menu..."
+xdg-desktop-menu install $APPSDIR/*.directory $APPSDIR/*.desktop
+

+ 82 - 0
dom0/aux-tools/patch_appvm_initramfs.sh

@@ -0,0 +1,82 @@
+#!/bin/sh
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+#
+# This script can be used to patch the initramfs of the Qubes AppVM
+# It inserts an additional script that is responsible for setting up
+# COW-based root fs and VM private fs
+# 
+
+INITRAMFS=$1
+INITRAMFS_QUBES=$2
+QUBES_COW_SETUP_FILE=$3
+
+
+TMP_DIR=`mktemp -d /tmp/qubes-initramfs-patching-XXXXXXX`
+
+if [ $# != 3 ] ; then
+    echo "usage: $0 <original initramfs to patch> <patched initramfs file> <qubes_cow_setup_file>"
+    exit 0
+fi
+
+if [ x$INITRAMFS = x ] ; then
+    echo "INITRAMFS missing!"
+    exit 1
+fi
+
+if [ x$INITRAMFS_QUBES = x ] ; then
+    echo "INITRAMFS_QUBES missing!"
+    exit 1
+fi
+
+if [ x$QUBES_COW_SETUP_FILE = x ] ; then
+    echo "$QUBES_COW_SETUP_FILE missing!"
+    exit 1
+fi
+
+
+ID=$(id -ur)
+
+if [ $ID != 0 ] ; then
+    echo "This script should be run as root user. Apparently the initramfs files must have root.root owener..."
+    exit 1
+fi
+
+mkdir $TMP_DIR/initramfs.qubes || exit 1
+
+cp $INITRAMFS $TMP_DIR/initramfs.cpio.gz
+
+pushd $TMP_DIR/initramfs.qubes
+
+gunzip < ../initramfs.cpio.gz | cpio -i --quiet || exit 1
+
+cp $QUBES_COW_SETUP_FILE pre-trigger/90_qubes_cow_setup.sh || exit 1
+
+find ./ | cpio -H newc -o --quiet > $TMP_DIR/initramfs.qubes.cpio || exit 1
+
+popd
+
+gzip $TMP_DIR/initramfs.qubes.cpio || exit 1
+
+mv $TMP_DIR/initramfs.qubes.cpio.gz $INITRAMFS_QUBES || exit 1
+
+rm -fr $TMP_DIR || exit 1

+ 12 - 0
dom0/aux-tools/remove_appvm_appmenus.sh

@@ -0,0 +1,12 @@
+#!/bin/sh
+VMNAME=$1
+VMDIR=/var/lib/qubes/appvms/$VMNAME
+APPSDIR=$VMDIR/apps
+
+if [ $# != 1 ]; then
+    echo "usage: $0 <vmname>"
+    exit
+fi
+
+xdg-desktop-menu uninstall $APPSDIR/*.directory $APPSDIR/*.desktop
+

+ 6 - 0
dom0/aux-tools/remove_dom0_appmenus.sh

@@ -0,0 +1,6 @@
+#!/bin/sh
+
+echo "--> Removing unnecessary Dom0 Appmenus..."
+find /usr/share/applications -name *.desktop -exec /usr/lib/qubes/check_and_remove_appmenu.sh {} \; 
+
+xdg-desktop-menu forceupdate

+ 74 - 0
dom0/aux-tools/unbind_all_network_devices

@@ -0,0 +1,74 @@
+#!/usr/bin/python2.6
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+from optparse import OptionParser
+import subprocess
+import shutil
+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():
+    usage = "usage: %prog [options] <netvm-name>"
+    parser = OptionParser (usage)
+    parser.add_option ("-v", "--verbose", dest="verbose", action="store_true", default=False)
+    (options, args) = parser.parse_args ()
+
+    if options.verbose:
+        print "Loading Xen PCI Backend..."
+    retcode = subprocess.call (["/sbin/modprobe", "xen-pciback"])
+    if retcode != 0:
+        print "ERROR: Cannot load xen-pciback module!"
+        exit(1)
+
+    if options.verbose:
+        print "Unbinding the following net devices:"
+
+    net_devices = find_net_devices()
+
+    for dev in net_devices:
+        if options.verbose:
+            print "--> {0}".format(dev)
+        retcode = subprocess.call (["/usr/lib/qubes/unbind_pci_device.sh", dev])
+        if (retcode != 0):
+            print "WARNING: Could not unbind device {0}".format(dev)
+
+
+main()

+ 35 - 0
dom0/aux-tools/unbind_pci_device.sh

@@ -0,0 +1,35 @@
+#!/bin/sh
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+BDF=$1
+if [ x$BDF = x ] ; then
+    echo "usage: $0 <BDF>"
+    exit 0
+fi
+BDF=0000:$BDF
+#echo -n "Binding device $BDF to xen-pciback..."
+if [ -e /sys/bus/pci/devices/$BDF/driver/unbind ] ; then 
+    echo -n $BDF > /sys/bus/pci/devices/$BDF/driver/unbind || exit 1
+fi
+echo -n $BDF > /sys/bus/pci/drivers/pciback/new_slot || exit 1
+echo -n $BDF > /sys/bus/pci/drivers/pciback/bind || exit 1
+#echo ok

+ 85 - 0
dom0/clipboard_notifier/qclipd

@@ -0,0 +1,85 @@
+#!/usr/bin/python2.6
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+import os
+import daemon
+from pyinotify import WatchManager, Notifier, ThreadedNotifier, EventsCodes, ProcessEvent
+import dbus
+from qubes.qubes import QubesDaemonPidfile
+
+qubes_clipboard_info_file = "/var/run/qubes/qubes_clipboard.bin.source"
+
+def watch_qubes_clipboard():
+
+    def tray_notify(str, timeout = 3000):
+        notify_object.Notify("Qubes", 0, "dialog-information", "", str, [], [], timeout, dbus_interface="org.freedesktop.Notifications")
+
+    notify_object = dbus.SessionBus().get_object("org.freedesktop.Notifications", "/org/freedesktop/Notifications")
+    wm = WatchManager()
+    mask = EventsCodes.OP_FLAGS.get('IN_CLOSE_WRITE')
+
+    if not os.path.exists (qubes_clipboard_info_file):
+        file = open (qubes_clipboard_info_file, 'w')
+        file.close()
+
+
+    class ClipboardWatcher(ProcessEvent):
+        def process_IN_CLOSE_WRITE (self, event):
+            src_info_file = open (qubes_clipboard_info_file, 'r')
+            src_vmname = src_info_file.readline().strip('\n')
+            if src_vmname == "":
+                tray_notify ("Qubes Clipboard has been copied to the VM and wiped.\n"\
+                             "<small>Trigger a paste operation (e.g. Ctrl-v) to insert it into an application.</small>" )
+            else:
+                print src_vmname
+                tray_notify ("Qubes Clipboard fetched from VM: <b>'{0}'</b>\n"\
+                             "<small>Press Ctrl-Shift-v to copy this clipboard onto dest VM's clipboard.</small>".format(src_vmname))
+            src_info_file.close()
+
+
+    notifier = Notifier(wm, ClipboardWatcher())
+    wdd = wm.add_watch(qubes_clipboard_info_file, mask)
+
+    while True:
+        notifier.process_events()
+        if notifier.check_events():
+            notifier.read_events()
+
+
+def main():
+    
+    lock = QubesDaemonPidfile ("qclipd")
+    if lock.pidfile_exists():
+        if lock.pidfile_is_stale():
+            lock.remove_pidfile()
+            print "Removed stale pidfile (has the previous daemon instance crashed?)."
+        else:
+            exit (0)
+
+    context = daemon.DaemonContext(
+        working_directory = "/var/run/qubes",
+        pidfile = lock)
+
+    with context:
+        watch_qubes_clipboard()
+
+main()

BIN
dom0/icons/black.png


BIN
dom0/icons/blue.png


+ 10 - 0
dom0/icons/credits-crystal-icons

@@ -0,0 +1,10 @@
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+This copyright and license notice covers the images in this directory.
+************************************************************************
+
+TITLE:	Crystal Project Icons
+AUTHOR:	Everaldo Coelho
+SITE:	http://www.everaldo.com
+CONTACT: everaldo@everaldo.com
+
+Copyright (c)  2006-2007  Everaldo Coelho.

+ 1 - 0
dom0/icons/credits-padlock-icons

@@ -0,0 +1 @@
+Color padlock images downloaded from www.openclipart.org

BIN
dom0/icons/gray.png


BIN
dom0/icons/green.png


BIN
dom0/icons/netvm.png


BIN
dom0/icons/orange.png


BIN
dom0/icons/purple.png


BIN
dom0/icons/red.png


BIN
dom0/icons/template.png


BIN
dom0/icons/yellow.png


+ 63 - 0
dom0/init.d/qubes_core

@@ -0,0 +1,63 @@
+#!/bin/sh
+#
+# chkconfig: 2345 99 00
+# description: Executes Qubes core scripts at Dom0 boot
+#
+### BEGIN INIT INFO
+# Provides:          qubes-core
+# Required-Start:    xend
+# Default-Start:     3 4 5
+# Default-Stop:      0 1 2 6
+# Default-Enabled:   yes
+# Short-Description: Start/stop qubes-core services
+# Description:       Starts and stops the qubes-core serives
+### END INIT INFO
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+
+
+start()
+{
+    echo -n $"Executing Qubes Core scripts:"
+    chgrp qubes /etc/xen
+    chmod 710 /etc/xen
+    chgrp qubes /var/run/xend
+    chmod 710 /var/run/xend
+    chgrp qubes /var/run/xend/xen-api.sock /var/run/xend/xmlrpc.sock
+    chmod 660 /var/run/xend/xen-api.sock /var/run/xend/xmlrpc.sock
+    chgrp qubes /var/run/xenstored/*
+    chmod 660 /var/run/xenstored/*
+    xm sched-credit -d 0 -w 65535
+    cp /var/lib/qubes/qubes.xml /var/lib/qubes/backup/qubes-$(date +%F-%T).xml
+    touch /var/lock/subsys/qubes_core
+    success
+    echo
+
+}
+
+stop()
+{
+    echo -n $"Shutting down all Qubes VMs:"
+    NETVM=$(qvm-get-default-netvm)
+    qvm-run -q --shutdown --all --wait --exclude $NETVM
+    rm -f /var/lock/subsys/qubes_core
+    success
+    echo
+}
+
+case "$1" in
+  start)
+	start
+	;;
+  stop)
+	stop
+	;;
+  *)
+	echo $"Usage: $0 {start|stop}"
+	exit 3
+	;;
+esac
+
+exit $RETVAL

+ 102 - 0
dom0/init.d/qubes_netvm

@@ -0,0 +1,102 @@
+#!/bin/sh
+#
+# chkconfig: 2345 99 00
+# description: Starts/stops Qubes default netvm
+#
+### BEGIN INIT INFO
+# Provides:          qubes-networking
+# Required-Start:    qubes-core
+# Default-Start:     3 4 5
+# Default-Stop:      0 1 2 6
+# Default-Enabled:   yes
+# Short-Description: Start/stop qubes networking
+# Description:       Starts and stops the qubes networking
+### END INIT INFO
+
+
+#
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+NETVM=$(qvm-get-default-netvm)
+
+start()
+{
+    if [ x$NETVM = x ] ; then
+
+        echo WARNING: Qubes NetVM not configured!
+        echo -n $"Doing nothing:"
+
+    elif [ $NETVM = "dom0" ] ; then 
+
+        echo -n $"Setting up net backend in Dom0:"
+        /etc/init.d/NetworkManager start
+        brctl addbr br0 || exit 1
+        ifconfig br0 10.0.0.1 netmask 255.255.0.0 up || exit 1
+        echo "1" > /proc/sys/net/ipv4/ip_forward || exit 1
+        /usr/sbin/dnsmasq  --listen-address 10.0.0.1 --bind-interfaces || exit 1
+        iptables -t nat -A POSTROUTING -s 10.0.0.0/16 '!' -d 10.0.0.0/16 -j MASQUERADE || exit 1
+        iptables -I INPUT 1 -i br0 -s 10.0.0.0/16 -j ACCEPT || exit 1
+        iptables -I FORWARD 1 -i br0 -s 10.0.0.0/16 -j ACCEPT || exit 1
+        iptables -I FORWARD 1 -o br0 -d 10.0.0.0/16 -m state --state ESTABLISHED,RELATED -j  ACCEPT || exit 1
+
+    else
+
+        echo -n $"Starting default NetVM:"
+        /usr/lib/qubes/unbind_all_network_devices || exit 1
+        qvm-start -q --no-guid $NETVM || exit 1
+
+    fi
+    touch /var/lock/subsys/qubes_netvm
+    success
+    echo
+	return 0
+}
+
+stop()
+{
+    if [ x$NETVM = x ] ; then
+
+        echo WARNING: Qubes NetVM not configured!
+        echo -n $"Doing nothing:"
+
+    elif [ $NETVM = "dom0" ] ; then 
+
+        echo -n $"Stopping Qubes networking in Dom0:"
+        iptables -t nat -D POSTROUTING -s 10.0.0.0/16 '!' -d 10.0.0.0/16 -j MASQUERADE 
+        iptables -D INPUT -i br0 -s 10.0.0.0/16 -j ACCEPT || exit 1
+        iptables -D FORWARD -i br0 -s 10.0.0.0/16 -j ACCEPT || exit 1
+        iptables -D FORWARD -o br0 -d 10.0.0.0/16 -m state --state ESTABLISHED,RELATED -j ACCEPT || exit 1
+
+
+        killall dnsmasq
+        ifconfig br0 down
+        brctl delbr br0
+
+	else
+
+        echo -n $"Stopping default NetVM:"
+        qvm-run -q --shutdown --wait $NETVM
+
+    fi
+    rm -f /var/lock/subsys/qubes_netvm
+    success
+    echo
+
+	return 0
+}
+
+case "$1" in
+  start)
+	start
+	;;
+  stop)
+	stop
+	;;
+  *)
+	echo $"Usage: $0 {start|stop}"
+	exit 3
+	;;
+esac
+
+exit $RETVAL

+ 223 - 0
dom0/pendrive_swapper/qfilexchgd

@@ -0,0 +1,223 @@
+#!/usr/bin/python2.6
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Rafal Wojtczuk  <rafal@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+import xen.lowlevel.xs
+import os
+import sys
+import subprocess
+import daemon
+import time
+from qubes.qubes import QubesVmCollection
+from qubes.qubes import QubesException
+from qubes.qubes import QubesDaemonPidfile
+
+filename_seq = 50
+pen_cmd = '/usr/lib/qubes/qubes_pencmd'
+
+def get_next_filename_seq():
+    global filename_seq
+    filename_seq = filename_seq + 1
+    return str(filename_seq)
+
+def logproc(msg):
+        f = file('/var/log/qubes/qfileexchgd', 'a')
+        f.write(msg+'\n')
+        f.close()
+
+def get_req_node(domain_id):
+    return '/local/domain/'+domain_id+'/device/qpen'
+
+def get_name_node(domain_id):
+    return '/local/domain/'+domain_id+'/name'
+
+def only_in_first_list(l1, l2):
+    ret=[]
+    for i in l1:
+        if not i in l2:
+            ret.append(i)
+    return ret
+
+
+class WatchType:
+    def __init__(self, fn, param):
+        self.fn = fn
+        self.param = param
+
+class DomainState:
+    def __init__(self, domain, dict):
+        self.rcv_state = 'idle'
+        self.send_state = 'idle'
+        self.domain_id = domain
+        self.domdict = dict
+        self.send_seq = None
+        self.rcv_seq = None
+        self.waiting_sender = None
+
+    def handle_request(self, request):
+        req_ok = False
+        if request is None:
+            return
+        tmp = request.split()
+        rq = tmp[0]
+        if len(tmp) > 1:
+            arg = tmp[1]
+        else:
+            arg = None
+        if rq == 'new' and self.send_state == 'idle':
+            self.send_seq = get_next_filename_seq()
+            retcode = subprocess.call([pen_cmd, 'new', self.domain_id, self.send_seq])
+            logproc( 'Give domain ' + self.domain_id + ' a clean pendrive, retcode= ' + str(retcode))
+            if retcode == 0:
+                self.send_state = 'has_clean_pendrive'
+            req_ok = True
+        if rq == 'send' and self.send_state == 'has_clean_pendrive' and arg is not None:
+            logproc( 'send from ' + self.domain_id + ' to ' + arg)
+            if self.handle_transfer(arg):
+                self.send_state = 'idle'
+            req_ok = True;
+        if rq == 'umount' and self.rcv_state == 'has_loaded_pendrive':
+            retcode = subprocess.call([pen_cmd, 'umount', self.domain_id, self.rcv_seq])
+            if retcode == 0:
+                self.rcv_state = 'idle'
+                self.rcv_seq = None
+            logproc( 'set state of ' + self.domain_id + ' loaded->idle retcode=' + str(retcode))
+            req_ok = True
+        if rq == 'umount' and self.rcv_state == 'waits_to_umount':
+            req_ok = True
+            retcode = subprocess.call([pen_cmd, 'umount', self.domain_id, self.rcv_seq])
+            if retcode != 0:
+                return
+            assert(self.waiting_sender != None)
+            self.rcv_state = 'idle'
+            self.rcv_seq = None
+            tmp = self.waiting_sender
+            self.waiting_sender = None
+            if tmp.send_state == 'has_clean_pendrive':
+                if tmp.handle_transfer(self.name):
+                    tmp.send_state = 'idle'
+
+        if not req_ok:
+            logproc( 'request ' + request + ' not served due to nonmatching state')
+
+    def ask_to_umount(self, vmname):
+        q = 'VM ' + vmname + ' has already an incoming pendrive, and thus '
+        q+= 'cannot accept another one. If you intend to unmount its current '
+        q+= 'pendrive and retry this transfer, press Yes. '
+        q+= 'Otherwise press No to fail this transfer.'
+        retcode = subprocess.call(['/usr/bin/kdialog', '--yesno', q, '--title', 'Some additional action required'])
+        if retcode == 0:
+            return True
+        else:
+            return False
+
+    def handle_transfer(self, vmname):
+        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:
+            logproc( 'Domain ' + vmname + ' does not exist ?')
+            return False
+        if not vm.is_running():
+            logproc( 'Domain ' + vmname + ' is not running ?')
+            return False
+        target=self.domdict[str(vm.get_xid())]
+        if target.rcv_state != 'idle':
+            if self.ask_to_umount(vmname):
+                target.rcv_state='waits_to_umount'
+                target.waiting_sender=self
+            logproc( 'target domain ' + target.domain_id + ' is not idle, now ' + target.rcv_state)
+            return False
+        retcode = subprocess.call(['/usr/bin/kdialog', '--yesno', 'Do you authorize pendrive transfer from ' + self.name + ' to ' + vmname + '?' , '--title', 'Security confirmation'])
+        logproc('handle_transfer: kdialog retcode=' + str(retcode))
+        if retcode != 0:
+            return False
+        target.rcv_state='has_loaded_pendrive'
+        retcode = subprocess.call([pen_cmd, 'send', self.domain_id, target.domain_id, self.send_seq])
+        target.rcv_seq = self.send_seq
+        self.send_seq = None
+        logproc( 'set state of ' + target.domain_id + ' to has_loaded_pendrive, retcode=' + str(retcode))
+        return True
+
+
+class XS_Watcher:
+    def __init__(self):
+        self.handle = xen.lowlevel.xs.xs()
+        self.handle.watch('/local/domain', WatchType(XS_Watcher.dom_list_change, None))
+        self.domdict = {}
+
+    def dom_list_change(self, param):
+        curr = self.handle.ls('', '/local/domain')
+        if curr == None:
+            return
+        for i in only_in_first_list(curr, self.domdict.keys()):
+            newdom = DomainState(i, self.domdict)
+            newdom.watch_token = WatchType(XS_Watcher.request, newdom)
+            newdom.watch_name = WatchType(XS_Watcher.namechange, newdom)
+            self.domdict[i] = newdom
+            self.handle.watch(get_req_node(i), newdom.watch_token)
+            self.handle.watch(get_name_node(i), newdom.watch_name)
+            newdom.name = ''
+            logproc( 'added domain ' + i)
+        for i in only_in_first_list(self.domdict.keys(), curr):
+            self.handle.unwatch(get_req_node(i), self.domdict[i].watch_token)
+            self.handle.unwatch(get_name_node(i), self.domdict[i].watch_name)
+            self.domdict.pop(i)
+            logproc( 'removed domain ' + i)
+
+    def request(self, domain_param):
+        ret = self.handle.read('', get_req_node(domain_param.domain_id))
+        domain_param.handle_request(ret)
+
+    def namechange(self, domain_param):
+        ret = self.handle.read('', get_name_node(domain_param.domain_id))
+	if ret!= '' and ret!=None:
+		domain_param.name = ret
+                logproc( 'Name for domain xid ' + domain_param.domain_id + ' is ' + ret )
+
+    def watch_loop(self):
+        sys.stderr = file('/var/log/qubes/qfileexchgd.errors', 'a')
+        while True:
+            result = self.handle.read_watch()
+            token = result[1]
+            token.fn(self, token.param)
+
+def main():
+
+    lock = QubesDaemonPidfile ("qfileexchgd")
+    if lock.pidfile_exists():
+        if lock.pidfile_is_stale():
+            lock.remove_pidfile()
+            print "Removed stale pidfile (has the previous daemon instance crashed?)."
+        else:
+            exit (0)
+
+
+    context = daemon.DaemonContext(
+        working_directory = "/var/run/qubes",
+        pidfile = lock)
+    with context:
+        XS_Watcher().watch_loop()
+
+main()

+ 74 - 0
dom0/pendrive_swapper/qubes_pencmd

@@ -0,0 +1,74 @@
+#!/bin/sh
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Rafal Wojtczuk  <rafal@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+SHARE_DIR=/var/run/qubes
+function do_new()
+{
+	FILE=$SHARE_DIR/pendrive.$2
+	truncate -s 1G $FILE  || exit 1
+	vmname=$(xenstore-read /local/domain/$1/name)
+	mkfs.vfat -n "$vmname" $FILE || exit 1 
+	xm block-attach $1 file:/$FILE /dev/xvdg w || exit 1
+}
+
+function do_umount()
+{
+	xm block-detach $1 /dev/xvdh || exit 1
+	rm $SHARE_DIR/pendrive.$2
+}
+
+function do_send()
+{
+	FILE=$SHARE_DIR/pendrive.$3
+	vmname=$(xenstore-read /local/domain/$1/name)
+	xenstore-write /local/domain/$2/qubes_blocksrc $vmname
+	xm block-detach $1 /dev/xvdg || exit 1
+	xm block-attach $2 file:/$FILE /dev/xvdh w || exit 1
+}
+
+if [ $# -lt 1 ] ; then
+	echo args missing ?
+	exit 1
+fi
+if [ "$1" = "new" ] ; then
+	if ! [ $# = 3 ] ; then
+		echo new requires 2 more args 
+		exit 1
+	fi
+	do_new "$2" "$3"
+elif [ "$1" = "umount" ] ; then
+	if ! [ $# = 3 ] ; then
+		echo umount requires 2 more args 
+		exit 1
+	fi
+	do_umount "$2" "$3"
+elif [ "$1" = "send" ] ; then
+	if ! [ $# = 4 ] ; then
+		echo send requires 3 more args 
+		exit 1
+	fi
+	do_send "$2" "$3" "$4"
+else
+	echo bad cmd
+	exit 1
+fi
+

+ 0 - 0
dom0/qvm-core/__init__.py


+ 1515 - 0
dom0/qvm-core/qubes.py

@@ -0,0 +1,1515 @@
+#!/usr/bin/python2.6
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+import sys
+import os
+import os.path
+import subprocess
+import xml.etree.ElementTree
+import xml.parsers.expat
+import fcntl
+import re
+import shutil
+
+# Do not use XenAPI or create/read any VM files
+# This is for testing only!
+dry_run = False
+#dry_run = True
+
+
+if not dry_run:
+    # Xen API
+    import xmlrpclib
+    from xen.xm import XenAPI
+    from xen.xend import sxp
+
+
+if dry_run:
+    qubes_base_dir   = "."
+else:
+    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_store_filename = qubes_base_dir + "/qubes.xml"
+
+qubes_max_qid = 254*254
+qubes_max_netid = 254
+vm_default_netmask = "255.255.0.0"
+
+default_root_img = "root.img"
+default_rootcow_img = "root-cow.img"
+default_swapcow_img = "swap-cow.img"
+default_private_img = "private.img"
+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"
+
+# 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
+# must be able to ask Dom0 VM about how much memory it currently has...
+dom0_vm = None
+
+qubes_appmenu_create_cmd = "/usr/lib/qubes/create_apps_for_appvm.sh"
+qubes_appmenu_remove_cmd = "/usr/lib/qubes/remove_appvm_appmenus.sh"
+
+# TODO: we should detect the actual size of the AppVM's swap partition
+# rather than using this ugly hardcoded value, which was choosen here
+# as "should be good for everyone"
+swap_cow_sz = 1024*1024*1024
+
+VM_TEMPLATE = 'TempleteVM'
+VM_APPVM = 'AppVM'
+VM_NETVM = 'NetVM'
+
+def get_xend_session_old_api():
+    from xen.xend import XendClient
+    from xen.util.xmlrpcclient import ServerProxy
+    xend_server = ServerProxy(XendClient.uri)
+    return xend_server
+
+def get_xend_session_new_api():
+    xend_socket_uri = "httpu:///var/run/xend/xen-api.sock"
+    session = XenAPI.Session (xend_socket_uri)
+    session.login_with_password ("", "")
+    return session
+
+ 
+class QubesException (Exception) : pass
+
+class QubesVmLabel(object):
+    def __init__(self, name, color = None, icon = None):
+        self.name = name
+        self.color = color if color is not None else name
+        self.icon = icon if icon is not None else name
+        self.icon_path = "/usr/share/qubes/icons/" + self.icon + ".png"
+
+# Globally defined lables
+QubesVmLabels = {
+    "yellow" : QubesVmLabel ("yellow"),
+    "orange" : QubesVmLabel ("orange"),
+    "red" : QubesVmLabel ("red"),
+    "purple" : QubesVmLabel ("purple"),
+    "green" : QubesVmLabel ("green", color="0x5fa05e"),
+    "blue" : QubesVmLabel ("blue"),
+    "gray" : QubesVmLabel ("gray"),
+    "black" : QubesVmLabel ("black"),
+}
+
+default_appvm_label = QubesVmLabels["red"]
+default_template_label = QubesVmLabels["gray"]
+default_servicevm_label = QubesVmLabels["red"]
+
+class QubesVm(object):
+    """
+    A representation of one Qubes VM
+    Only persistent information are stored here, while all the runtime
+    information, e.g. Xen dom id, etc, are to be retrieved via Xen API
+    Note that qid is not the same as Xen's domid!
+    """
+
+    def __init__(self, qid, name, type,
+                 dir_path, conf_file = None,
+                 uses_default_netvm = True,
+                 netvm_vm = None,
+                 installed_by_rpm = False,
+                 updateable = False,
+                 label = None):
+
+
+        assert qid < qubes_max_qid, "VM id out of bounds!"
+        self.__qid = qid
+        self.name = name
+
+        dir_path = dir_path
+        self.dir_path = dir_path
+        conf_file = conf_file
+        if self.dir_path is not None:
+            if (conf_file is None):
+                self.conf_file = dir_path + "/" + name + ".conf"
+            else:
+                if os.path.isabs(conf_file):
+                    self.conf_file = conf_file
+                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)
+        self.installed_by_rpm = installed_by_rpm
+
+        self.updateable = updateable
+        self.label = label if label is not None else QubesVmLabels["red"]
+        self.icon_path = self.dir_path + "/icon.png"
+
+    @property
+    def qid(self):
+        return self.__qid
+
+    @property
+    def type(self):
+        return self.__type
+
+    @property
+    def ip(self):
+        if self.netvm_vm is not None:
+            return self.netvm_vm.get_ip_for_vm(self.qid)
+        else:
+            return None
+
+    @property
+    def netmask(self):
+        if self.netvm_vm is not None:
+            return self.netvm_vm.netmask
+        else:
+            return None
+
+    @property
+    def gateway(self):
+        if self.netvm_vm is not None:
+            return self.netvm_vm.gateway
+        else:
+            return None
+
+    def is_updateable(self):
+        return self.updateable
+
+    def is_networked(self):
+        if self.is_netvm():
+            return True
+   
+        if self.netvm_vm is not None:
+            return True
+        else:
+            return False
+
+
+    def set_nonupdateable(self):
+        if not self.is_updateable():
+            return
+
+        assert not self.is_running()
+        # 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_appvm(self):
+        if self.type == VM_APPVM:
+            return True
+        else:
+            return False
+
+    def is_netvm(self):
+        if self.type == VM_NETVM:
+            return True
+        else:
+            return False
+
+    def add_to_xen_storage(self):
+        if dry_run:
+            return
+
+        retcode = subprocess.call (["/usr/sbin/xm", "new", "-q",  self.conf_file])
+        if retcode != 0:
+            raise OSError ("Cannot add VM '{0}' to Xen Store!".format(self.name))
+
+        return True
+
+    def remove_from_xen_storage(self):
+        if dry_run:
+            return
+
+        retcode = subprocess.call (["/usr/sbin/xm", "delete", self.name])
+        if retcode != 0:
+            raise OSError ("Cannot remove VM '{0}' from Xen Store!".format(self.name))
+
+        self.in_xen_storage = False
+
+    def update_xen_storage(self):
+        self.remove_from_xen_storage()
+        self.add_to_xen_storage()
+
+    def get_xid(self):
+        if dry_run:
+            return 666
+
+        session = get_xend_session_new_api()
+        uuids = session.xenapi.VM.get_by_name_label (self.name)
+        uuid = uuids[0]
+        xid = int (session.xenapi.VM.get_domid (uuid))
+        return xid
+
+    def get_mem(self):
+        if dry_run:
+            return 666
+
+        session = get_xend_session_new_api()
+        uuids = session.xenapi.VM.get_by_name_label (self.name)
+        uuid = uuids[0]
+        metrics = session.xenapi.VM.get_metrics(uuid)
+        mem = int (session.xenapi.VM_metrics.get_memory_actual (metrics))
+        return mem
+
+    def get_mem_static_max(self):
+        if dry_run:
+            return 666
+
+        session = get_xend_session_new_api()
+        uuids = session.xenapi.VM.get_by_name_label (self.name)
+        uuid = uuids[0]
+        mem = int(session.xenapi.VM.get_memory_static_max(uuid))
+        return mem
+
+
+    def get_cpu_total_load(self):
+        if dry_run:
+            import random
+            return random.random() * 100
+
+        session = get_xend_session_new_api()
+        uuids = session.xenapi.VM.get_by_name_label (self.name)
+        uuid = uuids[0]
+        metrics = session.xenapi.VM.get_metrics(uuid)
+        cpus_util = session.xenapi.VM_metrics.get_VCPUs_utilisation (metrics)
+        if len (cpus_util) == 0:
+            return 0
+
+        cpu_total_load = 0.0
+        for cpu in cpus_util:
+            cpu_total_load += cpus_util[cpu]
+        cpu_total_load /= len(cpus_util)
+        p = 100*cpu_total_load 
+        if p > 100:
+            p = 100
+        return p
+
+    def get_power_state(self):
+        if dry_run:
+            return "NA"
+
+
+        session = get_xend_session_new_api()
+        uuids = session.xenapi.VM.get_by_name_label (self.name)
+        if len (uuids) == 0:
+            return "NA"
+        uuid = uuids[0]
+        power_state = session.xenapi.VM.get_power_state (uuid)
+        return power_state
+
+    def is_running(self):
+        if self.get_power_state() == "Running":
+            return True
+        else:
+            return False
+
+
+    def get_disk_usage(self, file_or_dir):
+        if not os.path.exists(file_or_dir):
+            return 0
+        p = subprocess.Popen (["du", "-s", "--block-size=1", file_or_dir],
+                              stdout=subprocess.PIPE)
+        result = p.communicate()
+        m = re.match(r"^(\d+)\s.*", result[0])
+        sz = int(m.group(1)) if m is not None else 0
+        return sz
+
+    def get_disk_utilization(self):
+        return self.get_disk_usage(self.dir_path)
+
+    def get_disk_utilization_private_img(self):
+        return self.get_disk_usage(self.private_img)
+
+    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):
+        if dry_run:
+            return
+
+        # Set Xen Store entires with VM networking info:
+
+        retcode = subprocess.check_call ([
+                "/usr/bin/xenstore-write",
+                "/local/domain/{0}/qubes_vm_type".format(xid),
+                self.type])
+
+        if self.is_netvm():
+            retcode = subprocess.check_call ([
+                "/usr/bin/xenstore-write",
+                "/local/domain/{0}/qubes_netvm_gateway".format(xid),
+                self.gateway])
+
+            retcode = subprocess.check_call ([
+                "/usr/bin/xenstore-write",
+                "/local/domain/{0}/qubes_netvm_netmask".format(xid),
+                self.netmask])
+
+            retcode = subprocess.check_call ([
+                "/usr/bin/xenstore-write",
+                "/local/domain/{0}/qubes_netvm_network".format(xid),
+                self.network])
+ 
+        elif self.netvm_vm is not None:
+            retcode = subprocess.check_call ([
+                "/usr/bin/xenstore-write",
+                "/local/domain/{0}/qubes_ip".format(xid),
+                self.ip])
+
+            retcode = subprocess.check_call ([
+                "/usr/bin/xenstore-write",
+                "/local/domain/{0}/qubes_netmask".format(xid),
+                self.netmask])
+
+            retcode = subprocess.check_call ([
+                "/usr/bin/xenstore-write",
+                "/local/domain/{0}/qubes_gateway".format(xid),
+                self.gateway])
+        else:
+            pass
+
+
+    def start(self, debug_console = False, verbose = False):
+        if dry_run:
+            return
+
+        if self.is_running():
+            raise QubesException ("VM is already running!")
+
+        if verbose:
+            print "--> Rereading the VM's conf file ({0})...".format(self.conf_file)
+        self.update_xen_storage()
+
+        session = get_xend_session_new_api()
+        uuids = session.xenapi.VM.get_by_name_label (self.name)
+        uuid = uuids[0]
+        if verbose:
+            print "--> Loading the VM (type = {0})...".format(self.type)
+
+        mem_required = self.get_mem_static_max()
+        dom0_mem = dom0_vm.get_mem()
+        dom0_mem_new = dom0_mem - mem_required
+        if verbose:
+            print "--> AppVM required mem     : {0}".format(mem_required)
+            print "--> Dom0 mem after launch  : {0}".format(dom0_mem_new)
+
+        if dom0_mem_new < dom0_min_memory:
+            raise MemoryError ("ERROR: starting this VM would cause Dom0 memory to go below {0}B".format(dom0_min_memory))
+
+        session.xenapi.VM.start (uuid, True) # Starting a VM paused
+        xid = int (session.xenapi.VM.get_domid (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:
+            assert self.netvm_vm is not None
+            if verbose:
+                print "--> Attaching to the network backend (netvm={0})...".format(self.netvm_vm.name)
+
+            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")
+                retcode = subprocess.call (["/usr/sbin/xm", "network-attach", self.name, "backend={0}".format(self.netvm_vm.name)])
+                if retcode != 0:
+                    self.force_shutdown()
+                    raise OSError ("ERROR: Cannot attach to network backend!")
+
+            else:
+                retcode = subprocess.call (["/usr/sbin/xm", "network-attach", self.name, "backend=0"])
+                if retcode != 0:
+                    self.force_shutdown()
+                    raise OSError ("ERROR: Cannot attach to network backend!")
+
+        if verbose:
+            print "--> Starting the VM..."
+        session.xenapi.VM.unpause (uuid) 
+
+        # perhaps we should move it before unpause and fork?
+        if debug_console:
+            from xen.xm import console
+            if verbose:
+                print "--> Starting debug console..."
+            console.execConsole (xid)
+
+        return xid
+
+    def force_shutdown(self):
+        if dry_run:
+            return
+
+
+        session = get_xend_session_new_api()
+        uuids = session.xenapi.VM.get_by_name_label (self.name)
+        uuid = uuids[0]
+        session.xenapi.VM.hard_shutdown (uuid)
+
+    def remove_from_disk(self):
+        if dry_run:
+            return
+
+
+        shutil.rmtree (self.dir_path)
+
+
+class QubesTemplateVm(QubesVm):
+    """
+    A class that represents an TemplateVM. A child of QubesVM.
+    """
+    def __init__(self, **kwargs):
+
+        if "dir_path" not in kwargs or kwargs["dir_path"] is None:
+            kwargs["dir_path"] = qubes_templates_dir + "/" + kwargs["name"]
+
+        if "updateable" not in kwargs or kwargs["updateable"] is None :
+            kwargs["updateable"] = True
+
+        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
+        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)
+
+        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)
+
+        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)
+
+        if appvms_conf_file is not None and os.path.isabs(appvms_conf_file):
+            self.appvms_conf_file = appvms_conf_file
+        else:
+            self.appvms_conf_file = dir_path + "/" + (
+                appvms_conf_file if appvms_conf_file is not None else default_appvms_conf_file)
+
+        self.templatevm_conf_template = self.dir_path + "/" + default_templatevm_conf_template
+        self.kernels_dir = self.dir_path + "/" + default_kernels_subdir
+        self.appmenus_templates_dir = self.dir_path + "/" + default_appmenus_templates_subdir
+        self.appvms = QubesVmCollection()
+
+    def set_updateable(self):
+        if self.is_updateable():
+            return
+
+        assert not self.is_running()
+        # Make sure that all the AppVMs are non-updateable...
+        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'".\
+                                     format (appvm.name, self.name))
+        self.updateable = True
+
+
+    def clone_disk_files(self, src_template_vm, verbose):
+        if dry_run:
+            return
+
+
+        assert not src_template_vm.is_running(), "Attempt to clone a running Template VM!"
+
+        if verbose:
+            print "--> Creating directory: {0}".format(self.dir_path)
+        os.mkdir (self.dir_path)
+
+        if verbose:
+            print "--> Copying the VM config file:\n{0} =*>\n{1}".\
+                    format(src_template_vm.templatevm_conf_template, self.conf_file)
+        conf_templatevm_template = open (src_template_vm.templatevm_conf_template, "r")
+        conf_file = open(self.conf_file, "w")
+        rx_templatename = re.compile (r"%TEMPLATENAME%")
+
+        for line in conf_templatevm_template:
+            line = rx_templatename.sub (self.name, line)
+            conf_file.write(line)
+
+        conf_templatevm_template.close()
+        conf_file.close()
+
+        if verbose:
+            print "--> Copying the VM config template :\n{0} ==>\n{1}".\
+                    format(src_template_vm.templatevm_conf_template, self.templatevm_conf_template)
+        shutil.copy (src_template_vm.templatevm_conf_template, self.templatevm_conf_template)
+
+        if verbose:
+            print "--> Copying the VM config template :\n{0} ==>\n{1}".\
+                    format(src_template_vm.appvms_conf_file, self.appvms_conf_file)
+        shutil.copy (src_template_vm.appvms_conf_file, self.appvms_conf_file)
+
+        if verbose:
+            print "--> Copying the template's private image:\n{0} ==>\n{1}".\
+                    format(src_template_vm.private_img, self.private_img)
+        # We prefer to use Linux's cp, because it nicely handles sparse files
+        retcode = subprocess.call (["cp", src_template_vm.private_img, self.private_img])
+        retcode = 0
+        if retcode != 0:
+            raise IOError ("Error while copying {0} to {1}".\
+                           format(src_template_vm.private_img, self.private_img))
+
+        if verbose:
+            print "--> Copying the template's root image:\n{0} ==>\n{1}".\
+                    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])
+        retcode = 0
+        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 kernel dir:\n{0} ==>\n{1}".\
+                    format(src_template_vm.kernels_dir, self.kernels_dir)
+        shutil.copytree (src_template_vm.kernels_dir, self.kernels_dir)
+
+        if verbose:
+            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):
+        if dry_run:
+            return
+
+
+        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.appvms_conf_file):
+            raise QubesException (
+                "Appvm template config file doesn't exist: {0}".\
+                format(self.appvms_conf_file))
+
+        if not os.path.exists (self.root_img):
+            raise QubesException (
+                "VM root image file doesn't exist: {0}".\
+                format(self.root_img))
+
+        if not os.path.exists (self.private_img):
+            raise QubesException (
+                "VM private image file doesn't exist: {0}".\
+                format(self.private_img))
+
+        if not os.path.exists (self.kernels_dir):
+            raise QubesException (
+                "VM's kernels directory does not exist: {0}".\
+                format(self.kernels_dir))
+ 
+        return True
+
+    def start(self, debug_console = False, verbose = False):
+        if dry_run:
+            return
+
+
+        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!")
+
+        return super(QubesTemplateVm, self).start(debug_console=debug_console, verbose=verbose)
+
+    def create_xml_element(self):
+        element = xml.etree.ElementTree.Element(
+            "QubesTemplateVm",
+            qid=str(self.qid),
+            name=self.name,
+            dir_path=self.dir_path,
+            conf_file=self.conf_file,
+            appvms_conf_file=self.appvms_conf_file,
+            root_img=self.root_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",
+            installed_by_rpm=str(self.installed_by_rpm),
+            updateable=str(self.updateable),
+            )
+        return element
+
+class QubesServiceVm(QubesVm):
+    """
+    A class that represents a ServiceVM, e.g. NetVM. A child of QubesVM.
+    """
+    def __init__(self, **kwargs):
+
+        if "dir_path" not in kwargs or kwargs["dir_path"] is None:
+            kwargs["dir_path"] = qubes_servicevms_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
+
+        kwargs["updateable"] = True
+        super(QubesServiceVm, self).__init__(**kwargs)
+
+        dir_path = kwargs["dir_path"]
+        assert dir_path is not None
+
+        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)
+
+        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)
+
+    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 
+
+    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):
+        if dry_run:
+            return
+
+
+        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.root_img):
+            raise QubesException (
+                "VM root image file doesn't exist: {0}".\
+                format(self.root_img))
+
+        return True
+
+    def create_xml_element(self):
+        raise NotImplementedError
+
+class QubesNetVm(QubesServiceVm):
+    """
+    A class that represents a NetVM. A child of QubesServiceVM.
+    """
+    def __init__(self, **kwargs):
+        netid = kwargs.pop("netid")
+        self.netid = netid
+        self.__network = "10.{0}.0.0".format(netid)
+        self.netprefix = "10.{0}.".format(netid)
+        self.__netmask = vm_default_netmask
+        self.__gateway = self.netprefix + "0.1"
+
+        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)
+
+    @property
+    def gateway(self):
+        return self.__gateway
+
+    @property
+    def netmask(self):
+        return self.__netmask
+
+    @property
+    def network(self):
+        return self.__network
+
+    def get_ip_for_vm(self, qid):
+        hi = qid / 253
+        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_xml_element(self):
+        element = xml.etree.ElementTree.Element(
+            "QubesNetVm",
+            qid=str(self.qid),
+            netid=str(self.netid),
+            name=self.name,
+            dir_path=self.dir_path,
+            conf_file=self.conf_file,
+            root_img=self.root_img,
+            private_img=self.private_img,
+            installed_by_rpm=str(self.installed_by_rpm),
+            )
+        return element
+
+class QubesDom0NetVm(QubesNetVm):
+    def __init__(self):
+        super(QubesDom0NetVm, self).__init__(qid=0, name="dom0", netid=0,
+                                             dir_path=None, root_img = None,
+                                             private_img = None,
+                                             label = default_template_label)
+
+    def is_running(self):
+        return True
+
+    def get_cpu_total_load(self):
+        if dry_run:
+            import random
+            return random.random() * 100
+
+        session = get_xend_session_new_api()
+        hosts = session.xenapi.host.get_all()
+        cpus = session.xenapi.host.get_host_CPUs(hosts[0])
+        cpu_total_load = 0.0
+        for cpu in cpus:
+            cpu_total_load += session.xenapi.host_cpu.get_utilisation(cpu)
+        cpu_total_load /= len(cpus)
+        p = 100*cpu_total_load 
+        if p > 100:
+            p = 100
+        return p
+
+    def get_mem(self):
+
+        # Unfortunately XenAPI provides only info about total memory, not the one actually usable by Dom0...
+        #session = get_xend_session_new_api()
+        #hosts = session.xenapi.host.get_all()
+        #metrics = session.xenapi.host.get_metrics(hosts[0])
+        #memory_total = int(session.xenapi.metrics.get_memory_total(metrics))
+
+        # ... so we must read /proc/meminfo, just like free command does
+        f = open ("/proc/meminfo")
+        for line in f:
+            match = re.match(r"^MemTotal\:\s*(\d+) kB", line)
+            if match is not None:
+                break
+        f.close()
+        assert match is not None
+        return int(match.group(1))*1024
+
+    def get_xid(self):
+        return 0
+
+    def get_power_state(self):
+        return "Running"
+
+    def get_disk_usage(self, file_or_dir):
+        return 0
+
+    def get_disk_utilization(self):
+        return 0
+
+    def get_disk_utilization_private_img(self):
+        return 0
+
+    def get_private_img_sz(self):
+        return 0
+
+    def start(self, debug_console = False, verbose = False):
+        raise QubesException ("Cannot start Dom0 fake domain!")
+
+    def create_xml_element(self):
+        return None
+
+    def verify_files(self):
+        return True
+
+
+class QubesAppVm(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"]
+
+        if "updateable" not in kwargs or kwargs["updateable"] is None:
+            kwargs["updateable"] = False
+
+        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
+
+
+    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()
+        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_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)
+ 
+        conf_template = open (self.template_vm.appvms_conf_file, "r")
+        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()
+
+        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))
+
+        if verbose:
+            print "--> Creating icon symlink: {0} -> {1}".format(self.icon_path, self.label.icon_path)
+            os.symlink (self.label.icon_path, self.icon_path)
+
+        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 get_root_img_sz(self):
+        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}".\
+                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 create_xml_element(self):
+        element = xml.etree.ElementTree.Element(
+            "QubesAppVm",
+            qid=str(self.qid),
+            name=self.name,
+            dir_path=self.dir_path,
+            conf_file=self.conf_file,
+            template_qid=str(self.template_vm.qid),
+            uses_default_netvm=str(self.uses_default_netvm),
+            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),
+            updateable=str(self.updateable),
+            label=self.label.name)
+        return element
+
+    def start(self, debug_console = False, verbose = False):
+        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()
+
+        return super(QubesAppVm, self).start(debug_console=debug_console, verbose=verbose)
+
+    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()
+
+        print "--> Resetting the 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)
+    """
+
+    def __init__(self):
+        super(QubesVmCollection, self).__init__()
+        self.default_netvm_qid = None
+        self.default_template_qid = None
+
+    def values(self):
+        for qid in self.keys():
+            yield self[qid]
+
+    def items(self):
+        for qid in self.keys():
+            yield (qid, self[qid])
+
+    def __iter__(self):
+        for qid in sorted(super(QubesVmCollection, self).keys()):
+            yield qid
+
+    keys = __iter__
+
+    def __setitem__(self, key, value):
+        if key not in self:
+            return super(QubesVmCollection, self).__setitem__(key, value)
+        else:
+            assert False, "Attempt to add VM with qid that already exists in the collection!"
+
+
+    def add_new_appvm(self, name, template_vm,
+                      dir_path = None, conf_file = None,
+                      private_img = None,
+                      label = None):
+
+        qid = self.get_new_unused_qid()
+        vm = QubesAppVm (qid=qid, name=name, template_vm=template_vm,
+                         dir_path=dir_path, conf_file=conf_file,
+                         private_img=private_img,
+                         netvm_vm = self.get_default_netvm_vm(),
+                         label=label)
+
+        if not self.verify_new_vm (vm):
+            assert False, "Wrong VM description!"
+        self[vm.qid]=vm
+        return vm
+
+    def add_new_templatevm(self, name,
+                           dir_path = None, conf_file = None,
+                           root_img = None, private_img = None,
+                           installed_by_rpm = True):
+
+        qid = self.get_new_unused_qid()
+        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,
+                              netvm_vm = self.get_default_netvm_vm())
+
+        if not self.verify_new_vm (vm):
+            assert False, "Wrong VM description!"
+        self[vm.qid]=vm
+
+        if self.default_template_qid is None:
+            self.set_default_template_vm(vm)
+
+        return vm
+
+    def clone_templatevm(self, src_template_vm, name, dir_path = None, verbose = False):
+
+        assert not src_template_vm.is_running(), "Attempt to clone a running Template VM!"
+
+        vm = self.add_new_templatevm (name=name, dir_path=dir_path, installed_by_rpm = False)
+
+        return vm
+
+
+    def add_new_netvm(self, name,
+                      dir_path = None, conf_file = None,
+                      root_img = None):
+
+        qid = self.get_new_unused_qid()
+        netid = self.get_new_unused_netid()
+        vm = QubesNetVm (qid=qid, name=name, 
+                         netid=netid,
+                         dir_path=dir_path, conf_file=conf_file,
+                         root_img=root_img)
+
+        if not self.verify_new_vm (vm):
+            assert False, "Wrong VM description!"
+        self[vm.qid]=vm
+
+        if self.default_netvm_qid is None:
+            self.set_default_netvm_vm(vm)
+
+        return vm
+
+    def set_default_template_vm(self, vm):
+        assert vm.is_templete(), "VM {0} is not a TempleteVM!".format(vm.name)
+        self.default_template_qid = vm.qid
+
+    def get_default_template_vm(self):
+        if self.default_template_qid is None:
+            return None
+        else:
+            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)
+        self.default_netvm_qid = vm.qid
+
+    def get_default_netvm_vm(self):
+        if self.default_netvm_qid is None:
+            return None
+        else:
+            return self[self.default_netvm_qid]
+
+    def get_vm_by_name(self, name):
+        for vm in self.values():
+            if (vm.name == name):
+                return vm
+        return None
+
+    def get_qid_by_name(self, name):
+        vm = self.get_vm_by_name(name)
+        return vm.qid if vm is not None else None
+
+    def get_vms_based_on(self, template_qid):
+        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
+        for vm in self.values():
+            if vm.qid == new_vm.qid:
+                print "ERROR: The qid={0} is already used by VM '{1}'!".\
+                        format(vm.qid, vm.name)
+                return False
+
+        # Verify that name is unique
+        for vm in self.values():
+            if vm.name == new_vm.name:
+                print "ERROR: The name={0} is already used by other VM with qid='{1}'!".\
+                        format(vm.name, vm.qid)
+                return False
+
+        return True
+
+    def get_new_unused_qid(self):
+        used_ids = set([vm.qid for vm in self.values()])
+        for id in range (1, qubes_max_qid):
+            if id not in used_ids:
+                return id
+        raise LookupError ("Cannot find unused qid!")
+
+    def get_new_unused_netid(self):
+        used_ids = set([vm.netid for vm in self.values() if vm.is_netvm()])
+        for id in range (1, qubes_max_netid):
+            if id not in used_ids:
+                return id
+        raise LookupError ("Cannot find unused netid!")
+
+
+    def check_if_storage_exists(self):
+        try:
+            f = open (qubes_store_filename, 'r')
+        except IOError:
+            return False
+        f.close()
+        return True
+
+    def create_empty_storage(self):
+        self.qubes_store_file = open (qubes_store_filename, 'w')
+        self.clear()
+        self.save()
+
+    def lock_db_for_reading(self):
+        self.qubes_store_file = open (qubes_store_filename, 'r')
+        fcntl.lockf (self.qubes_store_file, fcntl.LOCK_SH)
+
+    def lock_db_for_writing(self):
+        self.qubes_store_file = open (qubes_store_filename, 'r+')
+        fcntl.lockf (self.qubes_store_file, fcntl.LOCK_EX)
+
+    def unlock_db(self):
+        fcntl.lockf (self.qubes_store_file, fcntl.LOCK_UN)
+        self.qubes_store_file.close()
+
+    def save(self):
+        root = xml.etree.ElementTree.Element(
+            "QubesVmCollection",
+
+            default_template=str(self.default_template_qid) \
+            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"
+        )
+
+        for vm in self.values():
+            element = vm.create_xml_element()
+            if element is not None:
+                root.append(element)
+        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(
+                os.path.basename(sys.argv[0]), err))
+            return False
+        return True
+
+    def load(self):
+        self.clear()
+
+        dom0vm = QubesDom0NetVm ()
+        self[dom0vm.qid] = dom0vm
+        self.default_netvm_qid = 0
+
+        global dom0_vm
+        dom0_vm = dom0vm
+
+        try:
+            tree = xml.etree.ElementTree.parse(self.qubes_store_file)
+        except (EnvironmentError,
+                xml.parsers.expat.ExpatError) as err:
+            print("{0}: import error: {1}".format(
+                os.path.basename(sys.argv[0]), err))
+            return False
+
+        element = tree.getroot()
+        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") 
+        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",
+                              )
+
+                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 
+        for element in tree.findall("QubesTemplateVm"):
+            try:
+
+                kwargs = {}
+                attr_list = ("qid", "name", "dir_path", "conf_file",
+                             "appvms_conf_file", "private_img", "root_img",
+                             "installed_by_rpm", "updateable",
+                             "uses_default_netvm", "netvm_qid")
+
+                for attribute in attr_list:
+                    kwargs[attribute] = element.get(attribute)
+
+                kwargs["qid"] = int(kwargs["qid"])
+                kwargs["installed_by_rpm"] = True if kwargs["installed_by_rpm"] == "True" else False
+                if kwargs["updateable"] is not None:
+                    kwargs["updateable"] = True if kwargs["updateable"] == "True" else False
+
+                if "uses_default_netvm" not in kwargs:
+                    kwargs["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:
+                    netvm_vm = self.get_default_netvm_vm()
+                    kwargs.pop("netvm_qid")
+                else:
+                    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]
+
+                kwargs["netvm_vm"] = netvm_vm
+ 
+                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
+
+        # Finally, read in the AppVMs
+        for element in tree.findall("QubesAppVm"):
+            try:
+                kwargs = {}
+                attr_list = ("qid", "name", "dir_path", "conf_file",
+                             "private_img", "template_qid",
+                             "updateable", "label", "netvm_qid",
+                             "uses_default_netvm")
+
+                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: AppVM '{0}' uses unkown template qid='{1}'!".\
+                            format(kwargs["name"], kwargs["template_qid"])
+
+                kwargs["template_vm"] = template_vm
+
+                if "uses_default_netvm" not in kwargs:
+                    kwargs["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:
+                    netvm_vm = self.get_default_netvm_vm()
+                    kwargs.pop("netvm_qid")
+                else:
+                    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]
+
+                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"])
+                        kwargs.pop ("label")
+                    else:
+                        kwargs["label"] = QubesVmLabels[kwargs["label"]]
+
+                vm = QubesAppVm(**kwargs)
+
+                self[vm.qid] = vm
+            except (ValueError, LookupError) as err:
+                print("{0}: import error (QubesAppVm): {1}".format(
+                    os.path.basename(sys.argv[0]), err))
+                return False
+
+        return True
+
+
+
+
+class QubesDaemonPidfile(object):
+    def __init__(self, name):
+        self.name = name
+        self.path = "/var/run/qubes/" + name + ".pid"
+
+    def create_pidfile(self):
+        f = open (self.path, 'w')
+        f.write(str(os.getpid()))
+        f.close()
+
+    def pidfile_exists(self):
+        return os.path.exists(self.path)
+
+    def read_pid(self):
+        f = open (self.path)
+        pid = f.read ().strip()
+        f.close()
+        return int(pid)
+
+    def pidfile_is_stale(self):
+        if not self.pidfile_exists():
+            return False
+
+        # check if the pid file is valid...
+        proc_path = "/proc/" + str(self.read_pid()) + "/cmdline"
+        if not os.path.exists (proc_path):
+            print "Path {0} doesn't exist, assuming stale pidfile.".format(proc_path)
+            return True
+
+        f = open (proc_path)
+        cmdline = f.read ()
+        f.close()
+
+#       The following doesn't work with python -- one would have to get argv[1] and compare it with self.name...
+#        if not cmdline.strip().endswith(self.name):
+#            print "{0} = {1} doesn't seem to point to our process ({2}), assuming stale pidile.".format(proc_path, cmdline, self.name)
+#            return True
+
+        return False # It's a good pidfile
+
+    def remove_pidfile(self):
+        os.remove (self.path)
+
+    def __enter__ (self):
+        # assumes the pidfile doesn't exist -- you should ensure it before opening the context
+        self.create_pidfile()
+    def __exit__ (self):
+        self.remove_pidfile()
+
+

+ 82 - 0
dom0/qvm-tools/qvm-add-appvm

@@ -0,0 +1,82 @@
+#!/usr/bin/python2.6
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+from qubes.qubes import QubesVmCollection
+from qubes.qubes import QubesException
+from optparse import OptionParser;
+
+def main():
+    usage = "usage: %prog [options] <appvm-name> <vm-template-name>\n\n"\
+            "Adds an already installed appvm to the Qubes DB\n"\
+            "WARNING: Noramlly you would not need this command,\n"\
+            "and you would use qvm-create instead!"
+
+    parser = OptionParser (usage)
+    parser.add_option ("-p", "--path", dest="dir_path",
+                       help="Specify path to the template directory")
+    parser.add_option ("-c", "--conf", dest="conf_file",
+                       help="Specify the Xen VM .conf file to use\
+                       (relative to the template dir path)")
+
+    (options, args) = parser.parse_args ()
+    if (len (args) != 2):
+        parser.error ("You must specify at least the AppVM and TemplateVM names!")
+    vmname = args[0]
+    templatename = args[1]
+
+    qvm_collection = QubesVmCollection()
+    qvm_collection.lock_db_for_writing()
+    qvm_collection.load()
+
+    if qvm_collection.get_vm_by_name(vmname) is not None:
+        print "ERROR: A VM with the name '{0}' already exists in the system.".format(vmname)
+        exit(1)
+
+    template_vm = qvm_collection.get_vm_by_name(templatename)
+    if template_vm is None:
+        print "ERROR: A Template VM with the name '{0}' does not exist in the system.".format(templatename)
+        exit(1)
+
+
+    vm = qvm_collection.add_new_appvm(vmname, template_vm,
+                                      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)
+
+    try:
+        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()

+ 119 - 0
dom0/qvm-tools/qvm-add-netvm

@@ -0,0 +1,119 @@
+#!/usr/bin/python2.6
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+from 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] <netvm-name>"
+    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()

+ 78 - 0
dom0/qvm-tools/qvm-add-template

@@ -0,0 +1,78 @@
+#!/usr/bin/python2.6
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+from qubes.qubes import QubesVmCollection
+from qubes.qubes import QubesException
+from optparse import OptionParser;
+
+def main():
+    usage = "usage: %prog [options] <vm-template-name>\n"\
+            "Adds an already installed template to the Qubes DB"
+          
+    parser = OptionParser (usage)
+    parser.add_option ("-p", "--path", dest="dir_path",
+                       help="Specify path to the template directory")
+    parser.add_option ("-c", "--conf", dest="conf_file",
+                       help="Specify the Xen VM .conf file to use\
+                       (relative to the template dir path)")
+
+    parser.add_option ("--rpm", action="store_true", dest="installed_by_rpm",
+                       help="Template files have been installed by RPM", default=False)
+
+
+    (options, args) = parser.parse_args ()
+    if (len (args) != 1):
+        parser.error ("You must specify at least the TemplateVM name!")
+    vmname = args[0]
+
+    qvm_collection = QubesVmCollection()
+    qvm_collection.lock_db_for_writing()
+    qvm_collection.load()
+
+    if qvm_collection.get_vm_by_name(vmname) is not None:
+        print "ERROR: A VM with the name '{0}' already exists in the system.".format(vmname)
+        exit(1)
+
+    vm = qvm_collection.add_new_templatevm(vmname, 
+                                         conf_file=options.conf_file,
+                                         dir_path=options.dir_path,
+                                         installed_by_rpm=options.installed_by_rpm)
+
+    try:
+        vm.verify_files()
+    except QubesException as err:
+        print "ERROR: {0}".format(err)
+        qvm_collection.pop(vm.qid)
+        exit (1)
+
+    try:
+        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()

+ 75 - 0
dom0/qvm-tools/qvm-clone-template

@@ -0,0 +1,75 @@
+#!/usr/bin/python2.6
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+from qubes.qubes import QubesVmCollection
+from qubes.qubes import QubesException
+from optparse import OptionParser;
+
+def main():
+    usage = "usage: %prog [options] <src-template-name> <new-template-name>\n"\
+            "Clones an existing template by copying all its disk files"
+          
+    parser = OptionParser (usage)
+    parser.add_option ("-q", "--quiet", action="store_false", dest="verbose", default=True)
+    parser.add_option ("-p", "--path", dest="dir_path",
+                       help="Specify path to the template directory")
+
+    (options, args) = parser.parse_args ()
+    if (len (args) != 2):
+        parser.error ("You must specify at least the src and dst TemplateVM names!")
+    srcname = args[0]
+    dstname = args[1]
+
+    qvm_collection = QubesVmCollection()
+    qvm_collection.lock_db_for_writing()
+    qvm_collection.load()
+
+    src_tvm = qvm_collection.get_vm_by_name(srcname)
+    if src_tvm is  None:
+        print "ERROR: A VM with the name '{0}' does not exist in the system.".format(srcname)
+        exit(1)
+
+    if qvm_collection.get_vm_by_name(dstname) is not None:
+        print "ERROR: A VM with the name '{0}' already exists in the system.".format(dstname)
+        exit(1)
+
+    dst_tvm = qvm_collection.clone_templatevm(src_template_vm=src_tvm,
+                                              name=dstname, 
+                                              dir_path=options.dir_path)
+
+    try:
+        dst_tvm.clone_disk_files (src_template_vm=src_tvm, verbose=options.verbose)
+
+        if options.verbose:
+            print "--> Adding to Xen Storage..."
+ 
+        dst_tvm.add_to_xen_storage()
+
+    except (IOError, OSError) as err:
+        print "ERROR: {0}".format(err)
+        qvm_collection.pop(dst_tvm.qid)
+        exit (1)
+
+    qvm_collection.save()
+    qvm_collection.unlock_db()
+    
+main()

+ 101 - 0
dom0/qvm-tools/qvm-create

@@ -0,0 +1,101 @@
+#!/usr/bin/python2.6
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+from qubes.qubes import QubesVmCollection
+from qubes.qubes import QubesVmLabels
+from optparse import OptionParser;
+import subprocess
+
+
+def main():
+    usage = "usage: %prog [options] <vm-name>"
+    parser = OptionParser (usage)
+    parser.add_option ("-t", "--template", dest="template",
+                       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 ("-q", "--quiet", action="store_false", dest="verbose", default=True)
+    (options, args) = parser.parse_args ()
+    if (len (args) != 1):
+        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]
+
+
+    qvm_collection = QubesVmCollection()
+    qvm_collection.lock_db_for_writing()
+    qvm_collection.load()
+
+    if qvm_collection.get_vm_by_name(vmname) is not None:
+        print "A VM with the name '{0}' already exists in the system.".format(vmname)
+        exit(1)
+
+    if options.template is not None:
+        template_vm = qvm_collection.get_vm_by_name(options.template)
+        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():
+            print "VM '{0}' is not a TemplateVM".format(options.template)
+            exit (1)
+        if (options.verbose):
+            print "--> Using TemplateVM: {0}".format(template_vm.name)
+
+    else:
+        if qvm_collection.get_default_template_vm() is None:
+            print "No default TempleteVM defined!"
+            exit (1)
+        else:
+            template_vm = qvm_collection.get_default_template_vm()
+            if (options.verbose):
+                print "--> Using default TemplateVM: {0}".format(template_vm.name)
+
+    vm = qvm_collection.add_new_appvm(vmname, template_vm, label = label)
+    try:
+        vm.create_on_disk(verbose=options.verbose)
+        vm.add_to_xen_storage()
+
+    except (IOError, OSError) as err:
+        print "ERROR: {0}".format(err)
+        vm.remove_from_disk()
+        exit (1)
+
+
+    qvm_collection.save()
+    qvm_collection.unlock_db()
+
+
+main()

+ 39 - 0
dom0/qvm-tools/qvm-get-default-netvm

@@ -0,0 +1,39 @@
+#!/usr/bin/python2.6
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+from qubes.qubes import QubesVmCollection
+from optparse import OptionParser;
+
+def main():
+    qvm_collection = QubesVmCollection()
+    qvm_collection.lock_db_for_reading()
+    qvm_collection.load()
+    qvm_collection.unlock_db()
+    netvm = qvm_collection.get_default_netvm_vm()
+    if netvm is None:
+        print ""
+    else:
+        print netvm.name
+
+
+
+main()

+ 32 - 0
dom0/qvm-tools/qvm-init-storage

@@ -0,0 +1,32 @@
+#!/usr/bin/python2.6
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+from qubes.qubes import QubesVmCollection
+
+def main():
+    qvm_collection = QubesVmCollection()
+    if qvm_collection.check_if_storage_exists():
+        print "Storage exists, not overwriting."
+        exit(1)
+    qvm_collection.create_empty_storage()
+
+main()

+ 54 - 0
dom0/qvm-tools/qvm-kill

@@ -0,0 +1,54 @@
+#!/usr/bin/python2.6
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+from qubes.qubes import QubesVmCollection
+from optparse import OptionParser;
+import subprocess
+
+qubes_guid_path = "/usr/bin/qubes_guid"
+
+def main():
+    usage = "usage: %prog [options] <vm-name>"
+    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)
+
+    try:
+        vm.force_shutdown()
+    except (IOError, OSError) as err:
+        print "ERROR: {0}".format(err)
+        exit (1)
+
+
+main()

+ 191 - 0
dom0/qvm-tools/qvm-ls

@@ -0,0 +1,191 @@
+#!/usr/bin/python2.6
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+from qubes.qubes import QubesVmCollection
+from qubes.qubes import QubesException
+from optparse import OptionParser
+
+
+fields = {
+    "qid": {"func": "vm.qid"},
+
+    "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_netvm() else '')\
+             + vm.name \
+             + (']' if vm.is_templete() else '')\
+             + ('}' if vm.is_netvm() else '')"},
+
+    "type": {"func": "'Tpl' if vm.is_templete() 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"},
+
+    "netvm": {"func": "'n/a' if vm.is_netvm() else\
+              ('*' if vm.uses_default_netvm else '') +\
+              qvm_collection[vm.netvm_vm.qid].name\
+                     if vm.netvm_vm is not None else '-'"},
+
+    "ip" : {"func": "vm.ip"},
+    "netmask" : {"func": "vm.netmask"},
+    "gateway" : {"func": "vm.gateway"},
+
+    "xid" : {"func" : "vm.get_xid() if vm.is_running() else '-'"},
+
+    "mem" : {"func" : "(str(vm.get_mem()/1024/1024) + ' MB') if vm.is_running() else '-'"},
+    "cpu" : {"func" : "round (vm.get_cpu_total_load(), 1) if vm.is_running() else '-'"},
+    "disk": {"func" : "str(vm.get_disk_utilization()/(1024*1024)) + ' MB'"}, 
+    "state": {"func" : "vm.get_power_state()"},
+
+    "priv-curr": {"func" : "str(vm.get_disk_utilization_private_img()/(1024*1024)) + ' MB'"},
+    "priv-max": {"func" : "str(vm.get_private_img_sz()/(1024*1024)) + ' MB'"},
+    "priv-util": {"func" : "str(vm.get_disk_utilization_private_img()*100/vm.get_private_img_sz()) + '%' if vm.get_private_img_sz() != 0 else '-'"},
+
+    "root-curr": {"func" : "str(vm.get_disk_utilization_root_img()/(1024*1024)) + ' MB'"},
+    "root-max": {"func" : "str(vm.get_root_img_sz()/(1024*1024)) + ' MB'"},
+    "root-util": {"func" : "str(vm.get_disk_utilization_root_img()*100/vm.get_root_img_sz()) + '%' if vm.get_root_img_sz() != 0 else '-'"},
+
+    "label" : {"func" : "vm.label.name"},
+
+    "on" : {"func" : "'*' if vm.is_running() else ''"}
+
+}
+
+
+
+def main():
+    usage = "usage: %prog [options] <vm-name>"
+    parser = OptionParser (usage)
+
+    parser.add_option ("-n", "--network", dest="network",
+                       action="store_true", default=False,
+                       help="Show network addresses assigned to VMs")
+
+    parser.add_option ("-c", "--cpu", dest="cpu",
+                       action="store_true", default=False,
+                       help="Show CPU load")
+
+    parser.add_option ("-m", "--mem", dest="mem",
+                       action="store_true", default=False,
+                       help="Show memory usage")
+
+    parser.add_option ("-d", "--disk", dest="disk",
+                       action="store_true", default=False,
+                       help="Show VM disk utilization statistics")
+
+    parser.add_option ("-i", "--ids", dest="ids",
+                       action="store_true", default=False,
+                       help="Show Qubes and Xen id#s")
+
+
+    (options, args) = parser.parse_args ()
+
+    qvm_collection = QubesVmCollection()
+    qvm_collection.lock_db_for_reading()
+    qvm_collection.load()
+    qvm_collection.unlock_db()
+
+    fields_to_display = ["name", "on", "state", "updbl", "type", "template", "netvm", "label" ]
+
+    if (options.ids):
+        fields_to_display += ["qid", "xid"]
+
+    if (options.cpu):
+        fields_to_display += ["cpu"]
+
+    if (options.mem):
+        fields_to_display += ["mem"]
+
+    if (options.network):
+        fields_to_display.remove ("template")
+        fields_to_display += ["ip", "netmask", "gateway"]
+
+    if (options.disk):
+        fields_to_display.remove ("template")
+        fields_to_display.remove ("netvm")
+        fields_to_display += ["priv-curr", "priv-max", "root-curr", "root-max", "disk" ]
+
+    
+    vms_list = [vm for vm in qvm_collection.values()]
+    no_vms = len (vms_list)
+    vms_to_display = []
+    # Frist, the NetVMs...
+    for netvm in vms_list:
+        if netvm.is_netvm():
+            vms_to_display.append (netvm)
+
+    # Now, the template, and all its AppVMs...
+    for tvm in vms_list:
+        if tvm.is_templete():
+            vms_to_display.append (tvm)
+            for appvm in vms_list:
+                if appvm.is_appvm() and appvm.template_vm.qid == tvm.qid:
+                    vms_to_display.append(appvm)
+
+    assert len(vms_to_display) == no_vms
+
+    # First calculate the maximum width of each field we want to display
+    total_width = 0;
+    for f in fields_to_display:
+        fields[f]["max_width"] = len(f)
+        for vm in vms_to_display:
+            l = len(str(eval(fields[f]["func"])))
+            if l > fields[f]["max_width"]:
+                fields[f]["max_width"] = l
+        total_width += fields[f]["max_width"]
+
+
+    # Display the header
+    s = ""
+    for f in fields_to_display:
+        fmt="{{0:-^{0}}}-+".format(fields[f]["max_width"] + 1)
+        s += fmt.format('-')
+    print s
+    s = ""
+    for f in fields_to_display:
+        fmt="{{0:>{0}}} |".format(fields[f]["max_width"] + 1)
+        s += fmt.format(f) 
+    print s
+    s = ""
+    for f in fields_to_display:
+        fmt="{{0:-^{0}}}-+".format(fields[f]["max_width"] + 1)
+        s += fmt.format('-')
+    print s
+
+    # ... and the actual data
+    for vm in vms_to_display:
+        s = ""
+        for f in fields_to_display:
+            fmt="{{0:>{0}}} |".format(fields[f]["max_width"] + 1)
+            s += fmt.format(eval(fields[f]["func"])) 
+        print s
+
+        try:
+            vm.verify_files()
+        except QubesException as err:
+            print "WARNING: VM '{0}' has corrupted files!".format(vm.name)
+
+main()

+ 219 - 0
dom0/qvm-tools/qvm-prefs

@@ -0,0 +1,219 @@
+#!/usr/bin/python2.6
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+from qubes.qubes import QubesVmCollection
+from qubes.qubes import QubesVmLabels
+from optparse import OptionParser
+import subprocess
+
+def do_list(vm):
+    label_width = 18
+    fmt="{{0:<{0}}}: {{1}}".format(label_width)
+
+    print fmt.format ("name", vm.name)
+    print fmt.format ("label", vm.label.name)
+    print fmt.format ("type", vm.type)
+    if vm.is_appvm():
+        print fmt.format ("template", vm.template_vm.name)
+    if vm.netvm_vm is not None:
+        print fmt.format ("netvm", vm.netvm_vm.name)
+    print fmt.format ("updateable?", vm.is_updateable())
+    print fmt.format ("installed by RPM?", vm.installed_by_rpm)
+    print fmt.format ("dir", vm.dir_path)
+    print fmt.format ("config", vm.conf_file)
+    if not vm.is_appvm():
+        print fmt.format ("root img", vm.root_img)
+    if vm.is_appvm():
+        print fmt.format ("root img", vm.template_vm.root_img)
+        print fmt.format ("root COW img", vm.rootcow_img)
+
+    print fmt.format ("private img", vm.private_img)
+
+
+def set_label(vms, vm, args):
+    if len (args) != 1:
+        print "Missing label name argument!"
+
+    label = args[0]
+    if 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)
+
+    vm.label = QubesVmLabels[label]
+    subprocess.check_call (["ln", "-sf", vm.label.icon_path, vm.icon_path])
+
+
+def set_netvm(vms, vm, args):
+    if len (args) != 1:
+        print "Missing netvm name argument!"
+        print "Possible values:"
+        print "1) default"
+        print "2) none"
+        print "3) <vmaname>"
+        return
+
+    netvm = args[0]
+    if netvm == "none":
+        netvm_vm = None
+        vm.uses_default_netvm = False
+    elif netvm == "default":
+        netvm_vm = vms.get_default_netvm_vm()
+        vm.uses_default_netvm = True
+    else:
+        netvm_vm = vms.get_vm_by_name (netvm)
+        if netvm_vm is None:
+            print "A VM with the name '{0}' does not exist in the system.".format(netvm)
+            exit(1)
+        if not netvm_vm.is_netvm():
+            print "VM '{0}' is not a NetVM".format(netvm)
+            exit (1)
+        vm.uses_default_netvm = False
+
+    vm.netvm_vm = netvm_vm
+
+
+def set_updateable(vms, vm, args):
+    if vm.is_updateable():
+        print "VM '{0}' is already set 'updateable', no action required.".format(vm.name)
+        return True
+
+    if vm.is_running():
+        print "Cannot change 'updateable' attribute of a running VM. Shut it down first."
+        return False
+
+    if vm.is_appvm():
+        # Check if the Template is *non* updateable...
+        if not vm.template_vm.is_updateable():
+            print "VM '{0}': Setting 'updateable' attribute to True.".format(vm.name)
+            vm.set_updateable()
+        else:
+            print "The Template VM ('{0}') is marked as 'updateable' itself!".format(vm.template_vm.name)
+            print "Cannot make the AppVM updateable too, as this might cause COW-backed storage incoherency."
+            print "If you want to make this AppVM updateable, you must first make the Template VM nonupdateable."
+            return False
+
+    if vm.is_templete():
+        # Make sure that all the AppVMs are non-updateable...
+        for appvm in vm.appvms.values():
+            if appvm.is_updateable():
+                print "At least one of the AppVMs ('{0}') of this Template VM is also marked 'updateable'.".format(appvm.name)
+                print "Cannot make the Template VM updateable too, as this might cause COW-backed storage incoherency."
+                print "If you want to make this Template VM updateable, you must first make all its decedent AppVMs nonupdateable."
+                return False
+
+
+        print "VM '{0}': Setting 'updateable' attribute to True.".format(vm.name)
+        vm.set_updateable()
+ 
+    return True
+
+def set_nonupdateable(vms, vm, args):
+    if not vm.is_updateable():
+        print "VM '{0}' is already set 'nonupdateable', no action required.".format(vm.name)
+        return True
+
+    if vm.is_running():
+        print "Cannot change 'updateable' attribute of a running VM. Shut it down first."
+        return False
+
+    if vm.is_netvm():
+        print "Why, on earth, would you want to make a NetVM 'nonupdateable'?"
+        return False
+
+
+    print "VM '{0}': Setting 'updateable' attribute to False.".format(vm.name)
+    vm.set_nonupdateable()
+    return True
+
+properties = {
+    "updateable": set_updateable,
+    "nonupdateable": set_nonupdateable,
+    "label" : set_label,
+    "netvm" : set_netvm,
+}
+
+
+def do_set(vms, vm, property, args):
+    if property not in properties.keys():
+        print "ERROR: Wrong property name: '{0}'".format(property)
+        return False
+
+    return properties[property](vms, vm, args)
+
+
+def main():
+    usage = "usage: %prog -l [options] <vm-name>\n"\
+            "usage: %prog -s [options] <vm-name> <property> [...]\n"\
+            "List/set various per-VM properties."
+
+    parser = OptionParser (usage)
+    parser.add_option ("-l", "--list", action="store_true", dest="do_list", default=False)
+    parser.add_option ("-s", "--set", action="store_true", dest="do_set", default=False)
+
+    (options, args) = parser.parse_args ()
+    if (len (args) < 1):
+        parser.error ("You must provide at least the vmname!")
+
+    vmname = args[0]
+
+    if options.do_list and options.do_set:
+        print "You cannot provide -l and -s at the same time!"
+        exit (1)
+
+
+
+    if options.do_set:
+        qvm_collection = QubesVmCollection()
+        qvm_collection.lock_db_for_writing()
+        qvm_collection.load()
+    else:
+        qvm_collection = QubesVmCollection()
+        qvm_collection.lock_db_for_reading()
+        qvm_collection.load()
+        qvm_collection.unlock_db()
+
+    vm = qvm_collection.get_vm_by_name(vmname)
+    if vm is None or vm.qid not in qvm_collection:
+        print "A VM with the name '{0}' does not exist in the system.".format(vmname)
+        exit(1)
+
+    if options.do_set:
+        if len (args) < 2:
+            print "You must specify the property you wish to set..."
+            print "Available properties:"
+            for p in properties.keys():
+                print "--> '{0}'".format(p)
+            exit (1)
+
+        property = args[1]
+        do_set(qvm_collection, vm, property, args[2:])
+        qvm_collection.save()
+        qvm_collection.unlock_db()
+
+
+    else: 
+        # do_list
+        do_list(vm)
+
+main()

+ 101 - 0
dom0/qvm-tools/qvm-remove

@@ -0,0 +1,101 @@
+#!/usr/bin/python2.6
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+from qubes.qubes import QubesVmCollection
+from optparse import OptionParser;
+
+def main():
+    usage = "usage: %prog [options] <vm-name>"
+    parser = OptionParser (usage)
+    parser.add_option ("-q", "--quiet", action="store_false", dest="verbose", default=True)
+    parser.add_option ("--just-db", action="store_true", dest="remove_from_db_only", default=False,
+                      help="Remove only from the Qubes Xen DB, do not remove any files")
+    (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_writing()
+    qvm_collection.load()
+    vm = qvm_collection.get_vm_by_name(vmname)
+    if vm is None or vm.qid not in qvm_collection:
+        print "A VM with the name '{0}' does not exist in the system.".format(vmname)
+        exit(1)
+
+    if vm.is_templete():
+        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)
+            for vm in dependent_vms:
+                print "{name:<12} (qid={qid})".format(qid=vm.qid, name=vm.name)
+            print "Please remove those VMs first, or use the --force option."
+            exit (1)
+        if qvm_collection.default_template_qid == vm.qid:
+            qvm_collection.default_template_qid = None
+
+    if vm.is_netvm():
+       if qvm_collection.default_netvm_qid == vm.qid:
+           qvm_collection.default_netvm_qid = None
+
+
+    if vm.is_running():
+        print "Cannot remove a running VM, stop it first"
+        exit (1)
+
+    if vm.installed_by_rpm and not options.remove_from_db_only:
+        if options.verbose:
+            print "This VM has been installed by RPM, use rpm -e <pkg name> to remove it!"
+            exit (1)
+
+    try:
+        if options.verbose:
+            print "--> Removing from Xen Storage..."
+        vm.remove_from_xen_storage()
+    except (IOError, OSError) as err:
+        print "Warning: {0}".format(err)
+        # Do not exit, perhaps the VM was not in the Xen store
+        # so just remove it from Qubes DB
+
+    try:
+        if vm.installed_by_rpm:
+            if options.verbose:
+                print "--> VM installed by RPM, leaving all the files on disk"
+        else:
+            if options.verbose:
+                print "--> Removing all the files on disk..."
+            #TODO:  ask for confirmation, perhaps?
+            vm.remove_from_disk()
+
+
+
+    except (IOError, OSError) as err:
+        print "Warning: {0}".format(err)
+        # Do not exit, perhaps the VM files were somehow removed
+        # so just remove it from Qubes DB
+
+
+    qvm_collection.pop(vm.qid)
+    qvm_collection.save()
+    qvm_collection.unlock_db()
+
+main()

+ 227 - 0
dom0/qvm-tools/qvm-run

@@ -0,0 +1,227 @@
+#!/usr/bin/python2.6
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+# Copyright (C) 2010  Rafal Wojtczuk  <rafal@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+from qubes.qubes import QubesVmCollection
+from qubes.qubes import QubesException
+from optparse import OptionParser
+import subprocess
+import socket
+import errno
+import dbus
+import time
+
+qubes_guid_path = "/usr/bin/qubes_guid"
+qubes_clipd_path = "/usr/bin/qclipd"
+qubes_qfilexchgd_path= "/usr/bin/qfilexchgd"
+notify_object = None
+
+# how long (in sec) to wait for VMs to shutdown
+# before killing them (when used with --wait option)
+shutdown_counter_max =  30
+
+def tray_notify(str, label, timeout = 3000):
+    notify_object.Notify("Qubes", 0, label.icon, "Qubes", str, [], [], timeout, dbus_interface="org.freedesktop.Notifications")
+
+def tray_notify_error(str, timeout = 3000):
+    notify_object.Notify("Qubes", 0, "dialog-error", "Qubes", str, [], [], timeout, dbus_interface="org.freedesktop.Notifications")
+
+
+def vm_run_cmd(vm, cmd, options):
+    if options.shutdown:
+        if options.verbose:
+            print "Shutting down VM: '{0}'...".format(vm.name)
+        subprocess.call (["/usr/sbin/xm", "shutdown", vm.name])
+        return
+
+    if options.verbose:
+        print "Running command on VM: '{0}'...".format(vm.name)
+
+    if not vm.is_running():
+        if not options.auto:
+            print "VM '{0}' is not running, please start it first, or use the '--auto' switch"
+            exit (1)
+    	try:
+            if options.verbose:
+                print "Starting the VM '{0}'...".format(vm.name)
+            if options.tray:
+                tray_notify ("Starting the '{0}' VM...".format(vm.name), label=vm.label)
+            xid = vm.start(verbose=options.verbose)
+        except (IOError, OSError, QubesException) as err:
+            print "ERROR: {0}".format(err)
+            if options.tray:
+                tray_notify_error ("Error while starting the '{0}' VM: {1}".format(vm.name, err))
+            exit (1)
+        except (MemoryError) as err:
+            print "ERROR: {0}".format(err)
+            print "Close one or more running VMs and try again."
+            if options.tray:
+                subprocess.call(["kdialog", "--error", "Not enough memory to start '{0}' VM! Close one or more running VMs and try again.".format(vm.name)])
+            exit (1)
+
+        if options.verbose:
+            print "--> Starting Qubes GUId..."
+
+        retcode = subprocess.call ([qubes_guid_path, "-d", str(xid), "-c", vm.label.color, "-e", cmd, "-i", vm.label.icon])
+        if (retcode != 0) :
+            print "ERROR: Cannot start qubes_guid!"
+            if options.tray:
+                tray_notify_error ("ERROR: Cannot start qubes_guid!")
+            exit (1)
+    else: # VM already running...
+        guid_is_running = True
+        xid = vm.get_xid()
+        s = socket.socket (socket.AF_UNIX)
+        try:
+            s.connect ("/var/run/qubes/cmd_socket.{0}".format(xid))
+        except (IOError, OSError) as e:
+            if e.errno in [errno.ENOENT,errno.ECONNREFUSED]:
+                guid_is_running = False
+            else:
+                print "ERROR: unix-connect: {0}".format(e)                                  
+                if options.tray:
+                    tray_notify_error ("ERROR: Cannot connect to GUI daemon for this VM!")
+                exit(1)
+        if guid_is_running:
+            s.send (cmd)
+            s.close()
+        else:
+            retcode = subprocess.call ([qubes_guid_path, "-d", str(xid), "-c", vm.label.color, "-e", cmd, "-i", vm.label.icon])
+            if (retcode != 0) :
+                print "ERROR: Cannot start qubes_guid!"
+                if options.tray:
+                    tray_notify_error ("ERROR: Cannot start the GUI daemon for this VM!")
+                exit (1)
+
+def main():
+    usage = "usage: %prog [options] [<vm-name>] [<cmd>]"
+    parser = OptionParser (usage)
+    parser.add_option ("-q", "--quiet", action="store_false", dest="verbose", default=True)
+    parser.add_option ("-a", "--auto", action="store_true", dest="auto", default=False,
+                       help="Auto start the VM if not running")
+    parser.add_option ("-u", "--user", action="store", dest="user", default="user",
+                       help="Run command in a VM as a specified user")
+    parser.add_option ("--tray", action="store_true", dest="tray", default=False,
+                       help="Use tray notifications instead of stdout" )
+ 
+    parser.add_option ("--all", action="store_true", dest="run_on_all_running", default=False,
+                      help="Run command on all currently running VMs")
+
+    parser.add_option ("--exclude", action="append", dest="exclude_list",
+                       help="When --all is used: exclude this VM name (might be repeated)")
+
+    parser.add_option ("--wait", action="store_true", dest="wait_for_shutdown", default=False,
+                      help="Wait for the VM(s) to shutdown")
+
+    parser.add_option ("--shutdown", action="store_true", dest="shutdown", default=False,
+                      help="Do 'xm shutdown' for the VM(s) (can be combined this with --all and --wait)")
+
+    (options, args) = parser.parse_args ()
+
+
+    if options.run_on_all_running:
+        if len(args) < 1 and not options.shutdown:
+            parser.error ("You must provide a command to execute on all the VMs.")
+        if len(args) > 1 or (options.shutdown and len(args) > 0):
+            parser.error ("To many arguments...")
+        cmdstr = args[0] if not options.shutdown else None
+    else:
+        if len (args) < 1 and options.shutdown:
+            parser.error ("You must specify the VM name to shutdown.")
+        if len (args) < 2 and not options.shutdown:
+            parser.error ("You must specify the VM name and the command to execute in the VM.")
+        if len (args) > 2 or (options.shutdown and len(args) > 1):
+            parser.error ("To many arguments...")
+        vmname = args[0]
+        cmdstr = args[1] if not options.shutdown else None
+
+    if options.tray:
+        global notify_object
+        notify_object = dbus.SessionBus().get_object("org.freedesktop.Notifications", "/org/freedesktop/Notifications")
+
+    qvm_collection = QubesVmCollection()
+    qvm_collection.lock_db_for_reading()
+    qvm_collection.load()
+    qvm_collection.unlock_db()
+
+    vms_list = []
+    if options.run_on_all_running:
+        all_vms = [vm for vm in qvm_collection.values()]
+        for vm in all_vms:
+            if options.exclude_list is not None and vm.name in options.exclude_list:
+                continue
+            if vm.qid == 0:
+                continue
+            if vm.is_running():
+                vms_list.append (vm)
+    else:
+        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)
+        vms_list.append(vm)
+
+    if options.shutdown:
+        cmd = None
+    else:
+        cmd = "{user}:{cmd}".format(user=options.user, cmd=cmdstr)
+
+    for vm in vms_list:
+        vm_run_cmd(vm, cmd, options)
+
+
+    if options.wait_for_shutdown:
+        if options.verbose:
+            print "Waiting for the VM(s) to shutdown..."
+        shutdown_counter = 0
+
+        while len (vms_list):
+            if options.verbose:
+                print "Waiting for VMs: ", [vm.name for vm in vms_list] 
+            for vm in vms_list:
+                if not vm.is_running():
+                    vms_list.remove (vm)
+                if shutdown_counter > shutdown_counter_max:
+                    # kill the VM
+                    if options.verbose:
+                        print "Killing the (apparently hanging) VM '{0}'...".format(vm.name)
+                    vm.force_shutdown()
+                    #vms_list.remove(vm)
+
+            shutdown_counter += 1
+            time.sleep (1)
+        exit (0) # there is no point in executing the other daemons in the case of --wait
+
+    retcode = subprocess.call([qubes_clipd_path])
+    if retcode != 0:
+        print "ERROR: Cannot start qclipd!"
+        if options.tray:
+            tray_notify ("ERROR: Cannot start the Qubes Clipboard Notifier!")
+
+    retcode = subprocess.call([qubes_qfilexchgd_path])
+    if retcode != 0:
+        print "ERROR: Cannot start qfilexchgd!"
+        if options.tray:
+            tray_notify ("ERROR: Cannot start the Qubes Inter-VM File Exchange Daemon!")
+
+
+main()

+ 50 - 0
dom0/qvm-tools/qvm-set-default-netvm

@@ -0,0 +1,50 @@
+#!/usr/bin/python2.6
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+from qubes.qubes import QubesVmCollection
+from optparse import OptionParser;
+
+def main():
+    usage = "usage: %prog <netvm-name>"
+    parser = OptionParser (usage)
+    (options, args) = parser.parse_args ()
+    if (len (args) != 1):
+        parser.error ("Missing argument!")
+    vmname = args[0]
+
+    qvm_collection = QubesVmCollection()
+    qvm_collection.lock_db_for_writing()
+    qvm_collection.load()
+    vm = qvm_collection.get_vm_by_name(vmname)
+    if vm is None or vm.qid not in qvm_collection:
+        print "A VM with the name '{0}' does not exist in the system.".format(vmname)
+        exit(1)
+
+    if not vm.is_netvm():
+        print "VM '{0}' is not a NetVM".format(vmname)
+        exit (1)
+
+    qvm_collection.set_default_netvm_vm(vm)
+    qvm_collection.save()
+    qvm_collection.unlock_db()
+
+main()

+ 50 - 0
dom0/qvm-tools/qvm-set-default-template

@@ -0,0 +1,50 @@
+#!/usr/bin/python2.6
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+from qubes.qubes import QubesVmCollection
+from optparse import OptionParser;
+
+def main():
+    usage = "usage: %prog <template-vm-name>"
+    parser = OptionParser (usage)
+    (options, args) = parser.parse_args ()
+    if (len (args) != 1):
+        parser.error ("Missing argument!")
+    vmname = args[0]
+
+    qvm_collection = QubesVmCollection()
+    qvm_collection.lock_db_for_writing()
+    qvm_collection.load()
+    vm = qvm_collection.get_vm_by_name(vmname)
+    if vm is None or vm.qid not in qvm_collection:
+        print "A VM with the name '{0}' does not exist in the system.".format(vmname)
+        exit(1)
+
+    if not vm.is_templete():
+        print "VM '{0}' is not a TemplateVM".format(vmname)
+        exit (1)
+
+    qvm_collection.set_default_template_vm(vm)
+    qvm_collection.save()
+    qvm_collection.unlock_db()
+
+main()

+ 72 - 0
dom0/qvm-tools/qvm-start

@@ -0,0 +1,72 @@
+#!/usr/bin/python2.6
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+from qubes.qubes import QubesVmCollection
+from qubes.qubes import QubesException
+from optparse import OptionParser
+import subprocess
+
+qubes_guid_path = "/usr/bin/qubes_guid"
+
+def main():
+    usage = "usage: %prog [options] <vm-name>"
+    parser = OptionParser (usage)
+    parser.add_option ("-q", "--quiet", action="store_false", dest="verbose", default=True)
+    parser.add_option ("--no-guid", action="store_true", dest="noguid", default=False,
+		      help="Do not start the GUId")
+    parser.add_option ("--console", action="store_true", dest="debug_console", default=False,
+                      help="Attach debugging console to the newly started VM")
+
+    (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)
+
+    try:
+        vm.verify_files()
+        xid = vm.start(debug_console=options.debug_console, verbose=options.verbose)
+    except (IOError, OSError, QubesException) as err:
+        print "ERROR: {0}".format(err)
+        exit (1)
+
+    if options.noguid:
+	exit (0)
+    if options.verbose:
+        print "--> Starting Qubes GUId..."
+
+    retcode = subprocess.call ([qubes_guid_path, "-d", str(xid), "-c", vm.label.color, "-i", vm.label.icon])
+    if (retcode != 0) :
+        print "ERROR: Cannot start qubes_guid!"
+        exit (1)
+
+
+main()

+ 14 - 0
netvm/fstab

@@ -0,0 +1,14 @@
+
+#
+# /etc/fstab
+# Created by anaconda on Thu Dec  3 11:26:49 2009
+#
+# Accessible filesystems, by reference, are maintained under '/dev/disk'
+# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
+#
+/dev/mapper/dmroot /                       ext4 defaults,noatime        1 1
+/dev/mapper/dmswap swap                    swap    defaults        0 0
+tmpfs                   /dev/shm                tmpfs   defaults        0 0
+devpts                  /dev/pts                devpts  gid=5,mode=620  0 0
+sysfs                   /sys                    sysfs   defaults        0 0
+proc                    /proc                   proc    defaults        0 0

+ 17 - 0
netvm/iptables

@@ -0,0 +1,17 @@
+# Generated by iptables-save v1.4.5 on Thu Apr  1 10:55:18 2010
+*nat
+:PREROUTING ACCEPT [3:696]
+:POSTROUTING ACCEPT [1:67]
+:OUTPUT ACCEPT [1:67]
+-A POSTROUTING -s 10.1.0.0/16 -j MASQUERADE
+COMMIT
+# Completed on Thu Apr  1 10:55:18 2010
+# Generated by iptables-save v1.4.5 on Thu Apr  1 10:55:18 2010
+*filter
+:INPUT ACCEPT [0:0]
+:FORWARD ACCEPT [0:0]
+:OUTPUT ACCEPT [0:0]
+-A INPUT -i br0 -p udp -m udp --dport 68 -j DROP
+-A INPUT -i vif+ -p udp -m udp --dport 68 -j DROP
+COMMIT
+# Completed on Thu Apr  1 10:55:18 2010

+ 55 - 0
netvm/qubes_core

@@ -0,0 +1,55 @@
+#!/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...
+    brctl addbr br0
+    gateway=$(/usr/bin/xenstore-read qubes_netvm_gateway)
+    netmask=$(/usr/bin/xenstore-read qubes_netvm_netmask)
+    network=$(/usr/bin/xenstore-read qubes_netvm_network)
+    ifconfig br0 $gateway netmask $netmask up
+    echo "1" > /proc/sys/net/ipv4/ip_forward
+    dnsmasq --listen-address $gateway --bind-interfaces
+#now done by iptables rc script
+#    iptables -t nat -A POSTROUTING -s $network/$netmask -j MASQUERADE
+	
+	success
+	echo ""
+	return 0
+}
+
+stop()
+{
+	return 0
+}
+
+case "$1" in
+  start)
+	start
+	;;
+  stop)
+	stop
+	;;
+  *)
+	echo $"Usage: $0 {start|stop}"
+	exit 3
+	;;
+esac
+
+exit $RETVAL

+ 162 - 0
rpm_spec/core-appvm.spec

@@ -0,0 +1,162 @@
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+# Copyright (C) 2010  Rafal Wojtczuk  <rafal@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+%{!?version: %define version %(cat version_vm)}
+
+Name:		qubes-core-appvm
+Version:	%{version}
+Release:	1
+Summary:	The Qubes core files for AppVM
+
+Group:		Qubes
+Vendor:		Invisible Things Lab
+License:	GPL
+URL:		http://www.qubes-os.org
+Requires:	/usr/bin/xenstore-read
+Provides:   qubes-core-vm
+
+%define _builddir %(pwd)/appvm
+
+%define kde_service_dir /usr/share/kde4/services/ServiceMenus 
+
+%description
+The Qubes core files for installation inside a Qubes AppVM.
+
+%pre
+
+mkdir -p $RPM_BUILD_ROOT/var/lib/qubes
+[ -e $RPM_BUILD_ROOT/etc/fstab ] && mv $RPM_BUILD_ROOT/etc/fstab $RPM_BUILD_ROOT/var/lib/qubes/fstab.orig
+
+%build
+make clean all
+
+%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/usr/bin
+cp qubes_add_pendrive_script qubes_penctl qvm-copy-to-vm  qvm-copy-to-vm.kde $RPM_BUILD_ROOT/usr/bin
+mkdir -p $RPM_BUILD_ROOT/%{kde_service_dir}
+cp qvm-copy.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
+
+%post
+
+if [ "$1" !=  1 ] ; then
+# do this whole %post thing only when updating for the first time...
+exit 0
+fi
+
+echo "--> Disabling SELinux..."
+sed -e s/^SELINUX=.*$/SELINUX=disabled/ </etc/selinux/config >/etc/selinux/config.processed
+mv /etc/selinux/config.processed /etc/selinux/config
+setenforce 0
+
+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
+        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!"
+
+
+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/
+fi
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(-,root,root,-)
+/etc/fstab
+/etc/init.d/qubes_core
+/usr/bin/qvm-copy-to-vm
+/usr/bin/qvm-copy-to-vm.kde
+%{kde_service_dir}/qvm-copy.desktop
+%attr(4755,root,root) /usr/bin/qubes_penctl
+/usr/bin/qubes_add_pendrive_script
+/etc/udev/rules.d/qubes.rules
+/etc/sysconfig/iptables
+%dir /var/lib/qubes
+%dir /mnt/incoming
+%dir /mnt/outgoing

+ 156 - 0
rpm_spec/core-dom0.spec

@@ -0,0 +1,156 @@
+#
+# This is the SPEC file for creating binary RPMs for the Dom0.
+#
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+# Copyright (C) 2010  Rafal Wojtczuk  <rafal@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+%{!?python_sitearch: %define python_sitearch %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)")}
+
+%{!?version: %define version %(cat version_dom0)}
+
+Name:		qubes-core-dom0
+Version:	%{version}
+Release:	1
+Summary:	The Qubes core files (Dom0-side)
+
+Group:		Qubes
+Vendor:		Invisible Things Lab
+License:	GPL
+URL:		http://www.qubes-os.org
+Requires:	python, xen-runtime, pciutils, python-inotify, python-daemon, kernel-qubes-dom0
+
+%define _builddir %(pwd)/dom0
+
+%description
+The Qubes core files for installation on Dom0.
+
+%install
+
+mkdir -p $RPM_BUILD_ROOT/etc/init.d
+cp init.d/qubes_core $RPM_BUILD_ROOT/etc/init.d/
+cp init.d/qubes_netvm $RPM_BUILD_ROOT/etc/init.d/
+
+mkdir -p $RPM_BUILD_ROOT/usr/bin/
+cp qvm-tools/qvm-* $RPM_BUILD_ROOT/usr/bin
+cp clipboard_notifier/qclipd $RPM_BUILD_ROOT/usr/bin
+cp pendrive_swapper/qfilexchgd $RPM_BUILD_ROOT/usr/bin
+
+mkdir -p $RPM_BUILD_ROOT%{python_sitearch}/qubes
+cp qvm-core/qubes.py $RPM_BUILD_ROOT%{python_sitearch}/qubes
+cp qvm-core/__init__.py $RPM_BUILD_ROOT%{python_sitearch}/qubes
+
+mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes
+cp aux-tools/patch_appvm_initramfs.sh $RPM_BUILD_ROOT/usr/lib/qubes
+cp aux-tools/unbind_pci_device.sh $RPM_BUILD_ROOT/usr/lib/qubes
+cp aux-tools/unbind_all_network_devices $RPM_BUILD_ROOT/usr/lib/qubes
+cp aux-tools/convert_apptemplate2vm.sh $RPM_BUILD_ROOT/usr/lib/qubes
+cp aux-tools/convert_dirtemplate2vm.sh $RPM_BUILD_ROOT/usr/lib/qubes
+cp aux-tools/create_apps_for_appvm.sh $RPM_BUILD_ROOT/usr/lib/qubes
+cp aux-tools/remove_appvm_appmenus.sh $RPM_BUILD_ROOT/usr/lib/qubes
+cp pendrive_swapper/qubes_pencmd $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/backup
+
+mkdir -p $RPM_BUILD_ROOT/usr/share/qubes/icons
+cp icons/*.png $RPM_BUILD_ROOT/usr/share/qubes/icons
+
+
+%post
+if [ "$1" !=  1 ] ; then
+# do this whole %post thing only when updating for the first time...
+exit 0
+fi
+
+#echo "Enabling essential services..."
+chkconfig haldaemon on
+chkconfig messagebus on
+chkconfig xenstored on
+chkconfig xend on
+chkconfig xenconsoled on
+
+chkconfig --add qubes_core || echo "WARNING: Cannot add service qubes_core!"
+chkconfig --add qubes_netvm || echo "WARNING: Cannot add service qubes_netvm!"
+
+chkconfig qubes_core on || echo "WARNING: Cannot enable service qubes_core!"
+chkconfig qubes_netvm on || echo "WARNING: Cannot enable service qubes_netvm!"
+
+if ! [ -e /var/lib/qubes/qubes.xml ]; then
+#    echo "Initializing Qubes DB..."
+    umask 007; sg qubes -c qvm-init-storage
+fi
+for i in /usr/share/qubes/icons/*.png ; do
+	xdg-icon-resource install --novendor --size 48 $i
+done
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%pre
+if ! grep -q ^qubes: /etc/group ; then
+		groupadd qubes
+fi
+
+%preun
+if [ "$1" = 0 ] ; then
+	for i in /usr/share/qubes/icons/*.png ; do
+		xdg-icon-resource uninstall --novendor --size 48 $i
+	done
+fi
+
+%postun
+if [ "$1" = 0 ] ; then
+	# no more packages left
+    chgrp root /etc/xen
+    chmod 700 /etc/xen
+    groupdel qubes
+fi
+
+%files
+%defattr(-,root,root,-)
+/etc/init.d/qubes_core
+/etc/init.d/qubes_netvm
+/usr/bin/qvm-*
+/usr/bin/qclipd
+/usr/bin/qfilexchgd
+%{python_sitearch}/qubes/qubes.py
+%{python_sitearch}/qubes/qubes.pyc
+%{python_sitearch}/qubes/qubes.pyo
+%{python_sitearch}/qubes/__init__.py
+%{python_sitearch}/qubes/__init__.pyc
+%{python_sitearch}/qubes/__init__.pyo
+/usr/lib/qubes/patch_appvm_initramfs.sh
+/usr/lib/qubes/unbind_pci_device.sh
+/usr/lib/qubes/unbind_all_network_devices
+/usr/lib/qubes/convert_apptemplate2vm.sh
+/usr/lib/qubes/convert_dirtemplate2vm.sh
+/usr/lib/qubes/create_apps_for_appvm.sh
+/usr/lib/qubes/remove_appvm_appmenus.sh
+/usr/lib/qubes/qubes_pencmd
+%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/backup
+%dir /usr/share/qubes/icons/*.png

+ 142 - 0
rpm_spec/core-netvm.spec

@@ -0,0 +1,142 @@
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+# Copyright (C) 2010  Rafal Wojtczuk  <rafal@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+%{!?version: %define version %(cat version_vm)}
+
+Name:		qubes-core-netvm
+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
+Provides:   qubes-core-vm
+
+%define _builddir %(pwd)/netvm
+
+%description
+The Qubes core files for installation inside a Qubes NetVM.
+
+%pre
+
+mkdir -p $RPM_BUILD_ROOT/var/lib/qubes
+[ -e $RPM_BUILD_ROOT/etc/fstab ] && mv $RPM_BUILD_ROOT/etc/fstab $RPM_BUILD_ROOT/var/lib/qubes/fstab.orig
+
+%build
+
+%install
+
+mkdir -p $RPM_BUILD_ROOT/etc/sysconfig
+cp 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/
+mkdir -p $RPM_BUILD_ROOT/var/lib/qubes
+
+%post
+
+if [ "$1" !=  1 ] ; then
+# do this whole %post thing only when updating for the first time...
+exit 0
+fi
+
+echo "--> Disabling SELinux..."
+sed -e s/^SELINUX=.*$/SELINUX=disabled/ </etc/selinux/config >/etc/selinux/config.processed
+mv /etc/selinux/config.processed /etc/selinux/config
+setenforce 0
+
+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
+        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!"
+
+
+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
+
+%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/
+fi
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(-,root,root,-)
+/etc/fstab
+/etc/sysconfig/iptables
+/etc/init.d/qubes_core
+%dir /var/lib/qubes

+ 78 - 0
rpm_spec/dom0-cleanup.spec

@@ -0,0 +1,78 @@
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010  Joanna Rutkowska <joanna@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+#
+#
+
+Name:		qubes-dom0-cleanup
+Version:	0.2.2
+Release:	1
+Summary:	Additional tools that cleans up some unnecessary stuff in Qubes's Dom0
+
+Group:		Qubes
+Vendor:		Invisible Things Lab
+License:	GPL
+URL:		http://www.qubes-os.org
+Requires:	qubes-core-dom0
+
+%define _builddir %(pwd)/dom0
+
+%description
+Additional tools that cleans up some unnecessary stuff in Qubes's Dom0
+
+%install
+
+mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes
+cp aux-tools/check_and_remove_appmenu.sh $RPM_BUILD_ROOT/usr/lib/qubes
+cp aux-tools/remove_dom0_appmenus.sh $RPM_BUILD_ROOT/usr/lib/qubes
+
+%post
+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
+        chkconfig $srv off
+done
+
+#echo "--> Enabling essential services..."
+chkconfig abrtd on
+chkconfig haldaemon on
+chkconfig messagebus on
+chkconfig xenstored on
+chkconfig xend on
+chkconfig xenconsoled on
+chkconfig qubes_core on || echo "WARNING: Cannot enable service qubes_core!"
+chkconfig qubes_netvm on || echo "WARNING: Cannot enable service qubes_core!"
+
+/usr/lib/qubes/remove_dom0_appmenus.sh
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%postun
+
+mv /var/lib/qubes/backup/removed-apps/* /usr/share/applications
+xdg-desktop-menu forceupdate
+
+%files
+/usr/lib/qubes/check_and_remove_appmenu.sh
+/usr/lib/qubes/remove_dom0_appmenus.sh

+ 1 - 0
version_dom0

@@ -0,0 +1 @@
+1.0.0

+ 1 - 0
version_vm

@@ -0,0 +1 @@
+1.0.0