/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 *
 * @APPLE_LICENSE_HEADER_END@
 */
#include <libc.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach/message.h>
#include <servers/bootstrap.h>
#include <mach/mach_error.h>
#include <pthread.h>
#include <assert.h>


#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <dev/disk.h>

#include <IOKit/OSMessageNotification.h>
#include <IOKit/IOKitLib.h>


#include "ServerToClient.h"
#include "ClientToServer.h"
#include "DiskArbitrationTypes.h"
#include "GetRegistry.h"
#include "FSParticular.h"

#include <mach/mach_interface.h>
#include <IOKit/IOBSD.h>
#include <IOKit/storage/IOMedia.h>

#include <syslog.h> /* bek - 12/18/98 */

#include "DiskArbitrationServerMain.h"

mach_port_t ioMasterPort;

void autodiskmount(int ownership);
	
/*
	External globals
*/

#ifdef DEBUG
int gDebug = 1;
#else
int gDebug = 0;
#endif

int gDaemon;

/*
	External routines
*/

extern void ClientDeath(mach_port_t clientPort);

extern boolean_t ClientToServer_server(mach_msg_header_t * msg, mach_msg_header_t * reply);

/*
	Public globals
*/

/*
	Static constants
*/

enum {
	kMsgSize = 2048, // bek - 7/15/99 - Messages can contain path names as long as PATH_MAX = 1024
};

/*
	Static globals
*/

static char * programName = NULL;

mach_port_t gNotifyPort = MACH_PORT_NULL;

static int gBlueBoxBootVolume = -1;

/*
	Static function prototypes
*/

static kern_return_t InitNotifyPort(void);
static mach_port_t GetNotifyPort(void);
static boolean_t MessageIsNotificationDeath(const mach_msg_header_t *msg, mach_port_t *deadPort);

static int NextSequenceNumber( void );

struct IONotificationMsg {
	mach_msg_header_t		msgHdr;
	OSNotificationHeader	notifyHeader;
	mach_msg_trailer_t		trailer;
};

CFStringRef yankedHeader;
CFStringRef yankedMessage;
CFStringRef unrecognizedHeader;
CFStringRef unrecognizedMessage;
CFStringRef ejectString;
CFStringRef ignoreString;
CFStringRef initString;

typedef struct IONotificationMsg IONotificationMsg, * IONotificationMsgPtr;

static void HandleIONotificationMsg( IONotificationMsgPtr ioNotificationMsgPtr );

void * YankedDiskThread(void * args)
{
        // display UI to inform the user that an disk was yanked badly.
        SInt32			retval = ERR_SUCCESS;
        CFURLRef		daFrameworkURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR("/System/Library/PrivateFrameworks/DiskArbitration.framework"), kCFURLPOSIXPathStyle, TRUE);

        // use the url of the DiskArbitration Framework and put a Localizable.strings file in there.

        retval = CFUserNotificationDisplayNotice(0.0, kCFUserNotificationStopAlertLevel, NULL, NULL, daFrameworkURL, yankedHeader, yankedMessage, NULL);

        CFRelease(daFrameworkURL);
        //CFRelease(daBundle);

        return NULL;
}

// Disconnected arbitration messages
void StartYankedDiskMessage()
{
    pthread_attr_t attr;
    pthread_t tid;

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    pthread_create(&tid, &attr, YankedDiskThread, nil);
    pthread_attr_destroy(&attr);
}


/*
	Static functions
*/

//------------------------------------------------------------------------

static kern_return_t InitNotifyPort(void)
{
	kern_return_t r;

	r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &gNotifyPort);
	if (r != KERN_SUCCESS)
	{
		LogErrorMessage("(%s:%d) mach_port_allocate failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
		return MACH_PORT_NULL;
	}

	dwarning(("%s: gNotifyPort = $%08x\n", programName, (int)gNotifyPort));

	return r;

} // InitNotifyPort
	
static mach_port_t GetNotifyPort(void)
{

	return gNotifyPort;
	
} // GetNotifyPort

//------------------------------------------------------------------------

static boolean_t MessageIsNotificationDeath(const mach_msg_header_t *msg, mach_port_t *deadPort)
{
	boolean_t result;
	
	if ( GetNotifyPort() == msg->msgh_local_port )
	{
		if ( MACH_NOTIFY_DEAD_NAME == msg->msgh_id )
		{
			// If the caller supplied a pointer, fill it with the dead port's name
			if ( deadPort != NULL )
			{
				const mach_dead_name_notification_t *deathMessage = (const mach_dead_name_notification_t *)msg;
				*deadPort = deathMessage->not_port;
			}
			result = TRUE;
			goto Return;
		}
                else if ( INTERNAL_MSG == msg->msgh_id) 
                {
                        // do nothing
                        dwarning(("Internal message received"));
						result = FALSE;
						goto Return;
                }
                else
                {
			/* A message for the notification port other than dead name notification */
			// This should never happen.  Log it.
			LogErrorMessage("(%s:%d) received unrecognized message (id=0x%x) on notify port\n", __FILE__, __LINE__, (int)msg->msgh_id);
			result = FALSE;
			goto Return;
		}
	}
	else
	{
		/* Not a message for the notification port */
		result = FALSE;
		goto Return;
	}

Return:
	return result;

} // MessageIsNotificationDeath

//------------------------------------------------------------------------

void LogErrorMessage(const char *format, ...)
{
        va_list ap;
        
        va_start(ap, format);

        // syslog first....
        openlog(programName, LOG_PID | LOG_CONS, LOG_DAEMON);
        // This should be LOG_WARNING instead of LOG_ERR according to "man 3 syslog", but experiments show that would prevent it from appearing in the log.
        vsyslog(LOG_ERR, format, ap);
        closelog();

        // ...printf second
//        fprintf(stderr, "%s [%d]", programName, getpid());
        vfprintf(stderr, format, ap);

        va_end(ap);

} // LogErrorMessage

//------------------------------------------------------------------------

static void HandleIONotificationMsg( IONotificationMsgPtr msg )
{
	kern_return_t r;
	unsigned long int  	notifyType;
	unsigned long int  	ref;
	
	r = OSGetNotificationFromMessage(
		&msg->msgHdr,		//	mach_msg_header_t     * msg,
		0,			//	unsigned int            index,
		&notifyType,		//	unsigned long int     * type,
		&ref,			//	unsigned long int     * reference,
		0,			//	void                 ** content,
		0 );			//	vm_size_t             * size
	if ( r )
	{
		dwarning(("%s(%d): OSGetNotificationFromMessage returned error %d\n", __FUNCTION__, __LINE__, (int)r));
		goto Return;
	}

	// we passed a string for the refcon
	dwarning(("got notification, type=%d(%s), local=%d, remote=%d\n",
			(int)notifyType, (char*)ref,
			(int)msg->msgHdr.msgh_local_port,
			(int)msg->msgHdr.msgh_remote_port ));
			
	// remote port is the notification (an iterator_t) that fired

        if (notifyType != 102) {  // anything except terminate
            GetDisksFromRegistry( (io_iterator_t) msg->msgHdr.msgh_remote_port );

            autodiskmount(FALSE);
        } else {
            io_iterator_t iter = (io_iterator_t) msg->msgHdr.msgh_remote_port;

            io_registry_entry_t	entry;

            while ( entry = IOIteratorNext( iter ) )
            {
                kern_return_t		kr;
                CFDictionaryRef properties = 0; // (needs release)
                CFStringRef     string     = 0; // (don't release)
                io_name_t		ioMediaName;
                char *          ioBSDName  = NULL; // (needs release)

                DiskPtr dp;


                kr = IORegistryEntryGetName(entry, ioMediaName);
                if ( KERN_SUCCESS != kr )
                {
                        dwarning(("can't obtain name for media object\n"));
                        continue;
                }

                
                kr = IORegistryEntryCreateCFProperties(entry, &properties, kCFAllocatorDefault, kNilOptions);
                if ( KERN_SUCCESS != kr )
                {
                        dwarning(("can't obtain properties for '%s'\n", ioMediaName));
                        continue;
                }

                assert(CFGetTypeID(properties) == CFDictionaryGetTypeID());

                // BSDName

                string = (CFStringRef) CFDictionaryGetValue(properties, CFSTR(kIOBSDNameKey));

                if (!string) {
                        // whoa no kIOBSDName.  Ouch.  get out now.
                        dwarning(("can't obtain properties for ioBSDName\n"));
                        continue;
                }
                
                ioBSDName = daCreateCStringFromCFString(string);
                // figure out the disk and see if it exists, and if so, remove it ...

                /* Is there an existing disk on our list with this IOBSDName? */

                dp = LookupDiskByIOBSDName( ioBSDName );
                
                if (dp != NULL) {  // disk has been forcefully removed in some strange manner

                        if (dp->mountpoint && strlen(dp->mountpoint)) {
                                DiskPtr wholeDiskPtr = LookupWholeDiskForThisPartition( dp );
                                if (wholeDiskPtr && !wholeDiskPtr->wholeDiskHasBeenYanked) {
                                        wholeDiskPtr->wholeDiskHasBeenYanked++;
                                        StartYankedDiskMessage();
                               }
                        }
                        
                        kr = UnmountDisk( dp, TRUE );
                    	SendUnmountPostNotifyMsgsForOnePartition( dp->ioBSDName, 0, 0 );
                    	SendEjectPostNotifyMsgsForOnePartition( dp->ioBSDName, 0, 0 );
                   	FreeDisk( dp );

                }
                IOObjectRelease(entry);
                if ( properties )	CFRelease( properties );
                if ( ioBSDName )	free( ioBSDName );
            }
        }


Return:
	return;

} // HandleIONotificationMsg

//------------------------------------------------------------------------

/*
	Public functions
*/

//------------------------------------------------------------------------

/*
-- Allocates memory for new client record.
-- Links it into the existing list.
-- Fills in the <port> and <flags> fields and sets <state>.
-- Increments the global count of client records.
*/

ClientPtr NewClient( mach_port_t port, unsigned pid, unsigned flags )
{
	ClientPtr result;

	/* Allocate memory */

	result = (ClientPtr) malloc( sizeof( * result ) );
	if ( result == NULL )
	{
		dwarning(("%s(port = $%08x, pid = %d, flags = $%08x): malloc failed!\n", __FUNCTION__, port, pid, flags));
		/* result = NULL; */
		goto Return;
	}

	/* Link it onto the front of the list */

	result->next = g.Clients;
	g.Clients = result;

	/* Fill in the fields */

	result->port = port;
	result->pid = pid;
	result->flags = flags;
	result->state = kDiskStateNew;
	result->numAcksRequired = 0;
	
	/* Increment count */

	g.NumClients ++ ;
	
Return:
	return result;

} // NewClient

//------------------------------------------------------------------------

void PrintClient(ClientPtr clientPtr)
{
	if ( ! clientPtr ) return;

	dwarning(("port = $%08x, pid = %5d, flags = $%08x, numAcksRequired = %d\n", clientPtr->port, clientPtr->pid, clientPtr->flags, clientPtr->numAcksRequired));

} // PrintClients

//------------------------------------------------------------------------

void PrintClients(void)
{
	ClientPtr clientPtr;
	int i;
	
	if ( ! gDebug ) goto Return;

	dwarning(("g.NumClients = %d\n", g.NumClients));
	for (clientPtr = g.Clients, i = 0; clientPtr != NULL; clientPtr = clientPtr->next, i++)
	{
		PrintClient( clientPtr );
	}

Return:
	return;

} // PrintClients

//------------------------------------------------------------------------

ClientPtr LookupClientByPID( pid_t pid )
{
	ClientPtr clientPtr;
	
	for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
	{
		if ( clientPtr->pid == pid )
		{
			goto Return;
		}
	}

Return:
	return clientPtr;

} // LookupClientByPID

//------------------------------------------------------------------------

/*
-- Allocates memory for new disk record.
-- Links it into the existing list.
-- Fills in the <ioBSDName>, <ioContent>, <mountpoint>, <ioDeviceTreePath>, and <flags> fields and sets <state> = kDiskStateNew.
-- Makes its own copies of input strings - doesn't retain pointers to its arguments.
-- Increments the global count of disk records.
*/

