Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
SimpleDial.c
/* |
File: SimpleDial.c |
Contains: Demonstrates the System Configuration framework network connection APIs. |
Written by: DTS |
Copyright: Copyright (c) 2004 by Apple Computer, Inc., All Rights Reserved. |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. |
("Apple") in consideration of your agreement to the following terms, and your |
use, installation, modification or redistribution of this Apple software |
constitutes acceptance of these terms. If you do not agree with these terms, |
please do not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject |
to these terms, Apple grants you a personal, non-exclusive license, under Apple's |
copyrights in this original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or without |
modifications, in source and/or binary forms; provided that if you redistribute |
the Apple Software in its entirety and without modifications, you must retain |
this notice and the following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks or logos of |
Apple Computer, Inc. may be used to endorse or promote products derived from the |
Apple Software without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any patent rights that |
may be infringed by your derivative works or by other works in which the Apple |
Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT |
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
Change History (most recent first): |
$Log: SimpleDial.c,v $ |
Revision 1.2 2004/07/13 15:25:45 eskimo1 |
Work around a problem with the callback get incorrect status values. Tidy up the termination of the runloop. Add "\n" to some text strings that needed them. |
Revision 1.1 2004/04/28 15:44:28 eskimo1 |
First checked in. |
*/ |
#include <assert.h> |
#include <unistd.h> |
#include <CoreFoundation/CoreFoundation.h> |
#include <SystemConfiguration/SystemConfiguration.h> |
static const char * StatusToString(SCNetworkConnectionStatus status) |
// Returns a string representation of the network connection status. |
{ |
const char * result; |
static const char * kStatusStrings[] = { |
"kSCNetworkConnectionInvalid", |
"kSCNetworkConnectionDisconnected", |
"kSCNetworkConnectionConnecting", |
"kSCNetworkConnectionConnected", |
"kSCNetworkConnectionDisconnecting" |
}; |
if ( (status < kSCNetworkConnectionInvalid) |
|| (status > kSCNetworkConnectionDisconnecting) |
) { |
result = "unknown"; |
} else { |
result = kStatusStrings[status + 1]; |
} |
return result; |
} |
static const char * MinorStatusToString( |
SCNetworkConnectionPPPStatus minorStatus |
) |
// Returns a string representation of the minor connection status. |
{ |
const char * result; |
static const char * kMinorStatusStrings[] = { |
"kSCNetworkConnectionPPPDisconnected", |
"kSCNetworkConnectionPPPInitializing", |
"kSCNetworkConnectionPPPConnectingLink", |
"kSCNetworkConnectionPPPDialOnTraffic", |
"kSCNetworkConnectionPPPNegotiatingLink", |
"kSCNetworkConnectionPPPAuthenticating", |
"kSCNetworkConnectionPPPWaitingForCallBack", |
"kSCNetworkConnectionPPPNegotiatingNetwork", |
"kSCNetworkConnectionPPPConnected", |
"kSCNetworkConnectionPPPTerminating", |
"kSCNetworkConnectionPPPDisconnectingLink", |
"kSCNetworkConnectionPPPHoldingLinkOff", |
"kSCNetworkConnectionPPPSuspended", |
"kSCNetworkConnectionPPPWaitingForRedial" |
}; |
if ( (minorStatus < kSCNetworkConnectionPPPDisconnected) |
|| (minorStatus > kSCNetworkConnectionPPPWaitingForRedial) |
) { |
result = "unknown"; |
} else { |
result = kMinorStatusStrings[minorStatus]; |
} |
return result; |
} |
static SCNetworkConnectionPPPStatus GetMinorStatus( |
SCNetworkConnectionRef connection |
) |
// Gets the minor connection status from the extended status |
// dictionary associated with the connection. Returns -1 if |
// it can't get the status, for whatever reason. |
{ |
SCNetworkConnectionPPPStatus result; |
CFDictionaryRef statusDict; |
CFDictionaryRef pppDict; |
CFNumberRef minorStatusNum; |
result = -1; |
// Get the extended status dictionary. |
statusDict = SCNetworkConnectionCopyExtendedStatus(connection); |
if (statusDict != NULL) { |
// Extract the PPP sub-dictionary. |
pppDict = CFDictionaryGetValue(statusDict, kSCEntNetPPP); |
if ( (pppDict != NULL) |
&& (CFGetTypeID(pppDict) == CFDictionaryGetTypeID()) |
) { |
// Extract the minor status value. |
minorStatusNum = CFDictionaryGetValue( |
pppDict, |
kSCPropNetPPPStatus |
); |
if ( (minorStatusNum != NULL) |
&& (CFGetTypeID(minorStatusNum) == CFNumberGetTypeID()) |
) { |
SInt32 tmp; |
if ( CFNumberGetValue( |
minorStatusNum, |
kCFNumberSInt32Type, &tmp |
) |
) { |
result = tmp; |
} |
} |
} |
CFRelease(statusDict); |
} |
return result; |
} |
enum { |
kCallbackParamsMagic = 0x66642666 |
}; |
struct CallbackParams { |
int magic; // kCallbackParamsMagic |
Boolean forcePrintStatus; |
SCNetworkConnectionStatus lastMajorStatus; |
SCNetworkConnectionPPPStatus lastMinorStatus; |
}; |
typedef struct CallbackParams CallbackParams; |
static void MyNetworkConnectionCallBack( |
SCNetworkConnectionRef connection, |
SCNetworkConnectionStatus status, |
void *info |
) |
// Our network connection callback. Called out of the runloop |
// when there's a change in the status of the network connection. |
// It can be called as part of both a connection attempt and a |
// disconnection attempt. |
// |
// In response to this callback we do two things: |
// |
// 1. Print the current connection status. |
// 2. Once the [dis]connection attempt is resolved (the status |
// hits either 'connected' or 'disconnected'), we stop |
// the runloop. This has the effect of breaking the "main" |
// function out of its called to CFRunLoopRun, after which |
// it can examine the results of the connection. |
// |
// The "info" parameter is a pointer to our per-connection data. |
// In this case we use this as a pointer to a CallbackParams |
// structure. We use this to track the previous state of the |
// connection so that we don't print redundant status changes. |
{ |
CallbackParams * params; |
SCNetworkConnectionPPPStatus minorStatus; |
time_t now; |
struct tm nowLocal; |
char nowLocalStr[30]; |
Boolean printMinorStatus; |
assert(connection != NULL); |
assert(info != NULL); |
params = (CallbackParams *) info; |
assert(params->magic = kCallbackParamsMagic); |
// Get a string that represents the current time. |
(void) time(&now); |
(void) localtime_r(&now, &nowLocal); |
(void) strftime(nowLocalStr, sizeof(nowLocalStr), "%X", &nowLocal); |
// Due to a bug <rdar://problem/3725976>, it's best to get the major status via |
// SCNetworkConnectionGetStatus than rely on the value being passed into |
// the callback. |
status = SCNetworkConnectionGetStatus(connection); |
// Get the minor status from the extended status associated with |
// the connection. |
minorStatus = GetMinorStatus(connection); |
// Print any status changes. |
printMinorStatus = (params->lastMinorStatus != minorStatus); |
if ( (params->forcePrintStatus) |
|| (params->lastMajorStatus != status) |
) { |
fprintf( |
stderr, |
"%s %s (%ld)\n", |
nowLocalStr, |
StatusToString(status), |
(long) status |
); |
printMinorStatus = true; |
params->forcePrintStatus = false; |
} |
if (printMinorStatus) { |
fprintf( |
stderr, |
"%s %s (%ld)\n", |
nowLocalStr, |
MinorStatusToString(minorStatus), |
(long) minorStatus |
); |
} |
// If we hit either the connected or disconnected state, |
// we signal the runloop to stop so that the main function |
// can process the result of the [dis]connection attempt. |
if ( ( minorStatus == kSCNetworkConnectionPPPDisconnected ) |
|| ( minorStatus == kSCNetworkConnectionPPPConnected ) |
) { |
CFRunLoopStop(CFRunLoopGetCurrent()); |
} |
// Record the new status. |
params->lastMajorStatus = status; |
params->lastMinorStatus = minorStatus; |
} |
int main (int argc, const char * argv[]) |
// This program finds the first PPP connection service in the |
// current network configuration and then connects and |
// disconnects that service. |
{ |
int err; |
Boolean ok; |
CFStringRef serviceToDial; |
CFDictionaryRef optionsForDial; |
SCNetworkConnectionRef connection; |
CallbackParams params; |
serviceToDial = NULL; |
optionsForDial = NULL; |
connection = NULL; |
// If we're run without an argument, just print the usage. |
err = 0; |
if (argc != 1) { |
const char *programName; |
programName = strrchr(argv[0], '/'); |
if (programName == NULL) { |
programName = argv[0]; |
} else { |
programName += 1; |
} |
fprintf(stderr, "Usage: %s\n", programName); |
err = ECANCELED; |
} |
// Find the serviceID of the PPP service to dial and the user's |
// preferred dialling options. This routine picks up the last |
// service dialled using Internet Connect (and the associated |
// options) or, if Internet Connect has not been used, returns |
// the first PPP service listed in the Network preferences pane. |
if (err == 0) { |
ok = SCNetworkConnectionCopyUserPreferences( |
NULL, |
&serviceToDial, |
&optionsForDial |
); |
if ( ! ok ) { |
err = SCError(); |
} |
} |
// Create a SCNetworkConnectionRef for it. |
if (err == 0) { |
SCNetworkConnectionContext context; |
// Set up the parameters to our callback function. |
params.magic = kCallbackParamsMagic; |
params.forcePrintStatus = true; |
params.lastMinorStatus = kSCNetworkConnectionDisconnected; |
params.lastMinorStatus = kSCNetworkConnectionPPPDisconnected; |
// Set up the context to reference those parameters. |
context.version = 0; |
context.info = (void *) ¶ms; |
context.retain = NULL; |
context.release = NULL; |
context.copyDescription = NULL; |
connection = SCNetworkConnectionCreateWithServiceID( |
NULL, |
serviceToDial, |
MyNetworkConnectionCallBack, |
&context |
); |
if (connection == NULL) { |
err = SCError(); |
} |
} |
// Schedule our callback with the runloop. |
if (err == 0) { |
ok = SCNetworkConnectionScheduleWithRunLoop( |
connection, |
CFRunLoopGetCurrent(), |
kCFRunLoopDefaultMode |
); |
if ( ! ok ) { |
err = SCError(); |
} |
} |
// Check the status. If we're already connected tell the user. |
// If we're not connected, initiate the connection. |
if (err == 0) { |
err = ECANCELED; // Most cases involve us bailing out, |
// so set the error here. |
switch ( SCNetworkConnectionGetStatus(connection) ) { |
case kSCNetworkConnectionDisconnected: |
err = 0; |
break; |
case kSCNetworkConnectionConnecting: |
fprintf(stderr, "Service is already connecting.\n"); |
break; |
case kSCNetworkConnectionDisconnecting: |
fprintf(stderr, "Service is disconnecting.\n"); |
break; |
case kSCNetworkConnectionConnected: |
fprintf(stderr, "Service is already connected.\n"); |
break; |
case kSCNetworkConnectionInvalid: |
fprintf(stderr, "Service is invalid. Weird.\n"); |
break; |
default: |
fprintf(stderr, "Unexpected status.\n"); |
break; |
} |
} |
// Initiate the connection. |
if (err == 0) { |
fprintf(stderr, "Connecting...\n"); |
ok = SCNetworkConnectionStart( |
connection, |
optionsForDial, |
false |
); |
if ( ! ok ) { |
err = SCError(); |
} |
} |
// Run the runloop and wait for our connection attempt to be resolved. |
// Once that happens, print the result. |
if (err == 0) { |
CFRunLoopRun(); |
switch (params.lastMinorStatus) { |
case kSCNetworkConnectionPPPConnected: |
fprintf(stderr, "Connection succeeded\n"); |
break; |
case kSCNetworkConnectionPPPDisconnected: |
fprintf(stderr, "Connection failed\n"); |
err = ECANCELED; |
break; |
default: |
fprintf( |
stderr, |
"Bad params.lastMinorStatus (%ld)\n", |
(long) params.lastMinorStatus |
); |
err = EINVAL; |
break; |
} |
} |
// Wait for a few seconds. |
if (err == 0) { |
fprintf(stderr, "Waiting for a few seconds...\n"); |
(void) sleep(5); |
// Initiate a disconnect. |
params.forcePrintStatus = true; |
fprintf(stderr, "Disconnecting...\n"); |
ok = SCNetworkConnectionStop( |
connection, |
true |
); |
if ( ! ok ) { |
err = SCError(); |
} |
} |
// Run the runloop and wait for our disconnection attempt to be |
// resolved. Once that happens, print the result. |
if (err == 0) { |
CFRunLoopRun(); |
switch (params.lastMinorStatus) { |
case kSCNetworkConnectionPPPDisconnected: |
fprintf(stderr, "Disconnection succeeded\n"); |
break; |
case kSCNetworkConnectionPPPConnected: |
fprintf(stderr, "Disconnection failed\n"); |
err = ECANCELED; |
break; |
default: |
fprintf( |
stderr, |
"Bad params.lastMinorStatus (%ld)\n", |
(long) params.lastMinorStatus |
); |
err = EINVAL; |
break; |
} |
} |
// Clean up. |
if (serviceToDial != NULL) { |
CFRelease(serviceToDial); |
} |
if (optionsForDial != NULL) { |
CFRelease(optionsForDial); |
} |
if (connection != NULL) { |
(void) SCNetworkConnectionUnscheduleFromRunLoop( |
connection, |
CFRunLoopGetCurrent(), |
kCFRunLoopDefaultMode |
); |
CFRelease(connection); |
} |
if (err == 0) { |
return EXIT_SUCCESS; |
} else { |
if (err != ECANCELED) { |
fprintf(stderr, "Failed with error %d\n.", err); |
} |
return EXIT_FAILURE; |
} |
} |
Copyright © 2005 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2005-07-26