test8.c 3.56 KB
O'Reilly Media, Inc. committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
// Simple example of how to handle DNSServiceDiscovery events using a CFRunLoop

#include <dns_sd.h>
#include <CoreFoundation/CoreFoundation.h>


// Structure to hold CFRunLoop-related state

typedef struct MyDNSServiceState
    {
    DNSServiceRef       service;    // Active DNSServiceDiscovery operation
    CFSocketRef         cfsocket;   // CFSocket for this operation
    CFRunLoopSourceRef  source;     // RunLoopSource for this CFSocket
    } MyDNSServiceState;


// Remove a DNSServiceDiscovery operation from a CFRunLoop's
// set of active event sources

static void DNSServiceRemoveSource(CFRunLoopRef rl, MyDNSServiceState *ref)
    {
    assert(rl != NULL);
    assert(ref != NULL);

    // Remove the CFRunLoopSource from the current run loop.
    CFRunLoopRemoveSource(rl, ref->source, kCFRunLoopCommonModes);
    CFRelease(ref->source);

    // Invalidate the CFSocket.
    CFSocketInvalidate(ref->cfsocket);
    CFRelease(ref->cfsocket);

    // Workaround to give time to CFSocket's select() thread
    // so it can remove the socket from its FD set before we
    // close the socket by calling DNSServiceRefDeallocate.
    usleep(1000);

    // Terminate the connection with the daemon, which cancels the operation.
    DNSServiceRefDeallocate(ref->service);
    free(ref);
    }


// Helper function: When CFRunLoop indicates an interesting event,
// this function calls DNSServiceProcessResult() to handle it

static void MySocketCallBack(CFSocketRef s, CFSocketCallBackType type,
                 CFDataRef address, const void *data, void *info)
    {
    #pragma unused(s)
    #pragma unused(type)
    #pragma unused(address)
    #pragma unused(data)

    DNSServiceErrorType err;
    MyDNSServiceState *ref = (MyDNSServiceState *)info;
    assert(ref != NULL);

    // Read a reply from the daemon, which will call the appropriate callback.
    err= DNSServiceProcessResult(ref->service);
    if (err != kDNSServiceErr_NoError)
        {
        fprintf(stderr, "DNSServiceProcessResult returned %d\n", err);
        // Terminate the discovery operation and release everything.
        DNSServiceRemoveSource(CFRunLoopGetCurrent(), ref);
        }
    }


// Add a DNSServiceDiscovery operation to a CFRunLoop's
// set of active event sources

static void DNSServiceAddSource(CFRunLoopRef rl, MyDNSServiceState *ref)
    {
    CFSocketContext context = { 0, ref, NULL, NULL, NULL };
    CFSocketNativeHandle sock = DNSServiceRefSockFD(ref->service);
    assert(sock != -1);

    // Create a CFSocket from the underlying Unix Domain socket.
    ref->cfsocket = CFSocketCreateWithNative(NULL, sock,
                    kCFSocketReadCallBack, MySocketCallBack, &context);

    // Prevent CFSocketInvalidate from closing DNSServiceRef's socket.
    CFOptionFlags f = CFSocketGetSocketFlags(ref->cfsocket);
    CFSocketSetSocketFlags(ref->cfsocket, f & ~kCFSocketCloseOnInvalidate);

    // Create a CFRunLoopSource from the CFSocket.
    ref->source = CFSocketCreateRunLoopSource(NULL, ref->cfsocket, 0);

    // Add the CFRunLoopSource to the current run loop.
    CFRunLoopAddSource(rl, ref->source, kCFRunLoopCommonModes);
    }


// Simple example: Here we just add a single DNSServiceDiscovery event source,
// and then call CFRunLoopRun() to handle the events. In a program that already
// has a main RunLoop, you'd just keep that as is, and use DNSServiceAddSource/
// DNSServiceRemoveSource to add and remove event sources from that RunLoop.

void HandleEvents(DNSServiceRef serviceRef)
    {
    MyDNSServiceState ref = { serviceRef };
    DNSServiceAddSource(CFRunLoopGetCurrent(), &ref);

    CFRunLoopRun();
    }