DiskPtr NewDisk(	char * ioBSDName,
					int ioBSDUnit,
					char * ioContentOrNull,
					DiskFamily family,
					char * mountpoint,
					char * ioMediaNameOrNull,
					char * ioDeviceTreePathOrNull,
                                        io_object_t	service,
					unsigned flags )
{
	DiskPtr result;
	
	dwarning(("%s(ioBSDName = '%s', ioContentOrNull = '%s', family = %d, mountpoint = '%s', ioMediaNameOrNull = '%s', ioDeviceTreePathOrNull = '%s', flags = $%08x)\n",
				__FUNCTION__,
				ioBSDName,
				ioContentOrNull ? ioContentOrNull : "(NULL)",
				family,
				mountpoint,
				ioMediaNameOrNull ? ioMediaNameOrNull : "(NULL)",
				ioDeviceTreePathOrNull ? ioDeviceTreePathOrNull : "(NULL)",
				flags ));

	/* Allocate memory */

	result = (DiskPtr) malloc( sizeof( * result ) );
	if ( result == NULL )
	{
		dwarning(("%s(...): malloc failed!\n", __FUNCTION__));
		/* result = NULL; */
		goto Return;
	}

	bzero( result, sizeof( * result ) );

	/* Link it onto the front of the list */

	result->next = g.Disks;
	g.Disks = result;

	/* Fill in the fields */

	result->ioBSDName = strdup( ioBSDName ? ioBSDName : "" );
	result->ioBSDUnit = ioBSDUnit;
	result->ioContent = strdup( ioContentOrNull ? ioContentOrNull : "" );
	result->family = family;
	result->mountpoint = strdup( mountpoint ? mountpoint : "" );
        result->service = service;
        dwarning(("setting a diskPtr->service\n"));


	if ( ioMediaNameOrNull )
	{
	    result->ioMediaNameOrNull = strdup( ioMediaNameOrNull );
	}
	else
	{
	    result->ioMediaNameOrNull = NULL;
	}
	
	result->ioDeviceTreePath = strdup( ioDeviceTreePathOrNull ? ioDeviceTreePathOrNull : "" );
	
	result->flags = flags;

	/* Assign a sequence number if mounted, otherwise -1.   See DiskSetMountpoint(). */

	result->sequenceNumber = ( 0 == strcmp( result->mountpoint, "" ) ) ? -1 : NextSequenceNumber();

	result->state = kDiskStateNew;
	result->ackValues = NULL;
        result->mountedUser = currentConsoleUser;  // -1 if noone logged in on the console
        result->wholeDiskContainsMountedChild = 0;
	
	/* Increment count */

	g.NumDisks ++ ;
	g.NumDisksAddedOrDeleted ++;
	
Return:
	return result;

} // NewDisk


//------------------------------------------------------------------------


boolean_t IsWhole( DiskPtr diskPtr )
{
	boolean_t result;
	
	result = ( 0 != ( diskPtr->flags & kDiskArbDiskAppearedWholeDiskMask ) );
	
	return result;

} // IsNetwork


//------------------------------------------------------------------------


boolean_t IsNetwork( DiskPtr diskPtr )
{
	boolean_t result;
	
	result = ( 0 != ( diskPtr->flags & kDiskArbDiskAppearedNetworkDiskMask ) );
	
	return result;

} // IsNetwork


//------------------------------------------------------------------------

/*
-- Allocate a sequence number for use by Carbon and BlueBox to synchronize
-- their vRefNums.
-- Use approximately the same algorithm as MacOS uses for assigning drive
-- numbers: find the largest so far, and then add one.
-- This will leave gaps in the allocation, but will eventually fill in
-- those gaps.
-- It will also tend not to reuse numbers too quickly, thus avoiding the
-- possibility of having an application access the wrong volume by accident.
-- An unmounted disk has a sequence number of -1.
-- The first sequence number is 0.
*/

static int NextSequenceNumber( void )
{
	int maxSequenceNumber;
	int result;
	DiskPtr diskPtr;

	dwarning(("%s...\n", __FUNCTION__));

	maxSequenceNumber = -1; /* Thus the first sequence number will be zero. */

	for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
	{
		if ( diskPtr->sequenceNumber > maxSequenceNumber )
		{
			maxSequenceNumber = diskPtr->sequenceNumber;
		}	
	}

	/* No other disk has this sequence number. */

	result = maxSequenceNumber + 1;

//Return:

	dwarning(("%s => %d\n", __FUNCTION__, result ));
	return result;

} // NextSequenceNumber

//------------------------------------------------------------------------

/*
-- This is called from autodiskmount.m when it either (a) discovers that
-- the disk is already mounted, or (b) explicitly mounts the disk.
-- It also triggers the assignment of a sequence number, in an attempt
-- to conserve sequence numbers by not assigning them to every disk.
-- (Most partitions are never mounted.)
*/

void DiskSetMountpoint( DiskPtr diskPtr, const char * mountpoint )
{
	if ( diskPtr->mountpoint )
	{
		free( diskPtr->mountpoint );
	}

	diskPtr->mountpoint = strdup( mountpoint ? mountpoint : "" );

	diskPtr->sequenceNumber = NextSequenceNumber();

} // DiskSetMountpoint

//------------------------------------------------------------------------

DiskPtr LookupDiskByIOBSDName( char * ioBSDName )
{
	DiskPtr diskPtr;
	
//	dwarning(("%s('%s')\n", __FUNCTION__,  ioBSDName));

        if (!ioBSDName) {
        	pwarning(("%s(null ioBSDName passed! abort!)\n", __FUNCTION__));
        	return nil;
        }


	for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
	{
                if (!diskPtr->ioBSDName) {
                // something went very wrong, skip this disk
                	continue;
                }
		if ( 0 == strcmp( ioBSDName, diskPtr->ioBSDName ) )
		{
			goto Return;
		}
	}

	/* Assert: diskPtr == NULL */

Return:
	return diskPtr;

} // LookupDiskByIOBSDName

void FreeAllDisks(  )
{
    DiskPtr oldDiskPtr= g.Disks;
    DiskPtr newDiskPtr= g.Disks;

    while (oldDiskPtr) {
        newDiskPtr = oldDiskPtr->next;
        FreeDisk(oldDiskPtr);
        oldDiskPtr = newDiskPtr;
    }

} // FreeAllDisks

//------------------------------------------------------------------------

/*
-- Unmount the disk and delete the mountpoint that was created for it
*/

int UnmountDisk( DiskPtr diskPtr, int forceUnmount )
{
	int result = 0;
        struct stat 	sb;
        char		cookieFile[MAXPATHLEN];

        sprintf(cookieFile, "/%s/%s", diskPtr->mountpoint, ADM_COOKIE_FILE);
       
	dwarning(("%s('%s')\n", __FUNCTION__, diskPtr->ioBSDName));

	if ( 0 == strcmp( diskPtr->mountpoint, "" ) )
	{
		dwarning(("%s('%s'): disk not mounted\n", __FUNCTION__, diskPtr->ioBSDName));
		SetStateForOnePartition( diskPtr, kDiskStateIdle );
		/* result == 0 */
		goto Return;
	}

        result = unmount( diskPtr->mountpoint, (forceUnmount ? MNT_FORCE : 0) );
	if ( -1 == result )
	{
		result = errno;
		LogErrorMessage("%s('%s') unmount('%s') failed: %d (%s)\n", __FUNCTION__, diskPtr->ioBSDName, diskPtr->mountpoint, result, strerror(result));
		SetStateForOnePartition( diskPtr, kDiskStateIdle );
		goto Return;
	}

	result = rmdir( diskPtr->mountpoint );
	if ( -1 == result )
	{
		result = errno;

                if (result == ENOTEMPTY) {
                        if (stat(cookieFile, &sb) == 0) {
                                if (remove(cookieFile) == 0) {
                                        if (rmdir(diskPtr->mountpoint) != 0) {
                                                LogErrorMessage("%s('%s') rmdir('%s') failed: %d (%s)\n", __FUNCTION__, diskPtr->ioBSDName, diskPtr->mountpoint, result, strerror(result));
                                                /* Continue despite this error. */
                                                result = errno;
                                        } else {
                                            result = 0;
                                        }
                                }
                        }

                        
                }
	}
	
	free( diskPtr->mountpoint );
	diskPtr->mountpoint = strdup( "" ); // We need to send this in messages via MIG, so it cannot be NULL

	SetStateForOnePartition( diskPtr, kDiskStateNewlyUnmounted );

Return:
	if ( result ) dwarning(("%s('%s') error: %d (%s)\n", __FUNCTION__, diskPtr->ioBSDName, result, strerror(result)));
	return result;

} // UnmountDisk

//------------------------------------------------------------------------

/*
-- For each disk, <d>, that comes from the same device (i.e., whose "family"
-- and "device number" match the given disk), call Unmount(<d>).
-- Ignore errors - attempt to unmount each partition.  This should help with
-- debugging problems, since only the "problem" partition(s) will remain mounted.
--
-- Handle network volumes (kDiskArbDiskAppearedNetworkDiskMask) specially:
-- do not try to parse their names, and only unmount the one disk.
--
*/

int UnmountAllPartitions( DiskPtr diskPtr, int forceUnmount )
{
	int result = 0; /* mandatory initialization */
	DiskPtr dp;
	int	family1, family2;
	int	deviceNum1, deviceNum2;

        if (diskPtr->ioBSDName && strlen(diskPtr->ioBSDName)) {
                dwarning(("%s('%s')\n", __FUNCTION__, diskPtr->ioBSDName));
        }
	

	if ( IsNetwork( diskPtr ) )
	{
            result = UnmountDisk( diskPtr, forceUnmount ); // Ignore errors - attempt to unmount each partition
		goto Return;
	}

	family1 = diskPtr->family;
	deviceNum1 = diskPtr->ioBSDUnit;

	for (dp = g.Disks; dp != NULL; dp = dp->next)
	{
		family2 = dp->family;
		deviceNum2 = dp->ioBSDUnit;

		if ( family1 == family2 && deviceNum1 == deviceNum2 )
		{
			int err;

                    err = UnmountDisk( dp , forceUnmount); // Ignore errors - attempt to unmount each partition
			if ( err && ! result )
			{
				/* Only return the first error we encounter */
				result = err;
			}
		}
	}
	
Return:
	return result;

} // UnmountAllPartitions

//------------------------------------------------------------------------

/*
-- Eject the disk.
-- Precondition: the disk must not be in use (e.g., by a mounted filesystem)
--
-- Handle network volumes (kDiskArbDiskAppearedNetworkDiskMask) specially:
-- do not try to parse their names, and just mark them as ejected (this routine
-- only gets called if the unmount succeeded).
--
*/

int EjectDisk( DiskPtr diskPtr )
{
	int		result = 0; /* mandatory initialization */
    int		fd = -1; /* mandatory initialization */
    char	livePartitionPathname[PATH_MAX];
	DiskPtr	wholeDiskPtr;

	dwarning(("%s('%s')\n", __FUNCTION__, diskPtr->ioBSDName));

	if ( IsNetwork( diskPtr ) )
	{
		/* If the unmount succeeded, then mark the disk as ejected. */
		if ( 0 == strcmp( "", diskPtr->mountpoint ) )
		{
			SetStateForOnePartition( diskPtr, kDiskStateNewlyEjected );
		}
		goto Return; /* result == 0 */
	}

	/* Find the correct LIVE partition (whole media) for this device */

	wholeDiskPtr = LookupWholeDiskForThisPartition( diskPtr );
	if ( NULL == wholeDiskPtr )
	{
		result = EINVAL;
		LogErrorMessage("%s('%s') can't find whole media\n", __FUNCTION__, diskPtr->ioBSDName);
		SetStateForAllPartitions( diskPtr, kDiskStateIdle );
		goto Return;
	}
	
	sprintf(livePartitionPathname, "/dev/r%s", wholeDiskPtr->ioBSDName);

	dwarning(("%s: livePartitionPathname = '%s'\n", __FUNCTION__, livePartitionPathname));

	/* IOKit has requested that we open with O_RDONLY and not use O_NDELAY */

    fd = open(livePartitionPathname, O_RDONLY);
    if ( -1 == fd )
    {
    	result = errno;
		LogErrorMessage("%s('%s') open('%s') failed: %d (%s)\n", __FUNCTION__, diskPtr->ioBSDName, livePartitionPathname, result, strerror(result));
		SetStateForAllPartitions( diskPtr, kDiskStateIdle );
    	goto Return;
    }
        /* Mark each partition on this disk with <state> = kDiskStateNewlyEjected. */
        if (diskPtr) {
                SetStateForAllPartitions( diskPtr, kDiskStateNewlyEjected );
        }

	result = ioctl(fd, DKIOCEJECT, 0);
	if ( -1 == result )
	{
    	result = errno;
		LogErrorMessage("%s('%s') ioctl(DKIOCEJECT) failed: %d (%s)\n", __FUNCTION__, diskPtr->ioBSDName, result, strerror(result));
		SetStateForAllPartitions( diskPtr, kDiskStateIdle );
    	goto Return;
	}


Return:

	if ( fd >= 0 )
	{
		close( fd );
	}

//	if ( result ) dwarning(("%s('%s') error: %d (%s)\n", __FUNCTION__, diskPtr->ioBSDName, result, strerror(result)));
	return result;

} // EjectDisk

