Index: xen-3.2.1-testing/tools/python/xen/xend/server/HalDaemon.py
===================================================================
--- /dev/null
+++ xen-3.2.1-testing/tools/python/xen/xend/server/HalDaemon.py
@@ -0,0 +1,238 @@
+#!/usr/bin/env python
+#  -*- mode: python; -*-
+#============================================================================
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#============================================================================
+# Copyright (C) 2007 Pat Campbell <plc@novell.com>
+# Copyright (C) 2007 Novell Inc.
+#============================================================================
+
+"""hald (Hardware Abstraction Layer Daemon) watcher for Xen management
+   of removable block device media.
+
+"""
+
+import gobject
+import dbus
+import dbus.glib
+import os
+import types
+import sys
+import signal
+import traceback
+from xen.xend.xenstore.xstransact import xstransact, complete
+from xen.xend.xenstore.xsutil import xshandle
+from xen.xend import PrettyPrint
+from xen.xend import XendLogging
+from xen.xend.XendLogging import log
+
+class HalDaemon:
+    """The Hald block device watcher for XEN
+    """
+
+    """Default path to the log file. """
+    logfile_default = "/var/log/xen/hald.log"
+
+    """Default level of information to be logged."""
+    loglevel_default = 'INFO'
+
+    def __init__(self):
+
+        XendLogging.init(self.logfile_default, self.loglevel_default)
+        log.debug( "%s", "__init__")
+
+        self.udi_dict = {}
+        self.debug = 0
+        self.dbpath = "/local/domain/0/backend/vbd"
+        self.bus = dbus.SystemBus()
+        self.hal_manager_obj = self.bus.get_object('org.freedesktop.Hal', '/org/freedesktop/Hal/Manager')
+        self.hal_manager = dbus.Interface( self.hal_manager_obj, 'org.freedesktop.Hal.Manager')
+        self.gatherBlockDevices()
+        self.registerDeviceCallbacks()
+
+    def run(self):
+        log.debug( "%s", "In new run" );
+        try:
+            self.mainloop = gobject.MainLoop()
+            self.mainloop.run()
+        except KeyboardInterrupt, ex:
+            log.debug('Keyboard exception handler: %s', ex )
+            self.mainloop.quit()
+        except Exception, ex:
+            log.debug('Generic exception handler: %s', ex )
+            self.mainloop.quit()
+
+    def __del__(self):
+        log.debug( "%s", "In del " );
+        self.unRegisterDeviceCallbacks()
+        self.mainloop.quit()
+
+    def shutdown(self):
+        log.debug( "%s", "In shutdown now " );
+        self.unRegisterDeviceCallbacks()
+        self.mainloop.quit()
+
+    def stop(self):
+        log.debug( "%s", "In stop now " );
+        self.unRegisterDeviceCallbacks()
+        self.mainloop.quit()
+
+    def gatherBlockDevices(self):
+
+        # Get all the current devices from hal and save in a dictionary
+        try:
+            device_names = self.hal_manager.GetAllDevices()
+            i = 0;
+            for name in device_names:
+               #log.debug("device name, device=%s",name)
+               dev_obj = self.bus.get_object ('org.freedesktop.Hal', name)
+               dev = dbus.Interface (dev_obj, 'org.freedesktop.Hal.Device')
+               dev_properties = dev_obj.GetAllProperties(dbus_interface="org.freedesktop.Hal.Device")
+               if dev_properties.has_key('block.device'):
+                   dev_str = dev_properties['block.device']
+                   dev_major = dev_properties['block.major']
+                   dev_minor = dev_properties['block.minor']
+                   udi_info = {}
+                   udi_info['device'] = dev_str
+                   udi_info['major'] = dev_major
+                   udi_info['minor'] = dev_minor
+                   udi_info['udi'] = name
+                   self.udi_dict[i] = udi_info
+                   i = i + 1
+        except Exception, ex:
+            print >>sys.stderr, 'Exception gathering block devices:', ex
+            log.warn("Exception gathering block devices (%s)",ex)
+
+    #
+    def registerDeviceCallbacks(self):
+        # setup the callbacks for when the gdl changes
+        self.hal_manager.connect_to_signal('DeviceAdded', self.device_added_callback)
+        self.hal_manager.connect_to_signal('DeviceRemoved', self.device_removed_callback)
+
+    #
+    def unRegisterDeviceCallbacks(self):
+        # setup the callbacks for when the gdl changes
+        self.hal_manager.remove_signal_receiver(self.device_added_callback,'DeviceAdded')
+        self.hal_manager.remove_signal_receiver(self.device_removed_callback,'DeviceRemoved')
+
+    #
+    def device_removed_callback(self,udi):
+        log.debug('UDI %s was removed',udi)
+        self.show_dict(self.udi_dict)
+        for key in self.udi_dict:
+            udi_info = self.udi_dict[key]
+            if udi_info['udi'] == udi:
+                device = udi_info['device']
+                major = udi_info['major']
+                minor = udi_info['minor']
+                self.change_xenstore( "remove", device, major, minor)
+
+    # Adds device to dictionary if not already there
+    def device_added_callback(self,udi):
+        log.debug('UDI %s was added', udi)
+        self.show_dict(self.udi_dict)
+        dev_obj = self.bus.get_object ('org.freedesktop.Hal', udi)
+        dev = dbus.Interface (dev_obj, 'org.freedesktop.Hal.Device')
+        device = dev.GetProperty ('block.device')
+        major = dev.GetProperty ('block.major')
+        minor = dev.GetProperty ('block.minor')
+        udi_info = {}
+        udi_info['device'] = device
+        udi_info['major'] = major
+        udi_info['minor'] = minor
+        udi_info['udi'] = udi
+        already = 0
+        cnt = 0;
+        for key in self.udi_dict:
+            info = self.udi_dict[key]
+            if info['udi'] == udi:
+                already = 1
+                break
+            cnt = cnt + 1
+        if already == 0:
+           self.udi_dict[cnt] = udi_info;
+           log.debug('UDI %s was added, device:%s major:%s minor:%s index:%d\n', udi, device, major, minor, cnt)
+        self.change_xenstore( "add", device, major, minor)
+
+    # Debug helper, shows dictionary contents
+    def show_dict(self,dict=None):
+        if self.debug == 0 :
+            return
+        if dict == None :
+            dict = self.udi_dict
+        for key in dict:
+            log.debug('udi_info %s udi_info:%s',key,dict[key])
+
+    # Set or clear xenstore media-present depending on the action argument
+    #  for every vbd that has this block device
+    def change_xenstore(self,action, device, major, minor):
+        domains = xstransact.List(self.dbpath)
+        log.debug('domains: %s', domains)
+        for domain in domains:   # for each domain
+            devices = xstransact.List( self.dbpath + '/' + domain)
+            log.debug('devices: %s',devices)
+            for device in devices:  # for each vbd device
+               str = device.split('/')
+               vbd_type = None;
+               vbd_physical_device = None
+               vbd_media = None
+               vbd_device_path = self.dbpath + '/' + domain + '/' + device
+               listing = xstransact.List(vbd_device_path)
+               for entry in listing: # for each entry
+                   item = self.dbpath + '/' + entry
+                   value = xstransact.Read( vbd_device_path + '/' + entry)
+                   log.debug('%s=%s',item,value)
+                   if item.find('media-present') != -1:
+                       vbd_media = item;
+                       vbd_media_path = item
+                   if item.find('physical-device') != -1:
+                       vbd_physical_device = value;
+                   if item.find('type') != -1:
+                       vbd_type = value;
+               if vbd_type is not None and vbd_physical_device is not None and vbd_media is not None :
+                   inode = vbd_physical_device.split(':')
+                   imajor = parse_hex(inode[0])
+                   iminor = parse_hex(inode[1])
+                   log.debug("action:%s major:%s- minor:%s- imajor:%s- iminor:%s- inode: %s",
+                           action,major,minor, imajor, iminor, inode)
+                   if int(imajor) == int(major) and int(iminor) == int(minor):
+                       if action == "add":
+                           xs_dict = {'media': "1"}
+                           xstransact.Write(vbd_device_path, 'media-present', "1" )
+                           log.debug("wrote xenstore media-present 1 path:%s",vbd_media_path)
+                       else:
+                           xstransact.Write(vbd_device_path, 'media-present', "0" )
+                           log.debug("wrote xenstore media 0 path:%s",vbd_media_path)
+
+def mylog( fmt, *args):
+    f = open('/tmp/haldaemon.log', 'a')
+    print >>f, "HalDaemon ", fmt % args
+    f.close()
+
+
+def parse_hex(val):
+    try:
+        if isinstance(val, types.StringTypes):
+            return int(val, 16)
+        else:
+            return val
+    except ValueError:
+        return None
+
+if __name__ == "__main__":
+    watcher = HalDaemon()
+    watcher.run()
+    print 'Falling off end'
+
+
Index: xen-3.2.1-testing/tools/python/xen/xend/server/Hald.py
===================================================================
--- /dev/null
+++ xen-3.2.1-testing/tools/python/xen/xend/server/Hald.py
@@ -0,0 +1,125 @@
+#============================================================================
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of version 2.1 of the GNU Lesser General Public
+# License as published by the Free Software Foundation.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#============================================================================
+# Copyright (C) 2007 Pat Campbell <plc@novell.com>
+# Copyright (C) 2007 Novell Inc.
+#============================================================================
+
+import errno
+import types
+import os
+import sys
+import time
+import signal
+from traceback import print_exc
+
+from xen.xend.XendLogging import log
+
+class Hald:
+    def __init__(self):
+        self.ready = False
+        self.running = True
+
+    def run(self):
+        """Starts the HalDaemon process
+        """
+        self.ready = True
+        try:
+            myfile =  self.find("xen/xend/server/HalDaemon.py")
+            args = (["python", myfile ])
+            self.pid = self.daemonize("python", args )
+            #log.debug( "%s %s pid:%d", "Hald.py starting ", args, self.pid )
+        except:
+            self.pid = -1
+            log.debug("Unable to start HalDaemon process")
+
+    def shutdown(self):
+        """Shutdown the HalDaemon process
+        """
+        log.debug("%s  pid:%d", "Hald.shutdown()", self.pid)
+        self.running = False
+        self.ready = False
+        if self.pid != -1:
+            try:
+                os.kill(self.pid, signal.SIGINT)
+            except:
+                print_exc()
+
+    def daemonize(self,prog, args):
+        """Runs a program as a daemon with the list of arguments.  Returns the PID
+        of the daemonized program, or returns 0 on error.
+        Copied from xm/create.py instead of importing to reduce coupling
+        """
+        r, w = os.pipe()
+        pid = os.fork()
+
+        if pid == 0:
+            os.close(r)
+            w = os.fdopen(w, 'w')
+            os.setsid()
+            try:
+                pid2 = os.fork()
+            except:
+                pid2 = None
+            if pid2 == 0:
+                os.chdir("/")
+                env = os.environ.copy()
+                env['PYTHONPATH'] = self.getpythonpath()
+                for fd in range(0, 256):
+                    try:
+                        os.close(fd)
+                    except:
+                        pass
+                os.open("/dev/null", os.O_RDWR)
+                os.dup2(0, 1)
+                os.dup2(0, 2)
+                os.execvpe(prog, args, env)
+                os._exit(1)
+            else:
+                w.write(str(pid2 or 0))
+                w.close()
+                os._exit(0)
+        os.close(w)
+        r = os.fdopen(r)
+        daemon_pid = int(r.read())
+        r.close()
+        os.waitpid(pid, 0)
+        #log.debug( "daemon_pid: %d", daemon_pid )
+        return daemon_pid
+
+    def getpythonpath(self):
+        str = " "
+        for p in sys.path:
+            if str != " ":
+                str = str + ":" + p
+            else:
+                if str != "":
+                   str = p
+        return str
+
+    def find(self,path, matchFunc=os.path.isfile):
+        """Find a module in the sys.path
+        From web page: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52224
+        """
+        for dirname in sys.path:
+            candidate = os.path.join(dirname, path)
+            if matchFunc(candidate):
+                return candidate
+        raise Error("Can't find file %s" % path)
+
+if __name__ == "__main__":
+    watcher = Hald()
+    watcher.run()
+    time.sleep(10)
+    watcher.shutdown()
Index: xen-3.2.1-testing/tools/python/xen/xend/server/SrvServer.py
===================================================================
--- xen-3.2.1-testing.orig/tools/python/xen/xend/server/SrvServer.py
+++ xen-3.2.1-testing/tools/python/xen/xend/server/SrvServer.py
@@ -56,6 +56,7 @@ from xen.web.SrvDir import SrvDir
 
 from SrvRoot import SrvRoot
 from XMLRPCServer import XMLRPCServer
