1/*
2 * Copyright (c) 1998-2011 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28#include <IOKit/IOBSD.h>
29#include <IOKit/IOLib.h>
30#include <IOKit/IOService.h>
31#include <IOKit/IOCatalogue.h>
32#include <IOKit/IODeviceTreeSupport.h>
33#include <IOKit/IOKitKeys.h>
34#include <IOKit/IOPlatformExpert.h>
35
36extern "C" {
37
38#include <pexpert/pexpert.h>
39#include <kern/clock.h>
40#include <uuid/uuid.h>
41
42// how long to wait for matching root device, secs
43#if DEBUG
44#define ROOTDEVICETIMEOUT       120
45#else
46#define ROOTDEVICETIMEOUT       60
47#endif
48
49extern dev_t mdevadd(int devid, uint64_t base, unsigned int size, int phys);
50extern dev_t mdevlookup(int devid);
51extern void mdevremoveall(void);
52extern void di_root_ramfile(IORegistryEntry * entry);
53
54kern_return_t
55IOKitBSDInit( void )
56{
57    IOService::publishResource("IOBSD");
58
59    return( kIOReturnSuccess );
60}
61
62void
63IOServicePublishResource( const char * property, boolean_t value )
64{
65    if ( value)
66        IOService::publishResource( property, kOSBooleanTrue );
67    else
68        IOService::getResourceService()->removeProperty( property );
69}
70
71boolean_t
72IOServiceWaitForMatchingResource( const char * property, uint64_t timeout )
73{
74    OSDictionary *	dict = 0;
75    IOService *         match = 0;
76    boolean_t		found = false;
77
78    do {
79
80        dict = IOService::resourceMatching( property );
81        if( !dict)
82            continue;
83        match = IOService::waitForMatchingService( dict, timeout );
84        if ( match)
85            found = true;
86
87    } while( false );
88
89    if( dict)
90        dict->release();
91    if( match)
92        match->release();
93
94    return( found );
95}
96
97boolean_t
98IOCatalogueMatchingDriversPresent( const char * property )
99{
100    OSDictionary *	dict = 0;
101    OSOrderedSet *	set = 0;
102    SInt32		generationCount = 0;
103    boolean_t		found = false;
104
105    do {
106
107        dict = OSDictionary::withCapacity(1);
108        if( !dict)
109            continue;
110        dict->setObject( property, kOSBooleanTrue );
111        set = gIOCatalogue->findDrivers( dict, &generationCount );
112        if ( set && (set->getCount() > 0))
113            found = true;
114
115    } while( false );
116
117    if( dict)
118        dict->release();
119    if( set)
120        set->release();
121
122    return( found );
123}
124
125OSDictionary * IOBSDNameMatching( const char * name )
126{
127    OSDictionary *	dict;
128    const OSSymbol *	str = 0;
129
130    do {
131
132	dict = IOService::serviceMatching( gIOServiceKey );
133	if( !dict)
134	    continue;
135        str = OSSymbol::withCString( name );
136	if( !str)
137	    continue;
138        dict->setObject( kIOBSDNameKey, (OSObject *) str );
139        str->release();
140
141        return( dict );
142
143    } while( false );
144
145    if( dict)
146	dict->release();
147    if( str)
148	str->release();
149
150    return( 0 );
151}
152
153OSDictionary * IOUUIDMatching( void )
154{
155    return IOService::resourceMatching( "boot-uuid-media" );
156}
157
158OSDictionary * IONetworkNamePrefixMatching( const char * prefix )
159{
160    OSDictionary *	 matching;
161    OSDictionary *   propDict = 0;
162    const OSSymbol * str      = 0;
163	char networkType[128];
164
165    do {
166        matching = IOService::serviceMatching( "IONetworkInterface" );
167        if ( matching == 0 )
168            continue;
169
170        propDict = OSDictionary::withCapacity(1);
171        if ( propDict == 0 )
172            continue;
173
174        str = OSSymbol::withCString( prefix );
175        if ( str == 0 )
176            continue;
177
178        propDict->setObject( "IOInterfaceNamePrefix", (OSObject *) str );
179        str->release();
180        str = 0;
181
182		// see if we're contrained to netroot off of specific network type
183		if(PE_parse_boot_argn( "network-type", networkType, 128 ))
184		{
185			str = OSSymbol::withCString( networkType );
186			if(str)
187			{
188				propDict->setObject( "IONetworkRootType", str);
189				str->release();
190				str = 0;
191			}
192		}
193
194        if ( matching->setObject( gIOPropertyMatchKey,
195                                  (OSObject *) propDict ) != true )
196            continue;
197
198        propDict->release();
199        propDict = 0;
200
201        return( matching );
202
203    } while ( false );
204
205    if ( matching ) matching->release();
206    if ( propDict ) propDict->release();
207    if ( str      ) str->release();
208
209    return( 0 );
210}
211
212static bool IORegisterNetworkInterface( IOService * netif )
213{
214    // A network interface is typically named and registered
215    // with BSD after receiving a request from a user space
216    // "namer". However, for cases when the system needs to
217    // root from the network, this registration task must be
218    // done inside the kernel and completed before the root
219    // device is handed to BSD.
220
221    IOService *    stack;
222    OSNumber *     zero    = 0;
223    OSString *     path    = 0;
224    OSDictionary * dict    = 0;
225    char *         pathBuf = 0;
226    int            len;
227    enum { kMaxPathLen = 512 };
228
229    do {
230        stack = IOService::waitForService(
231                IOService::serviceMatching("IONetworkStack") );
232        if ( stack == 0 ) break;
233
234        dict = OSDictionary::withCapacity(3);
235        if ( dict == 0 ) break;
236
237        zero = OSNumber::withNumber((UInt64) 0, 32);
238        if ( zero == 0 ) break;
239
240        pathBuf = (char *) IOMalloc( kMaxPathLen );
241        if ( pathBuf == 0 ) break;
242
243        len = kMaxPathLen;
244        if ( netif->getPath( pathBuf, &len, gIOServicePlane )
245             == false ) break;
246
247        path = OSString::withCStringNoCopy( pathBuf );
248        if ( path == 0 ) break;
249
250        dict->setObject( "IOInterfaceUnit", zero );
251        dict->setObject( kIOPathMatchKey,   path );
252
253        stack->setProperties( dict );
254    }
255    while ( false );
256
257    if ( zero ) zero->release();
258    if ( path ) path->release();
259    if ( dict ) dict->release();
260    if ( pathBuf ) IOFree(pathBuf, kMaxPathLen);
261
262	return ( netif->getProperty( kIOBSDNameKey ) != 0 );
263}
264
265OSDictionary * IOOFPathMatching( const char * path, char * buf, int maxLen )
266{
267    OSDictionary *	matching;
268    OSString *		str;
269    char *		comp;
270    int			len;
271
272    do {
273
274	len = strlen( kIODeviceTreePlane ":" );
275	maxLen -= len;
276	if( maxLen <= 0)
277	    continue;
278
279	strlcpy( buf, kIODeviceTreePlane ":", len + 1 );
280	comp = buf + len;
281
282	len = strlen( path );
283	maxLen -= len;
284	if( maxLen <= 0)
285	    continue;
286	strlcpy( comp, path, len + 1 );
287
288	matching = OSDictionary::withCapacity( 1 );
289	if( !matching)
290	    continue;
291
292	str = OSString::withCString( buf );
293	if( !str)
294	    continue;
295        matching->setObject( kIOPathMatchKey, str );
296	str->release();
297
298	return( matching );
299
300    } while( false );
301
302    if( matching)
303        matching->release();
304
305    return( 0 );
306}
307
308static int didRam = 0;
309static int rootMountTries = 0;
310
311kern_return_t IOFindBSDRoot( char * rootName, unsigned int rootNameSize,
312				dev_t * root, u_int32_t * oflags )
313{
314    mach_timespec_t	t;
315    IOService *		service;
316    IORegistryEntry *	regEntry;
317    OSDictionary *	matching = 0;
318    OSString *		iostr;
319    OSNumber *		off;
320    OSData *		data = 0;
321
322    UInt32		flags = 0;
323    int			mnr, mjr;
324    const char *        mediaProperty = 0;
325    char *		rdBootVar;
326    enum {		kMaxPathBuf = 512, kMaxBootVar = 128 };
327    char *		str;
328    const char *	look = 0;
329    int			len;
330    bool		debugInfoPrintedOnce = false;
331    const char * 	uuidStr = NULL;
332
333    static int		mountAttempts = 0;
334
335    int xchar, dchar;
336
337
338    if( mountAttempts++)
339	IOSleep( 5 * 1000 );
340
341    str = (char *) IOMalloc( kMaxPathBuf + kMaxBootVar );
342    if( !str)
343	return( kIOReturnNoMemory );
344    rdBootVar = str + kMaxPathBuf;
345
346    if (!PE_parse_boot_argn("rd", rdBootVar, kMaxBootVar )
347     && !PE_parse_boot_argn("rootdev", rdBootVar, kMaxBootVar ))
348	rdBootVar[0] = 0;
349
350    do {
351	if( (regEntry = IORegistryEntry::fromPath( "/chosen", gIODTPlane ))) {
352	    di_root_ramfile(regEntry);
353            data = OSDynamicCast(OSData, regEntry->getProperty( "root-matching" ));
354            if (data) {
355               matching = OSDynamicCast(OSDictionary, OSUnserializeXML((char *)data->getBytesNoCopy()));
356                if (matching) {
357                    continue;
358                }
359            }
360
361	    data = (OSData *) regEntry->getProperty( "boot-uuid" );
362	    if( data) {
363		uuidStr = (const char*)data->getBytesNoCopy();
364		OSString *uuidString = OSString::withCString( uuidStr );
365
366		// match the boot-args boot-uuid processing below
367		if( uuidString) {
368		    IOLog("rooting via boot-uuid from /chosen: %s\n", uuidStr);
369		    IOService::publishResource( "boot-uuid", uuidString );
370		    uuidString->release();
371		    matching = IOUUIDMatching();
372		    mediaProperty = "boot-uuid-media";
373		    regEntry->release();
374		    continue;
375		} else {
376		    uuidStr = NULL;
377		}
378	    }
379	    regEntry->release();
380	}
381    } while( false );
382
383//
384//	See if we have a RAMDisk property in /chosen/memory-map.  If so, make it into a device.
385//	It will become /dev/mdx, where x is 0-f.
386//
387
388	if(!didRam) {												/* Have we already build this ram disk? */
389		didRam = 1;												/* Remember we did this */
390		if((regEntry = IORegistryEntry::fromPath( "/chosen/memory-map", gIODTPlane ))) {	/* Find the map node */
391			data = (OSData *)regEntry->getProperty("RAMDisk");	/* Find the ram disk, if there */
392			if(data) {											/* We found one */
393				UInt32		*ramdParms = 0;
394				ramdParms = (UInt32 *)data->getBytesNoCopy();	/* Point to the ram disk base and size */
395				(void)mdevadd(-1, ml_static_ptovirt(ramdParms[0]) >> 12, ramdParms[1] >> 12, 0);	/* Initialize it and pass back the device number */
396			}
397			regEntry->release();								/* Toss the entry */
398		}
399	}
400
401//
402//	Now check if we are trying to root on a memory device
403//
404
405	if((rdBootVar[0] == 'm') && (rdBootVar[1] == 'd') && (rdBootVar[3] == 0)) {
406		dchar = xchar = rdBootVar[2];							/* Get the actual device */
407		if((xchar >= '0') && (xchar <= '9')) xchar = xchar - '0';	/* If digit, convert */
408		else {
409			xchar = xchar & ~' ';								/* Fold to upper case */
410			if((xchar >= 'A') && (xchar <= 'F')) {				/* Is this a valid digit? */
411				xchar = (xchar & 0xF) + 9;						/* Convert the hex digit */
412				dchar = dchar | ' ';							/* Fold to lower case */
413			}
414			else xchar = -1;									/* Show bogus */
415		}
416		if(xchar >= 0) {										/* Do we have a valid memory device name? */
417			*root = mdevlookup(xchar);							/* Find the device number */
418			if(*root >= 0) {									/* Did we find one? */
419
420				rootName[0] = 'm';								/* Build root name */
421				rootName[1] = 'd';								/* Build root name */
422				rootName[2] = dchar;							/* Build root name */
423				rootName[3] = 0;								/* Build root name */
424				IOLog("BSD root: %s, major %d, minor %d\n", rootName, major(*root), minor(*root));
425				*oflags = 0;									/* Show that this is not network */
426				goto iofrootx;									/* Join common exit... */
427			}
428			panic("IOFindBSDRoot: specified root memory device, %s, has not been configured\n", rdBootVar);	/* Not there */
429		}
430	}
431
432      if( (!matching) && rdBootVar[0] ) {
433	// by BSD name
434	look = rdBootVar;
435	if( look[0] == '*')
436	    look++;
437
438	if ( strncmp( look, "en", strlen( "en" )) == 0 ) {
439	    matching = IONetworkNamePrefixMatching( "en" );
440	} else if ( strncmp( look, "uuid", strlen( "uuid" )) == 0 ) {
441            char *uuid;
442            OSString *uuidString;
443
444            uuid = (char *)IOMalloc( kMaxBootVar );
445
446            if ( uuid ) {
447                if (!PE_parse_boot_argn( "boot-uuid", uuid, kMaxBootVar )) {
448                    panic( "rd=uuid but no boot-uuid=<value> specified" );
449                }
450                uuidString = OSString::withCString( uuid );
451                if ( uuidString ) {
452                    IOService::publishResource( "boot-uuid", uuidString );
453                    uuidString->release();
454                    IOLog( "\nWaiting for boot volume with UUID %s\n", uuid );
455                    matching = IOUUIDMatching();
456                    mediaProperty = "boot-uuid-media";
457                }
458                IOFree( uuid, kMaxBootVar );
459            }
460	} else {
461	    matching = IOBSDNameMatching( look );
462	}
463    }
464
465    if( !matching) {
466	OSString * astring;
467	// Match any HFS media
468
469        matching = IOService::serviceMatching( "IOMedia" );
470        astring = OSString::withCStringNoCopy("Apple_HFS");
471        if ( astring ) {
472            matching->setObject("Content", astring);
473            astring->release();
474        }
475    }
476
477    if( true && matching) {
478        OSSerialize * s = OSSerialize::withCapacity( 5 );
479
480        if( matching->serialize( s )) {
481            IOLog( "Waiting on %s\n", s->text() );
482            s->release();
483        }
484    }
485
486    do {
487        t.tv_sec = ROOTDEVICETIMEOUT;
488        t.tv_nsec = 0;
489	matching->retain();
490        service = IOService::waitForService( matching, &t );
491        rootMountTries++;
492
493        /* Give up after a while. */
494        if(rootMountTries == 30)
495            panic("Gave up trying to mount root");
496
497	if( (!service) || (mountAttempts == 10)) {
498            PE_display_icon( 0, "noroot");
499            IOLog( "Still waiting for root device\n" );
500            if( !debugInfoPrintedOnce) {
501                debugInfoPrintedOnce = true;
502                if( gIOKitDebug & kIOLogDTree) {
503                    IOLog("\nDT plane:\n");
504                    IOPrintPlane( gIODTPlane );
505                }
506                if( gIOKitDebug & kIOLogServiceTree) {
507                    IOLog("\nService plane:\n");
508                    IOPrintPlane( gIOServicePlane );
509                }
510                if( gIOKitDebug & kIOLogMemory)
511                    IOPrintMemory();
512            }
513	}
514    } while( !service);
515    matching->release();
516
517    if ( service && mediaProperty ) {
518        service = (IOService *)service->getProperty(mediaProperty);
519    }
520
521    mjr = 0;
522    mnr = 0;
523
524    // If the IOService we matched to is a subclass of IONetworkInterface,
525    // then make sure it has been registered with BSD and has a BSD name
526    // assigned.
527
528    if ( service
529    &&   service->metaCast( "IONetworkInterface" )
530    &&   !IORegisterNetworkInterface( service ) )
531    {
532        service = 0;
533    }
534
535    if( service) {
536
537	len = kMaxPathBuf;
538	service->getPath( str, &len, gIOServicePlane );
539	IOLog( "Got boot device = %s\n", str );
540
541	iostr = (OSString *) service->getProperty( kIOBSDNameKey );
542	if( iostr)
543	    strlcpy( rootName, iostr->getCStringNoCopy(), rootNameSize );
544	off = (OSNumber *) service->getProperty( kIOBSDMajorKey );
545	if( off)
546	    mjr = off->unsigned32BitValue();
547	off = (OSNumber *) service->getProperty( kIOBSDMinorKey );
548	if( off)
549	    mnr = off->unsigned32BitValue();
550
551	if( service->metaCast( "IONetworkInterface" ))
552	    flags |= 1;
553
554    } else {
555
556	IOLog( "Wait for root failed\n" );
557        strlcpy( rootName, "en0", rootNameSize );
558        flags |= 1;
559    }
560
561    IOLog( "BSD root: %s", rootName );
562    if( mjr)
563	IOLog(", major %d, minor %d\n", mjr, mnr );
564    else
565	IOLog("\n");
566
567    *root = makedev( mjr, mnr );
568    *oflags = flags;
569
570    IOFree( str,  kMaxPathBuf + kMaxBootVar );
571
572iofrootx:
573    if( (gIOKitDebug & (kIOLogDTree | kIOLogServiceTree | kIOLogMemory)) && !debugInfoPrintedOnce) {
574#if 0
575	IOService::getPlatform()->waitQuiet();
576#endif
577        if( gIOKitDebug & kIOLogDTree) {
578            IOLog("\nDT plane:\n");
579            IOPrintPlane( gIODTPlane );
580        }
581        if( gIOKitDebug & kIOLogServiceTree) {
582            IOLog("\nService plane:\n");
583            IOPrintPlane( gIOServicePlane );
584        }
585        if( gIOKitDebug & kIOLogMemory)
586            IOPrintMemory();
587    }
588
589    return( kIOReturnSuccess );
590}
591
592void IOSecureBSDRoot(const char * rootName)
593{
594#if CONFIG_EMBEDDED
595    IOReturn         result;
596    IOPlatformExpert *pe;
597    const OSSymbol   *functionName = OSSymbol::withCStringNoCopy("SecureRootName");
598
599    while ((pe = IOService::getPlatform()) == 0) IOSleep(1 * 1000);
600
601    // Returns kIOReturnNotPrivileged is the root device is not secure.
602    // Returns kIOReturnUnsupported if "SecureRootName" is not implemented.
603    result = pe->callPlatformFunction(functionName, false, (void *)rootName, (void *)0, (void *)0, (void *)0);
604
605    functionName->release();
606
607    if (result == kIOReturnNotPrivileged) mdevremoveall();
608#endif
609}
610
611void *
612IOBSDRegistryEntryForDeviceTree(char * path)
613{
614    return (IORegistryEntry::fromPath(path, gIODTPlane));
615}
616
617void
618IOBSDRegistryEntryRelease(void * entry)
619{
620    IORegistryEntry * regEntry = (IORegistryEntry *)entry;
621
622    if (regEntry)
623	regEntry->release();
624    return;
625}
626
627const void *
628IOBSDRegistryEntryGetData(void * entry, char * property_name,
629			  int * packet_length)
630{
631    OSData *		data;
632    IORegistryEntry * 	regEntry = (IORegistryEntry *)entry;
633
634    data = (OSData *) regEntry->getProperty(property_name);
635    if (data) {
636	*packet_length = data->getLength();
637        return (data->getBytesNoCopy());
638    }
639    return (NULL);
640}
641
642kern_return_t IOBSDGetPlatformUUID( uuid_t uuid, mach_timespec_t timeout )
643{
644    IOService * resources;
645    OSString *  string;
646
647    resources = IOService::waitForService( IOService::resourceMatching( kIOPlatformUUIDKey ), ( timeout.tv_sec || timeout.tv_nsec ) ? &timeout : 0 );
648    if ( resources == 0 ) return KERN_OPERATION_TIMED_OUT;
649
650    string = ( OSString * ) IOService::getPlatform( )->getProvider( )->getProperty( kIOPlatformUUIDKey );
651    if ( string == 0 ) return KERN_NOT_SUPPORTED;
652
653    uuid_parse( string->getCStringNoCopy( ), uuid );
654
655    return KERN_SUCCESS;
656}
657
658kern_return_t IOBSDGetPlatformSerialNumber( char *serial_number_str, u_int32_t len )
659{
660    OSDictionary * platform_dict;
661    IOService *platform;
662    OSString *  string;
663
664    if (len < 1) {
665	    return 0;
666    }
667    serial_number_str[0] = '\0';
668
669    platform_dict = IOService::serviceMatching( "IOPlatformExpertDevice" );
670    if (platform_dict == NULL) {
671	    return KERN_NOT_SUPPORTED;
672    }
673
674    platform = IOService::waitForService( platform_dict );
675    if (platform) {
676	    string = ( OSString * ) platform->getProperty( kIOPlatformSerialNumberKey );
677	    if ( string == 0 ) {
678		    return KERN_NOT_SUPPORTED;
679	    } else {
680		    strlcpy( serial_number_str, string->getCStringNoCopy( ), len );
681	    }
682    }
683
684    return KERN_SUCCESS;
685}
686
687dev_t IOBSDGetMediaWithUUID( const char *uuid_cstring, char *bsd_name, int bsd_name_len, int timeout)
688{
689    dev_t dev = 0;
690    OSDictionary *dictionary;
691    OSString *uuid_string;
692
693    if (bsd_name_len < 1) {
694	return 0;
695    }
696    bsd_name[0] = '\0';
697
698    dictionary = IOService::serviceMatching( "IOMedia" );
699    if( dictionary ) {
700	uuid_string = OSString::withCString( uuid_cstring );
701	if( uuid_string ) {
702	    IOService *service;
703	    mach_timespec_t tv = { timeout, 0 };    // wait up to "timeout" seconds for the device
704
705	    dictionary->setObject( "UUID", uuid_string );
706	    dictionary->retain();
707	    service = IOService::waitForService( dictionary, &tv );
708	    if( service ) {
709		OSNumber *dev_major = (OSNumber *) service->getProperty( kIOBSDMajorKey );
710		OSNumber *dev_minor = (OSNumber *) service->getProperty( kIOBSDMinorKey );
711		OSString *iostr = (OSString *) service->getProperty( kIOBSDNameKey );
712
713		if( iostr)
714		    strlcpy( bsd_name, iostr->getCStringNoCopy(), bsd_name_len );
715
716		if ( dev_major && dev_minor )
717		    dev = makedev( dev_major->unsigned32BitValue(), dev_minor->unsigned32BitValue() );
718	    }
719	    uuid_string->release();
720	}
721	dictionary->release();
722    }
723
724    return dev;
725}
726
727
728void IOBSDIterateMediaWithContent(const char *content_uuid_cstring, int (*func)(const char *bsd_dev_name, const char *uuid_str, void *arg), void *arg)
729{
730    OSDictionary *dictionary;
731    OSString *content_uuid_string;
732
733    dictionary = IOService::serviceMatching( "IOMedia" );
734    if( dictionary ) {
735	content_uuid_string = OSString::withCString( content_uuid_cstring );
736	if( content_uuid_string ) {
737	    IOService *service;
738	    OSIterator *iter;
739
740	    dictionary->setObject( "Content", content_uuid_string );
741	    dictionary->retain();
742
743	    iter = IOService::getMatchingServices(dictionary);
744	    while (iter && (service = (IOService *)iter->getNextObject())) {
745		    if( service ) {
746			    OSString *iostr = (OSString *) service->getProperty( kIOBSDNameKey );
747			    OSString *uuidstr = (OSString *) service->getProperty( "UUID" );
748			    const char *uuid;
749
750			    if( iostr) {
751				    if (uuidstr) {
752					    uuid = uuidstr->getCStringNoCopy();
753				    } else {
754					    uuid = "00000000-0000-0000-0000-000000000000";
755				    }
756
757				    // call the callback
758				    if (func && func(iostr->getCStringNoCopy(), uuid, arg) == 0) {
759					    break;
760				    }
761			    }
762		    }
763	    }
764	    if (iter)
765		    iter->release();
766
767	    content_uuid_string->release();
768	}
769	dictionary->release();
770    }
771}
772
773
774int IOBSDIsMediaEjectable( const char *cdev_name )
775{
776    int ret = 0;
777    OSDictionary *dictionary;
778    OSString *dev_name;
779
780    if (strncmp(cdev_name, "/dev/", 5) == 0) {
781	    cdev_name += 5;
782    }
783
784    dictionary = IOService::serviceMatching( "IOMedia" );
785    if( dictionary ) {
786	dev_name = OSString::withCString( cdev_name );
787	if( dev_name ) {
788	    IOService *service;
789	    mach_timespec_t tv = { 5, 0 };    // wait up to "timeout" seconds for the device
790
791	    dictionary->setObject( kIOBSDNameKey, dev_name );
792	    dictionary->retain();
793	    service = IOService::waitForService( dictionary, &tv );
794	    if( service ) {
795		OSBoolean *ejectable = (OSBoolean *) service->getProperty( "Ejectable" );
796
797		if( ejectable ) {
798			ret = (int)ejectable->getValue();
799		}
800
801	    }
802	    dev_name->release();
803	}
804	dictionary->release();
805    }
806
807    return ret;
808}
809
810} /* extern "C" */
811