//------------------------------------------------------------------------

/*
-- Unlink the given disk record from the global list and free the strings
-- it points to as well as the record itself.
*/

void FreeDisk( DiskPtr diskPtr )
{
	DiskPtr dp;
	DiskPtr * dpp;

	dwarning(("%s('%s')\n", __FUNCTION__, diskPtr->ioBSDName));
	
	for (	dpp = & g.Disks, dp = * dpp;
			dp != NULL;
			dpp = & (dp->next), dp = * dpp )
	{
		if ( dp == diskPtr )
		{
			*dpp = dp->next;

			free( diskPtr->ioBSDName );
			diskPtr->ioBSDName = NULL; // Attempt to catch accidental re-use.
			free( diskPtr->ioContent );
			diskPtr->ioContent = NULL; // Attempt to catch accidental re-use.
			free( diskPtr->mountpoint );
			diskPtr->mountpoint = NULL; // Attempt to catch accidental re-use.
			if ( diskPtr->ioMediaNameOrNull)
			{
			    free( diskPtr->ioMediaNameOrNull);
				diskPtr->ioMediaNameOrNull = NULL; // Attempt to catch accidental re-use.
			}
			free( diskPtr->ioDeviceTreePath );
			diskPtr->ioDeviceTreePath = NULL; // Attempt to catch accidental re-use.

			if ( diskPtr->ackValues )
			{
				FreeAckValues( diskPtr->ackValues );
			}
			diskPtr->ackValues = NULL; // Attempt to catch accidental re-use.

                        IOObjectRelease(diskPtr->service);
                        
			free( diskPtr );
                        
			g.NumDisks --;
			g.NumDisksAddedOrDeleted ++;
			
			goto Return;

		}
	}

	/* If we get here, it means we couldn't find <diskPtr> in the list */

Return:
	return;

} // FreeDisk

//------------------------------------------------------------------------

void PrintDisks(void)
{
	DiskPtr diskPtr;
	int i;

	if ( ! gDebug ) goto Return;

	dwarning(("g.NumDisks = %d\n", g.NumDisks));
	dwarning(("g.NumDisksAddedOrDeleted = %d\n", g.NumDisksAddedOrDeleted));
        dwarning(("%2s %4s %10s %9s %5s %8s %25s %35s %8s %40s %35s %3s\n", "#", "seq#", "BSDName", "flags", "state", "BSDUnit", "Content", "FS Type", "MediaName", "DeviceTreePath", "mountpoint", "uid"));
	for (diskPtr = g.Disks, i = 0; diskPtr != NULL; diskPtr = diskPtr->next, i++)
	{
		dwarning(("%2d %4d %10s $%08x %5d %8d %25s %35s %8s %40s %35s %3d\n",
					i,
					diskPtr->sequenceNumber,
					diskPtr->ioBSDName,
					diskPtr->flags,
					diskPtr->state,
					diskPtr->ioBSDUnit,
                                        diskPtr->ioContent,
                                        diskPtr->mountedFilesystemName,
					diskPtr->ioMediaNameOrNull ? diskPtr->ioMediaNameOrNull : "<null>",
					diskPtr->ioDeviceTreePath,
					diskPtr->mountpoint ? diskPtr->mountpoint : "<not mounted>",
                                        diskPtr->mountedUser
		));
		PrintAckValues( diskPtr->ackValues );
	}

Return:
	return;

} // PrintDisks


//------------------------------------------------------------------------


int IsStateNeedingAckValueTable( DiskState diskState );
int IsStateNeedingAckValueTable( DiskState diskState )
{
	switch ( diskState )
	{
		case kDiskStateToBeEjected:
		case kDiskStateToBeUnmounted:
		case kDiskStateToBeUnmountedAndEjected:
			return 1;
		break;

		default:
			return 0;
		break;
	}

} // IsStateNeedingAckValueTable


//------------------------------------------------------------------------


void SetStateForOnePartition( DiskPtr diskPtr, DiskState newState )
{

	dwarning(("%s('%s',state=%s (%d))\n", __FUNCTION__, diskPtr->ioBSDName, DISKSTATE(newState),newState));

	/* Free an existing ack value table. */

	if ( diskPtr->ackValues )
	{
		FreeAckValues( diskPtr->ackValues );
	}

	diskPtr->state = newState;

	/* Allocate a table to record the values returned by the interested clients. */

	if ( IsStateNeedingAckValueTable( newState ) )
	{
		diskPtr->ackValues = NewAckValues( NumClientsDesiringAsyncNotification() );
	}
	else
	{
		diskPtr->ackValues = NULL;
	}

} // SetStateForOnePartition


//------------------------------------------------------------------------


/*
-- For each disk, <d>, that comes from the same device (i.e., whose "family"
-- and "device number" match the given disk), set its state.
--
-- Handle network volumes (kDiskArbDiskAppearedNetworkDiskMask) specially.
--
*/

void SetStateForAllPartitions( DiskPtr diskPtr, DiskState newState )
{
	DiskPtr diskPtr2;
	int	family1, family2;
	int	deviceNum1, deviceNum2;
	
	dwarning(("%s('%s',newState=%s (%d))\n", __FUNCTION__, diskPtr->ioBSDName, DISKSTATE(newState),newState));

	if ( IsNetwork( diskPtr ) )
	{
		SetStateForOnePartition( diskPtr, newState );
		goto Return;
	}

	family1 = diskPtr->family;
	deviceNum1 = diskPtr->ioBSDUnit;

	for (diskPtr2 = g.Disks; diskPtr2 != NULL; diskPtr2 = diskPtr2->next)
	{
		family2 = diskPtr2->family;
		deviceNum2 = diskPtr2->ioBSDUnit;

		if ( family1 == family2 && deviceNum1 == deviceNum2 )
		{
			SetStateForOnePartition( diskPtr2, newState );
		}
	}

Return:
	return;
	
} // SetStateForAllPartitions


//------------------------------------------------------------------------


/* inclusive */

int NumPartitionsMountedFromThisDisk( DiskPtr diskPtr )
{
	int result = 0; // mandatory initialization
	DiskPtr diskPtr2;
	int	family1, family2;
	int	deviceNum1, deviceNum2;
	
	if ( IsNetwork( diskPtr ) )
	{
		result = ( 0 != strcmp("", diskPtr->mountpoint) );
		goto Return;
	}

	family1 = diskPtr->family;
	deviceNum1 = diskPtr->ioBSDUnit;

	for (diskPtr2 = g.Disks; diskPtr2 != NULL; diskPtr2 = diskPtr2->next)
	{
		family2 = diskPtr2->family;
		deviceNum2 = diskPtr2->ioBSDUnit;

		if ( family1 == family2 && deviceNum1 == deviceNum2 && 0 != strcmp( "", diskPtr2->mountpoint ) )
		{
			result ++ ;
		}
	}

Return:
	dwarning(("%s('%s') => %d\n", __FUNCTION__, diskPtr->ioBSDName, result));
	return result;
	
} // NumPartitionsMountedFromThisDisk


//------------------------------------------------------------------------


/* inclusive */

int NumPartitionsToBeUnmountedAndEjectedFromThisDisk( DiskPtr diskPtr )
{
	int result = 0; // mandatory initialization
	DiskPtr diskPtr2;
	int	family1, family2;
	int	deviceNum1, deviceNum2;

	if ( IsNetwork( diskPtr ) )
	{
		result = ( 0 != strcmp("", diskPtr->mountpoint) );
		goto Return;
	}

	family1 = diskPtr->family;
	deviceNum1 = diskPtr->ioBSDUnit;

	for (diskPtr2 = g.Disks; diskPtr2 != NULL; diskPtr2 = diskPtr2->next)
	{
		family2 = diskPtr2->family;
		deviceNum2 = diskPtr2->ioBSDUnit;

		if ( family1 == family2 && deviceNum1 == deviceNum2 && diskPtr2->state == kDiskStateToBeUnmountedAndEjected )
		{
			result ++ ;
		}
	}

Return:
	dwarning(("%s('%s') => %d\n", __FUNCTION__, diskPtr->ioBSDName, result));
	return result;
	
} // NumPartitionsToBeUnmountedAndEjectedFromThisDisk


//------------------------------------------------------------------------


DiskPtr LookupWholeDiskForThisPartition( DiskPtr diskPtr )
{
	DiskPtr diskPtr2;
	int	family1, family2;
	int	deviceNum1, deviceNum2;

	if ( IsNetwork( diskPtr ) )
	{
		diskPtr2 = diskPtr;
		goto Return;
	}

	family1 = diskPtr->family;
	deviceNum1 = diskPtr->ioBSDUnit;

	for (diskPtr2 = g.Disks; diskPtr2 != NULL; diskPtr2 = diskPtr2->next)
	{
		family2 = diskPtr2->family;
		deviceNum2 = diskPtr2->ioBSDUnit;

		if ( family1 == family2 && deviceNum1 == deviceNum2 && IsWhole( diskPtr2 ) )
		{
			goto Return;
		}
	}

	/* diskPtr2 == NULL */

Return:
	if ( diskPtr2 )
	{
		dwarning(("%s('%s') => '%s'\n", __FUNCTION__, diskPtr->ioBSDName, STR(diskPtr2->ioBSDName)));
	}
	else
	{
		dwarning(("%s('%s') => NULL\n", __FUNCTION__, diskPtr->ioBSDName));
	}
	return diskPtr2;
	
} // LookupWholeDiskForThisPartition


//------------------------------------------------------------------------


DiskPtr LookupWholeDiskToBeEjected( void )
{
	DiskPtr diskPtr;
	
	for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
	{
		/* Skip it if it is not a whole disk. */

		if ( ! ( IsWhole( diskPtr) || IsNetwork( diskPtr ) ) )
		{
			continue;
		}
		
		/* Skip it if it is not scheduled to be ejected. */

		if ( ! ( ( diskPtr->state == kDiskStateToBeUnmountedAndEjected ) || ( diskPtr->state == kDiskStateToBeEjected ) ) )
		{
			continue;
		}

		/* If we have not weeded it out, then it is a whole disk that is scheduled to be ejected. */

		goto Return;
	}

	/* diskPtr == NULL */

Return:
	if ( diskPtr )
	{
		dwarning(("%s() => '%s' (state=%s (%d))\n", __FUNCTION__, STR(diskPtr->ioBSDName), DISKSTATE(diskPtr->state), diskPtr->state));
	}
	else
	{
		dwarning(("%s() => NULL\n", __FUNCTION__));
	}
	return diskPtr;
	
} // LookupWholeDiskToBeEjected


//------------------------------------------------------------------------


/* Enable dead name notifications for the given port (send them to our global notification port) */

kern_return_t EnableDeathNotifications(mach_port_t port)
{
	kern_return_t r;
	mach_port_t oldPort;
	
	dwarning(("%s($%08x)\n", __FUNCTION__, port));
	
	r = mach_port_request_notification(mach_task_self(), port, MACH_NOTIFY_DEAD_NAME, 0, GetNotifyPort(), MACH_MSG_TYPE_MAKE_SEND_ONCE, &oldPort);
	if ( r != KERN_SUCCESS)
	{
		/* 5/20/99 - This can happen if the client died after sending the message but before we received it */
		LogErrorMessage("(%s:%d) failed to set up death notifications for port %d: {0x%x} %s\n", __FILE__, __LINE__, (int)port, r, mach_error_string(r));
	}
	
	return r;

} // EnableDeathNotifications