+from xen.xend.server.Hald import Hald
 
 xoptions = XendOptions.instance()
 
@@ -245,6 +246,8 @@ def _loadConfig(servers, root, reload):
     if xoptions.get_xend_unix_xmlrpc_server():
         servers.add(XMLRPCServer(XendAPI.AUTH_PAM, False))
 
+    servers.add(Hald())
+
 
 def create():
     root = SrvDir()
Index: xen-3.2.1-testing/tools/ioemu/xenstore.c
===================================================================
--- xen-3.2.1-testing.orig/tools/ioemu/xenstore.c
+++ xen-3.2.1-testing/tools/ioemu/xenstore.c
@@ -232,6 +232,16 @@ void xenstore_parse_domain_config(int do
                 fprintf(stderr, "qemu: could not open hard disk image '%s'\n",
                         params);
         }
+        /* if pyhsical CDROM put a watch on media-present after creating it */
+        if (type && !strcmp(type, "cdrom") && drv && !strcmp(drv, "phy")) {
+            if (pasprintf(&buf, "%s/media-present", bpath) != -1) {
+                if (bdrv_is_inserted(bs_table[hd_index + (is_scsi ? MAX_DISKS : 0)]))
+                    xs_write(xsh, XBT_NULL, buf, "1", strlen("1"));
+                else
+                    xs_write(xsh, XBT_NULL, buf, "0", strlen("0"));
+                xs_watch(xsh, buf, "media-present");
+            }
+        }
     }
 
     /* Set a watch for log-dirty requests from the migration tools */
