smb-configuration.c [plain text]
#include <ctype.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb_async.h>
#include <notify.h>
#include <smb_server_prefs.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFStringDefaultEncoding.h> // for __CFStringGetInstallationEncodingAndRegion()
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCValidation.h>
#include <SystemConfiguration/SCPrivate.h> // for SCLog(), SCPrint()
#ifdef MAIN
#define my_log(__level, fmt, ...) SCPrint(TRUE, stdout, CFSTR(fmt "\n"), ## __VA_ARGS__)
#else // MAIN
#include "ip_plugin.h"
#endif // MAIN
#define HW_MODEL_LEN 64 // Note: must be >= NETBIOS_NAME_LEN (below)
#define NETBIOS_NAME_LEN 16
#define SMB_STARTUP_DELAY 60.0
#define SMB_DEBOUNCE_DELAY 5.0
static SCDynamicStoreRef store = NULL;
static CFRunLoopRef rl = NULL;
static CFRunLoopSourceRef rls = NULL;
static int notify_token = -1;
static struct timeval ptrQueryStart;
static SCNetworkReachabilityRef ptrTarget = NULL;
static CFRunLoopTimerRef timer = NULL;
static CFAbsoluteTime
boottime(void)
{
static CFAbsoluteTime bt = 0;
if (bt == 0) {
int mib[2] = { CTL_KERN, KERN_BOOTTIME };
struct timeval tv;
size_t tv_len = sizeof(tv);
if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &tv, &tv_len, NULL, 0) == -1) {
my_log(LOG_ERR, "sysctl() CTL_KERN/KERN_BOOTTIME failed: %s", strerror(errno));
return kCFAbsoluteTimeIntervalSince1970;
}
bt = (CFTimeInterval)tv.tv_sec - kCFAbsoluteTimeIntervalSince1970;
bt += (1.0E-6 * (CFTimeInterval)tv.tv_usec);
}
return bt;
}
static CFStringRef
copy_default_name(void)
{
CFStringRef model;
size_t n;
CFMutableStringRef str;
model = _SC_hw_model(TRUE);
if (model == NULL) {
return NULL;
}
str = CFStringCreateMutable(NULL, 0);
CFStringAppend(str, model);
n = CFStringGetLength(str);
if (n > (NETBIOS_NAME_LEN - 1)) {
CFStringReplace(str,
CFRangeMake(NETBIOS_NAME_LEN, n - (NETBIOS_NAME_LEN - 1)),
CFSTR(""));
n = NETBIOS_NAME_LEN - 1;
}
if (n < (NETBIOS_NAME_LEN - 1 - 3)) {
SCNetworkInterfaceRef interface;
interface = _SCNetworkInterfaceCreateWithBSDName(NULL, CFSTR("en0"),
kIncludeNoVirtualInterfaces);
if (interface != NULL) {
CFMutableStringRef en0_MAC;
en0_MAC = (CFMutableStringRef)SCNetworkInterfaceGetHardwareAddressString(interface);
if (en0_MAC != NULL) {
CFIndex en0_MAC_len;
en0_MAC = CFStringCreateMutableCopy(NULL, 0, en0_MAC);
CFStringFindAndReplace(en0_MAC,
CFSTR(":"),
CFSTR(""),
CFRangeMake(0, CFStringGetLength(en0_MAC)),
0);
n = ((NETBIOS_NAME_LEN - 1 - n - 1) / 2) * 2;
if (n > 6) {
n = 6;
}
en0_MAC_len = CFStringGetLength(en0_MAC);
if (en0_MAC_len > n) {
CFStringDelete(en0_MAC, CFRangeMake(0, en0_MAC_len - n));
}
CFStringAppendFormat(str, NULL, CFSTR("-%@"), en0_MAC);
CFRelease(en0_MAC);
}
CFRelease(interface);
}
}
CFStringUppercase(str, NULL);
return str;
}
static CFDictionaryRef
smb_copy_global_configuration(SCDynamicStoreRef store)
{
CFDictionaryRef dict;
CFStringRef key;
key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
kSCDynamicStoreDomainState,
kSCEntNetSMB);
dict = SCDynamicStoreCopyValue(store, key);
CFRelease(key);
if (dict != NULL) {
if (isA_CFDictionary(dict)) {
return dict;
}
CFRelease(dict);
}
dict = CFDictionaryCreate(NULL, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
return dict;
}
static void
update_pref(SCPreferencesRef prefs, CFStringRef key, CFTypeRef newVal, Boolean *changed)
{
CFTypeRef curVal;
curVal = SCPreferencesGetValue(prefs, key);
if (!_SC_CFEqual(curVal, newVal)) {
if (newVal != NULL) {
SCPreferencesSetValue(prefs, key, newVal);
} else {
SCPreferencesRemoveValue(prefs, key);
}
*changed = TRUE;
}
return;
}
static void
smb_set_configuration(SCDynamicStoreRef store, CFDictionaryRef dict)
{
CFArrayRef array;
Boolean changed = FALSE;
UInt32 dosCodepage = 0;
CFStringEncoding dosEncoding = 0;
CFStringEncoding macEncoding = kCFStringEncodingMacRoman;
uint32_t macRegion = 0;
Boolean ok;
SCPreferencesRef prefs;
CFStringRef str;
prefs = SCPreferencesCreate(NULL, CFSTR("smb-configuration"), CFSTR(kSMBPreferencesAppID));
if (prefs == NULL) {
my_log(LOG_ERR,
"smb_set_configuration: SCPreferencesCreate() failed: %s",
SCErrorString(SCError()));
return;
}
ok = SCPreferencesLock(prefs, TRUE);
if (!ok) {
my_log(LOG_ERR,
"smb_set_configuration: SCPreferencesLock() failed: %s",
SCErrorString(SCError()));
goto done;
}
str = SCDynamicStoreCopyComputerName(store, &macEncoding);
update_pref(prefs, CFSTR(kSMBPrefServerDescription), str, &changed);
if (str != NULL) {
if (macEncoding == kCFStringEncodingMacRoman) {
CFStringRef key;
CFDictionaryRef dict;
key = SCDynamicStoreKeyCreateComputerName(NULL);
dict = SCDynamicStoreCopyValue(store, key);
CFRelease(key);
if (dict != NULL) {
if (isA_CFDictionary(dict)) {
CFNumberRef num;
SInt32 val;
num = CFDictionaryGetValue(dict, kSCPropSystemComputerNameRegion);
if (isA_CFNumber(num) &&
CFNumberGetValue(num, kCFNumberSInt32Type, &val)) {
macRegion = (uint32_t)val;
}
}
CFRelease(dict);
}
}
CFRelease(str);
} else {
__CFStringGetInstallationEncodingAndRegion((uint32_t *)&macEncoding, &macRegion);
}
_SC_dos_encoding_and_codepage(macEncoding, macRegion, &dosEncoding, &dosCodepage);
str = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), (unsigned int)dosCodepage);
assert(str != NULL);
update_pref(prefs, CFSTR(kSMBPrefDOSCodePage), str, &changed);
CFRelease(str);
str = CFDictionaryGetValue(dict, kSCPropNetSMBNetBIOSName);
str = isA_CFString(str);
update_pref(prefs, CFSTR(kSMBPrefNetBIOSName), str, &changed);
str = CFDictionaryGetValue(dict, kSCPropNetSMBNetBIOSNodeType);
str = isA_CFString(str);
if (str != NULL) {
if (CFEqual(str, kSCValNetSMBNetBIOSNodeTypeBroadcast)) {
str = CFSTR(kSMBPrefNetBIOSNodeBroadcast);
} else if (CFEqual(str, kSCValNetSMBNetBIOSNodeTypePeer)) {
str = CFSTR(kSMBPrefNetBIOSNodePeer);
} else if (CFEqual(str, kSCValNetSMBNetBIOSNodeTypeMixed)) {
str = CFSTR(kSMBPrefNetBIOSNodeMixed);
} else if (CFEqual(str, kSCValNetSMBNetBIOSNodeTypeHybrid)) {
str = CFSTR(kSMBPrefNetBIOSNodeHybrid);
} else {
str = NULL;
}
}
update_pref(prefs, CFSTR(kSMBPrefNetBIOSNodeType), str, &changed);
#ifdef ADD_NETBIOS_SCOPE
str = CFDictionaryGetValue(dict, kSCPropNetSMBNetBIOSScope);
str = isA_CFString(str);
update_pref(prefs, CFSTR(kSMBPrefNetBIOSScope), str, &changed);
#endif // ADD_NETBIOS_SCOPE
array = CFDictionaryGetValue(dict, kSCPropNetSMBWINSAddresses);
array = isA_CFArray(array);
update_pref(prefs, CFSTR(kSMBPrefWINSServerAddressList), array, &changed);
str = CFDictionaryGetValue(dict, kSCPropNetSMBWorkgroup);
str = isA_CFString(str);
update_pref(prefs, CFSTR(kSMBPrefWorkgroup), str, &changed);
if (changed) {
ok = SCPreferencesCommitChanges(prefs);
if (!ok) {
if ((SCError() != EROFS)) {
my_log(LOG_ERR,
"smb_set_configuration: SCPreferencesCommitChanges() failed: %s",
SCErrorString(SCError()));
}
goto done;
}
ok = SCPreferencesApplyChanges(prefs);
if (!ok) {
my_log(LOG_ERR,
"smb_set_configuration: SCPreferencesApplyChanges() failed: %s",
SCErrorString(SCError()));
goto done;
}
}
done :
(void) SCPreferencesUnlock(prefs);
CFRelease(prefs);
return;
}
static CFStringRef
copy_primary_service(SCDynamicStoreRef store)
{
CFDictionaryRef dict;
CFStringRef key;
CFStringRef serviceID = NULL;
key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
kSCDynamicStoreDomainState,
kSCEntNetIPv4);
dict = SCDynamicStoreCopyValue(store, key);
CFRelease(key);
if (dict != NULL) {
if (isA_CFDictionary(dict)) {
serviceID = CFDictionaryGetValue(dict, kSCDynamicStorePropNetPrimaryService);
if (isA_CFString(serviceID)) {
CFRetain(serviceID);
} else {
serviceID = NULL;
}
}
CFRelease(dict);
}
return serviceID;
}
static CFStringRef
copy_primary_ip(SCDynamicStoreRef store, CFStringRef serviceID)
{
CFStringRef address = NULL;
CFDictionaryRef dict;
CFStringRef key;
key = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainState,
serviceID,
kSCEntNetIPv4);
dict = SCDynamicStoreCopyValue(store, key);
CFRelease(key);
if (dict != NULL) {
if (isA_CFDictionary(dict)) {
CFArrayRef addresses;
addresses = CFDictionaryGetValue(dict, kSCPropNetIPv4Addresses);
if (isA_CFArray(addresses) && (CFArrayGetCount(addresses) > 0)) {
address = CFArrayGetValueAtIndex(addresses, 0);
if (isA_CFString(address)) {
CFRetain(address);
} else {
address = NULL;
}
}
}
CFRelease(dict);
}
return address;
}
static void
ptr_query_stop()
{
if (ptrTarget == NULL) {
return;
}
my_log(LOG_INFO, "NetBIOS name: ptr query stop");
SCNetworkReachabilitySetCallback(ptrTarget, NULL, NULL);
SCNetworkReachabilityUnscheduleFromRunLoop(ptrTarget, rl, kCFRunLoopDefaultMode);
CFRelease(ptrTarget);
ptrTarget = NULL;
return;
}
static void
ptr_query_callback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void *info)
{
CFDictionaryRef dict;
CFStringRef name;
CFMutableDictionaryRef newDict;
struct timeval ptrQueryComplete;
struct timeval ptrQueryElapsed;
(void) gettimeofday(&ptrQueryComplete, NULL);
timersub(&ptrQueryComplete, &ptrQueryStart, &ptrQueryElapsed);
my_log(LOG_INFO, "NetBIOS name: ptr query complete%s (query time = %ld.%3.3d)",
(flags & kSCNetworkReachabilityFlagsReachable) ? "" : ", host not found",
ptrQueryElapsed.tv_sec,
ptrQueryElapsed.tv_usec / 1000);
dict = smb_copy_global_configuration(store);
name = CFDictionaryGetValue(dict, kSCPropNetSMBNetBIOSName);
if ((name != NULL) && _SC_CFStringIsValidNetBIOSName(name)) {
my_log(LOG_INFO, "NetBIOS name (network configuration) = %@", name);
goto setDict;
}
name = NULL;
if (flags & kSCNetworkReachabilityFlagsReachable) {
int error_num;
CFArrayRef hosts;
hosts = SCNetworkReachabilityCopyResolvedAddress(target, &error_num);
if (hosts != NULL) {
if (CFArrayGetCount(hosts) > 0) {
CFIndex ptrLen;
CFMutableStringRef ptrName;
CFRange range;
name = CFArrayGetValueAtIndex(hosts, 0);
ptrName = CFStringCreateMutableCopy(NULL, 0, name);
ptrLen = CFStringGetLength(ptrName);
if (CFStringFindWithOptions(ptrName,
CFSTR("."),
CFRangeMake(0, ptrLen),
0,
&range)) {
CFStringDelete(ptrName,
CFRangeMake(range.location, ptrLen - range.location));
}
name = ptrName;
}
CFRelease(hosts);
}
}
if (name != NULL) {
if (_SC_CFStringIsValidNetBIOSName(name)) {
my_log(LOG_INFO, "NetBIOS name (reverse DNS query) = %@", name);
goto setName;
}
CFRelease(name);
}
name = SCDynamicStoreCopyLocalHostName(store);
if (name != NULL) {
if (_SC_CFStringIsValidNetBIOSName(name)) {
my_log(LOG_INFO, "NetBIOS name (multicast DNS) = %@", name);
goto setName;
}
CFRelease(name);
}
name = copy_default_name();
if (name != NULL) {
my_log(LOG_INFO, "NetBIOS name (default) = %@", name);
goto setName;
}
goto setDict;
setName :
newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
CFDictionarySetValue(newDict, kSCPropNetSMBNetBIOSName, name);
CFRelease(dict);
dict = newDict;
CFRelease(name);
setDict :
smb_set_configuration(store, dict);
CFRelease(dict);
ptr_query_stop();
#ifdef MAIN
CFRunLoopStop(rl);
#endif // MAIN
return;
}
static Boolean
ptr_query_start(CFStringRef address)
{
union {
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
} addr;
char buf[64];
CFDataRef data;
CFMutableDictionaryRef options;
if (_SC_cfstring_to_cstring(address, buf, sizeof(buf), kCFStringEncodingASCII) == NULL) {
my_log(LOG_ERR, "could not convert [primary] address string");
return FALSE;
}
if (_SC_string_to_sockaddr(buf, AF_UNSPEC, (void *)&addr, sizeof(addr)) == NULL) {
my_log(LOG_ERR, "could not convert [primary] address");
return FALSE;
}
options = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
data = CFDataCreate(NULL, (const UInt8 *)&addr.sa, addr.sa.sa_len);
CFDictionarySetValue(options, kSCNetworkReachabilityOptionPTRAddress, data);
CFRelease(data);
ptrTarget = SCNetworkReachabilityCreateWithOptions(NULL, options);
CFRelease(options);
if (ptrTarget == NULL) {
my_log(LOG_ERR, "could not resolve [primary] address");
return FALSE;
}
my_log(LOG_INFO, "NetBIOS name: ptr query start");
(void) SCNetworkReachabilitySetCallback(ptrTarget, ptr_query_callback, NULL);
(void) SCNetworkReachabilityScheduleWithRunLoop(ptrTarget, rl, kCFRunLoopDefaultMode);
return TRUE;
}
static void
smb_update_configuration(__unused CFRunLoopTimerRef _timer, void *info)
{
CFStringRef address = NULL;
CFDictionaryRef dict;
CFStringRef name;
CFStringRef serviceID = NULL;
SCDynamicStoreRef store = (SCDynamicStoreRef)info;
dict = smb_copy_global_configuration(store);
name = CFDictionaryGetValue(dict, kSCPropNetSMBNetBIOSName);
if ((name != NULL) && _SC_CFStringIsValidNetBIOSName(name)) {
my_log(LOG_INFO, "NetBIOS name (network configuration) = %@", name);
goto set;
}
serviceID = copy_primary_service(store);
if (serviceID == NULL) {
goto mDNS;
}
address = copy_primary_ip(store, serviceID);
if (address != NULL) {
Boolean ok;
ok = ptr_query_start(address);
if (ok) {
goto done;
}
}
mDNS :
name = SCDynamicStoreCopyLocalHostName(store);
if (name != NULL) {
if (_SC_CFStringIsValidNetBIOSName(name)) {
CFMutableDictionaryRef newDict;
my_log(LOG_INFO, "NetBIOS name (multicast DNS) = %@", name);
newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
CFDictionarySetValue(newDict, kSCPropNetSMBNetBIOSName, name);
CFRelease(dict);
dict = newDict;
CFRelease(name);
goto set;
}
CFRelease(name);
}
name = copy_default_name();
if (name != NULL) {
CFMutableDictionaryRef newDict;
my_log(LOG_INFO, "NetBIOS name (default) = %@", name);
newDict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
CFDictionarySetValue(newDict, kSCPropNetSMBNetBIOSName, name);
CFRelease(dict);
dict = newDict;
CFRelease(name);
}
set :
smb_set_configuration(store, dict);
done :
if (address != NULL) CFRelease(address);
if (dict != NULL) CFRelease(dict);
if (serviceID != NULL) CFRelease(serviceID);
if (timer != NULL) {
CFRunLoopTimerInvalidate(timer);
CFRelease(timer);
timer = NULL;
}
return;
}
static void
configuration_changed(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info)
{
CFRunLoopTimerContext context = { 0, (void *)store, CFRetain, CFRelease, NULL };
CFAbsoluteTime time_boot;
CFAbsoluteTime time_now ;
if (ptrTarget != NULL) {
ptr_query_stop();
}
if (timer != NULL) {
CFRunLoopTimerInvalidate(timer);
CFRelease(timer);
timer = NULL;
}
time_boot = boottime() + SMB_STARTUP_DELAY;
time_now = CFAbsoluteTimeGetCurrent() + SMB_DEBOUNCE_DELAY;
timer = CFRunLoopTimerCreate(NULL,
time_now > time_boot ? time_now : time_boot,
0,
0,
0,
smb_update_configuration,
&context);
CFRunLoopAddTimer(rl, timer, kCFRunLoopDefaultMode);
return;
}
__private_extern__
void
load_smb_configuration(Boolean verbose)
{
CFStringRef key;
CFMutableArrayRef keys = NULL;
dispatch_block_t notify_block;
Boolean ok;
CFMutableArrayRef patterns = NULL;
uint32_t status;
store = SCDynamicStoreCreate(NULL, CFSTR("smb-configuration"), configuration_changed, NULL);
if (store == NULL) {
my_log(LOG_ERR,
"SCDynamicStoreCreate() failed: %s",
SCErrorString(SCError()));
goto error;
}
keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
kSCDynamicStoreDomainState,
kSCEntNetSMB);
CFArrayAppendValue(keys, key);
CFRelease(key);
key = SCDynamicStoreKeyCreateComputerName(NULL);
CFArrayAppendValue(keys, key);
CFRelease(key);
key = SCDynamicStoreKeyCreateHostNames(NULL);
CFArrayAppendValue(keys, key);
CFRelease(key);
ok = SCDynamicStoreSetNotificationKeys(store, keys, patterns);
CFRelease(keys);
CFRelease(patterns);
if (!ok) {
my_log(LOG_ERR,
"SCDynamicStoreSetNotificationKeys() failed: %s",
SCErrorString(SCError()));
goto error;
}
rl = CFRunLoopGetCurrent();
rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
if (rls == NULL) {
my_log(LOG_ERR,
"SCDynamicStoreCreateRunLoopSource() failed: %s",
SCErrorString(SCError()));
goto error;
}
CFRunLoopAddSource(rl, rls, kCFRunLoopDefaultMode);
notify_block = ^{
CFArrayRef changes;
CFStringRef key;
key = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
kSCDynamicStoreDomainState,
kSCEntNetDNS);
changes = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks);
(*configuration_changed)(store, changes, NULL);
CFRelease(changes);
CFRelease(key);
return;
};
status = notify_register_dispatch(_SC_NOTIFY_NETWORK_CHANGE,
¬ify_token,
dispatch_get_main_queue(),
^(int token){
CFRunLoopPerformBlock(rl,
kCFRunLoopDefaultMode,
notify_block);
CFRunLoopWakeUp(rl);
});
if (status != NOTIFY_STATUS_OK) {
my_log(LOG_ERR, "notify_register_dispatch() failed: %u", status);
goto error;
}
return;
error :
if (rls != NULL) {
CFRunLoopRemoveSource(rl, rls, kCFRunLoopDefaultMode);
CFRelease(rls);
rls = NULL;
}
if (store != NULL) {
CFRelease(store);
store = NULL;
}
return;
}
#ifdef MAIN
int
main(int argc, char **argv)
{
#ifdef DEBUG
CFStringRef address;
CFStringRef name;
CFStringRef serviceID;
SCDynamicStoreRef store;
_sc_log = FALSE;
if ((argc > 1) && (strcmp(argv[1], "-d") == 0)) {
_sc_verbose = TRUE;
argv++;
argc--;
}
store = SCDynamicStoreCreate(NULL, CFSTR("smb-configuration"), NULL, NULL);
if (store == NULL) {
SCPrint(TRUE, stdout,
CFSTR("SCDynamicStoreCreate() failed: %s\n"),
SCErrorString(SCError()));
exit(1);
}
name = copy_default_name();
if (name != NULL) {
SCPrint(TRUE, stdout, CFSTR("default name = %@\n"), name);
CFRelease(name);
}
serviceID = copy_primary_service(store);
if (serviceID != NULL) {
SCPrint(TRUE, stdout, CFSTR("primary service ID = %@\n"), serviceID);
} else {
SCPrint(TRUE, stdout, CFSTR("No primary service\n"));
goto done;
}
if ((argc == (2+1)) && (argv[1][0] == 's')) {
if (serviceID != NULL) CFRelease(serviceID);
serviceID = CFStringCreateWithCString(NULL, argv[2], kCFStringEncodingUTF8);
SCPrint(TRUE, stdout, CFSTR("alternate service ID = %@\n"), serviceID);
}
address = copy_primary_ip(store, serviceID);
CFRelease(serviceID);
if (address != NULL) {
SCPrint(TRUE, stdout, CFSTR("primary address = %@\n"), address);
if ((argc == (2+1)) && (argv[1][0] == 'a')) {
if (address != NULL) CFRelease(address);
address = CFStringCreateWithCString(NULL, argv[2], kCFStringEncodingUTF8);
SCPrint(TRUE, stdout, CFSTR("alternate primary address = %@\n"), address);
}
(void) ptr_query_start(address);
CFRelease(address);
}
done :
smb_update_configuration(NULL, (void *)store);
CFRelease(store);
CFRunLoopRun();
#else
_sc_log = FALSE;
_sc_verbose = (argc > 1) ? TRUE : FALSE;
load_smb_configuration((argc > 1) ? TRUE : FALSE);
CFRunLoopRun();
#endif
exit(0);
return 0;
}
#endif