//------------------------------------------------------------------------


AckValues * NewAckValues( int size )
{
	AckValue	*	v;
	AckValues	*	result;
	
	dwarning(("%s(%d)\n", __FUNCTION__, size));

	v = (AckValue *)malloc( sizeof( AckValue ) * size );
	if ( v == NULL )
	{
		result = NULL;
		goto Return;
	}
	
	bzero( v, sizeof( AckValue ) * size );
	
	result = (AckValues *)malloc( sizeof( AckValues ) );
	if ( result == NULL )
	{
		free( v );
		goto Return;
	}
	
	bzero ( result, sizeof( AckValues ) );
	
	result->physicalLength = size;
	result->logicalLength = 0;
	result->ackValues = v;
	
Return:
	return result;

} // NewAckValues


void FreeAckValues( AckValues * ackValues )
{
	dwarning(("%s(0x%08x)\n", __FUNCTION__, (unsigned)ackValues));

	if ( ! ackValues ) goto Return;
	
	if ( ackValues->ackValues )
	{
		free( ackValues->ackValues );
		ackValues->ackValues = NULL;
	}
	free( ackValues );
	
Return:
	return;

} // FreeAckValues

void ClearAckValuesForAllDisks()
{
        DiskPtr diskPtr;
        AckValues * p;
        int i;

        dwarning(("%s\n", __FUNCTION__));
        
        for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
        {
                if (p = diskPtr->ackValues )
                {
                        for (i = 0; i < p->logicalLength; i++)
                        {
                                p->ackValues[ i ].state = kAckReceived;
                                p->ackValues[ i ].errorCode = 0;
                        }
                }
        } // FOREACH disk

        return;

} // ClearAckValuesForAllDisks

void InitAckValue( AckValues * p, pid_t pid )
{
	dwarning(("%s(0x%08x, pid=%d)\n", __FUNCTION__, (unsigned)p, (unsigned)pid));

	p->ackValues[ p->logicalLength ].pid = pid;
	p->ackValues[ p->logicalLength ].state = kSendMsg;
	p->ackValues[ p->logicalLength ].errorCode = 0;
	
	if ( p->logicalLength < p->physicalLength )
	{
		p->logicalLength ++ ;
	}
	else
	{
		LogErrorMessage("(%s:%d) %s(0x%08x, pid=%d): error: ! ( p->logicalLength < p->physicalLength )\n", __FILE__, __LINE__, __FUNCTION__, (unsigned)p, (unsigned)pid);
	}
	
} // InitAckValue


void UpdateAckValue( AckValues * p, pid_t pid, int errorCode )
{
	int i;
	
	dwarning(("%s(0x%08x, pid=%d, errorCode=%d)\n", __FUNCTION__, (unsigned)p, (unsigned)pid, errorCode));

	if ( ! p )
	{
		LogErrorMessage("(%s:%d) %s(0x%08x, pid=%d, errorCode=%d): null pointer\n", __FILE__, __LINE__, __FUNCTION__, (unsigned)p, (unsigned)pid, errorCode);
		goto Return;
	}

	for (i = 0; i < p->logicalLength; i++)
	{
		if ( p->ackValues[ i ].pid == pid )
		{
			if ( p->ackValues[ i ].state == kWaitingForAck )
			{
				p->ackValues[ i ].state = kAckReceived;
			}
			else
			{
				LogErrorMessage("(%s:%d) %s(0x%08x, pid=%d, errorCode=%d): error: state != kWaitingForAck\n", __FILE__, __LINE__, __FUNCTION__, (unsigned)p, (unsigned)pid, errorCode);
			}
			p->ackValues[ i ].errorCode = errorCode;
			goto Return;
		}
	}
	
	dwarning(("%s: ERROR, pid=%d not found!\n", __FUNCTION__, pid));
	LogErrorMessage("(%s:%d) %s(0x%08x, pid=%d, errorCode=%d): error: pid not found\n", __FILE__, __LINE__, __FUNCTION__, (unsigned)p, (unsigned)pid, errorCode);

Return:
	return;
	
} // UpdateAckValue


int NumUnsetAckValues( AckValues * p )
{
	int result;
	int i;

	result = 0;
	
	for (i = 0; i < p->logicalLength; i++)
	{
		if ( p->ackValues[ i ].state != kAckReceived )
		{
			result ++ ;
		}
	}

//	dwarning(("%s => %d\n", __FUNCTION__, result));
	
	return result;

} // NumUnsetAckValues


int NumUnsetAckValuesForAllDisks( void )
{
	DiskPtr diskPtr;
	int result;

	result = 0;
	
	for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
	{
		if ( diskPtr->ackValues )
		{
			result += NumUnsetAckValues( diskPtr->ackValues );
		}
	} // FOREACH disk

	dwarning(("%s => %d\n", __FUNCTION__, result));
	
	return result;

} // NumUnsetAckValuesForAllDisks


void PrintAckValues( AckValues * p )
{
	int i;
	
	if ( ! gDebug ) goto Return;
	
	if ( ! p ) goto Return;
	
//	dwarning(("%s: p->physicalLength = %d, p->logicalLength = %d\n", __FUNCTION__, p->physicalLength, p->logicalLength));
	for (i = 0; i < p->logicalLength; i++)
	{
		dwarning(("%2d: pid = %d, state = %d, errorCode = %d\n", i, p->ackValues[ i ].pid, p->ackValues[ i ].state, p->ackValues[ i ].errorCode));
	}
	
Return:
	return;
	
} // PrintAckValues


AckValue * GetDissenterFromAckValues( AckValues * ackValuesPtr )
{
	AckValue * result;
	int i;
	
	for (i = 0; i < ackValuesPtr->logicalLength; i++)
	{
		result = & ackValuesPtr->ackValues[ i ];

		if ( result->state == kAckReceived && result->errorCode != 0 )
		{
			goto Return;
		}
	}
	
	result = NULL;

Return:
	return result;

} // GetDissenterFromAckValues


AckValue * GetDissenterFromAckValuesForAllDisks( void )
{
	AckValue * result;
	DiskPtr diskPtr;

	for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
	{
		if ( diskPtr->ackValues )
		{
			result = GetDissenterFromAckValues( diskPtr->ackValues );
			if ( result )
			{
				goto Return;
			}
		}
	} // FOREACH disk
	
	result = NULL;

Return:
	return result;

} // GetDissenterFromAckValuesForAllDisks

AckValue * GetUnresponderFromAckValues( AckValues * ackValuesPtr )
{
        AckValue * result;
        int i;

        for (i = 0; i < ackValuesPtr->logicalLength; i++)
        {
                result = & ackValuesPtr->ackValues[ i ];

                if ( result->state == kWaitingForAck )
                {
                        goto Return;
                }
        }

        result = NULL;

Return:
        return result;

} // GetUnresponderFromAckValues


AckValue * GetUnresponderFromAckValuesForAllDisks( void )
{
        AckValue * result;
        DiskPtr diskPtr;

        for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
        {
                if ( diskPtr->ackValues )
                {
                        result = GetUnresponderFromAckValues( diskPtr->ackValues );
                        if ( result )
                        {
                                goto Return;
                        }
                }
        } // FOREACH disk

        result = NULL;

Return:
        return result;

} // GetUnresponderFromAckValuesForAllDisks


void MakeDeadClientAgreeable( ClientPtr clientPtr )
{
	DiskPtr diskPtr;
	int i;

	for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
	{
		if ( diskPtr->ackValues )
		{
			for (i = 0; i < diskPtr->ackValues->logicalLength; i++)
			{
				if ( clientPtr->pid == diskPtr->ackValues->ackValues[ i ].pid )
				{
					diskPtr->ackValues->ackValues[ i ].state = kAckReceived;
					diskPtr->ackValues->ackValues[ i ].errorCode = 0;
				}
			}
		}

	} // FOREACH disk

} // MakeDeadClientAgreeable


//------------------------------------------------------------------------

unsigned NumClientsDesiringAsyncNotification( void )
{
	unsigned result = 0; /* mandatory initialization */
	ClientPtr clientPtr;
	
	for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
	{
		if ( clientPtr->flags & kDiskArbNotifyAsync )
		{
			result ++ ;
		}
	}

	return result;

} // NumClientsDesiringAsyncNotification


//------------------------------------------------------------------------

// this structure's parts are significantly overloaded - see each thread for actual usage ...

typedef struct {
    int			diskAppearedType;
    mach_port_t		port;
    char		*ioBSDName;		// stores volume for DiskChanged
    unsigned		flags;
    char		*mountpoint;		// stores newMountpoint for DiskChanged
    int			pid;
    char		*ioDeviceTreePath;
    char		*ioContent;		// stores 
    int			sequenceNumber;
} DiskThreadRecord;

static void * NotifyDiskAppeared(void * args)
{
	DiskThreadRecord *record = args;
	kern_return_t r;

    if ( record->diskAppearedType == kDiskArbNotifyDiskAppearedWithoutMountpoint )
	{
		r = DiskArbDiskAppearedWithoutMountpoint_rpc(record->port, record->ioBSDName, record->flags);
	}
    else if ( record->diskAppearedType == kDiskArbNotifyDiskAppearedWithMountpoint )
        {
                r = DiskArbDiskAppearedWithMountpoint_rpc(record->port, record->ioBSDName, record->flags, record->mountpoint);
        }
    else if ( record->diskAppearedType == kDiskArbNotifyDiskAppeared )
        {
                r = DiskArbDiskAppeared_rpc(record->port, record->ioBSDName, record->flags, record->mountpoint, record->ioContent);
        }
    else if ( record->diskAppearedType == kDiskArbNotifyDiskAppeared2 )
        {
                r = DiskArbDiskAppeared2_rpc(record->port, record->ioBSDName, record->flags, record->mountpoint, record->ioContent, record->ioDeviceTreePath, record->sequenceNumber);
        }
	if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
	if ( r == MACH_SEND_INVALID_DEST )
	{
		/* The client has died */
		/* Don't do anything here ... wait for the notification */
		dwarning(("Dead client! Port = $%08x (pid=%d)\n", record->port, record->pid));
	}
	free(record->ioBSDName);
        free(record->ioDeviceTreePath);
        free(record->ioContent);
        free(record->mountpoint);
	free(record);
	return NULL;
}

static void * NotifyDiskRegistrationComplete(void * args)
{
        ClientPtr client = args;
        kern_return_t r;

        r = DiskArbRegistrationComplete_rpc(client->port);
        if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
        if ( r == MACH_SEND_INVALID_DEST )
        {
                /* The client has died */
                /* Don't do anything here ... wait for the notification */
                dwarning(("Dead client! Port = $%08x\n", client->port));
        }
        return NULL;
}


static void * NotifyDiskMessagesCompleted(void * args)
{
        DiskThreadRecord *record = args;
        kern_return_t r;
        r = DiskArbNotificationComplete_rpc(record->port, record->sequenceNumber);
        if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
        if ( r == MACH_SEND_INVALID_DEST )
        {
                /* The client has died */
                /* Don't do anything here ... wait for the notification */
                dwarning(("Dead client! Port = $%08x\n", record->port));
        }
        free(record);
        return NULL;
}

static void * NotifyClientDisconnected(void * args)
{
        ClientPtr client = args;
        kern_return_t r;
        r = DiskArbClientDisconnected_rpc(client->port);
        if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
        if ( r == MACH_SEND_INVALID_DEST )
        {
                /* The client has died */
                /* Don't do anything here ... wait for the notification */
                dwarning(("Dead client! Port = $%08x\n", client->port));
        }
        return NULL;
}

static void * NotifyBlueBoxMessages(void * args)
{
        DiskThreadRecord *record = args;
        kern_return_t r;
        r = DiskArbBlueBoxBootVolumeUpdated_async_rpc(record->port, record->sequenceNumber);
        if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
        if ( r == MACH_SEND_INVALID_DEST )
        {
                /* The client has died */
                /* Don't do anything here ... wait for the notification */
                dwarning(("Dead client! Port = $%08x\n", record->port));
        }
        free(record);
        return NULL;
}

