xref: /xnu-11215/iokit/bsddev/IOKitBSDInit.cpp (revision 186b8fce)
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 
36 extern "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 
49 extern dev_t mdevadd(int devid, uint64_t base, unsigned int size, int phys);
50 extern dev_t mdevlookup(int devid);
51 extern void mdevremoveall(void);
52 extern void di_root_ramfile(IORegistryEntry * entry);
53 
54 kern_return_t
55 IOKitBSDInit( void )
56 {
57     IOService::publishResource("IOBSD");
58 
59     return( kIOReturnSuccess );
60 }
61 
62 void
63 IOServicePublishResource( const char * property, boolean_t value )
64 {
65     if ( value)
66         IOService::publishResource( property, kOSBooleanTrue );
67     else
68         IOService::getResourceService()->removeProperty( property );
69 }
70 
71 boolean_t
72 IOServiceWaitForMatchingResource( 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 
97 boolean_t
98 IOCatalogueMatchingDriversPresent( 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 
125 OSDictionary * 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 
153 OSDictionary * IOUUIDMatching( void )
154 {
155     return IOService::resourceMatching( "boot-uuid-media" );
156 }
157 
158 OSDictionary * 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 
212 static 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 
265 OSDictionary * IOOFPathMatching( const char * path, char * buf, int maxLen )
266 {
267     OSDictionary *	matching = NULL;
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 
308 static int didRam = 0;
309 enum { kMaxPathBuf = 512, kMaxBootVar = 128 };
310 
311 kern_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     char *		str;
327     const char *	look = 0;
328     int			len;
329     bool		debugInfoPrintedOnce = false;
330     const char * 	uuidStr = NULL;
331 
332     static int		mountAttempts = 0;
333 
334     int xchar, dchar;
335 
336 
337     if( mountAttempts++)
338 	IOSleep( 5 * 1000 );
339 
340     str = (char *) IOMalloc( kMaxPathBuf + kMaxBootVar );
341     if( !str)
342 	return( kIOReturnNoMemory );
343     rdBootVar = str + kMaxPathBuf;
344 
345     if (!PE_parse_boot_argn("rd", rdBootVar, kMaxBootVar )
346      && !PE_parse_boot_argn("rootdev", rdBootVar, kMaxBootVar ))
347 	rdBootVar[0] = 0;
348 
349     do {
350 	if( (regEntry = IORegistryEntry::fromPath( "/chosen", gIODTPlane ))) {
351 	    di_root_ramfile(regEntry);
352             data = OSDynamicCast(OSData, regEntry->getProperty( "root-matching" ));
353             if (data) {
354                matching = OSDynamicCast(OSDictionary, OSUnserializeXML((char *)data->getBytesNoCopy()));
355                 if (matching) {
356                     continue;
357                 }
358             }
359 
360 	    data = (OSData *) regEntry->getProperty( "boot-uuid" );
361 	    if( data) {
362 		uuidStr = (const char*)data->getBytesNoCopy();
363 		OSString *uuidString = OSString::withCString( uuidStr );
364 
365 		// match the boot-args boot-uuid processing below
366 		if( uuidString) {
367 		    IOLog("rooting via boot-uuid from /chosen: %s\n", uuidStr);
368 		    IOService::publishResource( "boot-uuid", uuidString );
369 		    uuidString->release();
370 		    matching = IOUUIDMatching();
371 		    mediaProperty = "boot-uuid-media";
372 		    regEntry->release();
373 		    continue;
374 		} else {
375 		    uuidStr = NULL;
376 		}
377 	    }
378 	    regEntry->release();
379 	}
380     } while( false );
381 
382 //
383 //	See if we have a RAMDisk property in /chosen/memory-map.  If so, make it into a device.
384 //	It will become /dev/mdx, where x is 0-f.
385 //
386 
387 	if(!didRam) {												/* Have we already build this ram disk? */
388 		didRam = 1;												/* Remember we did this */
389 		if((regEntry = IORegistryEntry::fromPath( "/chosen/memory-map", gIODTPlane ))) {	/* Find the map node */
390 			data = (OSData *)regEntry->getProperty("RAMDisk");	/* Find the ram disk, if there */
391 			if(data) {											/* We found one */
392 				uintptr_t *ramdParms;
393 				ramdParms = (uintptr_t *)data->getBytesNoCopy();	/* Point to the ram disk base and size */
394 				(void)mdevadd(-1, ml_static_ptovirt(ramdParms[0]) >> 12, ramdParms[1] >> 12, 0);	/* Initialize it and pass back the device number */
395 			}
396 			regEntry->release();								/* Toss the entry */
397 		}
398 	}
399 
400 //
401 //	Now check if we are trying to root on a memory device
402 //
403 
404 	if((rdBootVar[0] == 'm') && (rdBootVar[1] == 'd') && (rdBootVar[3] == 0)) {
405 		dchar = xchar = rdBootVar[2];							/* Get the actual device */
406 		if((xchar >= '0') && (xchar <= '9')) xchar = xchar - '0';	/* If digit, convert */
407 		else {
408 			xchar = xchar & ~' ';								/* Fold to upper case */
409 			if((xchar >= 'A') && (xchar <= 'F')) {				/* Is this a valid digit? */
410 				xchar = (xchar & 0xF) + 9;						/* Convert the hex digit */
411 				dchar = dchar | ' ';							/* Fold to lower case */
412 			}
413 			else xchar = -1;									/* Show bogus */
414 		}
415 		if(xchar >= 0) {										/* Do we have a valid memory device name? */
416 			*root = mdevlookup(xchar);							/* Find the device number */
417 			if(*root >= 0) {									/* Did we find one? */
418 
419 				rootName[0] = 'm';								/* Build root name */
420 				rootName[1] = 'd';								/* Build root name */
421 				rootName[2] = dchar;							/* Build root name */
422 				rootName[3] = 0;								/* Build root name */
423 				IOLog("BSD root: %s, major %d, minor %d\n", rootName, major(*root), minor(*root));
424 				*oflags = 0;									/* Show that this is not network */
425 				goto iofrootx;									/* Join common exit... */
426 			}
427 			panic("IOFindBSDRoot: specified root memory device, %s, has not been configured\n", rdBootVar);	/* Not there */
428 		}
429 	}
430 
431       if( (!matching) && rdBootVar[0] ) {
432 	// by BSD name
433 	look = rdBootVar;
434 	if( look[0] == '*')
435 	    look++;
436 
437 	if ( strncmp( look, "en", strlen( "en" )) == 0 ) {
438 	    matching = IONetworkNamePrefixMatching( "en" );
439 	} else if ( strncmp( look, "uuid", strlen( "uuid" )) == 0 ) {
440             char *uuid;
441             OSString *uuidString;
442 
443             uuid = (char *)IOMalloc( kMaxBootVar );
444 
445             if ( uuid ) {
446                 if (!PE_parse_boot_argn( "boot-uuid", uuid, kMaxBootVar )) {
447                     panic( "rd=uuid but no boot-uuid=<value> specified" );
448                 }
449                 uuidString = OSString::withCString( uuid );
450                 if ( uuidString ) {
451                     IOService::publishResource( "boot-uuid", uuidString );
452                     uuidString->release();
453                     IOLog( "\nWaiting for boot volume with UUID %s\n", uuid );
454                     matching = IOUUIDMatching();
455                     mediaProperty = "boot-uuid-media";
456                 }
457                 IOFree( uuid, kMaxBootVar );
458             }
459 	} else {
460 	    matching = IOBSDNameMatching( look );
461 	}
462     }
463 
464     if( !matching) {
465 	OSString * astring;
466 	// Match any HFS media
467 
468         matching = IOService::serviceMatching( "IOMedia" );
469         astring = OSString::withCStringNoCopy("Apple_HFS");
470         if ( astring ) {
471             matching->setObject("Content", astring);
472             astring->release();
473         }
474     }
475 
476     if( true && matching) {
477         OSSerialize * s = OSSerialize::withCapacity( 5 );
478 
479         if( matching->serialize( s )) {
480             IOLog( "Waiting on %s\n", s->text() );
481             s->release();
482         }
483     }
484 
485     do {
486         t.tv_sec = ROOTDEVICETIMEOUT;
487         t.tv_nsec = 0;
488 	matching->retain();
489         service = IOService::waitForService( matching, &t );
490 	if( (!service) || (mountAttempts == 10)) {
491             PE_display_icon( 0, "noroot");
492             IOLog( "Still waiting for root device\n" );
493 
494             if( !debugInfoPrintedOnce) {
495                 debugInfoPrintedOnce = true;
496                 if( gIOKitDebug & kIOLogDTree) {
497                     IOLog("\nDT plane:\n");
498                     IOPrintPlane( gIODTPlane );
499                 }
500                 if( gIOKitDebug & kIOLogServiceTree) {
501                     IOLog("\nService plane:\n");
502                     IOPrintPlane( gIOServicePlane );
503                 }
504                 if( gIOKitDebug & kIOLogMemory)
505                     IOPrintMemory();
506             }
507 	}
508     } while( !service);
509     matching->release();
510 
511     if ( service && mediaProperty ) {
512         service = (IOService *)service->getProperty(mediaProperty);
513     }
514 
515     mjr = 0;
516     mnr = 0;
517 
518     // If the IOService we matched to is a subclass of IONetworkInterface,
519     // then make sure it has been registered with BSD and has a BSD name
520     // assigned.
521 
522     if ( service
523     &&   service->metaCast( "IONetworkInterface" )
524     &&   !IORegisterNetworkInterface( service ) )
525     {
526         service = 0;
527     }
528 
529     if( service) {
530 
531 	len = kMaxPathBuf;
532 	service->getPath( str, &len, gIOServicePlane );
533 	IOLog( "Got boot device = %s\n", str );
534 
535 	iostr = (OSString *) service->getProperty( kIOBSDNameKey );
536 	if( iostr)
537 	    strlcpy( rootName, iostr->getCStringNoCopy(), rootNameSize );
538 	off = (OSNumber *) service->getProperty( kIOBSDMajorKey );
539 	if( off)
540 	    mjr = off->unsigned32BitValue();
541 	off = (OSNumber *) service->getProperty( kIOBSDMinorKey );
542 	if( off)
543 	    mnr = off->unsigned32BitValue();
544 
545 	if( service->metaCast( "IONetworkInterface" ))
546 	    flags |= 1;
547 
548     } else {
549 
550 	IOLog( "Wait for root failed\n" );
551         strlcpy( rootName, "en0", rootNameSize );
552         flags |= 1;
553     }
554 
555     IOLog( "BSD root: %s", rootName );
556     if( mjr)
557 	IOLog(", major %d, minor %d\n", mjr, mnr );
558     else
559 	IOLog("\n");
560 
561     *root = makedev( mjr, mnr );
562     *oflags = flags;
563 
564     IOFree( str,  kMaxPathBuf + kMaxBootVar );
565 
566 iofrootx:
567     if( (gIOKitDebug & (kIOLogDTree | kIOLogServiceTree | kIOLogMemory)) && !debugInfoPrintedOnce) {
568 
569 	IOService::getPlatform()->waitQuiet();
570         if( gIOKitDebug & kIOLogDTree) {
571             IOLog("\nDT plane:\n");
572             IOPrintPlane( gIODTPlane );
573         }
574         if( gIOKitDebug & kIOLogServiceTree) {
575             IOLog("\nService plane:\n");
576             IOPrintPlane( gIOServicePlane );
577         }
578         if( gIOKitDebug & kIOLogMemory)
579             IOPrintMemory();
580     }
581 
582     return( kIOReturnSuccess );
583 }
584 
585 bool IORamDiskBSDRoot(void)
586 {
587     char rdBootVar[kMaxBootVar];
588     if (PE_parse_boot_argn("rd", rdBootVar, kMaxBootVar )
589      || PE_parse_boot_argn("rootdev", rdBootVar, kMaxBootVar )) {
590         if((rdBootVar[0] == 'm') && (rdBootVar[1] == 'd') && (rdBootVar[3] == 0)) {
591             return true;
592         }
593     }
594     return false;
595 }
596 
597 void IOSecureBSDRoot(const char * rootName)
598 {
599 }
600 
601 void *
602 IOBSDRegistryEntryForDeviceTree(char * path)
603 {
604     return (IORegistryEntry::fromPath(path, gIODTPlane));
605 }
606 
607 void
608 IOBSDRegistryEntryRelease(void * entry)
609 {
610     IORegistryEntry * regEntry = (IORegistryEntry *)entry;
611 
612     if (regEntry)
613 	regEntry->release();
614     return;
615 }
616 
617 const void *
618 IOBSDRegistryEntryGetData(void * entry, char * property_name,
619 			  int * packet_length)
620 {
621     OSData *		data;
622     IORegistryEntry * 	regEntry = (IORegistryEntry *)entry;
623 
624     data = (OSData *) regEntry->getProperty(property_name);
625     if (data) {
626 	*packet_length = data->getLength();
627         return (data->getBytesNoCopy());
628     }
629     return (NULL);
630 }
631 
632 kern_return_t IOBSDGetPlatformUUID( uuid_t uuid, mach_timespec_t timeout )
633 {
634     IOService * resources;
635     OSString *  string;
636 
637     resources = IOService::waitForService( IOService::resourceMatching( kIOPlatformUUIDKey ), ( timeout.tv_sec || timeout.tv_nsec ) ? &timeout : 0 );
638     if ( resources == 0 ) return KERN_OPERATION_TIMED_OUT;
639 
640     string = ( OSString * ) IOService::getPlatform( )->getProvider( )->getProperty( kIOPlatformUUIDKey );
641     if ( string == 0 ) return KERN_NOT_SUPPORTED;
642 
643     uuid_parse( string->getCStringNoCopy( ), uuid );
644 
645     return KERN_SUCCESS;
646 }
647 
648 kern_return_t IOBSDGetPlatformSerialNumber( char *serial_number_str, u_int32_t len )
649 {
650     OSDictionary * platform_dict;
651     IOService *platform;
652     OSString *  string;
653 
654     if (len < 1) {
655 	    return 0;
656     }
657     serial_number_str[0] = '\0';
658 
659     platform_dict = IOService::serviceMatching( "IOPlatformExpertDevice" );
660     if (platform_dict == NULL) {
661 	    return KERN_NOT_SUPPORTED;
662     }
663 
664     platform = IOService::waitForService( platform_dict );
665     if (platform) {
666 	    string = ( OSString * ) platform->getProperty( kIOPlatformSerialNumberKey );
667 	    if ( string == 0 ) {
668 		    return KERN_NOT_SUPPORTED;
669 	    } else {
670 		    strlcpy( serial_number_str, string->getCStringNoCopy( ), len );
671 	    }
672     }
673 
674     return KERN_SUCCESS;
675 }
676 
677 void IOBSDIterateMediaWithContent(const char *content_uuid_cstring, int (*func)(const char *bsd_dev_name, const char *uuid_str, void *arg), void *arg)
678 {
679     OSDictionary *dictionary;
680     OSString *content_uuid_string;
681 
682     dictionary = IOService::serviceMatching( "IOMedia" );
683     if( dictionary ) {
684 	content_uuid_string = OSString::withCString( content_uuid_cstring );
685 	if( content_uuid_string ) {
686 	    IOService *service;
687 	    OSIterator *iter;
688 
689 	    dictionary->setObject( "Content", content_uuid_string );
690 	    dictionary->retain();
691 
692 	    iter = IOService::getMatchingServices(dictionary);
693 	    while (iter && (service = (IOService *)iter->getNextObject())) {
694 		    if( service ) {
695 			    OSString *iostr = (OSString *) service->getProperty( kIOBSDNameKey );
696 			    OSString *uuidstr = (OSString *) service->getProperty( "UUID" );
697 			    const char *uuid;
698 
699 			    if( iostr) {
700 				    if (uuidstr) {
701 					    uuid = uuidstr->getCStringNoCopy();
702 				    } else {
703 					    uuid = "00000000-0000-0000-0000-000000000000";
704 				    }
705 
706 				    // call the callback
707 				    if (func && func(iostr->getCStringNoCopy(), uuid, arg) == 0) {
708 					    break;
709 				    }
710 			    }
711 		    }
712 	    }
713 	    if (iter)
714 		    iter->release();
715 
716 	    content_uuid_string->release();
717 	}
718 	dictionary->release();
719     }
720 }
721 
722 
723 int IOBSDIsMediaEjectable( const char *cdev_name )
724 {
725     int ret = 0;
726     OSDictionary *dictionary;
727     OSString *dev_name;
728 
729     if (strncmp(cdev_name, "/dev/", 5) == 0) {
730 	    cdev_name += 5;
731     }
732 
733     dictionary = IOService::serviceMatching( "IOMedia" );
734     if( dictionary ) {
735 	dev_name = OSString::withCString( cdev_name );
736 	if( dev_name ) {
737 	    IOService *service;
738 	    mach_timespec_t tv = { 5, 0 };    // wait up to "timeout" seconds for the device
739 
740 	    dictionary->setObject( kIOBSDNameKey, dev_name );
741 	    dictionary->retain();
742 	    service = IOService::waitForService( dictionary, &tv );
743 	    if( service ) {
744 		OSBoolean *ejectable = (OSBoolean *) service->getProperty( "Ejectable" );
745 
746 		if( ejectable ) {
747 			ret = (int)ejectable->getValue();
748 		}
749 
750 	    }
751 	    dev_name->release();
752 	}
753 	dictionary->release();
754     }
755 
756     return ret;
757 }
758 
759 } /* extern "C" */
760