/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <arpa/inet.h> #include <dirent.h> #include <errno.h> #include <linux/if.h> #include <netdb.h> #include <netinet/in.h> #include <pthread.h> #include <stdlib.h> #include <sys/poll.h> #include <sys/socket.h> #include <sys/types.h> #include <string.h> #include <resolv.h> #define LOG_TAG "MDnsDS" #define DBG 1 #define VDBG 1 #include <cutils/log.h> #include <cutils/properties.h> #include <sysutils/SocketClient.h> #include "MDnsSdListener.h" #include "ResponseCode.h" #include "thread_util.h" #define MDNS_SERVICE_NAME "mdnsd" #define MDNS_SERVICE_STATUS "init.svc.mdnsd" #define CEIL(x, y) (((x) + (y) - 1) / (y)) MDnsSdListener::MDnsSdListener() : FrameworkListener(SOCKET_NAME, true) { Monitor *m = new Monitor(); registerCmd(new Handler(m, this)); } MDnsSdListener::Handler::Handler(Monitor *m, MDnsSdListener *listener) : NetdCommand("mdnssd") { if (DBG) ALOGD("MDnsSdListener::Hander starting up"); mMonitor = m; mListener = listener; } MDnsSdListener::Handler::~Handler() {} void MDnsSdListener::Handler::discover(SocketClient *cli, const char *iface, const char *regType, const char *domain, const int requestId, const int requestFlags) { if (VDBG) { ALOGD("discover(%s, %s, %s, %d, %d)", iface, regType, domain, requestId, requestFlags); } Context *context = new Context(requestId, mListener); DNSServiceRef *ref = mMonitor->allocateServiceRef(requestId, context); if (ref == NULL) { ALOGE("requestId %d already in use during discover call", requestId); cli->sendMsg(ResponseCode::CommandParameterError, "RequestId already in use during discover call", false); return; } if (VDBG) ALOGD("using ref %p", ref); DNSServiceFlags nativeFlags = iToFlags(requestFlags); int interfaceInt = ifaceNameToI(iface); DNSServiceErrorType result = DNSServiceBrowse(ref, nativeFlags, interfaceInt, regType, domain, &MDnsSdListenerDiscoverCallback, context); if (result != kDNSServiceErr_NoError) { ALOGE("Discover request %d got an error from DNSServiceBrowse %d", requestId, result); mMonitor->freeServiceRef(requestId); cli->sendMsg(ResponseCode::CommandParameterError, "Discover request got an error from DNSServiceBrowse", false); return; } mMonitor->startMonitoring(requestId); if (VDBG) ALOGD("discover successful"); cli->sendMsg(ResponseCode::CommandOkay, "Discover operation started", false); return; } void MDnsSdListenerDiscoverCallback(DNSServiceRef /* sdRef */, DNSServiceFlags flags, uint32_t /* interfaceIndex */, DNSServiceErrorType errorCode, const char *serviceName, const char *regType, const char *replyDomain, void *inContext) { MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext); char *msg; int refNumber = context->mRefNumber; if (errorCode != kDNSServiceErr_NoError) { asprintf(&msg, "%d %d", refNumber, errorCode); context->mListener->sendBroadcast(ResponseCode::ServiceDiscoveryFailed, msg, false); if (DBG) ALOGE("discover failure for %d, error= %d", refNumber, errorCode); } else { int respCode; char *quotedServiceName = SocketClient::quoteArg(serviceName); if (flags & kDNSServiceFlagsAdd) { if (VDBG) { ALOGD("Discover found new serviceName %s, regType %s and domain %s for %d", serviceName, regType, replyDomain, refNumber); } respCode = ResponseCode::ServiceDiscoveryServiceAdded; } else { if (VDBG) { ALOGD("Discover lost serviceName %s, regType %s and domain %s for %d", serviceName, regType, replyDomain, refNumber); } respCode = ResponseCode::ServiceDiscoveryServiceRemoved; } asprintf(&msg, "%d %s %s %s", refNumber, quotedServiceName, regType, replyDomain); free(quotedServiceName); context->mListener->sendBroadcast(respCode, msg, false); } free(msg); } void MDnsSdListener::Handler::stop(SocketClient *cli, int argc, char **argv, const char *str) { if (argc != 3) { char *msg; asprintf(&msg, "Invalid number of arguments to %s", str); cli->sendMsg(ResponseCode::CommandParameterError, msg, false); free(msg); return; } int requestId = atoi(argv[2]); DNSServiceRef *ref = mMonitor->lookupServiceRef(requestId); if (ref == NULL) { if (DBG) ALOGE("%s stop used unknown requestId %d", str, requestId); cli->sendMsg(ResponseCode::CommandParameterError, "Unknown requestId", false); return; } if (VDBG) ALOGD("Stopping %s with ref %p", str, ref); DNSServiceRefDeallocate(*ref); mMonitor->freeServiceRef(requestId); char *msg; asprintf(&msg, "%s stopped", str); cli->sendMsg(ResponseCode::CommandOkay, msg, false); free(msg); } void MDnsSdListener::Handler::serviceRegister(SocketClient *cli, int requestId, const char *interfaceName, const char *serviceName, const char *serviceType, const char *domain, const char *host, int port, int txtLen, void *txtRecord) { if (VDBG) { ALOGD("serviceRegister(%d, %s, %s, %s, %s, %s, %d, %d, <binary>)", requestId, interfaceName, serviceName, serviceType, domain, host, port, txtLen); } Context *context = new Context(requestId, mListener); DNSServiceRef *ref = mMonitor->allocateServiceRef(requestId, context); port = htons(port); if (ref == NULL) { ALOGE("requestId %d already in use during register call", requestId); cli->sendMsg(ResponseCode::CommandParameterError, "RequestId already in use during register call", false); return; } DNSServiceFlags nativeFlags = 0; int interfaceInt = ifaceNameToI(interfaceName); DNSServiceErrorType result = DNSServiceRegister(ref, interfaceInt, nativeFlags, serviceName, serviceType, domain, host, port, txtLen, txtRecord, &MDnsSdListenerRegisterCallback, context); if (result != kDNSServiceErr_NoError) { ALOGE("service register request %d got an error from DNSServiceRegister %d", requestId, result); mMonitor->freeServiceRef(requestId); cli->sendMsg(ResponseCode::CommandParameterError, "serviceRegister request got an error from DNSServiceRegister", false); return; } mMonitor->startMonitoring(requestId); if (VDBG) ALOGD("serviceRegister successful"); cli->sendMsg(ResponseCode::CommandOkay, "serviceRegister started", false); return; } void MDnsSdListenerRegisterCallback(DNSServiceRef /* sdRef */, DNSServiceFlags /* flags */, DNSServiceErrorType errorCode, const char *serviceName, const char * /* regType */, const char * /* domain */, void *inContext) { MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext); char *msg; int refNumber = context->mRefNumber; if (errorCode != kDNSServiceErr_NoError) { asprintf(&msg, "%d %d", refNumber, errorCode); context->mListener->sendBroadcast(ResponseCode::ServiceRegistrationFailed, msg, false); if (DBG) ALOGE("register failure for %d, error= %d", refNumber, errorCode); } else { char *quotedServiceName = SocketClient::quoteArg(serviceName); asprintf(&msg, "%d %s", refNumber, quotedServiceName); free(quotedServiceName); context->mListener->sendBroadcast(ResponseCode::ServiceRegistrationSucceeded, msg, false); if (VDBG) ALOGD("register succeeded for %d as %s", refNumber, serviceName); } free(msg); } void MDnsSdListener::Handler::resolveService(SocketClient *cli, int requestId, const char *interfaceName, const char *serviceName, const char *regType, const char *domain) { if (VDBG) { ALOGD("resolveService(%d, %s, %s, %s, %s)", requestId, interfaceName, serviceName, regType, domain); } Context *context = new Context(requestId, mListener); DNSServiceRef *ref = mMonitor->allocateServiceRef(requestId, context); if (ref == NULL) { ALOGE("request Id %d already in use during resolve call", requestId); cli->sendMsg(ResponseCode::CommandParameterError, "RequestId already in use during resolve call", false); return; } DNSServiceFlags nativeFlags = 0; int interfaceInt = ifaceNameToI(interfaceName); DNSServiceErrorType result = DNSServiceResolve(ref, nativeFlags, interfaceInt, serviceName, regType, domain, &MDnsSdListenerResolveCallback, context); if (result != kDNSServiceErr_NoError) { ALOGE("service resolve request %d got an error from DNSServiceResolve %d", requestId, result); mMonitor->freeServiceRef(requestId); cli->sendMsg(ResponseCode::CommandParameterError, "resolveService got an error from DNSServiceResolve", false); return; } mMonitor->startMonitoring(requestId); if (VDBG) ALOGD("resolveService successful"); cli->sendMsg(ResponseCode::CommandOkay, "resolveService started", false); return; } void MDnsSdListenerResolveCallback(DNSServiceRef /* sdRef */, DNSServiceFlags /* flags */, uint32_t /* interface */, DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord , void *inContext) { MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext); char *msg; int refNumber = context->mRefNumber; port = ntohs(port); if (errorCode != kDNSServiceErr_NoError) { asprintf(&msg, "%d %d", refNumber, errorCode); context->mListener->sendBroadcast(ResponseCode::ServiceResolveFailed, msg, false); if (DBG) ALOGE("resolve failure for %d, error= %d", refNumber, errorCode); } else { char *quotedFullName = SocketClient::quoteArg(fullname); char *quotedHostTarget = SocketClient::quoteArg(hosttarget); // Base 64 encodes every 3 bytes into 4 characters, but then adds padding to the next // multiple of 4 and a \0 size_t dstLength = CEIL(CEIL(txtLen * 4, 3), 4) * 4 + 1; char *dst = (char *)malloc(dstLength); b64_ntop(txtRecord, txtLen, dst, dstLength); asprintf(&msg, "%d %s %s %d %d \"%s\"", refNumber, quotedFullName, quotedHostTarget, port, txtLen, dst); free(quotedFullName); free(quotedHostTarget); free(dst); context->mListener->sendBroadcast(ResponseCode::ServiceResolveSuccess, msg, false); if (VDBG) { ALOGD("resolve succeeded for %d finding %s at %s:%d with txtLen %d", refNumber, fullname, hosttarget, port, txtLen); } } free(msg); } void MDnsSdListener::Handler::getAddrInfo(SocketClient *cli, int requestId, const char *interfaceName, uint32_t protocol, const char *hostname) { if (VDBG) ALOGD("getAddrInfo(%d, %s %d, %s)", requestId, interfaceName, protocol, hostname); Context *context = new Context(requestId, mListener); DNSServiceRef *ref = mMonitor->allocateServiceRef(requestId, context); if (ref == NULL) { ALOGE("request ID %d already in use during getAddrInfo call", requestId); cli->sendMsg(ResponseCode::CommandParameterError, "RequestId already in use during getAddrInfo call", false); return; } DNSServiceFlags nativeFlags = 0; int interfaceInt = ifaceNameToI(interfaceName); DNSServiceErrorType result = DNSServiceGetAddrInfo(ref, nativeFlags, interfaceInt, protocol, hostname, &MDnsSdListenerGetAddrInfoCallback, context); if (result != kDNSServiceErr_NoError) { ALOGE("getAddrInfo request %d got an error from DNSServiceGetAddrInfo %d", requestId, result); mMonitor->freeServiceRef(requestId); cli->sendMsg(ResponseCode::CommandParameterError, "getAddrInfo request got an error from DNSServiceGetAddrInfo", false); return; } mMonitor->startMonitoring(requestId); if (VDBG) ALOGD("getAddrInfo successful"); cli->sendMsg(ResponseCode::CommandOkay, "getAddrInfo started", false); return; } void MDnsSdListenerGetAddrInfoCallback(DNSServiceRef /* sdRef */, DNSServiceFlags /* flags */, uint32_t /* interface */, DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *const sa, uint32_t ttl, void *inContext) { MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext); int refNumber = context->mRefNumber; if (errorCode != kDNSServiceErr_NoError) { char *msg; asprintf(&msg, "%d %d", refNumber, errorCode); context->mListener->sendBroadcast(ResponseCode::ServiceGetAddrInfoFailed, msg, false); if (DBG) ALOGE("getAddrInfo failure for %d, error= %d", refNumber, errorCode); free(msg); } else { char addr[INET6_ADDRSTRLEN]; char *msg; char *quotedHostname = SocketClient::quoteArg(hostname); if (sa->sa_family == AF_INET) { inet_ntop(sa->sa_family, &(((struct sockaddr_in *)sa)->sin_addr), addr, sizeof(addr)); } else { inet_ntop(sa->sa_family, &(((struct sockaddr_in6 *)sa)->sin6_addr), addr, sizeof(addr)); } asprintf(&msg, "%d %s %d %s", refNumber, quotedHostname, ttl, addr); free(quotedHostname); context->mListener->sendBroadcast(ResponseCode::ServiceGetAddrInfoSuccess, msg, false); if (VDBG) { ALOGD("getAddrInfo succeeded for %d: %s", refNumber, msg); } free(msg); } } void MDnsSdListener::Handler::setHostname(SocketClient *cli, int requestId, const char *hostname) { if (VDBG) ALOGD("setHostname(%d, %s)", requestId, hostname); Context *context = new Context(requestId, mListener); DNSServiceRef *ref = mMonitor->allocateServiceRef(requestId, context); if (ref == NULL) { ALOGE("request Id %d already in use during setHostname call", requestId); cli->sendMsg(ResponseCode::CommandParameterError, "RequestId already in use during setHostname call", false); return; } DNSServiceFlags nativeFlags = 0; DNSServiceErrorType result = DNSSetHostname(ref, nativeFlags, hostname, &MDnsSdListenerSetHostnameCallback, context); if (result != kDNSServiceErr_NoError) { ALOGE("setHostname request %d got an error from DNSSetHostname %d", requestId, result); mMonitor->freeServiceRef(requestId); cli->sendMsg(ResponseCode::CommandParameterError, "setHostname got an error from DNSSetHostname", false); return; } mMonitor->startMonitoring(requestId); if (VDBG) ALOGD("setHostname successful"); cli->sendMsg(ResponseCode::CommandOkay, "setHostname started", false); return; } void MDnsSdListenerSetHostnameCallback(DNSServiceRef /* sdRef */, DNSServiceFlags /* flags */, DNSServiceErrorType errorCode, const char *hostname, void *inContext) { MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext); char *msg; int refNumber = context->mRefNumber; if (errorCode != kDNSServiceErr_NoError) { asprintf(&msg, "%d %d", refNumber, errorCode); context->mListener->sendBroadcast(ResponseCode::ServiceSetHostnameFailed, msg, false); if (DBG) ALOGE("setHostname failure for %d, error= %d", refNumber, errorCode); } else { char *quotedHostname = SocketClient::quoteArg(hostname); asprintf(&msg, "%d %s", refNumber, quotedHostname); free(quotedHostname); context->mListener->sendBroadcast(ResponseCode::ServiceSetHostnameSuccess, msg, false); if (VDBG) ALOGD("setHostname succeeded for %d. Set to %s", refNumber, hostname); } free(msg); } int MDnsSdListener::Handler::ifaceNameToI(const char * /* iface */) { return 0; } const char *MDnsSdListener::Handler::iToIfaceName(int /* i */) { return NULL; } DNSServiceFlags MDnsSdListener::Handler::iToFlags(int /* i */) { return 0; } int MDnsSdListener::Handler::flagsToI(DNSServiceFlags /* flags */) { return 0; } int MDnsSdListener::Handler::runCommand(SocketClient *cli, int argc, char **argv) { if (argc < 2) { char* msg = NULL; asprintf( &msg, "Invalid number of arguments to mdnssd: %i", argc); ALOGW("%s", msg); cli->sendMsg(ResponseCode::CommandParameterError, msg, false); free(msg); return -1; } char* cmd = argv[1]; if (strcmp(cmd, "discover") == 0) { if (argc != 4) { cli->sendMsg(ResponseCode::CommandParameterError, "Invalid number of arguments to mdnssd discover", false); return 0; } int requestId = atoi(argv[2]); char *serviceType = argv[3]; discover(cli, NULL, serviceType, NULL, requestId, 0); } else if (strcmp(cmd, "stop-discover") == 0) { stop(cli, argc, argv, "discover"); } else if (strcmp(cmd, "register") == 0) { if (argc != 7) { cli->sendMsg(ResponseCode::CommandParameterError, "Invalid number of arguments to mdnssd register", false); return 0; } int requestId = atoi(argv[2]); char *serviceName = argv[3]; char *serviceType = argv[4]; int port = atoi(argv[5]); char *interfaceName = NULL; // will use all char *domain = NULL; // will use default char *host = NULL; // will use default hostname // TXT record length is <= 1300, see NsdServiceInfo.setAttribute char dst[1300]; int length = b64_pton(argv[6], (u_char *)dst, 1300); if (length < 0) { cli->sendMsg(ResponseCode::CommandParameterError, "Could not decode txtRecord", false); return 0; } serviceRegister(cli, requestId, interfaceName, serviceName, serviceType, domain, host, port, length, dst); } else if (strcmp(cmd, "stop-register") == 0) { stop(cli, argc, argv, "register"); } else if (strcmp(cmd, "resolve") == 0) { if (argc != 6) { cli->sendMsg(ResponseCode::CommandParameterError, "Invalid number of arguments to mdnssd resolve", false); return 0; } int requestId = atoi(argv[2]); char *interfaceName = NULL; // will use all char *serviceName = argv[3]; char *regType = argv[4]; char *domain = argv[5]; resolveService(cli, requestId, interfaceName, serviceName, regType, domain); } else if (strcmp(cmd, "stop-resolve") == 0) { stop(cli, argc, argv, "resolve"); } else if (strcmp(cmd, "start-service") == 0) { if (mMonitor->startService()) { cli->sendMsg(ResponseCode::CommandOkay, "Service Started", false); } else { cli->sendMsg(ResponseCode::ServiceStartFailed, "Service already running", false); } } else if (strcmp(cmd, "stop-service") == 0) { if (mMonitor->stopService()) { cli->sendMsg(ResponseCode::CommandOkay, "Service Stopped", false); } else { cli->sendMsg(ResponseCode::ServiceStopFailed, "Service still in use", false); } } else if (strcmp(cmd, "sethostname") == 0) { if (argc != 4) { cli->sendMsg(ResponseCode::CommandParameterError, "Invalid number of arguments to mdnssd sethostname", false); return 0; } int requestId = atoi(argv[2]); char *hostname = argv[3]; setHostname(cli, requestId, hostname); } else if (strcmp(cmd, "stop-sethostname") == 0) { stop(cli, argc, argv, "sethostname"); } else if (strcmp(cmd, "getaddrinfo") == 0) { if (argc != 4) { cli->sendMsg(ResponseCode::CommandParameterError, "Invalid number of arguments to mdnssd getaddrinfo", false); return 0; } int requestId = atoi(argv[2]); char *hostname = argv[3]; char *interfaceName = NULL; // default int protocol = 0; // intelligient heuristic (both v4 + v6) getAddrInfo(cli, requestId, interfaceName, protocol, hostname); } else if (strcmp(cmd, "stop-getaddrinfo") == 0) { stop(cli, argc, argv, "getaddrinfo"); } else { if (VDBG) ALOGE("Unknown cmd %s", cmd); cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown mdnssd cmd", false); return 0; } return 0; } MDnsSdListener::Monitor::Monitor() { mHead = NULL; mLiveCount = 0; mPollFds = NULL; mPollRefs = NULL; mPollSize = 10; socketpair(AF_LOCAL, SOCK_STREAM, 0, mCtrlSocketPair); pthread_mutex_init(&mHeadMutex, NULL); const int rval = ::android::net::threadLaunch(this); if (rval != 0) { ALOGW("Error spawning monitor thread: %s (%d)", strerror(-rval), -rval); } } #define NAP_TIME 200 // 200 ms between polls static int wait_for_property(const char *name, const char *desired_value, int maxwait) { char value[PROPERTY_VALUE_MAX] = {'\0'}; int maxnaps = (maxwait * 1000) / NAP_TIME; if (maxnaps < 1) { maxnaps = 1; } while (maxnaps-- > 0) { usleep(NAP_TIME * 1000); if (property_get(name, value, NULL)) { if (desired_value == NULL || strcmp(value, desired_value) == 0) { return 0; } } } return -1; /* failure */ } int MDnsSdListener::Monitor::startService() { int result = 0; char property_value[PROPERTY_VALUE_MAX]; pthread_mutex_lock(&mHeadMutex); property_get(MDNS_SERVICE_STATUS, property_value, ""); if (strcmp("running", property_value) != 0) { ALOGD("Starting MDNSD"); property_set("ctl.start", MDNS_SERVICE_NAME); wait_for_property(MDNS_SERVICE_STATUS, "running", 5); result = -1; } else { result = 0; } pthread_mutex_unlock(&mHeadMutex); return result; } int MDnsSdListener::Monitor::stopService() { int result = 0; pthread_mutex_lock(&mHeadMutex); if (mHead == NULL) { ALOGD("Stopping MDNSD"); property_set("ctl.stop", MDNS_SERVICE_NAME); wait_for_property(MDNS_SERVICE_STATUS, "stopped", 5); result = -1; } else { result = 0; } pthread_mutex_unlock(&mHeadMutex); return result; } void MDnsSdListener::Monitor::run() { int pollCount = 1; mPollFds = (struct pollfd *)calloc(sizeof(struct pollfd), mPollSize); mPollRefs = (DNSServiceRef **)calloc(sizeof(DNSServiceRef *), mPollSize); LOG_ALWAYS_FATAL_IF((mPollFds == NULL), "initial calloc failed on mPollFds with a size of %d", ((int)sizeof(struct pollfd)) * mPollSize); LOG_ALWAYS_FATAL_IF((mPollRefs == NULL), "initial calloc failed on mPollRefs with a size of %d", ((int)sizeof(DNSServiceRef *)) * mPollSize); mPollFds[0].fd = mCtrlSocketPair[0]; mPollFds[0].events = POLLIN; if (VDBG) ALOGD("MDnsSdListener starting to monitor"); while (1) { if (VDBG) ALOGD("Going to poll with pollCount %d", pollCount); int pollResults = poll(mPollFds, pollCount, 10000000); if (pollResults < 0) { ALOGE("Error in poll - got %d", errno); } else if (pollResults > 0) { if (VDBG) ALOGD("Monitor poll got data pollCount = %d, %d", pollCount, pollResults); for(int i = 1; i < pollCount; i++) { if (mPollFds[i].revents != 0) { if (VDBG) { ALOGD("Monitor found [%d].revents = %d - calling ProcessResults", i, mPollFds[i].revents); } DNSServiceProcessResult(*(mPollRefs[i])); mPollFds[i].revents = 0; } } if (VDBG) ALOGD("controlSocket shows revent= %d", mPollFds[0].revents); switch (mPollFds[0].revents) { case POLLIN: { char readBuf[2]; read(mCtrlSocketPair[0], &readBuf, 1); if (DBG) ALOGD("MDnsSdListener::Monitor got %c", readBuf[0]); if (memcmp(RESCAN, readBuf, 1) == 0) { pollCount = rescan(); } } } mPollFds[0].revents = 0; } else { if (VDBG) ALOGD("MDnsSdListener::Monitor poll timed out"); } } free(mPollFds); free(mPollRefs); } #define DBG_RESCAN 0 int MDnsSdListener::Monitor::rescan() { // rescan the list from mHead and make new pollfds and serviceRefs if (VDBG) { ALOGD("MDnsSdListener::Monitor poll rescanning - size=%d, live=%d", mPollSize, mLiveCount); } pthread_mutex_lock(&mHeadMutex); Element **prevPtr = &mHead; int i = 1; if (mPollSize <= mLiveCount) { mPollSize = mLiveCount + 5; free(mPollFds); free(mPollRefs); mPollFds = (struct pollfd *)calloc(sizeof(struct pollfd), mPollSize); mPollRefs = (DNSServiceRef **)calloc(sizeof(DNSServiceRef *), mPollSize); LOG_ALWAYS_FATAL_IF((mPollFds == NULL), "calloc failed on mPollFds with a size of %d", ((int)sizeof(struct pollfd)) * mPollSize); LOG_ALWAYS_FATAL_IF((mPollRefs == NULL), "calloc failed on mPollRefs with a size of %d", ((int)sizeof(DNSServiceRef *)) * mPollSize); } else { memset(mPollFds, 0, sizeof(struct pollfd) * mPollSize); memset(mPollRefs, 0, sizeof(DNSServiceRef *) * mPollSize); } mPollFds[0].fd = mCtrlSocketPair[0]; mPollFds[0].events = POLLIN; if (DBG_RESCAN) ALOGD("mHead = %p", mHead); while (*prevPtr != NULL) { if (DBG_RESCAN) ALOGD("checking %p, mReady = %d", *prevPtr, (*prevPtr)->mReady); if ((*prevPtr)->mReady == 1) { int fd = DNSServiceRefSockFD((*prevPtr)->mRef); if (fd != -1) { if (DBG_RESCAN) ALOGD(" adding FD %d", fd); mPollFds[i].fd = fd; mPollFds[i].events = POLLIN; mPollRefs[i] = &((*prevPtr)->mRef); i++; } else { ALOGE("Error retreving socket FD for live ServiceRef"); } prevPtr = &((*prevPtr)->mNext); // advance to the next element } else if ((*prevPtr)->mReady == -1) { if (DBG_RESCAN) ALOGD(" removing %p from play", *prevPtr); Element *cur = *prevPtr; *prevPtr = (cur)->mNext; // change our notion of this element and don't advance delete cur; } else if ((*prevPtr)->mReady == 0) { // Not ready so just skip this node and continue on if (DBG_RESCAN) ALOGD("%p not ready. Continuing.", *prevPtr); prevPtr = &((*prevPtr)->mNext); } } pthread_mutex_unlock(&mHeadMutex); return i; } DNSServiceRef *MDnsSdListener::Monitor::allocateServiceRef(int id, Context *context) { if (lookupServiceRef(id) != NULL) { delete(context); return NULL; } Element *e = new Element(id, context); pthread_mutex_lock(&mHeadMutex); e->mNext = mHead; mHead = e; pthread_mutex_unlock(&mHeadMutex); return &(e->mRef); } DNSServiceRef *MDnsSdListener::Monitor::lookupServiceRef(int id) { pthread_mutex_lock(&mHeadMutex); Element *cur = mHead; while (cur != NULL) { if (cur->mId == id) { DNSServiceRef *result = &(cur->mRef); pthread_mutex_unlock(&mHeadMutex); return result; } cur = cur->mNext; } pthread_mutex_unlock(&mHeadMutex); return NULL; } void MDnsSdListener::Monitor::startMonitoring(int id) { if (VDBG) ALOGD("startMonitoring %d", id); pthread_mutex_lock(&mHeadMutex); Element *cur = mHead; while (cur != NULL) { if (cur->mId == id) { if (DBG_RESCAN) ALOGD("marking %p as ready to be added", cur); mLiveCount++; cur->mReady = 1; pthread_mutex_unlock(&mHeadMutex); write(mCtrlSocketPair[1], RESCAN, 1); // trigger a rescan for a fresh poll if (VDBG) ALOGD("triggering rescan"); return; } cur = cur->mNext; } pthread_mutex_unlock(&mHeadMutex); } void MDnsSdListener::Monitor::freeServiceRef(int id) { if (VDBG) ALOGD("freeServiceRef %d", id); pthread_mutex_lock(&mHeadMutex); Element **prevPtr = &mHead; Element *cur; while (*prevPtr != NULL) { cur = *prevPtr; if (cur->mId == id) { if (DBG_RESCAN) ALOGD("marking %p as ready to be removed", cur); mLiveCount--; if (cur->mReady == 1) { cur->mReady = -1; // tell poll thread to delete cur->mRef = NULL; // do not process further results write(mCtrlSocketPair[1], RESCAN, 1); // trigger a rescan for a fresh poll if (VDBG) ALOGD("triggering rescan"); } else { *prevPtr = cur->mNext; delete cur; } pthread_mutex_unlock(&mHeadMutex); return; } prevPtr = &(cur->mNext); } pthread_mutex_unlock(&mHeadMutex); }