static void * NotifyDiskChanged(void * args)
{
        DiskThreadRecord *record = args;
        kern_return_t r;

        r = DiskArbDiskChanged_rpc(record->port, record->ioBSDName, record->mountpoint, record->ioContent, record->flags, record->sequenceNumber);
        if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
        if ( r == MACH_SEND_INVALID_DEST )
        {
                /* The client has died */
                /* Don't do anything here ... wait for the notification */
                dwarning(("Dead client! Port = $%08x\n", record->port));
        }
        free(record);
        return NULL;
}

static void * NotifyUnrecognizedDiskInserted(void * args)
{
        DiskThreadRecord *record = args;
        kern_return_t r;
        
        r = DiskArbUnknownFileSystemInserted_rpc(record->port, record->ioBSDName, record->mountpoint, record->ioContent, record->flags, record->sequenceNumber, record->diskAppearedType);
        if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
        if ( r == MACH_SEND_INVALID_DEST )
        {
                /* The client has died */
                /* Don't do anything here ... wait for the notification */
                dwarning(("Dead client! Port = $%08x\n", record->port));
        }
        free(record);
        return NULL;
}



static void StartDiskAppearedThread(DiskThreadRecord * record)
{
    pthread_attr_t attr;
    pthread_t tid;

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    pthread_create(&tid, &attr, NotifyDiskAppeared, record);
    pthread_attr_destroy(&attr);
}


static void StartDiskMessagesCompleted(DiskThreadRecord * record)
{
    pthread_attr_t attr;
    pthread_t tid;

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    pthread_create(&tid, &attr, NotifyDiskMessagesCompleted, record);
    pthread_attr_destroy(&attr);
}

static void StartBlueBoxNotificationThread(DiskThreadRecord * record)
{
    pthread_attr_t attr;
    pthread_t tid;

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    pthread_create(&tid, &attr, NotifyBlueBoxMessages, record);
    pthread_attr_destroy(&attr);
}


void StartDiskRegistrationCompleteThread(ClientPtr client)
{
    pthread_attr_t attr;
    pthread_t tid;

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    pthread_create(&tid, &attr, NotifyDiskRegistrationComplete, client);
    pthread_attr_destroy(&attr);
}

void StartClientDisconnectedThread(ClientPtr client)
{
    pthread_attr_t attr;
    pthread_t tid;

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    pthread_create(&tid, &attr, NotifyClientDisconnected, client);
    pthread_attr_destroy(&attr);
}

void StartDiskChangedThread(DiskThreadRecord * record)
{
    pthread_attr_t attr;
    pthread_t tid;

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    pthread_create(&tid, &attr, NotifyDiskChanged, record);
    pthread_attr_destroy(&attr);
}

void StartUnrecognizedDiskThread(DiskThreadRecord * record)
{
    pthread_attr_t attr;
    pthread_t tid;

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    pthread_create(&tid, &attr, NotifyUnrecognizedDiskInserted, record);
    pthread_attr_destroy(&attr);
}


void SendClientWasDisconnectedMsg(ClientPtr clientPtr)
{
    DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));
    dwarning(("DiskArbClientDisconnected_rpc($%08x) ...\n", clientPtr->port));

    record->port = clientPtr->port;

    StartClientDisconnectedThread(clientPtr);

} // SendClientWasDisconnectedMsg

//------------------------------------------------------------------------

void SendDiskAppearedMsgs( void )
{
	ClientPtr clientPtr;
	DiskPtr diskPtr;
	
	dwarning(("%s\n", __FUNCTION__));
	
	/* For each client, and disk, send a notification if either of them is new */

	for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
	{

		/* Each new client that wants it gets an initial message giving the Blue Box boot volume. */

		if ( ( kClientStateNew == clientPtr->state ) && ( clientPtr->flags & kDiskArbNotifyBlueBoxBootVolumeUpdated ) )
		{
                        DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));
                        dwarning(("DiskArbBlueBoxBootVolumeUpdated_async_rpc($%08x (pid=%d), gBlueBoxBootVolume=%d) ...\n", clientPtr->port, clientPtr->pid, gBlueBoxBootVolume));
                        record->sequenceNumber = gBlueBoxBootVolume;
                        record->port = clientPtr->port;
                        StartBlueBoxNotificationThread(record);
			
		}

		for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
		{
			if ( ( kClientStateNew == clientPtr->state ) || ( kDiskStateNew == diskPtr->state ) )
			{
                                if ((diskPtr->flags & kDiskArbDiskAppearedUnrecognizableFormat) && (clientPtr->flags & kDiskArbNotifyUnrecognizedVolumes) ) {
                                        SendUnrecognizedDiskMsgs(clientPtr->port, diskPtr->ioBSDName, "", "", (!((diskPtr->flags & kDiskArbDiskAppearedLockedMask) == kDiskArbDiskAppearedLockedMask)), ((diskPtr->flags & kDiskArbDiskAppearedEjectableMask) == kDiskArbDiskAppearedEjectableMask), ((diskPtr->flags & kDiskArbDiskAppearedWholeDiskMask) == kDiskArbDiskAppearedWholeDiskMask));
                                        continue;
                                }


				/* New semantics: DiskAppearedWithoutMountpoint and DiskAppearedWithMountpoint *are* mutually exclusive */
				
				/* But: DiskAppeared gets sent for *every* disk ... */

				if ( (clientPtr->flags & kDiskArbNotifyDiskAppearedWithoutMountpoint) && 0 == strcmp("",diskPtr->mountpoint) )
				{
					DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));

					dwarning(("DiskArbDiskAppearedWithoutMountpoint_rpc($%08x (pid=%d), '%s', $%08x) ...\n", clientPtr->port, clientPtr->pid, diskPtr->ioBSDName, diskPtr->flags));

                                        record->diskAppearedType = kDiskArbNotifyDiskAppearedWithoutMountpoint;
					record->port = clientPtr->port;
					record->ioBSDName = strdup( diskPtr->ioBSDName );
					record->flags = diskPtr->flags;
					record->mountpoint = strdup( diskPtr->mountpoint );
					record->pid = clientPtr->pid;
                                        record->ioContent = strdup(diskPtr->ioContent);
                                        record->ioDeviceTreePath = strdup(diskPtr->ioDeviceTreePath);
                                        record->sequenceNumber = diskPtr->sequenceNumber;

					StartDiskAppearedThread(record);
				}

				if ( (clientPtr->flags & kDiskArbNotifyDiskAppearedWithMountpoint) && 0 != strcmp("",diskPtr->mountpoint) )
				{
					DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));

					dwarning(("DiskArbDiskAppearedWithMountpoint_rpc($%08x (pid=%d), '%s', $%08x, '%s') ...\n", clientPtr->port, clientPtr->pid, diskPtr->ioBSDName, diskPtr->flags, diskPtr->mountpoint));

                                        record->diskAppearedType = kDiskArbNotifyDiskAppearedWithMountpoint;
					record->port = clientPtr->port;
					record->ioBSDName = strdup( diskPtr->ioBSDName );
					record->flags = diskPtr->flags;
					record->mountpoint = strdup( diskPtr->mountpoint );
					record->pid = clientPtr->pid;
                                        record->ioContent = strdup(diskPtr->ioContent);
                                        record->ioDeviceTreePath = strdup(diskPtr->ioDeviceTreePath);
                                        record->sequenceNumber = diskPtr->sequenceNumber;
					StartDiskAppearedThread(record);
				}
                                if ( clientPtr->flags & kDiskArbNotifyDiskAppeared )
                                {
                                        DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));

                                        dwarning(("DiskArbDiskAppeared_rpc($%08x (pid=%d), '%s', $%08x, '%s', '%s') ...\n", clientPtr->port, clientPtr->pid, diskPtr->ioBSDName, diskPtr->flags, diskPtr->mountpoint, diskPtr->ioContent));

                                        record->diskAppearedType = kDiskArbNotifyDiskAppeared;
                                        record->port = clientPtr->port;
                                        record->ioBSDName = strdup( diskPtr->ioBSDName );
                                        record->flags = diskPtr->flags;
                                        record->mountpoint = strdup( diskPtr->mountpoint );
                                        record->pid = clientPtr->pid;
                                        record->ioContent = strdup(diskPtr->ioContent);
                                        record->ioDeviceTreePath = strdup(diskPtr->ioDeviceTreePath);
                                        record->sequenceNumber = diskPtr->sequenceNumber;
                                        StartDiskAppearedThread(record);
                                }

                                if ( clientPtr->flags & kDiskArbNotifyDiskAppeared2 )
                                {
                                        DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));

                                        dwarning(("DiskArbDiskAppeared2_rpc($%08x (pid=%d), '%s', $%08x, '%s', '%s', '%s', %d) ...\n", clientPtr->port, clientPtr->pid, diskPtr->ioBSDName, diskPtr->flags, diskPtr->mountpoint, diskPtr->ioContent, diskPtr->ioDeviceTreePath, diskPtr->sequenceNumber));

                                        record->diskAppearedType = kDiskArbNotifyDiskAppeared2;
                                        record->port = clientPtr->port;
                                        record->ioBSDName = strdup( diskPtr->ioBSDName );
                                        record->flags = diskPtr->flags;
                                        record->mountpoint = strdup( diskPtr->mountpoint );
                                        record->pid = clientPtr->pid;
                                        record->ioContent = strdup(diskPtr->ioContent);
                                        record->ioDeviceTreePath = strdup(diskPtr->ioDeviceTreePath);
                                        record->sequenceNumber = diskPtr->sequenceNumber;
                                        StartDiskAppearedThread(record);
                                }

			} // IF client is new OR disk is new

		} // FOREACH disk

	} // FOREACH client
	
	/* For each disk and client, if it was new, then it is no longer new */
	
	for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
	{
		if ( kClientStateNew == clientPtr->state ) clientPtr->state = kClientStateIdle;
	}
	for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
	{
		if ( kDiskStateNew == diskPtr->state ) SetStateForOnePartition( diskPtr, kDiskStateIdle );
	}


} // SendDiskAppearedMsgs

void SendUnrecognizedDiskMsgs(mach_port_t port, char *devname, char *fstype, char *deviceType, int isWritable, int isRemovable, int isWhole )
{
        DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));

        dwarning(("DiskArbUnknownFileSystemInserted_rpc($%08x) ...\n", port));

        record->diskAppearedType = isWhole;
        record->port = port;
        record->ioBSDName = strdup( devname );
        record->flags = isWritable;
        record->mountpoint = strdup( fstype );
        record->pid = 0;
        record->ioContent = strdup(deviceType);
        record->ioDeviceTreePath = strdup("");
        record->sequenceNumber = isRemovable;

        StartUnrecognizedDiskThread(record);
        
} // SendUnrecognizedDiskMsgs

void SendDiskChangedMsgs(char *devname, char *newMountpoint, char *newVolumeName, int flags, int success)
{
        ClientPtr clientPtr;

        dwarning(("%s\n", __FUNCTION__));

        /* For each client, and disk, send a notification if either of them is new */

        for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
        {
                if ( ( clientPtr->flags & kDiskArbNotifyChangedDisks ) ) {
                        DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));

                        dwarning(("DiskArbDiskChanged_rpc($%08x (pid=%d)) ...\n", clientPtr->port, clientPtr->pid));

                        record->diskAppearedType = 0;
                        record->port = clientPtr->port;
                        record->ioBSDName = strdup( devname );
                        record->flags = flags;
                        record->mountpoint = strdup( newMountpoint );
                        record->pid = 0;
                        record->ioContent = strdup(newVolumeName);
                        record->ioDeviceTreePath = strdup("");
                        record->sequenceNumber = success;

                        StartDiskChangedThread(record);
                }
        }

} // SendDiskChangedMsgs


//------------------------------------------------------------------------


