#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPrivate.h>
#include <ApplicationServices/ApplicationServices.h>
#include "UserEventAgentInterface.h"
#define MY_BUNDLE_ID CFSTR("com.apple.SystemConfiguration.SCMonitor")
#define MY_ICON_PATH "/System/Library/PreferencePanes/Network.prefPane/Contents/Resources/Network.icns"
#define NETWORK_PREF_APP "/System/Library/PreferencePanes/Network.prefPane"
#define NETWORK_PREF_CMD "New Interface"
typedef struct {
UserEventAgentInterfaceStruct *_UserEventAgentInterface;
CFUUIDRef _factoryID;
UInt32 _refCount;
Boolean no_user_intervention;
CFRunLoopSourceRef monitorRls;
CFMutableSetRef knownInterfaces;
CFMutableArrayRef userInterfaces;
CFUserNotificationRef userNotification;
CFRunLoopSourceRef userRls;
} MyType;
static CFMutableDictionaryRef notify_to_instance = NULL;
#pragma mark -
#pragma mark Watch for new [network] interfaces
static void
open_NetworkPrefPane(void)
{
AEDesc aeDesc = { typeNull, NULL };
CFArrayRef prefArray;
CFURLRef prefURL;
LSLaunchURLSpec prefSpec;
OSStatus status;
prefURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
CFSTR(NETWORK_PREF_APP),
kCFURLPOSIXPathStyle,
FALSE);
prefArray = CFArrayCreate(NULL, (const void **)&prefURL, 1, &kCFTypeArrayCallBacks);
CFRelease(prefURL);
status = AECreateDesc('ptru',
(const void *)NETWORK_PREF_CMD,
strlen(NETWORK_PREF_CMD),
&aeDesc);
if (status != noErr) {
SCLog(TRUE, LOG_ERR, CFSTR("SCMonitor: AECreateDesc() failed: %d"), status);
}
prefSpec.appURL = NULL;
prefSpec.itemURLs = prefArray;
prefSpec.passThruParams = &aeDesc;
prefSpec.launchFlags = kLSLaunchAsync | kLSLaunchDontAddToRecents;
prefSpec.asyncRefCon = NULL;
status = LSOpenFromURLSpec(&prefSpec, NULL);
if (status != noErr) {
SCLog(TRUE, LOG_ERR, CFSTR("SCMonitor: LSOpenFromURLSpec() failed: %d"), status);
}
CFRelease(prefArray);
if (aeDesc.descriptorType != typeNull) AEDisposeDesc(&aeDesc);
return;
}
static void
notify_remove(MyType *myInstance, Boolean cancel)
{
if (myInstance->userInterfaces != NULL) {
CFRelease(myInstance->userInterfaces);
myInstance->userInterfaces = NULL;
}
if (myInstance->userRls != NULL) {
CFRunLoopSourceInvalidate(myInstance->userRls);
CFRelease(myInstance->userRls);
myInstance->userRls = NULL;
}
if (myInstance->userNotification != NULL) {
if (cancel) {
SInt32 status;
status = CFUserNotificationCancel(myInstance->userNotification);
if (status != 0) {
SCLog(TRUE, LOG_ERR,
CFSTR("SCMonitor: CFUserNotificationCancel() failed, status=%d"),
status);
}
}
CFRelease(myInstance->userNotification);
myInstance->userNotification = NULL;
}
return;
}
static void
notify_reply(CFUserNotificationRef userNotification, CFOptionFlags response_flags)
{
MyType *myInstance = NULL;
if (notify_to_instance != NULL) {
myInstance = (MyType *)CFDictionaryGetValue(notify_to_instance, userNotification);
if (myInstance != NULL) {
CFDictionaryRemoveValue(notify_to_instance, userNotification);
if (CFDictionaryGetCount(notify_to_instance) == 0) {
CFRelease(notify_to_instance);
notify_to_instance = NULL;
}
}
}
if (myInstance == NULL) {
SCLog(TRUE, LOG_ERR, CFSTR("SCMonitor: can't find user notification"));
return;
}
switch (response_flags & 0x3) {
case kCFUserNotificationDefaultResponse:
open_NetworkPrefPane();
break;
default:
break;
}
notify_remove(myInstance, FALSE);
return;
}
static void
notify_add(MyType *myInstance)
{
CFBundleRef bundle;
CFMutableDictionaryRef dict = NULL;
SInt32 error = 0;
CFIndex i;
CFMutableArrayRef message;
CFIndex n = CFArrayGetCount(myInstance->userInterfaces);
CFURLRef url = NULL;
if (myInstance->userNotification != NULL) {
CFMutableArrayRef save = NULL;
if (n > 0) {
CFRetain(myInstance->userInterfaces);
save = myInstance->userInterfaces;
}
notify_remove(myInstance, TRUE);
myInstance->userInterfaces = save;
if (n == 0) {
return;
}
}
dict = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
bundle = CFBundleGetBundleWithIdentifier(MY_BUNDLE_ID);
if (bundle != NULL) {
url = CFBundleCopyBundleURL(bundle);
}
if (url != NULL) {
CFDictionarySetValue(dict, kCFUserNotificationLocalizationURLKey, url);
CFRelease(url);
} else {
SCLog(TRUE, LOG_NOTICE, CFSTR("SCMonitor: can't find bundle"));
goto done;
}
url = CFURLCreateFromFileSystemRepresentation(NULL,
(const UInt8 *)MY_ICON_PATH,
strlen(MY_ICON_PATH),
FALSE);
if (url != NULL) {
CFDictionarySetValue(dict, kCFUserNotificationIconURLKey, url);
CFRelease(url);
}
CFDictionarySetValue(dict,
kCFUserNotificationAlertHeaderKey,
(n == 1) ? CFSTR("HEADER_1") : CFSTR("HEADER_N"));
message = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFArrayAppendValue(message,
(n == 1) ? CFSTR("MESSAGE_S1") : CFSTR("MESSAGE_SN"));
for (i = 0; i < n; i++) {
SCNetworkInterfaceRef interface;
CFStringRef name;
interface = CFArrayGetValueAtIndex(myInstance->userInterfaces, i);
name = SCNetworkInterfaceGetLocalizedDisplayName(interface);
if (n == 1) {
CFArrayAppendValue(message, name);
} else {
CFStringRef str;
str = CFStringCreateWithFormat(NULL, NULL, CFSTR("\r\t%@"), name);
CFArrayAppendValue(message, str);
CFRelease(str);
}
}
CFArrayAppendValue(message,
(n == 1) ? CFSTR("MESSAGE_E1") : CFSTR("MESSAGE_EN"));
CFDictionarySetValue(dict, kCFUserNotificationAlertMessageKey, message);
CFRelease(message);
CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, CFSTR("OPEN_NP"));
CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, CFSTR("CANCEL"));
myInstance->userNotification = CFUserNotificationCreate(NULL,
0,
kCFUserNotificationNoteAlertLevel,
&error,
dict);
if (myInstance->userNotification == NULL) {
SCLog(TRUE, LOG_ERR, CFSTR("SCMonitor: CFUserNotificationCreate() failed, %d"), error);
goto done;
}
myInstance->userRls = CFUserNotificationCreateRunLoopSource(NULL,
myInstance->userNotification,
notify_reply,
0);
if (myInstance->userRls == NULL) {
SCLog(TRUE, LOG_ERR, CFSTR("SCMonitor: CFUserNotificationCreateRunLoopSource() failed"));
CFRelease(myInstance->userNotification);
myInstance->userNotification = NULL;
goto done;
}
CFRunLoopAddSource(CFRunLoopGetCurrent(), myInstance->userRls, kCFRunLoopDefaultMode);
if (notify_to_instance == NULL) {
notify_to_instance = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
NULL); }
CFDictionarySetValue(notify_to_instance, myInstance->userNotification, myInstance);
done :
if (dict != NULL) CFRelease(dict);
return;
}
static void
notify_configure(MyType *myInstance)
{
AuthorizationRef authorization = NULL;
CFIndex i;
CFIndex n;
Boolean ok;
SCPreferencesRef prefs = NULL;
SCNetworkSetRef set = NULL;
if (geteuid() == 0) {
prefs = SCPreferencesCreate(NULL, CFSTR("SCMonitor"), NULL);
} else {
AuthorizationFlags flags = kAuthorizationFlagDefaults;
OSStatus status;
status = AuthorizationCreate(NULL,
kAuthorizationEmptyEnvironment,
flags,
&authorization);
if (status != errAuthorizationSuccess) {
SCLog(TRUE, LOG_ERR,
CFSTR("AuthorizationCreate() failed: status = %d\n"),
status);
return;
}
prefs = SCPreferencesCreateWithAuthorization(NULL, CFSTR("SCMonitor"), NULL, authorization);
}
set = SCNetworkSetCopyCurrent(prefs);
if (set == NULL) {
set = SCNetworkSetCreate(prefs);
if (set == NULL) {
goto done;
}
}
n = CFArrayGetCount(myInstance->userInterfaces);
for (i = 0; i < n; i++) {
SCNetworkInterfaceRef interface;
interface = CFArrayGetValueAtIndex(myInstance->userInterfaces, i);
ok = SCNetworkSetEstablishDefaultInterfaceConfiguration(set, interface);
if (ok) {
CFStringRef name;
name = SCNetworkInterfaceGetLocalizedDisplayName(interface);
SCLog(TRUE, LOG_NOTICE, CFSTR("add service for %@"), name);
}
}
ok = SCPreferencesCommitChanges(prefs);
if (!ok) {
SCLog(TRUE, LOG_ERR,
CFSTR("SCPreferencesCommitChanges() failed: %s\n"),
SCErrorString(SCError()));
goto done;
}
ok = SCPreferencesApplyChanges(prefs);
if (!ok) {
SCLog(TRUE, LOG_ERR,
CFSTR("SCPreferencesApplyChanges() failed: %s\n"),
SCErrorString(SCError()));
goto done;
}
done :
if (set != NULL) {
CFRelease(set);
set = NULL;
}
if (prefs != NULL) {
CFRelease(prefs);
prefs = NULL;
}
if (authorization != NULL) {
AuthorizationFree(authorization, kAuthorizationFlagDefaults);
authorization = NULL;
}
CFRelease(myInstance->userInterfaces);
myInstance->userInterfaces = NULL;
return;
}
static void
updateInterfaceList(SCDynamicStoreRef store, CFArrayRef changes, void * arg)
{
CFIndex i;
CFArrayRef interfaces;
MyType *myInstance = (MyType *)arg;
CFIndex n;
SCPreferencesRef prefs;
CFMutableSetRef previouslyKnown = NULL;
SCNetworkSetRef set = NULL;
prefs = SCPreferencesCreate(NULL, CFSTR("SCMonitor"), NULL);
if (prefs == NULL) {
return;
}
set = SCNetworkSetCopyCurrent(prefs);
if (set == NULL) {
set = SCNetworkSetCreate(prefs);
if (set == NULL) {
goto done;
}
}
previouslyKnown = CFSetCreateMutableCopy(NULL, 0, myInstance->knownInterfaces);
interfaces = SCNetworkInterfaceCopyAll();
if (interfaces != NULL) {
n = CFArrayGetCount(interfaces);
for (i = 0; i < n; i++) {
CFStringRef bsdName;
SCNetworkInterfaceRef interface;
Boolean ok;
interface = CFArrayGetValueAtIndex(interfaces, i);
bsdName = SCNetworkInterfaceGetBSDName(interface);
if (bsdName == NULL) {
continue;
}
CFSetRemoveValue(previouslyKnown, bsdName);
if (CFSetContainsValue(myInstance->knownInterfaces, bsdName)) {
continue;
}
CFSetAddValue(myInstance->knownInterfaces, bsdName);
ok = SCNetworkSetEstablishDefaultInterfaceConfiguration(set, interface);
if (ok) {
if (myInstance->userInterfaces == NULL) {
myInstance->userInterfaces = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
CFArrayAppendValue(myInstance->userInterfaces, interface);
}
}
CFRelease(interfaces);
}
n = CFSetGetCount(previouslyKnown);
if (n > 0) {
const void * names_q[32];
const void ** names = names_q;
if (n > (CFIndex)(sizeof(names_q) / sizeof(CFTypeRef)))
names = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
CFSetGetValues(previouslyKnown, names);
for (i = 0; i < n; i++) {
if (myInstance->userInterfaces != NULL) {
CFIndex j;
j = CFArrayGetCount(myInstance->userInterfaces);
while (--j >= 0) {
CFStringRef bsdName;
SCNetworkInterfaceRef interface;
interface = CFArrayGetValueAtIndex(myInstance->userInterfaces, j);
bsdName = SCNetworkInterfaceGetBSDName(interface);
if (CFEqual(bsdName, names[i])) {
CFArrayRemoveValueAtIndex(myInstance->userInterfaces, j);
}
}
}
CFSetRemoveValue(myInstance->knownInterfaces, names[i]);
}
if (names != names_q) CFAllocatorDeallocate(NULL, names);
}
done :
if (myInstance->userInterfaces != NULL) {
if (myInstance->no_user_intervention) {
notify_configure(myInstance);
} else {
notify_add(myInstance);
}
}
if (set != NULL) CFRelease(set);
CFRelease(prefs);
return;
}
static void
watcher_remove(MyType *myInstance)
{
if (myInstance->monitorRls != NULL) {
CFRunLoopSourceInvalidate(myInstance->monitorRls);
CFRelease(myInstance->monitorRls);
myInstance->monitorRls = NULL;
}
if (myInstance->knownInterfaces != NULL) {
CFRelease(myInstance->knownInterfaces);
myInstance->knownInterfaces = NULL;
}
return;
}
static void
watcher_add(MyType *myInstance)
{
CFBundleRef bundle;
SCDynamicStoreContext context = { 0, (void *)myInstance, NULL, NULL, NULL };
CFDictionaryRef dict;
CFStringRef key;
CFArrayRef keys;
SCDynamicStoreRef store;
bundle = CFBundleGetBundleWithIdentifier(MY_BUNDLE_ID);
if (bundle != NULL) {
CFDictionaryRef info;
CFBooleanRef user_intervention;
info = CFBundleGetInfoDictionary(bundle);
user_intervention = CFDictionaryGetValue(info, CFSTR("User Intervention"));
if (isA_CFBoolean(user_intervention)) {
myInstance->no_user_intervention = !CFBooleanGetValue(user_intervention);
}
}
store = SCDynamicStoreCreate(NULL, CFSTR("SCMonitor"), updateInterfaceList, &context);
if (store == NULL) {
SCLog(TRUE, LOG_ERR,
CFSTR("SCMonitor: SCDynamicStoreCreate() failed: %s"),
SCErrorString(SCError()));
return;
}
key = SCDynamicStoreKeyCreateNetworkInterface(NULL, kSCDynamicStoreDomainState);
keys = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks);
SCDynamicStoreSetNotificationKeys(store, NULL, keys);
CFRelease(keys);
myInstance->monitorRls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(),
myInstance->monitorRls,
kCFRunLoopDefaultMode);
myInstance->knownInterfaces = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
dict = SCDynamicStoreCopyValue(store, key);
if (dict != NULL) {
if (isA_CFDictionary(dict)) {
CFIndex i;
CFArrayRef interfaces;
CFIndex n;
interfaces = CFDictionaryGetValue(dict, kSCPropNetInterfaces);
n = isA_CFArray(interfaces) ? CFArrayGetCount(interfaces) : 0;
for (i = 0; i < n; i++) {
CFStringRef bsdName;
bsdName = CFArrayGetValueAtIndex(interfaces, i);
if (isA_CFString(bsdName)) {
CFSetAddValue(myInstance->knownInterfaces, bsdName);
}
}
}
CFRelease(dict);
}
CFRelease(key);
CFRelease(store);
return;
}
#pragma mark -
#pragma mark UserEventAgent stubs
static HRESULT
myQueryInterface(void *myInstance, REFIID iid, LPVOID *ppv)
{
CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes(NULL, iid);
if (CFEqual(interfaceID, kUserEventAgentInterfaceID)) {
((MyType *) myInstance)->_UserEventAgentInterface->AddRef(myInstance);
*ppv = myInstance;
CFRelease(interfaceID);
return S_OK;
}
if (CFEqual(interfaceID, IUnknownUUID)) {
((MyType *) myInstance)->_UserEventAgentInterface->AddRef(myInstance);
*ppv = myInstance;
CFRelease(interfaceID);
return S_OK;
}
*ppv = NULL;
CFRelease(interfaceID);
return E_NOINTERFACE;
}
static ULONG
myAddRef(void *myInstance)
{
((MyType *) myInstance)->_refCount++;
return ((MyType *) myInstance)->_refCount;
}
static ULONG
myRelease(void *myInstance)
{
((MyType *) myInstance)->_refCount--;
if (((MyType *) myInstance)->_refCount == 0) {
CFUUIDRef factoryID = ((MyType *) myInstance)->_factoryID;
if (factoryID != NULL) {
CFPlugInRemoveInstanceForFactory(factoryID);
CFRelease(factoryID);
watcher_remove((MyType *)myInstance);
notify_remove((MyType *)myInstance, TRUE);
}
free(myInstance);
return 0;
}
return ((MyType *) myInstance)->_refCount;
}
static void
myInstall(void *myInstance)
{
watcher_add((MyType *)myInstance);
return;
}
static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl = {
NULL, myQueryInterface, myAddRef,
myRelease,
myInstall };
void *
UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID)
{
MyType *newOne = NULL;
if (CFEqual(typeID, kUserEventAgentTypeID)) {
newOne = (MyType *)malloc(sizeof(MyType));
bzero(newOne, sizeof(*newOne));
newOne->_UserEventAgentInterface = &UserEventAgentInterfaceFtbl;
newOne->_factoryID = (CFUUIDRef)CFRetain(kUserEventAgentFactoryID);
CFPlugInAddInstanceForFactory(kUserEventAgentFactoryID);
newOne->_refCount = 1;
}
return newOne;
}
#ifdef MAIN
int
main(int argc, char **argv)
{
MyType *newOne = (MyType *)malloc(sizeof(MyType));
_sc_log = FALSE;
_sc_verbose = (argc > 1) ? TRUE : FALSE;
bzero(newOne, sizeof(*newOne));
myInstall(newOne);
CFRunLoopRun();
exit(0);
return (0);
}
#endif // MAIN