@@ -425,6 +435,50 @@ void xenstore_record_dm_state(char *stat
     free(path);
 }
 
+void xenstore_process_media_change_event(char **vec)
+{
+    char *media_present = NULL;
+    unsigned int len;
+
+    media_present = xs_read(xsh, XBT_NULL, vec[XS_WATCH_PATH], &len);
+
+    if (media_present) {
+        BlockDriverState *bs;
+        char *buf = NULL, *cp = NULL, *path = NULL, *dev = NULL;
+
+        path = strdup(vec[XS_WATCH_PATH]);
+        cp = strstr(path, "media-present");
+        if (cp){
+            *(cp-1) = '\0';
+            pasprintf(&buf, "%s/dev", path);
+            dev = xs_read(xsh, XBT_NULL, buf, &len);
+            if (dev) {
+                if ( !strncmp(dev, "xvd", 3)) {
+                    memmove(dev, dev+1, strlen(dev));
+                    dev[0] = 'h';
+                    dev[1] = 'd';
+                }
+                bs = bdrv_find(dev);
+                if (!bs) {
+                    term_printf("device not found\n");
+                    return;
+                }
+                if (strcmp(media_present, "0") == 0 && bs) {
+                    bdrv_close(bs);
+                }
+                else if (strcmp(media_present, "1") == 0 &&
+                        bs != NULL && bs->drv == NULL) {
+                    if (bdrv_open(bs, bs->filename, 0 /* snapshot */) < 0) {
+                        fprintf(logfile, "%s() qemu: could not open cdrom disk '%s'\n",
+                                __func__, bs->filename);
+                    }
+                    bs->media_changed = 1;
+                }
+            }
+        }
+    }
+}
+
 void xenstore_process_event(void *opaque)
 {
     char **vec, *offset, *bpath = NULL, *buf = NULL, *drv = NULL, *image = NULL;
@@ -444,6 +498,11 @@ void xenstore_process_event(void *opaque
         goto out;
     }
 
+    if (!strcmp(vec[XS_WATCH_TOKEN], "media-present")) {
+        xenstore_process_media_change_event(vec);
+        goto out;
+    }
+
     if (strncmp(vec[XS_WATCH_TOKEN], "hd", 2) ||
         strlen(vec[XS_WATCH_TOKEN]) != 3)
         goto out;
Index: xen-3.2.1-testing/tools/ioemu/hw/ide.c
===================================================================
--- xen-3.2.1-testing.orig/tools/ioemu/hw/ide.c
+++ xen-3.2.1-testing/tools/ioemu/hw/ide.c
@@ -350,6 +350,7 @@ typedef struct IDEState {
     uint8_t io_buffer[MAX_MULT_SECTORS*512 + 4];
     QEMUTimer *sector_write_timer; /* only used for win2k instal hack */
     uint32_t irq_count; /* counts IRQs when using win2k install hack */
+    uint8_t send_ua;    /* send SENSE_UNIT_ATTENTION on next ready */
 } IDEState;
 
 #define BM_STATUS_DMAING 0x01
@@ -1446,8 +1447,15 @@ static void ide_atapi_cmd(IDEState *s)
     switch(s->io_buffer[0]) {
     case GPCMD_TEST_UNIT_READY:
         if (bdrv_is_inserted(s->bs)) {
+            if (s->send_ua) {
+                ide_atapi_cmd_error(s, SENSE_UNIT_ATTENTION,
+                                    ASC_MEDIUM_NOT_PRESENT);
+                s->send_ua = 0;
+                break;
+            }
             ide_atapi_cmd_ok(s);
         } else {
+            s->send_ua = 1;
             ide_atapi_cmd_error(s, SENSE_NOT_READY, 
                                 ASC_MEDIUM_NOT_PRESENT);
             xenstore_check_new_media_present(1000);