void SendUnmountCommitMsgs( void )
{
	ClientPtr clientPtr;
	DiskPtr diskPtr;
	kern_return_t r = 0;
	
	dwarning(("%s\n", __FUNCTION__));
	
	/* For each client, and disk, send a notification if the disk was unmounted/ejected */

	for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
	{
		for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
		{
			if ( kDiskStateNewlyUnmounted == diskPtr->state || kDiskStateNewlyEjected == diskPtr->state )
			{

				if ( clientPtr->flags & kDiskArbNotifyUnmount )
				{
					dwarning(("DiskArbUnmountCommit($%08x (pid=%d), '%s') ...\n", clientPtr->port, clientPtr->pid, diskPtr->ioBSDName));
					if ( r == MACH_SEND_INVALID_DEST )
					{
						/* The client has died */
						/* Don't do anything here ... wait for the notification */
						dwarning(("Dead client! Port = $%08x (pid=%d)\n", clientPtr->port, clientPtr->pid));
					}
				}

			}

		} // FOREACH disk

	} // FOREACH client
	
} // SendUnmountCommitMsgs


//------------------------------------------------------------------------


void PrepareToSendPreUnmountMsgs( void )
{
	ClientPtr clientPtr;
	DiskPtr diskPtr;

	dwarning(("%s\n", __FUNCTION__));
	
	/* For each client and disk */

	for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
	{
		if ( kDiskStateToBeUnmounted == diskPtr->state || kDiskStateToBeUnmountedAndEjected == diskPtr->state )
		{
			// Assert: the <ackValues> field for this disk was allocated/initialized when its state was set.

			for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
			{
				if ( clientPtr->flags & kDiskArbNotifyAsync )
				{
					InitAckValue( diskPtr->ackValues, clientPtr->pid );
				}

			} // FOREACH client

		}
		
	} // FOREACH disk

} // PrepareToSendPreUnmountMsgs


//------------------------------------------------------------------------


void PrepareToSendPreEjectMsgs( void )
{
	ClientPtr clientPtr;
	DiskPtr diskPtr;
	
	dwarning(("%s\n", __FUNCTION__));
	
	/* For each client and disk */

	for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
	{
		if ( kDiskStateToBeEjected == diskPtr->state )
		{
			// Assert: the <ackValues> field for this disk was allocated/initialized when its state was set.

			for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
			{
				if ( clientPtr->flags & kDiskArbNotifyAsync )
				{
					InitAckValue( diskPtr->ackValues, clientPtr->pid );
				}

			} // FOREACH client

		}
		
	} // FOREACH disk

} // PrepareToSendPreEjectMsgs


//------------------------------------------------------------------------


void SendPreUnmountMsgs( void )
{
	DiskPtr diskPtr;
	int i;
	
	dwarning(("%s\n", __FUNCTION__));
	
	/* For each client and disk */

	for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
	{
		if ( kDiskStateToBeUnmounted == diskPtr->state || kDiskStateToBeUnmountedAndEjected == diskPtr->state )
		{
			for (i = 0; i < diskPtr->ackValues->logicalLength; i++)
			{
				ClientPtr clientPtr;
				kern_return_t r;
				
				if ( diskPtr->ackValues->ackValues[ i ].state != kSendMsg )
				{
					continue;
				}

				clientPtr = LookupClientByPID( diskPtr->ackValues->ackValues[ i ].pid );
				if ( ! clientPtr )
				{
					pwarning(("%s: pid = %d: no known client with this pid.\n", __FUNCTION__, diskPtr->ackValues->ackValues[ i ].pid));
				}
				
				if ( clientPtr->numAcksRequired > 0 )
				{
//					dwarning(("%s: skipping pid = %d for '%s' since numAcksRequired = %d\n", __FUNCTION__, clientPtr->pid, diskPtr->ioBSDName, clientPtr->numAcksRequired));
					continue;
				}
				
				dwarning(("DiskArbUnmountPreNotify_async_rpc($%08x (pid=%d), '%s', flags=$%08x) ...\n", clientPtr->port, clientPtr->pid, diskPtr->ioBSDName, 0 /* flags */));
				r = DiskArbUnmountPreNotify_async_rpc(clientPtr->port, diskPtr->ioBSDName, 0 /* flags */);
				if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
				if ( r == MACH_SEND_INVALID_DEST )
				{
					/* The client has died */
					/* Don't do anything here ... wait for the notification */
					dwarning(("Dead client! Port = $%08x (pid=%d)\n", clientPtr->port, clientPtr->pid));
				}

				if ( ! r )
				{
					clientPtr->numAcksRequired++;
				}

				diskPtr->ackValues->ackValues[ i ].state = kWaitingForAck;

			} // FOREACH ack value

		}
		
	} // FOREACH disk

} // SendPreUnmountMsgs


//------------------------------------------------------------------------


void SendPreEjectMsgs( void )
{
	DiskPtr diskPtr;
	int i;
	
	dwarning(("%s\n", __FUNCTION__));
	
	/* For each client and disk */

	for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
	{
		if ( kDiskStateToBeEjected == diskPtr->state )
		{
			for (i = 0; i < diskPtr->ackValues->logicalLength; i++)
			{
				ClientPtr clientPtr;
				kern_return_t r;
				
				if ( diskPtr->ackValues->ackValues[ i ].state != kSendMsg )
				{
					continue;
				}

				clientPtr = LookupClientByPID( diskPtr->ackValues->ackValues[ i ].pid );
				if ( ! clientPtr )
				{
					pwarning(("%s: pid = %d: no known client with this pid.\n", __FUNCTION__, diskPtr->ackValues->ackValues[ i ].pid));
				}
				
				if ( clientPtr->numAcksRequired > 0 )
				{
//					dwarning(("%s: skipping pid = %d for '%s' since numAcksRequired = %d\n", __FUNCTION__, clientPtr->pid, diskPtr->ioBSDName, clientPtr->numAcksRequired));
					continue;
				}
				
				dwarning(("DiskArbEjectPreNotify_async_rpc($%08x (pid=%d), '%s', flags=$%08x) ...\n", clientPtr->port, clientPtr->pid, diskPtr->ioBSDName, 0 /* flags */));
				r = DiskArbEjectPreNotify_async_rpc(clientPtr->port, diskPtr->ioBSDName, 0 /* flags */);
				if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
				if ( r == MACH_SEND_INVALID_DEST )
				{
					/* The client has died */
					/* Don't do anything here ... wait for the notification */
					dwarning(("Dead client! Port = $%08x (pid=%d)\n", clientPtr->port, clientPtr->pid));
				}

				if ( ! r )
				{
					clientPtr->numAcksRequired++;
				}

				diskPtr->ackValues->ackValues[ i ].state = kWaitingForAck;

			} // FOREACH ack value

		}
		
	} // FOREACH disk


} // SendPreEjectMsgs


//------------------------------------------------------------------------


void SendUnmountPostNotifyMsgsForOnePartition( char * ioBSDName, int errorCode, pid_t pid )
{
	ClientPtr clientPtr;

	dwarning(("%s('%s', errorCode = %d, pid = %d)\n", __FUNCTION__, ioBSDName, errorCode, pid));

	for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
	{
		if ( clientPtr->flags & kDiskArbNotifyAsync )
		{
			kern_return_t r;
			
			dwarning(("DiskArbUnmountPostNotify_async_rpc($%08x (pid=%d), '%s', errorCode=%d, pid=%d) ...\n", clientPtr->port, clientPtr->pid, ioBSDName, errorCode, pid));
			r = DiskArbUnmountPostNotify_async_rpc(clientPtr->port, ioBSDName, errorCode, pid);
			if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
			if ( r == MACH_SEND_INVALID_DEST )
			{
				/* The client has died */
				/* Don't do anything here ... wait for the notification */
				dwarning(("Dead client! Port = $%08x (pid=%d)\n", clientPtr->port, clientPtr->pid));
			}
		}

	} // FOREACH client


} // SendUnmountPostNotifyMsgsForOnePartition


//------------------------------------------------------------------------


void SendEjectPostNotifyMsgsForOnePartition( char * ioBSDName, int errorCode, pid_t pid )
{
	ClientPtr clientPtr;

	dwarning(("%s('%s', errorCode = %d, pid = %d)\n", __FUNCTION__, ioBSDName, errorCode, pid));

	for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
	{
		if ( clientPtr->flags & kDiskArbNotifyAsync )
		{
			kern_return_t r;
			
			dwarning(("DiskArbEjectPostNotify_async_rpc($%08x (pid=%d), '%s', errorCode=%d, pid=%d) ...\n", clientPtr->port, clientPtr->pid, ioBSDName, errorCode, pid));
			r = DiskArbEjectPostNotify_async_rpc(clientPtr->port, ioBSDName, errorCode, pid);
			if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
			if ( r == MACH_SEND_INVALID_DEST )
			{
				/* The client has died */
				/* Don't do anything here ... wait for the notification */
				dwarning(("Dead client! Port = $%08x (pid=%d)\n", clientPtr->port, clientPtr->pid));
			}
		}

	} // FOREACH client

} // SendEjectPostNotifyMsgsForOnePartition


//------------------------------------------------------------------------


void SendEjectPostNotifyMsgsForAllPartitions( DiskPtr diskPtr, int errorCode, pid_t pid )
{
	DiskPtr diskPtr2;
	int	family1, family2;
	int	deviceNum1, deviceNum2;
	
	dwarning(("%s('%s', errorCode = %d, pid = %d)\n", __FUNCTION__, diskPtr->ioBSDName, errorCode, pid));
	
	if ( IsNetwork( diskPtr ) )
	{
		SendEjectPostNotifyMsgsForOnePartition( diskPtr->ioBSDName, errorCode, pid );
		goto Return;
	}

	family1 = diskPtr->family;
	deviceNum1 = diskPtr->ioBSDUnit;

	for (diskPtr2 = g.Disks; diskPtr2 != NULL; diskPtr2 = diskPtr2->next)
	{
		family2 = diskPtr2->family;
		deviceNum2 = diskPtr2->ioBSDUnit;

		if ( family1 == family2 && deviceNum1 == deviceNum2 )
		{
			SendEjectPostNotifyMsgsForOnePartition( diskPtr2->ioBSDName, errorCode, pid );
		}
	}

Return:
	return;

} // SendEjectPostNotifyMsgsForAllPartitions


//------------------------------------------------------------------------


void SendBlueBoxBootVolumeUpdatedMsgs( void )
{
	ClientPtr clientPtr;

	dwarning(("%s()\n", __FUNCTION__));

	for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
	{
		if ( clientPtr->flags & kDiskArbNotifyBlueBoxBootVolumeUpdated )
		{
                        DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));
			dwarning(("DiskArbBlueBoxBootVolumeUpdated_async_rpc($%08x (pid=%d), gBlueBoxBootVolume=%d) ...\n", clientPtr->port, clientPtr->pid, gBlueBoxBootVolume));
                        record->sequenceNumber = gBlueBoxBootVolume;
                        record->port = clientPtr->port;
                        StartBlueBoxNotificationThread(record);
		}

	} // FOREACH client

} // SendBlueBoxBootVolumeUpdatedMsgs

//------------------------------------------------------------------------


void SendCompletedMsgs( int messageType )
{
        ClientPtr clientPtr;

        dwarning(("%s(%d)\n", __FUNCTION__, messageType));

        for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
        {

                if ( ( clientPtr->flags & kDiskArbNotifyCompleted ) ) {

                    DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));
                    dwarning(("DiskArbNotificationComplete_rpc($%08x (messageType=%d)) ...\n", clientPtr->port, messageType));

                    record->port = clientPtr->port;
                    record->sequenceNumber = messageType;

                    StartDiskMessagesCompleted(record);
                }
        } // FOREACH client

} // SendCompletedMsgs

//------------------------------------------------------------------------


void SetBlueBoxBootVolume( int seqno )
{
	dwarning(("%s(seqno=%d)\n", __FUNCTION__, seqno));

	gBlueBoxBootVolume = seqno;
	SendBlueBoxBootVolumeUpdatedMsgs();

} // SetBlueBoxBootVolume


//------------------------------------------------------------------------


int GetBlueBoxBootVolume( void )
{
//	dwarning(("%s() => %d\n", __FUNCTION__, gBlueBoxBootVolume));

	return gBlueBoxBootVolume;

} // GetBlueBoxBootVolume


//------------------------------------------------------------------------


