Mailing Lists: Apple Mailing Lists

Image of Mac OS face in stamp
 
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: MoreSCF fails horribly if the Automatic location is removed



At 14:38 +0200 22/10/04, Christian Weykopf wrote:
The Automatic location has the numerical setID 0. All other location have a GUID as setID.
When you delete the Automatic location you have no numerical setIDs an the MySCPreferencesPathCreateUniqueChild () and LargestNumericKey () routines doesn't work well.

Dave opened a DTS incident and I investigated this in detail. The problem is much as you describe it.


Here's how it transpired. In 10.1, set/service IDs were small integers, and life was good. In 10.2, we decided that set/service IDs should be GUIDs (generally a good idea), and we modified SCPreferencesPathCreateUniqueChild accordingly. However, the Network preferences panel was never updated, so it still created small integer set/service IDs. Worse yet, the Apple menu had a bug where it didn't handle long set ID strings, and thus any set you created with a GUID set ID couldn't be switched to. *sigh*

I worked around this problem in MoreSCF by /not/ calling SCPreferencesPathCreateUniqueChild and instead rolling my own routine that created small numeric set/service IDs, just like 10.1. However, I couldn't conditionalise that code because I had no idea when the Apple menu bug was going to be fixed.

In 10.3 the Apple menu bug was fixed, and the Network preferences panel started using GUIDs for set/service IDs (except for the "Automatic" location). I should've updated MoreSCF to start calling SCPreferencesPathCreateUniqueChild again, but I never got around to it. This proved to be the cause of your problem.

The basic bug is that the workaround code I introduced to generate unique small numeric set/service IDs (LargestNumericKey and its call site) doesn't work properly if there are /no/ small numeric set/service IDs. The result always comes back as 0x80000000. This eventually triggers an assert because we try to create two services with the same service ID. A dictionary must always have unique keys, so the number of elements in the Services dictionary doesn't match the number of elements in the ServiceOrder array, which trigger the assert in MoreSCSetServicesDict.

This situation can't arise on Mac OS X 10.2.x because all set/service IDs are small integers, and you can't delete the last set, thus there is always at least one small integer set/service ID and the code always generates meaningful results.

The situation doesn't arise on 10.3 by default because the "Automatic" location has services with small integer IDs. However, once you delete that location, all that's left is locations with GUID service IDs, and you get the bogus results.

I fixed the problem in two ways.

1. I updated the code to only run the workaround on 10.2.x; it's not needed on 10.3 and later (or on 10.1 for that matter).

2. I fixed the call site of LargestNumericKey to handle not getting any numeric keys.

I tested both of these fixes on 10.3.x, and the combined fix on both 10.2.x and 10.3.x. It all seems to work.

I've attached the diffs to the end of this email.

Share and Enjoy.
--
Quinn "The Eskimo!"                    <http://www.apple.com/developer/>
Apple Developer Technical Support * Networking, Communications, Hardware

Index: MoreIsBetter/MIB-Libraries/MoreSCF/MoreSCF.c
===================================================================
RCS file: /Volumes/xraid/cvs/samplecode/M/MoreIsBetter/MIB-Libraries/MoreSCF/MoreSCF.c,v
retrieving revision 1.10
retrieving revision 1.11
diff -r1.10 -r1.11
47a48,50
Revision 1.11 2004/10/28 12:39:00 eskimo1
Completely reworked the SCPreferencesPathCreateUniqueChild workaround code to a) not use the workaround on 10.3, because the original bug has been fixed, and b) to correctly handle the case where there are no numeric keys in the child dictionary.


