XrdOucAppleBonjour.cc

Go to the documentation of this file.
00001 /*
00002    C++ implementation of Bonjour services.  Code is based
00003    on the implementation of TBonjour* classes written by
00004    Fons Rademakers for the ROOT Framework.
00005 */
00006 
00007 #include <arpa/inet.h>
00008 #include <net/if.h>
00009 #include <sys/select.h>
00010 #include <cstdlib>
00011 #include "Xrd/XrdConfig.hh"
00012 #include "XrdOuc/XrdOucBonjour.hh"
00013 #include "XrdOuc/XrdOucFactoryBonjour.hh"
00014 #include "XrdSys/XrdSysError.hh"
00015 #include "Xrd/XrdInet.hh"
00016 #include "Xrd/XrdProtLoad.hh"
00017 
00018 /******************************************************************************/
00019 /*                      G l o b a l   V a r i a b l e s                       */
00020 /******************************************************************************/
00021 
00022 extern XrdConfig    XrdConf;              // Defined in XrdMain.cc
00023 extern XrdSysError  XrdLog;               // Defined in XrdMain.cc
00024 extern XrdInet     *XrdNetTCP[];          // Defined by config
00025 
00026 /******************************************************************************/
00027 /*        B o n j o u r   s e r v i c e s : r e g i s t r a t i o n           */
00028 /******************************************************************************/
00029 
00030 void XrdOucAppleBonjour::RegisterReply(DNSServiceRef ref, DNSServiceFlags flags,
00031                                        DNSServiceErrorType error, const char * name,
00032                                        const char * regtype, const char * domain,
00033                                        void * context)
00034 {
00035    XrdOucBonjourRegisteredEntry * entry;
00036    entry = (XrdOucBonjourRegisteredEntry *)context;
00037 
00038    // When this callback is called, it comes with the final results of the
00039    // procedure and the registered names if there was a renaming due to name
00040    // collisions with other services.
00041    if (error != kDNSServiceErr_NoError) {
00042       ((XrdOucAppleBonjour &)getInstance()).ListOfRegistrations.remove(entry);
00043       delete entry->record;
00044       free(entry);
00045       XrdLog.Emsg("OucBonjour", error, "complete the registration callback");
00046       return;
00047    }
00048 
00049    // Update the registered information.
00050    entry->record->SetServiceName(name);
00051    entry->record->SetRegisteredType(regtype);
00052    entry->record->SetReplyDomain(domain);
00053 }
00054 
00055 int XrdOucAppleBonjour::RegisterService(XrdOucBonjourRecord &record, unsigned short port)
00056 {
00057    XrdOucBonjourRegisteredEntry * entry;
00058    DNSServiceErrorType err;
00059    fd_set descriptors;
00060    int sockfd;
00061    struct timeval timeout;
00062 
00063    // Get the default port.
00064    if (port == 0)
00065       port = (XrdNetTCP[0] == XrdNetTCP[XrdProtLoad::ProtoMax]
00066               ?  -(XrdNetTCP[0]->Port()) : XrdNetTCP[0]->Port());
00067 
00068    // Store information on local list.
00069    entry = (XrdOucBonjourRegisteredEntry *)malloc(sizeof(XrdOucBonjourRegisteredEntry));
00070    if (!entry)
00071       return -1;
00072 
00073    entry->record = new XrdOucBonjourRecord(record);
00074    entry->port = port;
00075 
00076    // Start the registration procedure.
00077    err = DNSServiceRegister(&(entry->bonjourRef),
00078                             0, // No flags
00079                             0,
00080                             record.GetServiceName(),
00081                             record.GetRegisteredType(),
00082                             record.GetReplyDomain(),
00083                             NULL, // This host
00084                             htons(port),
00085                             record.GetTXTRecordLength(),
00086                             record.GetTXTRecordData(),
00087                             RegisterReply,
00088                             entry);
00089 
00090    if (err != kDNSServiceErr_NoError) {
00091       XrdLog.Emsg("OucBonjour", err, "rRegigster service", record.GetRegisteredType());
00092       XrdLog.Emsg("OucBonjour", err, "launch the registration");
00093       // Free memory.
00094       delete entry->record;
00095       free(entry);
00096       return -1;
00097    }
00098 
00099    // Wait for the callback. CAUTION: this call is blocking and may stuck you
00100    // thread. This will call the callback function.
00101    sockfd = DNSServiceRefSockFD(entry->bonjourRef);
00102    // Set the fd_set to start select()
00103    FD_ZERO(&descriptors);
00104    FD_SET(sockfd, &descriptors);
00105    timeout.tv_sec = TIMEOUT;
00106    timeout.tv_usec = 0;
00107    // Wait for the response.
00108    if (select(sockfd + 1, &descriptors, NULL, NULL, &timeout) > 0) {
00109       err = DNSServiceProcessResult(entry->bonjourRef);
00110       if (err != kDNSServiceErr_NoError) {
00111          XrdLog.Emsg("OucBonjour", err, "process the registration procedure");
00112          return -1;
00113       }
00114    } else {
00115       XrdLog.Emsg("OucBonjour", err, "wait to the registration response");
00116       return -1;
00117    }
00118 
00119    ListOfRegistrations.push_back(entry);
00120    return 0;
00121 }
00122 
00123 /******************************************************************************/
00124 /*           B o n j o u r   s e r v i c e s : d i s c o v e r y              */
00125 /******************************************************************************/
00126 
00127 void * XrdOucAppleBonjour::BrowseEventLoopThread(void * context)
00128 {
00129    int sockfd;
00130    int selRes;
00131    int stopEventLoop = 0;
00132    DNSServiceRef serviceRef;
00133    DNSServiceErrorType err;
00134    fd_set descriptors;
00135    XrdOucBonjourSubscribedEntry * callbackID;
00136 
00137    callbackID = (XrdOucBonjourSubscribedEntry *)context;
00138 
00139    // This thread is responsible for receiving the updates from the mDNS daemon.
00140    // We are using a select() based event-loop because is more extensible if we
00141    // would like to register for more services than one.
00142    XrdSysThread::SetCancelOn();
00143    XrdSysThread::SetCancelAsynchronous();
00144 
00145    // Start the DNS service browsing on the network.
00146    err = DNSServiceBrowse(&serviceRef,
00147                           0,
00148                           0,
00149                           callbackID->serviceType->c_str(),
00150                           NULL,
00151                           BrowseReply,
00152                           context);
00153 
00154    // If there are no errors, launch the event loop.
00155    if (err != kDNSServiceErr_NoError) {
00156       XrdLog.Emsg("OucBonjour", err, "launch the discovery process");
00157       return NULL; // Thread ends.
00158    }
00159 
00160    // Launch the event loop.
00161    sockfd = DNSServiceRefSockFD(serviceRef);
00162 
00163    while (!stopEventLoop) {
00164       // Set the fd_set to start select()
00165       FD_ZERO(&descriptors);
00166       FD_SET(sockfd, &descriptors);
00167 
00168       // Wait until there is something to do
00169       selRes = select(sockfd + 1, &descriptors, NULL, NULL, NULL);
00170 
00171       // Process the new result.
00172       if (selRes > 0) {
00173          if (FD_ISSET(sockfd, &descriptors)) {
00174             // This function will call the appropiate callback to process the
00175             // event, in this case the BrowseReply static method.
00176             err = DNSServiceProcessResult(serviceRef);
00177             if (err != kDNSServiceErr_NoError) {
00178                XrdLog.Emsg("OucBonjour", err, "process an event in event loop");
00179                stopEventLoop = 1;
00180             }
00181          }
00182       } else if (selRes == -1) {
00183          XrdLog.Emsg("OucBonjour", "The select() call failed");
00184          stopEventLoop = 1;
00185       }
00186    }
00187 
00188    XrdLog.Emsg("OucBonjour", "The browsing event loop has ended unexpectedly");
00189    return NULL; // Thread ends.
00190 }
00191 
00192 void XrdOucAppleBonjour::BrowseReply(DNSServiceRef ref, DNSServiceFlags flags,
00193                                      uint32_t interfaceIndex, DNSServiceErrorType error,
00194                                      const char * name, const char * regtype,
00195                                      const char * domain, void * context)
00196 {
00197    // Comes with the context parameter to avoid abusing of getInstance()
00198    XrdOucAppleBonjour *instance;
00199    XrdOucBonjourNode *node;
00200    XrdOucBonjourSubscribedEntry * callbackID;
00201    XrdOucBonjourResolutionEntry * nodeAndCallback;
00202 
00203    callbackID = (XrdOucBonjourSubscribedEntry *)context;
00204 
00205    if (error != kDNSServiceErr_NoError) {
00206       XrdLog.Emsg("OucBonjour", error, "complete the browse callback");
00207       return;
00208    }
00209 
00210    // Get the context (the XrdOucBonjour object which holds the lists of nodes).
00211    instance = &XrdOucAppleBonjour::getInstance();
00212 
00213    // Process the node. First let us know what type of update is.
00214    if (flags & kDNSServiceFlagsAdd) {
00215       // ADD a new node to the list.
00216       node = new XrdOucBonjourNode(name, regtype, domain);
00217       nodeAndCallback = (XrdOucBonjourResolutionEntry *)malloc(sizeof(XrdOucBonjourResolutionEntry));
00218       nodeAndCallback->node = node;
00219       nodeAndCallback->callbackID = callbackID;
00220 
00221       // Start resolution of the name.
00222       instance->ResolveNodeInformation(nodeAndCallback);
00223 
00224       // We are going to wait to add the node until it is completely resolved.
00225       //instance->LockNodeList();
00226       //instance->ListOfNodes.push_back(node);
00227       //instance->UnLockNodeList();
00228 
00229       XrdLog.Say("------ XrdOucBonjour: discovered a new node: ", name);
00230    } else {
00231       // REMOVE this node from the list.
00232       XrdOucAppleBonjourSearchNode predicate(name);
00233 
00234       instance->LockNodeList();
00235       instance->ListOfNodes.remove_if(predicate);
00236       instance->UnLockNodeList();
00237 
00238       XrdLog.Say("------ XrdOucBonjour: the node ", name, " went out the network");
00239 
00240       // Notify updates if there wont be more updates in a short period of time.
00241       if (!(flags & kDNSServiceFlagsMoreComing))
00242          callbackID->callback(callbackID->context);
00243    }
00244 }
00245 
00246 /******************************************************************************/
00247 /*       B o n j o u r   s e r v i c e s : n o t i f i c a t i o n s          */
00248 /******************************************************************************/
00249 
00250 int XrdOucAppleBonjour::SubscribeForUpdates(const char * servicetype,
00251                                             XrdOucBonjourUpdateCallback callback,
00252                                             void * context)
00253 {
00254    pthread_t thread;
00255    XrdOucBonjourSubscribedEntry * callbackID = (XrdOucBonjourSubscribedEntry *)malloc(sizeof(XrdOucBonjourSubscribedEntry));
00256    callbackID->callback = callback;
00257    callbackID->context = context;
00258    callbackID->serviceType = new XrdOucString(servicetype);
00259 
00260    // Lauch the new browsing thread.
00261    return XrdSysThread::Run(&thread, BrowseEventLoopThread, callbackID);
00262 }
00263 
00264 /******************************************************************************/
00265 /*          B o n j o u r   s e r v i c e s : r e s o l u t i o n             */
00266 /******************************************************************************/
00267 
00268 void XrdOucAppleBonjour::ResolveReply(DNSServiceRef ref, DNSServiceFlags flags,
00269                                       uint32_t interfaceIndex, DNSServiceErrorType error,
00270                                       const char * fullname, const char * hostname,
00271                                       uint16_t port, uint16_t txtLen,
00272                                       const unsigned char * txtVal, void * context)
00273 {
00274    XrdOucAppleBonjour * instance;
00275    XrdOucBonjourResolutionEntry * nodeAndCallback;
00276 
00277    if (error != kDNSServiceErr_NoError) {
00278       XrdLog.Emsg("OucBonjour", error, "complete the resolve callback");
00279       return;
00280    }
00281 
00282    nodeAndCallback = static_cast<XrdOucBonjourResolutionEntry *>(context);
00283 
00284    // Copy the information of resolution results to the node.
00285    nodeAndCallback->node->SetHostName(hostname);
00286    nodeAndCallback->node->SetPort(ntohs(port));
00287 
00288    // Also, copy the TXT values.
00289    nodeAndCallback->node->GetBonjourRecord().AddRawTXTRecord((const char *)txtVal);
00290 
00291    // Get the context (the XrdOucBonjour object which holds the lists of nodes).
00292    instance = &XrdOucAppleBonjour::getInstance();
00293 
00294    // We are ready to add the node to the list and invoke the callback.
00295    instance->LockNodeList();
00296    instance->ListOfNodes.push_back(nodeAndCallback->node);
00297    instance->UnLockNodeList();
00298 
00299    // Notify updates if there wont be more updates in a short period of time.
00300    if (!(flags & kDNSServiceFlagsMoreComing))
00301       nodeAndCallback->callbackID->callback(nodeAndCallback->callbackID->context);
00302 
00303    free(nodeAndCallback);
00304 }
00305 
00306 int XrdOucAppleBonjour::ResolveNodeInformation(XrdOucBonjourResolutionEntry * nodeAndCallback)
00307 {
00308    DNSServiceErrorType err;
00309    DNSServiceRef serviceRef;
00310    fd_set descriptors;
00311    int sockfd;
00312    struct timeval timeout;
00313 
00314    // Launch the resolution procedure.
00315    err = DNSServiceResolve(&serviceRef,
00316                            0,
00317                            0,
00318                            nodeAndCallback->node->GetBonjourRecord().GetServiceName(),
00319                            nodeAndCallback->node->GetBonjourRecord().GetRegisteredType(),
00320                            nodeAndCallback->node->GetBonjourRecord().GetReplyDomain(),
00321                            ResolveReply,
00322                            nodeAndCallback);
00323 
00324    // Check for errors
00325    if (err != kDNSServiceErr_NoError) {
00326       XrdLog.Emsg("OucBonjour", err, "start the resolution procedure");
00327       return -1;
00328    }
00329 
00330    // Wait for the callback. CAUTION: this call is blocking and may stuck you
00331    // thread. This will call the callback function.
00332    sockfd = DNSServiceRefSockFD(serviceRef);
00333    // Set the fd_set to start select()
00334    FD_ZERO(&descriptors);
00335    FD_SET(sockfd, &descriptors);
00336    timeout.tv_sec = TIMEOUT;
00337    timeout.tv_usec = 0;
00338    // Wait for the response.
00339    if (select(sockfd + 1, &descriptors, NULL, NULL, &timeout) > 0) {
00340       err = DNSServiceProcessResult(serviceRef);
00341       // Cancel the resolution process since we must have the data yet.
00342       DNSServiceRefDeallocate(serviceRef);
00343       if (err != kDNSServiceErr_NoError) {
00344          XrdLog.Emsg("OucBonjour", err, "process the resolution procedure");
00345          return -1;
00346       }
00347    } else {
00348       XrdLog.Emsg("OucBonjour", err, "wait to the resolution response");
00349       return -1;
00350    }
00351 
00352    // We finished this OK.
00353    return 0;
00354 }
00355 
00356 /******************************************************************************/
00357 /*        C o n s t r u c t o r s   &   S i n g l e t o n   s t u f f         */
00358 /******************************************************************************/
00359 
00360 bool XrdOucAppleBonjour::XrdOucAppleBonjourSearchNode::operator()(XrdOucBonjourNode * value)
00361 {
00362    return strcmp(value->GetBonjourRecord().GetServiceName(), ServiceName) == 0;
00363 }
00364 
00365 XrdOucAppleBonjour * XrdOucAppleBonjour::_Instance = NULL;
00366 
00367 XrdSysMutex XrdOucAppleBonjour::SingletonMutex;
00368 
00369 XrdOucAppleBonjour::XrdOucAppleBonjour()
00370 {
00371    char *env = new char[22];
00372    strcpy(env, "AVAHI_COMPAT_NOWARN=1");
00373    putenv(env);
00374 }
00375 
00376 XrdOucAppleBonjour::~XrdOucAppleBonjour() { }
00377 
00378 // In this case, to get a portable solution, we are not using any platform
00379 // specific keyword (like volatile on Win), so, to minimize the cost of this
00380 // function (mainly, gaining the lock) is highly recommended that any client of
00381 // this class stores a local reference to the singleton instance in order to
00382 // minimize the number of queries to the lock.
00383 XrdOucAppleBonjour &XrdOucAppleBonjour::getInstance()
00384 {
00385    // At the moment this object is destroyed, the singleton instance will be
00386    // deleted.
00387    static XrdOucAppleBonjourSingletonCleanup cleanGuard;
00388 
00389    SingletonMutex.Lock();
00390    if (!_Instance)
00391       _Instance = new XrdOucAppleBonjour();
00392    SingletonMutex.UnLock();
00393 
00394    return *_Instance;
00395 }
00396 
00397 XrdOucAppleBonjour::XrdOucAppleBonjourSingletonCleanup::~XrdOucAppleBonjourSingletonCleanup()
00398 {
00399    SingletonMutex.Lock();
00400    if (_Instance) {
00401       delete XrdOucAppleBonjour::_Instance;
00402       XrdOucAppleBonjour::_Instance = NULL;
00403    }
00404    SingletonMutex.UnLock();
00405 }

Generated on Tue Jul 5 14:46:48 2011 for ROOT_528-00b_version by  doxygen 1.5.1