void CompleteUnmount( void )
{
	DiskPtr diskPtr;
	AckValue * avp;
	int errorCode_out = 0;
	pid_t pid_out = 0;
	DiskPtr wholeDiskToBeEjectedOrNULL;
	
	dwarning(("%s\n", __FUNCTION__));

	// Unmount could be completed for each partition independently.
	// But it simplifies things to wait them to all complete before proceeding.
	
	if ( 0 != NumUnsetAckValuesForAllDisks() )
	{
		goto Return;
	}
	
	/* 0 == NumUnsetAckValuesForAllDisks() */
	
	/* Initialize this before we start changing their states.  It will be NULL if this is not an unmount-and-eject. */

	wholeDiskToBeEjectedOrNULL = LookupWholeDiskToBeEjected();

	for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
	{
		/* Skip disks that are not undergoing unmounting. */

		if ( diskPtr->state != kDiskStateToBeUnmounted && diskPtr->state != kDiskStateToBeUnmountedAndEjected )
		{
			continue;
		}
		
		avp = GetDissenterFromAckValues( diskPtr->ackValues );
		
		if ( NULL == avp )
		{
			int err;
			
			dwarning(("%s: there was no dissenter for '%s'\n", __FUNCTION__, diskPtr->ioBSDName));
	
			err = UnmountDisk( diskPtr, FALSE ); /* Updates diskPtr->state for this partition. */
			if ( err )
			{
				/* Failure.  The unmount failed.  diskPtr->state == kDiskStateIdle */
	
				dwarning(("%s: the unmount failed\n", __FUNCTION__));
				
				errorCode_out = err;
				pid_out = -1;
			}
			else
			{
				/* Success.  diskPtr->state == kDiskStateNewlyUnmounted */
	
				errorCode_out = 0;
				pid_out = -1;
	
			}
		}
		else
		{
			/* Failure.  There was a dissenter.  Report it. */
			
			dwarning(("%s: there was a dissenter for '%s'\n", __FUNCTION__, diskPtr->ioBSDName));
	
			errorCode_out = avp->errorCode;
			pid_out = avp->pid;

			SetStateForOnePartition( diskPtr, kDiskStateIdle );
		}
	
		/* Send each waiting client a notification with the errorcode and dissenter. */
	
		SendUnmountPostNotifyMsgsForOnePartition( diskPtr->ioBSDName, errorCode_out, pid_out );
	
	} // FOREACH disk

	/* Was this the first phase of an unmount-and-eject? */
	
	if ( wholeDiskToBeEjectedOrNULL && ( 0 == NumPartitionsMountedFromThisDisk( wholeDiskToBeEjectedOrNULL ) ) )
	{
		/* WARNING: this might rob old-style clients of their synchronous UnmountCommit notifications if the eject fails. */
		/* Why?  Because we keep no record that an unmount happened.  So if the eject fails, we won't send any UnmountCommit notifications. */
		/* This can only happen if there is someone holding open a file descriptor on one of the /dev nodes for the disk. */
	
		SetStateForAllPartitions( wholeDiskToBeEjectedOrNULL, kDiskStateToBeEjected );

		/* Prepare a list of pre-eject notifications to be sent. */

		PrepareToSendPreEjectMsgs();
		
		/* Return to the main msg loop to process sending of pre-eject msgs and receiving of acks. */

	}

Return:
	return;

} // CompleteUnmount


//------------------------------------------------------------------------


void CompleteEject( void )
{
	DiskPtr diskPtr;
	AckValue * avp;
	int errorCode_out = 0;
	pid_t pid_out = 0;
	
	dwarning(("%s\n", __FUNCTION__));

	/* Ejection differs from unmounting because it requires acks for all partitions before proceeding. */
	
	if ( 0 != NumUnsetAckValuesForAllDisks() )
	{
		goto Return;
	}
	
	diskPtr = LookupWholeDiskToBeEjected();
	if ( ! diskPtr )
	{
		LogErrorMessage("%s(): LookupWholeDiskToBeEjected() => NULL\n", __FUNCTION__);
		goto Return;
	}

	/* 0 == NumUnsetAckValuesForAllDisks() */
		
	avp = GetDissenterFromAckValuesForAllDisks();

	if ( NULL == avp )
	{
		int err;

		dwarning(("%s: there was no dissenter\n", __FUNCTION__));

		/* Assume that all the partitions have already been unmounted. */
	
		err = EjectDisk( diskPtr ); /* Updates diskPtr->state for each partition on this disk. */
		if ( err )
		{
			/* Failure.  The eject failed.  diskPtr->state == kDiskStateIdle */

			dwarning(("%s: the eject failed\n", __FUNCTION__));
			
			errorCode_out = err;
			pid_out = -1;
		}
		else
		{
			/* Success.  diskPtr->state == kDiskStateNewlyEjected */

			errorCode_out = 0;
			pid_out = -1;
		}
	}
	else
	{
		/* Failure.  There was a dissenter.  Report it. */
		
		dwarning(("%s: there was a dissenter\n", __FUNCTION__));

		errorCode_out = avp->errorCode;
		pid_out = avp->pid;

		SetStateForAllPartitions( diskPtr, kDiskStateIdle );
	}

	/* Send each waiting client a notification with the errorcode and dissenter. */

	SendEjectPostNotifyMsgsForAllPartitions( diskPtr, errorCode_out, pid_out );

Return:
	return;

} // CompleteEject


//------------------------------------------------------------------------


DiskState AreWeBusy( void )
{
        DiskState result;
        DiskPtr diskPtr;

        result = kDiskStateIdle;

        for (diskPtr = g.Disks; diskPtr != NULL ; diskPtr = diskPtr->next)
        {
                switch ( diskPtr->state )
                {
                        case kDiskStateToBeUnmounted:
                                if ( kDiskStateIdle == result )
                                {
                                        result = diskPtr->state;
                                }
                                else if ( result != diskPtr->state )
                                {
                                        dwarning(("%s: incompatible disk states: %s (%d) vs. %s (%d)\n", __FUNCTION__, DISKSTATE(diskPtr->state), diskPtr->state, DISKSTATE(result), result));
                                }
                        break;

                        case kDiskStateToBeEjected:
                                if ( kDiskStateIdle == result )
                                {
                                        result = diskPtr->state;
                                }
                                else if ( result != diskPtr->state )
                                {
                                        dwarning(("%s: incompatible disk states: %s (%d) vs. %s (%d)\n", __FUNCTION__, DISKSTATE(diskPtr->state), diskPtr->state, DISKSTATE(result), result));
                                }
                        break;

                        case kDiskStateToBeUnmountedAndEjected:
                                if ( kDiskStateIdle == result || kDiskStateToBeEjected == result )
                                {
                                        result = diskPtr->state;
                                }
                                else if ( result != diskPtr->state )
                                {
                                        dwarning(("%s: incompatible disk states: %s (%d) vs. %s (%d)\n", __FUNCTION__, DISKSTATE(diskPtr->state), diskPtr->state, DISKSTATE(result), result));
                                }
                        break;

                        case kDiskStateIdle: /* not busy */
                        case kDiskStateNew: /* not busy */
                        case kDiskStateNewlyEjected: /* not busy */
                        case kDiskStateNewlyUnmounted: /* not busy */
                        default:
                                /* do nothing - result initialized to zero / kDiskStateIdle */
                        break;
                }
        }

//Return:
        dwarning(("%s() => %s (%d)\n", __FUNCTION__, DISKSTATE(result), result));
        return result;

} // AreWeBusy

DiskState AreWeBusyForDisk( DiskPtr diskPtr )
{
        DiskState result;

        result = kDiskStateIdle;

        switch ( diskPtr->state )
        {
                case kDiskStateToBeUnmounted:
                        if ( kDiskStateIdle == result )
                        {
                                result = diskPtr->state;
                        }
                        else if ( result != diskPtr->state )
                        {
                                dwarning(("%s: incompatible disk states: %s (%d) vs. %s (%d)\n", __FUNCTION__, DISKSTATE(diskPtr->state), diskPtr->state, DISKSTATE(result), result));
                        }
                break;

                case kDiskStateToBeEjected:
                        if ( kDiskStateIdle == result )
                        {
                                result = diskPtr->state;
                        }
                        else if ( result != diskPtr->state )
                        {
                                dwarning(("%s: incompatible disk states: %s (%d) vs. %s (%d)\n", __FUNCTION__, DISKSTATE(diskPtr->state), diskPtr->state, DISKSTATE(result), result));
                        }
                break;

                case kDiskStateToBeUnmountedAndEjected:
                        if ( kDiskStateIdle == result || kDiskStateToBeEjected == result )
                        {
                                result = diskPtr->state;
                        }
                        else if ( result != diskPtr->state )
                        {
                                dwarning(("%s: incompatible disk states: %s (%d) vs. %s (%d)\n", __FUNCTION__, DISKSTATE(diskPtr->state), diskPtr->state, DISKSTATE(result), result));
                        }
                break;

                case kDiskStateIdle: /* not busy */
                case kDiskStateNew: /* not busy */
                case kDiskStateNewlyEjected: /* not busy */
                case kDiskStateNewlyUnmounted: /* not busy */
                default:
                        /* do nothing - result initialized to zero / kDiskStateIdle */
                break;
        }

//Return:
        dwarning(("%s() => %s (%d)\n", __FUNCTION__, DISKSTATE(result), result));
        return result;

} // AreWeBusyForDisk


//------------------------------------------------------------------------

// write out our pid

static void
writepid(void)
{
        FILE *fp;

        fp = fopen(PID_FILE, "w");
        if (fp != NULL)
        {
                fprintf(fp, "%d\n", getpid());
                fclose(fp);
        }
}