88a92
#include <CoreServices/CoreServices.h>
658c662,682
< #define WORKAROUND_RADAR_ID_3024328 1
---
static void LargestNumericKey(const void *key, const void *value, void *context)
// A CFDictionaryApplyFunction callback used by
// SCPreferencesPathCreateUniqueChildWorkaround to find the
// largest simple numeric key within a dictionary.
{
SInt32 * currentMax;
char keyStr[16];
char * endPtr;
SInt32 keyValue;
#pragma unused(value)
assert( CFGetTypeID(key) == CFStringGetTypeID() );
assert( context != NULL );
currentMax = (SInt32 *) context;
// Get the bytes out of the string. If keyStr is too small
// or key contains non-ASCII characters, we get false and
// ignore this key (it's obviously not a simple number).
if ( CFStringGetCString( (CFStringRef) key, keyStr, sizeof(keyStr), kCFStringEncodingASCII) ) {
660c684,710
< #if WORKAROUND_RADAR_ID_3024328
---
// Make sure that the first character of the keyStr
// is a digit, to avoid strtol's acceptance of
// leading whitespace and signs.
if ( keyStr[0] >= '0' && keyStr[0] <= '9' ) {
// Clean errno so that we can detect ERANGE
// errors coming back from strtol.
errno = 0;
keyValue = strtol(keyStr, &endPtr, 10);
// If we got no error and consumed all characters
// then we treat keyValue as valid.
if ( (errno == 0) && (*endPtr == 0) ) {
// If keyValue is greater than our current
// maximum, update the current maximum to keyValue.
if (keyValue > *currentMax) {
*currentMax = keyValue;
}
}
}
}
}
662,681c712,764
< // This implementation of SCPreferencesPathCreateUniqueChild was
< // largely stolen from the implementation on Mac OS X 10.1. By
< // using it we work around a problem [3024328] with Apple menu
< // location switching that's triggered by the new implementation
< // of SCPreferencesPathCreateUniqueChild on Mac OS X 10.2.
< //
< // The difference between this version and the original 10.1 version
< // is that this code finds the ID that's one larger than the largest ID.
< // The 10.1 code finds the first unused ID, which can results
< // in possible ID reuse.
< //
< // The 10.2 version does the same thing using GUIDs, but that
< // generates very long ID strings, which triggers the bug mentioned
< // above.
< //
< // When that problem is fixed I'll include conditional code
< // to call the real SCPreferencesPathCreateUniqueChild or this
< // compatibility code depending on the running system version.
< //
< // -- Quinn, 14 Aug 2002
---
 static CFStringRef SCPreferencesPathCreateUniqueChildWorkaround(
     SCPreferencesRef	session,
     CFStringRef         prefix
 )
     // SCPreferencesPathCreateUniqueChildWorkaround is needed to
     // work around a bug in the Apple menu on 10.2 (see the comments
     // for SCPreferencesPathCreateUniqueChildCompat for a full
     // description of this).  The implementation was stolen
     // almost verbatim from the Mac OS X 10.1 source, a fact which
     // explains the difference in coding style between this code
     // and the rest of MoreSCF.
     //
     // The difference between this version and the original 10.1 version
     // is that this code finds the ID that's one larger than the largest ID.
     // The 10.1 code finds the first unused ID, which can results
     // in possible ID reuse.
 {
     int						status;
     CFDictionaryRef			value;
     CFStringRef				newPath		= NULL;
     Boolean					newValue	= FALSE;
     SInt32					maxValue;
     CFMutableDictionaryRef	newDict		= NULL;

// SCPreferencesPathGetValue follows links, whereas the
// real SCF implementation calls an internal routine,
// getPath, that doesn't follow links. However, this isn't
// a problem because in the MoreSCF usage of
// SCPreferencesPathCreateUniqueChildWorkaround we never give a
// prefix that might be a link.
//
// Also note that the internal implementation used a
// mutable dictionary for value; I changed this to a
// immutable dictionary because a) that's what
// SCPreferencesPathGetValue returns, and b) the code
// doesn't change the contents of the dictionary anyway.
// -- Quinn, 14 Aug 2002
value = SCPreferencesPathGetValue(session, prefix);
status = SCError();
switch (status) {
case kSCStatusOK :
break;
case kSCStatusNoKey :
value = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
newValue = TRUE;
break;
default :
return NULL;
}
683,699c766,770
< static void LargestNumericKey(const void *key, const void *value, void *context)
< {
< SInt32 * currentMax;
< char keyStr[16];
< char * endPtr;
< SInt32 keyValue;
<
< #pragma unused(value)
< assert( CFGetTypeID(key) == CFStringGetTypeID() );
<
< currentMax = (SInt32 *) context;
<
< // Get the bytes out of the string. If keyStr is too small
< // or key contains non-ASCII characters, we get false and
< // ignore this key (it's obviously not a simple number).
<
< if ( CFStringGetCString( (CFStringRef) key, keyStr, sizeof(keyStr), kCFStringEncodingASCII) ) {
---
     if (CFGetTypeID(value) != CFDictionaryGetTypeID()) {
         /* if specified path is not a dictionary */
         status = kSCStatusNoKey;
         goto error;
     }
701,781c772,776
< // Make sure that the first character of the keyStr
< // is a digit, to avoid strtol's acceptance of
< // leading whitespace and signs.
<
< if ( keyStr[0] >= '0' && keyStr[0] <= '9' ) {
<
< // Clean errno so that we can detect ERANGE
< // errors coming back from strtol.
<
< errno = 0;
< keyValue = strtol(keyStr, &endPtr, 10);
<
< // If we got no error and consumed all characters
< // then we treat keyValue as valid.
<
< if ( (errno == 0) && (*endPtr == 0) ) {
<
< // If keyValue is greater than our current
< // maximum, update the current maximum to keyValue.
<
< if (keyValue > *currentMax) {
< *currentMax = keyValue;
< }
< }
< }
< }
< }
<
< static
< CFStringRef
< MySCPreferencesPathCreateUniqueChild(SCPreferencesRef session,
< CFStringRef prefix)
< {
< int status;
< CFDictionaryRef value;
< CFStringRef newPath = NULL;
< Boolean newValue = FALSE;
< SInt32 maxValue;
< CFMutableDictionaryRef newDict = NULL;
<
< // SCPreferencesPathGetValue follows links, whereas the
< // realy SCF implementation calls an internal routine,
< // getPath, that doesn't follow links. However, this isn't
< // a problem because in the MoreSCF usage of
< // MySCPreferencesPathCreateUniqueChild we never in a
< // prefix that might be a link.
< //
< // Also note that the internal implementation used a
< // mutable dictionary for value; I changed this to a
< // immutable dictionary because a) that's what
< // SCPreferencesPathGetValue returns, and b) the code
< // doesn't change the contents of the dictionary anyway.
< // -- Quinn, 14 Aug 2002
<
< value = SCPreferencesPathGetValue(session, prefix);
< status = SCError();
< switch (status) {
< case kSCStatusOK :
< break;
< case kSCStatusNoKey :
< value = CFDictionaryCreateMutable(NULL,
< 0,
< &kCFTypeDictionaryKeyCallBacks,
< &kCFTypeDictionaryValueCallBacks);
< newValue = TRUE;
< break;
< default :
< return NULL;
< }
<
< if (CFGetTypeID(value) != CFDictionaryGetTypeID()) {
< /* if specified path is not a dictionary */
< status = kSCStatusNoKey;
< goto error;
< }
<
< if (CFDictionaryContainsKey(value, kSCResvLink)) {
< /* the path is a link... */
< status = kSCStatusFailed;
< goto error;
< }
---
     if (CFDictionaryContainsKey(value, kSCResvLink)) {
         /* the path is a link... */
         status = kSCStatusFailed;
         goto error;
     }
783,791c778,819
<		// Find the large numeric key in the dictionary.
<
<		maxValue = 0x80000000;
<		CFDictionaryApplyFunction(value, LargestNumericKey, &maxValue);
<
<		if (maxValue == 0x7FFFFFFF) {
<			status = kSCStatusFailed;
<			goto error;
<		}
---
// Find the large numeric key in the dictionary.
maxValue = 0x80000000;
CFDictionaryApplyFunction(value, LargestNumericKey, &maxValue);
// If maxValue is unchanged, there are /no/ simple numeric
// keys. Ergo, 0 is a perfectly valid value for the new key,
// so set maxValue to -1 (we'll add 1 to it as part of the
// call to CFStringCreateWithFormat below). This fixes a
// bug reported by a developer in <sonr://request/5810451>.


if (maxValue == 0x80000000) {
maxValue = -1;
}
if (maxValue == 0x7FFFFFFF) {
status = kSCStatusFailed;
goto error;
}


// Create a new path one larger than that.
newPath = CFStringCreateWithFormat(NULL,
NULL,
CFSTR("%@/%i"),
prefix,
maxValue + 1);


     /* save the new dictionary */
     newDict = CFDictionaryCreateMutable(NULL,
                         0,
                         &kCFTypeDictionaryKeyCallBacks,
                         &kCFTypeDictionaryValueCallBacks);
     if (!SCPreferencesPathSetValue(session, newPath, newDict)) {
         goto error;
     }
     CFRelease(newDict);

     if (newValue)	CFRelease(value);
     return newPath;

error :
793c821,834
<		// Create a new path one large than that.
---
     if (newDict)	CFRelease(newDict);
     if (newValue)	CFRelease(value);
     if (newPath)	CFRelease(newPath);
     return NULL;
 }

static UInt32 MoreSCGetSystemVersion(void)
// An inline copy of MoreGetSystemVersion because I don't want to
// couple MoreSCF to MoreOSUtils.
{
static UInt32 sMoreSCSystemVersion = 0;
if (sMoreSCSystemVersion == 0) {
OSStatus junk;
795,824c836,891
< newPath = CFStringCreateWithFormat(NULL,
< NULL,
< CFSTR("%@/%i"),
< prefix,
< maxValue + 1);
<
< /* save the new dictionary */
< newDict = CFDictionaryCreateMutable(NULL,
< 0,
< &kCFTypeDictionaryKeyCallBacks,
< &kCFTypeDictionaryValueCallBacks);
< if (!SCPreferencesPathSetValue(session, newPath, newDict)) {
< goto error;
< }
< CFRelease(newDict);
<
< if (newValue) CFRelease(value);
< return newPath;
<
< error :
<
< if (newDict) CFRelease(newDict);
< if (newValue) CFRelease(value);
< if (newPath) CFRelease(newPath);
< return NULL;
< }
<
< #else
< #define MySCPreferencesPathCreateUniqueChild SCPreferencesPathCreateUniqueChild
< #endif
---
junk = Gestalt(gestaltSystemVersion, (SInt32 *) &sMoreSCSystemVersion);
assert(junk == noErr);
}


	return sMoreSCSystemVersion;
 }

