Re: MoreSCF fails horribly if the Automatic location is removed
Re: MoreSCF fails horribly if the Automatic location is removed
- Subject: Re: MoreSCF fails horribly if the Automatic location is removed
- From: Quinn <email@hidden>
- Date: Fri, 29 Oct 2004 16:05:34 +0100
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:
This email sent to email@hidden