int DiskArbitrationServerMain(int argc, char* argv[])
{
	kern_return_t r;
	mach_port_t serverPort;

	mach_port_t ioNotificationPort;
	const char * ioNotificationType;
        io_iterator_t ioIterator;  // first match
        io_iterator_t ioIterator2; // terminated
        io_registry_entry_t	entry; // for flushing and arming



	mach_port_t portSet;
	
	mach_msg_header_t* msg;
	mach_msg_header_t* reply;

        mach_msg_timeout_t timeout;

	// Handle command-line arguments

	programName = argv[0];
	
	if ((argc > 1) && (argv[1][0] == '-') && (argv[1][1] == 'd'))
	{
		/* Do nothing.  Do not daemonize yourself.  Good for "-d"(ebugging). */
		gDebug = 1;
		gDaemon = 0;
		dwarning(("DiskArbitration is not a daemon\n"));
	}
	else
	{
            gDaemon = 1;
		dwarning(("DiskArbitration is a daemon\n"));
	}        

	// Incoming message buffer needs room for message header, body, and trailer.
	msg = (mach_msg_header_t*)malloc(kMsgSize);

	// Outgoing message buffer needs room for only message header and body
	reply = (mach_msg_header_t*)malloc(kMsgSize);

        /*
        -- Cache needed "stuff"
        */
        cacheFileSystemDictionaries();
        cacheFileSystemMatchingArray();

        if (gDebug) {
            printArgsForFsname("ufs");
            printArgsForFsname("hfs");
            printArgsForFsname("cd9660");
            printArgsForFsname("msdos");
            printArgsForFsname("udf");
            printArgsForFsname("cdda");
            printArgsForFsname("foobar");
        }
	
	/*
	-- Phase 1 - handle fixed disks
	*/

	// Create and register a notify port for this task
	
	r = InitNotifyPort();
	if (r != KERN_SUCCESS)
	{
		return -1;
	}
	
	// Get the IO Master Port
	
	r = IOMasterPort(bootstrap_port, &ioMasterPort);
	if (r != KERN_SUCCESS)
	{
		LogErrorMessage("(%s:%d) IOMasterPort failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
		return -1;
	}

	// Allocate a port for receiving IO notifications

	r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &ioNotificationPort);
	if (r != KERN_SUCCESS)
	{
		LogErrorMessage("(%s:%d) mach_port_allocate failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
		return -1;
	}

	// Register for IO notifications of "IOMedia" objects and get an IO iterator in return

        ioNotificationType = kIOFirstMatchNotification;
        r = IOServiceAddNotification(	ioMasterPort, ioNotificationType,
                                                                        IOServiceMatching( "IOMedia" ),
                                                                        ioNotificationPort, (unsigned int) ioNotificationType,
                                                                        &ioIterator );
        if (r != KERN_SUCCESS)
        {
                LogErrorMessage("(%s:%d) IOServiceAddNotification failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
                return -1;
        }

        // Get the existing disks from the IO Registry and arm the notification

        GetDisksFromRegistry( ioIterator );

        ioNotificationType = kIOTerminatedNotification;
        r = IOServiceAddNotification(	ioMasterPort, ioNotificationType,
                                                                        IOServiceMatching( "IOMedia" ),
                                                                        ioNotificationPort, (unsigned int) ioNotificationType,
                                                                        &ioIterator2 );
        if (r != KERN_SUCCESS)
        {
                LogErrorMessage("(%s:%d) IOServiceAddNotification failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
                return -1;
        }

        while ( entry = IOIteratorNext( ioIterator2 ) ) {  // clear the iterator and move on
            IOObjectRelease( entry );
        }

	// Perform auto-mounting (including probing and fsck) for each disk we discovered

	autodiskmount(FALSE);
	
	/*
	-- Phase 2 - optionally daemonize and re-initialize ports in the child process
	*/

	// Daemonize
	
	dwarning(("Non-debug version calls daemon() here...\n"));
	
	if ( gDaemon )
	{
                // Free all the old disks (they have bad IOService pointers)
                // we *have* to do this before daemon or we will leak io_service_t objects

                FreeAllDisks();

		if (-1 == daemon(0, 0))
		{
			return -1;
		}
            
		/*
		-- Port re-initialization for child process
		*/
	
		// Create and register a notify port for this task
		
		r = InitNotifyPort();
		if (r != KERN_SUCCESS)
		{
			return -1;
		}
		
		// Get the IO Master Port
		
		r = IOMasterPort(bootstrap_port, &ioMasterPort);
		if (r != KERN_SUCCESS)
		{
			LogErrorMessage("(%s:%d) IOMasterPort failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
			return -1;
		}
		
		// Allocate a port for receiving IO notifications
	
		r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &ioNotificationPort);
		if (r != KERN_SUCCESS)
		{
			LogErrorMessage("(%s:%d) mach_port_allocate failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
			return -1;
		}
	
		// Register for IO notifications of "IOMedia" objects and get an IO iterator in return
	
		ioNotificationType = kIOFirstMatchNotification;
		r = IOServiceAddNotification(	ioMasterPort, ioNotificationType,
										IOServiceMatching( "IOMedia" ),
										ioNotificationPort, (unsigned int) ioNotificationType,
										&ioIterator );
		if (r != KERN_SUCCESS)
		{
			LogErrorMessage("(%s:%d) IOServiceAddNotification failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
			return -1;
		}


                
		// Get the existing disks from the IO Registry and arm the notification

                GetDisksFromRegistry( ioIterator );

                ioNotificationType = kIOTerminatedNotification;
                r = IOServiceAddNotification(	ioMasterPort, ioNotificationType,
                                                                                IOServiceMatching( "IOMedia" ),
                                                                                ioNotificationPort, (unsigned int) ioNotificationType,
                                                                                &ioIterator2 );
                if (r != KERN_SUCCESS)
                {
                        LogErrorMessage("(%s:%d) IOServiceAddNotification failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
                        return -1;
                }

                while ( entry = IOIteratorNext( ioIterator2 ) ) {  // clear the iterator and move on
                    IOObjectRelease( entry );
                }

		// Perform auto-mounting (including probing and fsck) for each disk we discovered
		// This should be a no-op here, since we probably discovered all these disks before daemonizing
	
		autodiskmount(FALSE);
	
	}

        /*  We have found CFSTR to not be thread safe, so let's initialize all CFSTR resources before moving on
                it appears that the CFSTR hash lookup is thread safe
        */
        {
                yankedHeader = YANKED_HEADER;
                yankedMessage = YANKED_MESSAGE;
                unrecognizedHeader = UNINITED_HEADER;
                unrecognizedMessage = UNINITED_MESSAGE;
                ejectString = EJECT_BUTTON;
                ignoreString = IGNORE_BUTTON;
                initString = INIT_BUTTON;
        }
	
	/*
	-- Phase 3 - become a server
	*/

	// Create the server port with receive and send rights
	
	r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &serverPort);
	if (r != KERN_SUCCESS)
	{
		LogErrorMessage("(%s:%d) mach_port_allocate failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
		return -1;
	}
	
	r = mach_port_insert_right(mach_task_self(), serverPort, serverPort, MACH_MSG_TYPE_MAKE_SEND);
	if (r != KERN_SUCCESS)
	{
		LogErrorMessage("(%s:%d) mach_port_insert_right failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
		return -1;
	}

	dwarning(("%s: serverPort = %d\n", programName, (int)serverPort));

	// Register the server port with the bootstrap server so clients can find it
	
	r = bootstrap_register(bootstrap_port, DISKARB_SERVER_NAME, serverPort);
	if (r != KERN_SUCCESS)
	{
		return -1;
	}
	
	// Create a port set....

	r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &portSet);
	if (r != KERN_SUCCESS)
	{
		LogErrorMessage("(%s:%d) mach_port_allocate failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
		return -1;
	}

	// ... and add the notify, server, and IO notification ports to it.

	r = mach_port_move_member(mach_task_self(), GetNotifyPort(), portSet);
	if (r != KERN_SUCCESS)
	{
		LogErrorMessage("(%s:%d) mach_port_move_member (notify port) failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
		return -1;
	}

	r = mach_port_move_member(mach_task_self(), serverPort, portSet);
	if (r != KERN_SUCCESS)
	{
		LogErrorMessage("(%s:%d) mach_port_move_member (server port) failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
		return -1;
	}

	r = mach_port_move_member(mach_task_self(), ioNotificationPort, portSet);
	if (r != KERN_SUCCESS)
	{
		LogErrorMessage("(%s:%d) mach_port_move_member (IO notification port) failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
		return -1;
	}

        // write out our pid
        writepid();

	// Server loop

	while( 1 )
	{
                DiskPtr diskPtr;
                DiskPtr nextDiskPtr;
		mach_port_t deadPort;
		DiskState busyState;

		/* Are we (still) busy, i.e., in the midst of processing an async unmount/eject transaction? */

		busyState = AreWeBusy();

		if ( kDiskStateToBeUnmounted == busyState || kDiskStateToBeUnmountedAndEjected == busyState )
		{
			CompleteUnmount();
			// Note: Recalculate <busyState> since we may have completed an unmount w/ zero clients.
                        if (NumUnsetAckValuesForAllDisks() == 0) {
                                SendCompletedMsgs(kDiskArbCompletedPostUnmount);
                        }
			busyState = AreWeBusy();
		}

		if ( kDiskStateToBeEjected == busyState )
		{
			CompleteEject();
			// Note: Recalculate <busyState> since we may have completed an unmount w/ zero clients.
                        if (NumUnsetAckValuesForAllDisks() == 0) {
                                SendCompletedMsgs(kDiskArbCompletedPostEject);
                        }
			busyState = AreWeBusy();
		}

		/* Send out async notifications about to any to-be-unmounted/ejected disks */

		if ( kDiskStateToBeUnmounted == busyState || kDiskStateToBeUnmountedAndEjected == busyState )
		{
			SendPreUnmountMsgs();
                        //SendCompletedMsgs(kDiskArbCompletedPreUnmount);

		}
		
		/* Send out async notifications about to any to-be-ejected disks */

		if ( kDiskStateToBeEjected == busyState )
		{
			SendPreEjectMsgs();
                        //SendCompletedMsgs(kDiskArbCompletedPreEject);

		}

		/* Send out synchronous notifications about to any new clients and about any new disks */

		if ( kDiskStateIdle == busyState )
		{
			SendDiskAppearedMsgs();
                        SendCompletedMsgs(kDiskArbCompletedDiskAppeared);

		}

		/* For each disk, if it was Ejected, then delete it, and if it was Unmounted, then mark it Idle again */

                nextDiskPtr = g.Disks;
		
		for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = nextDiskPtr)
		{
                        nextDiskPtr = diskPtr->next;
			switch ( diskPtr->state )
			{
				case kDiskStateNewlyUnmounted:
					SetStateForOnePartition( diskPtr, kDiskStateIdle );
				break;

				case kDiskStateNewlyEjected:
					FreeDisk( diskPtr );
				break;

				case kDiskStateIdle: /* do nothing */
				case kDiskStateNew: /* do nothing */
				case kDiskStateToBeUnmounted: /* do nothing - waiting for acknowledgements */
				case kDiskStateToBeEjected: /* do nothing - waiting for acknowledgements */
				case kDiskStateToBeUnmountedAndEjected: /* do nothing - waiting for acknowledgements */
				default:
					/* do nothing */
				break;
			}
		}

		if ( g.NumDisksAddedOrDeleted )
		{
			PrintDisks();
			g.NumDisksAddedOrDeleted = 0;
		}

		/* Wait for: client requests, disk insertions, and death notifications */

                /* if there are clients who we are waiting on acknowledgements from we need to
                    set a timeout on our recieve, if the timeout passes, then the clients never
                    responded and we should allow everything to continue without them (or post
                    a warning to the user in some format ... CFUserNotification?) */

                if (NumUnsetAckValuesForAllDisks() != 0) {
                    timeout = (mach_msg_timeout_t)(15000);
                } else {
                    timeout = MACH_MSG_TIMEOUT_NONE;
                }
                //printf("**********Setting timeout to  (%d)\n", timeout);

		dwarning(("%s: mach_msg\n", programName));
                r = mach_msg(msg, MACH_RCV_MSG | (MACH_MSG_TIMEOUT_NONE == timeout ? 0 : MACH_RCV_TIMEOUT), 0, kMsgSize, portSet, timeout, MACH_PORT_NULL);
                if (r == MACH_RCV_TIMED_OUT) {
                    AckValue *unresponder = GetUnresponderFromAckValuesForAllDisks();

                    if (unresponder && unresponder->pid) {
                            Client *unrespondingClient = LookupClientByPID(unresponder->pid);
                            mach_port_t unrespondingPort = unrespondingClient->port;
                            //ClearAckValuesForAllDisks();
                            dwarning(("**********Someone (pid = (%d)) isn't responding to a sent acknowledgement request\n", unresponder->pid));
                            SendClientWasDisconnectedMsg(unrespondingClient);
                            ClientDeath(unrespondingPort);
                    } else {
                            dwarning(("Someone timed out, but we don't know who ..."));
                    }
                }
                
		if (r != KERN_SUCCESS) {
			LogErrorMessage("(%s:%d) mach_msg failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
			continue;
		}
		
		/* Demultiplex based on the port: notification port or server port */

		if ( msg->msgh_local_port == GetNotifyPort() )
		{
			dwarning(("%s: Message received on notification port\n", programName));

			/* This test is partially redundant, and that's alright */
			
			if ( MessageIsNotificationDeath( msg, & deadPort ) == TRUE )
			{
				dwarning(("Death of port %d\n", (int)deadPort));
				
				/* Deallocate the send right that came in the dead name notification */
	
				{
					kern_return_t r;
			
					r = mach_port_deallocate( mach_task_self(), deadPort );
					if ( r ) dwarning(("%s(client = $%08x): mach_port_deallocate(...,$%08x) => $%08x: %s\n", __FUNCTION__, (int)deadPort, (int)deadPort, r, mach_error_string(r)));
				}
				
				/* Clean up the resources consumed by the client record */
				
				ClientDeath( deadPort );
			}
			else
			{
				/* Do nothing - MessageIsNotificationDeath() will log an error in this case */
			}
		}
		else if ( msg->msgh_local_port == serverPort )
		{
			dwarning(("%s: Message received on server port from port $%08x\n", programName, (int)(msg->msgh_remote_port)));
			bzero( reply, sizeof( mach_msg_header_t ) );
			(void) ClientToServer_server(msg, reply);
			(void) mach_msg_send(reply);
		}
		else if ( msg->msgh_local_port == ioNotificationPort )
		{
			dwarning(("%s: Message received on IO notification port from port $%08x\n", programName, (int)(msg->msgh_remote_port)));
			HandleIONotificationMsg( (IONotificationMsgPtr) msg );
		}
		else
		{
			// This should never happen.  Log it.
			LogErrorMessage("Unrecognized receive port %d.\n", (int)(msg->msgh_local_port));
		}

	} /* while (1) */
	
	// We only exit the server loop if something goes wrong

	dwarning(("%s: exit\n", programName));

	return -1;

}