static CFStringRef SCPreferencesPathCreateUniqueChildCompat(
SCPreferencesRef session,
CFStringRef prefix
)
// A replacement for SCPreferencesPathCreateUniqueChild that
// does the right thing on all platforms. This deserves some
// explanation (-:
// SCPreferencesPathCreateUnique works by creating a child
// node with a unique name. There are two basic approaches
// to creating a unique name, small unique integers and GUIDs. // We're in the process of transitioning from the former to the
// latter. Here's how the transition has progressed.
//
// SCPreferencesPathCreateUniqueChild System Preferences
// OS Version Behaviour Behaviour
// ---------- ---------------------------------- ------------------
// 10.1 small integers small integers
// 10.2 GUIDs small integers
// 10.3 GUIDs GUIDs
//
// The problem with this is that Mac OS X 10.2 had a bug,
// <rdar://problem/3024328>, whereby, if you create a set with a
// GUID name (rather than a small integer), the Apple menu would not
// be able to switch to that set. That bug was fixed in Mac OS X 10.3,
// but its existence means that you can't call SCPreferencesPathCreateUniqueChild
// on all platforms. It's fine on 10.1 (where it produces small
// integers) and 10.3 (where it produces GUIDs, but the GUIDs don't upset
// the Apple menu), but it's problematic on 10.2.
//
// This routine is the solution to the problem described above. It calls
// SCPreferencesPathCreateUniqueChild on all platforms other than 10.2. // On 10.2 it calls SCPreferencesPathCreateUniqueChildCompatWorkaround,
// which contains our own implementation of SCPreferencesPathCreateUniqueChild
// that creates small integer values that are compatible with the
// 10.2 Apple menu.
//
// *phew*
{
CFStringRef result;
static const Boolean kForceUseOfWorkAround = true;
if ( ((MoreSCGetSystemVersion() &~ 0x0F) == 0x01020) || kForceUseOfWorkAround ) {
result = SCPreferencesPathCreateUniqueChildWorkaround(session, prefix);
} else {
result = SCPreferencesPathCreateUniqueChild(session, prefix);
}
return result;
}
856c923
< *newSePath = MySCPreferencesPathCreateUniqueChild(gPrefsRef, sePrefix);
---
*newSePath = SCPreferencesPathCreateUniqueChildCompat(gPrefsRef, sePrefix);
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Macnetworkprog mailing list      (email@hidden)
Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/macnetworkprog/email@hidden

This email sent to email@hidden
References: 
 >MoreSCF fails horribly if the Automatic location is removed (From: Dave Camp <email@hidden>)
 >Re: MoreSCF fails horribly if the Automatic location is removed (From: Christian Weykopf <email@hidden>)



Visit the Apple Store online or at retail locations.
1-800-MY-APPLE

Contact Apple | Terms of Use | Privacy Policy

Copyright © 2007 Apple Inc. All rights reserved.