]> git.saurik.com Git - apple/xnu.git/blob - iokit/bsddev/IOKitBSDInit.cpp
xnu-792.18.15.tar.gz
[apple/xnu.git] / iokit / bsddev / IOKitBSDInit.cpp
1 /*
2 * Copyright (c) 1998-2000 Apple Computer, 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/IODeviceTreeSupport.h>
32 #include <IOKit/IOKitKeys.h>
33 #include <IOKit/IOPlatformExpert.h>
34
35 extern "C" {
36
37 #include <pexpert/pexpert.h>
38 #include <kern/clock.h>
39
40 // how long to wait for matching root device, secs
41 #define ROOTDEVICETIMEOUT 60
42
43 extern dev_t mdevadd(int devid, ppnum_t base, unsigned int size, int phys);
44 extern dev_t mdevlookup(int devid);
45
46 kern_return_t
47 IOKitBSDInit( void )
48 {
49 IOService::publishResource("IOBSD");
50
51 return( kIOReturnSuccess );
52 }
53
54 OSDictionary * IOBSDNameMatching( const char * name )
55 {
56 OSDictionary * dict;
57 const OSSymbol * str = 0;
58
59 do {
60
61 dict = IOService::serviceMatching( gIOServiceKey );
62 if( !dict)
63 continue;
64 str = OSSymbol::withCString( name );
65 if( !str)
66 continue;
67 dict->setObject( kIOBSDNameKey, (OSObject *) str );
68 str->release();
69
70 return( dict );
71
72 } while( false );
73
74 if( dict)
75 dict->release();
76 if( str)
77 str->release();
78
79 return( 0 );
80 }
81
82 OSDictionary * IOUUIDMatching( void )
83 {
84 return IOService::resourceMatching( "boot-uuid-media" );
85 }
86
87
88 OSDictionary * IOCDMatching( void )
89 {
90 OSDictionary * dict;
91 const OSSymbol * str;
92
93 dict = IOService::serviceMatching( "IOMedia" );
94 if( dict == 0 ) {
95 IOLog("Unable to find IOMedia\n");
96 return 0;
97 }
98
99 str = OSSymbol::withCString( "CD_ROM_Mode_1" );
100 if( str == 0 ) {
101 dict->release();
102 return 0;
103 }
104
105 dict->setObject( "Content Hint", (OSObject *)str );
106 str->release();
107 return( dict );
108 }
109
110 OSDictionary * IONetworkMatching( const char * path,
111 char * buf, int maxLen )
112 {
113 OSDictionary * matching = 0;
114 OSDictionary * dict;
115 OSString * str;
116 char * comp;
117 const char * skip;
118 int len;
119
120 do {
121
122 len = strlen( kIODeviceTreePlane ":" );
123 maxLen -= len;
124 if( maxLen < 0)
125 continue;
126
127 strcpy( buf, kIODeviceTreePlane ":" );
128 comp = buf + len;
129
130 // remove parameters following ':' from the path
131 skip = strchr( path, ':');
132 if( !skip)
133 continue;
134
135 len = skip - path;
136 maxLen -= len;
137 if( maxLen < 0)
138 continue;
139 strncpy( comp, path, len );
140 comp[ len ] = 0;
141
142 matching = IOService::serviceMatching( "IONetworkInterface" );
143 if( !matching)
144 continue;
145 dict = IOService::addLocation( matching );
146 if( !dict)
147 continue;
148
149 str = OSString::withCString( buf );
150 if( !str)
151 continue;
152 dict->setObject( kIOPathMatchKey, str );
153 str->release();
154
155 return( matching );
156
157 } while( false );
158
159 if( matching)
160 matching->release();
161
162 return( 0 );
163 }
164
165 OSDictionary * IONetworkNamePrefixMatching( const char * prefix )
166 {
167 OSDictionary * matching;
168 OSDictionary * propDict = 0;
169 const OSSymbol * str = 0;
170
171 do {
172 matching = IOService::serviceMatching( "IONetworkInterface" );
173 if ( matching == 0 )
174 continue;
175
176 propDict = OSDictionary::withCapacity(1);
177 if ( propDict == 0 )
178 continue;
179
180 str = OSSymbol::withCString( prefix );
181 if ( str == 0 )
182 continue;
183
184 propDict->setObject( "IOInterfaceNamePrefix", (OSObject *) str );
185 str->release();
186 str = 0;
187
188 if ( matching->setObject( gIOPropertyMatchKey,
189 (OSObject *) propDict ) != true )
190 continue;
191
192 propDict->release();
193 propDict = 0;
194
195 return( matching );
196
197 } while ( false );
198
199 if ( matching ) matching->release();
200 if ( propDict ) propDict->release();
201 if ( str ) str->release();
202
203 return( 0 );
204 }
205
206 static bool IORegisterNetworkInterface( IOService * netif )
207 {
208 // A network interface is typically named and registered
209 // with BSD after receiving a request from a user space
210 // "namer". However, for cases when the system needs to
211 // root from the network, this registration task must be
212 // done inside the kernel and completed before the root
213 // device is handed to BSD.
214
215 IOService * stack;
216 OSNumber * zero = 0;
217 OSString * path = 0;
218 OSDictionary * dict = 0;
219 char * pathBuf = 0;
220 int len;
221 enum { kMaxPathLen = 512 };
222
223 do {
224 stack = IOService::waitForService(
225 IOService::serviceMatching("IONetworkStack") );
226 if ( stack == 0 ) break;
227
228 dict = OSDictionary::withCapacity(3);
229 if ( dict == 0 ) break;
230
231 zero = OSNumber::withNumber((UInt64) 0, 32);
232 if ( zero == 0 ) break;
233
234 pathBuf = (char *) IOMalloc( kMaxPathLen );
235 if ( pathBuf == 0 ) break;
236
237 len = kMaxPathLen;
238 if ( netif->getPath( pathBuf, &len, gIOServicePlane )
239 == false ) break;
240
241 path = OSString::withCStringNoCopy( pathBuf );
242 if ( path == 0 ) break;
243
244 dict->setObject( "IOInterfaceUnit", zero );
245 dict->setObject( kIOPathMatchKey, path );
246
247 stack->setProperties( dict );
248 }
249 while ( false );
250
251 if ( zero ) zero->release();
252 if ( path ) path->release();
253 if ( dict ) dict->release();
254 if ( pathBuf ) IOFree(pathBuf, kMaxPathLen);
255
256 return ( netif->getProperty( kIOBSDNameKey ) != 0 );
257 }
258
259 OSDictionary * IODiskMatching( const char * path, char * buf, int maxLen )
260 {
261 const char * look;
262 const char * alias;
263 char * comp;
264 long unit = -1;
265 long partition = -1;
266 long lun = -1;
267 char c;
268
269 // scan the tail of the path for "@unit:partition"
270 do {
271 // Have to get the full path to the controller - an alias may
272 // tell us next to nothing, like "hd:8"
273 alias = IORegistryEntry::dealiasPath( &path, gIODTPlane );
274
275 look = path + strlen( path);
276 c = ':';
277 while( look != path) {
278 if( *(--look) == c) {
279 if( c == ':') {
280 partition = strtol( look + 1, 0, 0 );
281 c = '@';
282 } else if( c == '@') {
283 unit = strtol( look + 1, &comp, 16 );
284
285 if( *comp == ',') {
286 lun = strtol( comp + 1, 0, 16 );
287 }
288
289 c = '/';
290 } else if( c == '/') {
291 c = 0;
292 break;
293 }
294 }
295
296 if( alias && (look == path)) {
297 path = alias;
298 look = path + strlen( path);
299 alias = 0;
300 }
301 }
302 if( c || unit == -1 || partition == -1)
303 continue;
304
305 maxLen -= strlen( "{" kIOPathMatchKey "='" kIODeviceTreePlane ":" );
306 maxLen -= ( alias ? strlen( alias ) : 0 ) + (look - path);
307 maxLen -= strlen( "/@hhhhhhhh,hhhhhhhh:dddddddddd';}" );
308
309 if( maxLen > 0) {
310 sprintf( buf, "{" kIOPathMatchKey "='" kIODeviceTreePlane ":" );
311 comp = buf + strlen( buf );
312
313 if( alias) {
314 strcpy( comp, alias );
315 comp += strlen( alias );
316 }
317
318 if ( (look - path)) {
319 strncpy( comp, path, look - path);
320 comp += look - path;
321 }
322
323 if ( lun != -1 )
324 {
325 sprintf ( comp, "/@%lx,%lx:%ld';}", unit, lun, partition );
326 }
327 else
328 {
329 sprintf( comp, "/@%lx:%ld';}", unit, partition );
330 }
331 } else
332 continue;
333
334 return( OSDynamicCast(OSDictionary, OSUnserialize( buf, 0 )) );
335
336 } while( false );
337
338 return( 0 );
339 }
340
341 OSDictionary * IOOFPathMatching( const char * path, char * buf, int maxLen )
342 {
343 OSDictionary * matching;
344 OSString * str;
345 char * comp;
346 int len;
347
348 /* need to look up path, get device type,
349 call matching help based on device type */
350
351 matching = IODiskMatching( path, buf, maxLen );
352 if( matching)
353 return( matching );
354
355 do {
356
357 len = strlen( kIODeviceTreePlane ":" );
358 maxLen -= len;
359 if( maxLen < 0)
360 continue;
361
362 strcpy( buf, kIODeviceTreePlane ":" );
363 comp = buf + len;
364
365 len = strlen( path );
366 maxLen -= len;
367 if( maxLen < 0)
368 continue;
369 strncpy( comp, path, len );
370 comp[ len ] = 0;
371
372 matching = OSDictionary::withCapacity( 1 );
373 if( !matching)
374 continue;
375
376 str = OSString::withCString( buf );
377 if( !str)
378 continue;
379 matching->setObject( kIOPathMatchKey, str );
380 str->release();
381
382 return( matching );
383
384 } while( false );
385
386 if( matching)
387 matching->release();
388
389 return( 0 );
390 }
391
392 IOService * IOFindMatchingChild( IOService * service )
393 {
394 // find a matching child service
395 IOService * child = 0;
396 OSIterator * iter = service->getClientIterator();
397 if ( iter ) {
398 while( ( child = (IOService *) iter->getNextObject() ) ) {
399 OSDictionary * dict = OSDictionary::withCapacity( 1 );
400 if( dict == 0 ) {
401 iter->release();
402 return 0;
403 }
404 const OSSymbol * str = OSSymbol::withCString( "Apple_HFS" );
405 if( str == 0 ) {
406 dict->release();
407 iter->release();
408 return 0;
409 }
410 dict->setObject( "Content", (OSObject *)str );
411 str->release();
412 if ( child->compareProperty( dict, "Content" ) ) {
413 dict->release();
414 break;
415 }
416 dict->release();
417 IOService * subchild = IOFindMatchingChild( child );
418 if ( subchild ) {
419 child = subchild;
420 break;
421 }
422 }
423 iter->release();
424 }
425 return child;
426 }
427
428 static int didRam = 0;
429
430 kern_return_t IOFindBSDRoot( char * rootName,
431 dev_t * root, u_int32_t * oflags )
432 {
433 mach_timespec_t t;
434 IOService * service;
435 IORegistryEntry * regEntry;
436 OSDictionary * matching = 0;
437 OSString * iostr;
438 OSNumber * off;
439 OSData * data = 0;
440 UInt32 *ramdParms = 0;
441
442 UInt32 flags = 0;
443 int minor, major;
444 bool findHFSChild = false;
445 char * mediaProperty = 0;
446 char * rdBootVar;
447 enum { kMaxPathBuf = 512, kMaxBootVar = 128 };
448 char * str;
449 const char * look = 0;
450 int len;
451 bool forceNet = false;
452 bool debugInfoPrintedOnce = false;
453 const char * uuidStr = NULL;
454
455 static int mountAttempts = 0;
456
457 int xchar, dchar;
458
459
460 if( mountAttempts++)
461 IOSleep( 5 * 1000 );
462
463 str = (char *) IOMalloc( kMaxPathBuf + kMaxBootVar );
464 if( !str)
465 return( kIOReturnNoMemory );
466 rdBootVar = str + kMaxPathBuf;
467
468 if (!PE_parse_boot_arg("rd", rdBootVar )
469 && !PE_parse_boot_arg("rootdev", rdBootVar ))
470 rdBootVar[0] = 0;
471
472 do {
473 if( (regEntry = IORegistryEntry::fromPath( "/chosen", gIODTPlane ))) {
474 data = OSDynamicCast(OSData, regEntry->getProperty( "root-matching" ));
475 if (data) {
476 matching = OSDynamicCast(OSDictionary, OSUnserializeXML((char *)data->getBytesNoCopy()));
477 if (matching) {
478 continue;
479 }
480 }
481
482 data = (OSData *) regEntry->getProperty( "boot-uuid" );
483 if( data) {
484 uuidStr = (const char*)data->getBytesNoCopy();
485 OSString *uuidString = OSString::withCString( uuidStr );
486
487 // match the boot-args boot-uuid processing below
488 if( uuidString) {
489 IOLog("rooting via boot-uuid from /chosen: %s\n", uuidStr);
490 IOService::publishResource( "boot-uuid", uuidString );
491 uuidString->release();
492 matching = IOUUIDMatching();
493 mediaProperty = "boot-uuid-media";
494 regEntry->release();
495 continue;
496 } else {
497 uuidStr = NULL;
498 }
499 }
500
501 // else try for an OF Path
502 data = (OSData *) regEntry->getProperty( "rootpath" );
503 regEntry->release();
504 if( data) continue;
505 }
506 if( (regEntry = IORegistryEntry::fromPath( "/options", gIODTPlane ))) {
507 data = (OSData *) regEntry->getProperty( "boot-file" );
508 regEntry->release();
509 if( data) continue;
510 }
511 } while( false );
512
513 if( data && !uuidStr)
514 look = (const char *) data->getBytesNoCopy();
515
516 if( rdBootVar[0] == '*') {
517 look = rdBootVar + 1;
518 forceNet = false;
519 } else {
520 if( (regEntry = IORegistryEntry::fromPath( "/", gIODTPlane ))) {
521 forceNet = (0 != regEntry->getProperty( "net-boot" ));
522 regEntry->release();
523 }
524 }
525
526
527
528 //
529 // See if we have a RAMDisk property in /chosen/memory-map. If so, make it into a device.
530 // It will become /dev/mdx, where x is 0-f.
531 //
532
533 if(!didRam) { /* Have we already build this ram disk? */
534 didRam = 1; /* Remember we did this */
535 if((regEntry = IORegistryEntry::fromPath( "/chosen/memory-map", gIODTPlane ))) { /* Find the map node */
536 data = (OSData *)regEntry->getProperty("RAMDisk"); /* Find the ram disk, if there */
537 if(data) { /* We found one */
538
539 ramdParms = (UInt32 *)data->getBytesNoCopy(); /* Point to the ram disk base and size */
540 (void)mdevadd(-1, ramdParms[0] >> 12, ramdParms[1] >> 12, 0); /* Initialize it and pass back the device number */
541 }
542 regEntry->release(); /* Toss the entry */
543 }
544 }
545
546 //
547 // Now check if we are trying to root on a memory device
548 //
549
550 if((rdBootVar[0] == 'm') && (rdBootVar[1] == 'd') && (rdBootVar[3] == 0)) {
551 dchar = xchar = rdBootVar[2]; /* Get the actual device */
552 if((xchar >= '0') && (xchar <= '9')) xchar = xchar - '0'; /* If digit, convert */
553 else {
554 xchar = xchar & ~' '; /* Fold to upper case */
555 if((xchar >= 'A') && (xchar <= 'F')) { /* Is this a valid digit? */
556 xchar = (xchar & 0xF) + 9; /* Convert the hex digit */
557 dchar = dchar | ' '; /* Fold to lower case */
558 }
559 else xchar = -1; /* Show bogus */
560 }
561 if(xchar >= 0) { /* Do we have a valid memory device name? */
562 *root = mdevlookup(xchar); /* Find the device number */
563 if(*root >= 0) { /* Did we find one? */
564
565 rootName[0] = 'm'; /* Build root name */
566 rootName[1] = 'd'; /* Build root name */
567 rootName[2] = dchar; /* Build root name */
568 rootName[3] = 0; /* Build root name */
569 IOLog("BSD root: %s, major %d, minor %d\n", rootName, major(*root), minor(*root));
570 *oflags = 0; /* Show that this is not network */
571 goto iofrootx; /* Join common exit... */
572 }
573 panic("IOFindBSDRoot: specified root memory device, %s, has not been configured\n", rdBootVar); /* Not there */
574 }
575 }
576
577 if( look) {
578 // from OpenFirmware path
579 IOLog("From path: \"%s\", ", look);
580
581 if (!matching) {
582 if( forceNet || (0 == strncmp( look, "enet", strlen( "enet" ))) ) {
583 matching = IONetworkMatching( look, str, kMaxPathBuf );
584 } else {
585 matching = IODiskMatching( look, str, kMaxPathBuf );
586 }
587 }
588 }
589
590 if( (!matching) && rdBootVar[0] ) {
591 // by BSD name
592 look = rdBootVar;
593 if( look[0] == '*')
594 look++;
595
596 if ( strncmp( look, "en", strlen( "en" )) == 0 ) {
597 matching = IONetworkNamePrefixMatching( "en" );
598 } else if ( strncmp( look, "cdrom", strlen( "cdrom" )) == 0 ) {
599 matching = IOCDMatching();
600 findHFSChild = true;
601 } else if ( strncmp( look, "uuid", strlen( "uuid" )) == 0 ) {
602 char *uuid;
603 OSString *uuidString;
604
605 uuid = (char *)IOMalloc( kMaxBootVar );
606
607 if ( uuid ) {
608 if (!PE_parse_boot_arg( "boot-uuid", uuid )) {
609 panic( "rd=uuid but no boot-uuid=<value> specified" );
610 }
611 uuidString = OSString::withCString( uuid );
612 if ( uuidString ) {
613 IOService::publishResource( "boot-uuid", uuidString );
614 uuidString->release();
615 IOLog( "\nWaiting for boot volume with UUID %s\n", uuid );
616 matching = IOUUIDMatching();
617 mediaProperty = "boot-uuid-media";
618 }
619 IOFree( uuid, kMaxBootVar );
620 }
621 } else {
622 matching = IOBSDNameMatching( look );
623 }
624 }
625
626 if( !matching) {
627 OSString * astring;
628 // any HFS
629 matching = IOService::serviceMatching( "IOMedia" );
630 astring = OSString::withCStringNoCopy("Apple_HFS");
631 if ( astring ) {
632 matching->setObject("Content", astring);
633 astring->release();
634 }
635 }
636
637 if( true && matching) {
638 OSSerialize * s = OSSerialize::withCapacity( 5 );
639
640 if( matching->serialize( s )) {
641 IOLog( "Waiting on %s\n", s->text() );
642 s->release();
643 }
644 }
645
646 do {
647 t.tv_sec = ROOTDEVICETIMEOUT;
648 t.tv_nsec = 0;
649 matching->retain();
650 service = IOService::waitForService( matching, &t );
651 if( (!service) || (mountAttempts == 10)) {
652 PE_display_icon( 0, "noroot");
653 IOLog( "Still waiting for root device\n" );
654
655 if( !debugInfoPrintedOnce) {
656 debugInfoPrintedOnce = true;
657 if( gIOKitDebug & kIOLogDTree) {
658 IOLog("\nDT plane:\n");
659 IOPrintPlane( gIODTPlane );
660 }
661 if( gIOKitDebug & kIOLogServiceTree) {
662 IOLog("\nService plane:\n");
663 IOPrintPlane( gIOServicePlane );
664 }
665 if( gIOKitDebug & kIOLogMemory)
666 IOPrintMemory();
667 }
668 }
669 } while( !service);
670 matching->release();
671
672 if ( service && findHFSChild ) {
673 bool waiting = true;
674 // wait for children services to finish registering
675 while ( waiting ) {
676 t.tv_sec = ROOTDEVICETIMEOUT;
677 t.tv_nsec = 0;
678 if ( service->waitQuiet( &t ) == kIOReturnSuccess ) {
679 waiting = false;
680 } else {
681 IOLog( "Waiting for child registration\n" );
682 }
683 }
684 // look for a subservice with an Apple_HFS child
685 IOService * subservice = IOFindMatchingChild( service );
686 if ( subservice ) service = subservice;
687 } else if ( service && mediaProperty ) {
688 service = (IOService *)service->getProperty(mediaProperty);
689 }
690
691 major = 0;
692 minor = 0;
693
694 // If the IOService we matched to is a subclass of IONetworkInterface,
695 // then make sure it has been registered with BSD and has a BSD name
696 // assigned.
697
698 if ( service
699 && service->metaCast( "IONetworkInterface" )
700 && !IORegisterNetworkInterface( service ) )
701 {
702 service = 0;
703 }
704
705 if( service) {
706
707 len = kMaxPathBuf;
708 service->getPath( str, &len, gIOServicePlane );
709 IOLog( "Got boot device = %s\n", str );
710
711 iostr = (OSString *) service->getProperty( kIOBSDNameKey );
712 if( iostr)
713 strcpy( rootName, iostr->getCStringNoCopy() );
714 off = (OSNumber *) service->getProperty( kIOBSDMajorKey );
715 if( off)
716 major = off->unsigned32BitValue();
717 off = (OSNumber *) service->getProperty( kIOBSDMinorKey );
718 if( off)
719 minor = off->unsigned32BitValue();
720
721 if( service->metaCast( "IONetworkInterface" ))
722 flags |= 1;
723
724 } else {
725
726 IOLog( "Wait for root failed\n" );
727 strcpy( rootName, "en0");
728 flags |= 1;
729 }
730
731 IOLog( "BSD root: %s", rootName );
732 if( major)
733 IOLog(", major %d, minor %d\n", major, minor );
734 else
735 IOLog("\n");
736
737 *root = makedev( major, minor );
738 *oflags = flags;
739
740 IOFree( str, kMaxPathBuf + kMaxBootVar );
741
742 iofrootx:
743 if( (gIOKitDebug & (kIOLogDTree | kIOLogServiceTree | kIOLogMemory)) && !debugInfoPrintedOnce) {
744
745 IOService::getPlatform()->waitQuiet();
746 if( gIOKitDebug & kIOLogDTree) {
747 IOLog("\nDT plane:\n");
748 IOPrintPlane( gIODTPlane );
749 }
750 if( gIOKitDebug & kIOLogServiceTree) {
751 IOLog("\nService plane:\n");
752 IOPrintPlane( gIOServicePlane );
753 }
754 if( gIOKitDebug & kIOLogMemory)
755 IOPrintMemory();
756 }
757
758 return( kIOReturnSuccess );
759 }
760
761 void *
762 IOBSDRegistryEntryForDeviceTree(char * path)
763 {
764 return (IORegistryEntry::fromPath(path, gIODTPlane));
765 }
766
767 void
768 IOBSDRegistryEntryRelease(void * entry)
769 {
770 IORegistryEntry * regEntry = (IORegistryEntry *)entry;
771
772 if (regEntry)
773 regEntry->release();
774 return;
775 }
776
777 const void *
778 IOBSDRegistryEntryGetData(void * entry, char * property_name,
779 int * packet_length)
780 {
781 OSData * data;
782 IORegistryEntry * regEntry = (IORegistryEntry *)entry;
783
784 data = (OSData *) regEntry->getProperty(property_name);
785 if (data) {
786 *packet_length = data->getLength();
787 return (data->getBytesNoCopy());
788 }
789 return (NULL);
790 }
791
792 } /* extern "C" */