| /* -*- Mode: C; tab-width: 4 -*- |
| * |
| * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. |
| * |
| * 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. |
| */ |
| |
| #if __APPLE__ |
| // In Mac OS X 10.5 and later trying to use the daemon function gives a “‘daemon’ is deprecated” |
| // error, which prevents compilation because we build with "-Werror". |
| // Since this is supposed to be portable cross-platform code, we don't care that daemon is |
| // deprecated on Mac OS X 10.5, so we use this preprocessor trick to eliminate the error message. |
| #define daemon yes_we_know_that_daemon_is_deprecated_in_os_x_10_5_thankyou |
| #endif |
| |
| #include <assert.h> |
| #include <stdio.h> // For printf() |
| #include <stdlib.h> // For exit() etc. |
| #include <string.h> // For strlen() etc. |
| #include <unistd.h> // For select() |
| #include <errno.h> // For errno, EINTR |
| #include <signal.h> |
| #include <fcntl.h> |
| |
| #if __APPLE__ |
| #undef daemon |
| extern int daemon(int, int); |
| #endif |
| |
| #include "mDNSEmbeddedAPI.h"// Defines the interface to the client layer above |
| #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform |
| #include "mDNSUNP.h" // For daemon() |
| |
| #if COMPILER_LIKES_PRAGMA_MARK |
| #pragma mark ***** Globals |
| #endif |
| |
| static mDNS mDNSStorage; // mDNS core uses this to store its globals |
| static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals |
| |
| mDNSexport const char ProgramName[] = "mDNSResponderPosix"; |
| |
| static const char *gProgramName = ProgramName; |
| |
| #if COMPILER_LIKES_PRAGMA_MARK |
| #pragma mark ***** Signals |
| #endif |
| |
| static volatile mDNSBool gReceivedSigUsr1; |
| static volatile mDNSBool gReceivedSigHup; |
| static volatile mDNSBool gStopNow; |
| |
| // We support 4 signals. |
| // |
| // o SIGUSR1 toggles verbose mode on and off in debug builds |
| // o SIGHUP triggers the program to re-read its preferences. |
| // o SIGINT causes an orderly shutdown of the program. |
| // o SIGQUIT causes a somewhat orderly shutdown (direct but dangerous) |
| // o SIGKILL kills us dead (easy to implement :-) |
| // |
| // There are fatal race conditions in our signal handling, but there's not much |
| // we can do about them while remaining within the Posix space. Specifically, |
| // if a signal arrives after we test the globals its sets but before we call |
| // select, the signal will be dropped. The user will have to send the signal |
| // again. Unfortunately, Posix does not have a "sigselect" to atomically |
| // modify the signal mask and start a select. |
| |
| static void HandleSigUsr1(int sigraised) |
| // If we get a SIGUSR1 we toggle the state of the |
| // verbose mode. |
| { |
| assert(sigraised == SIGUSR1); |
| gReceivedSigUsr1 = mDNStrue; |
| } |
| |
| static void HandleSigHup(int sigraised) |
| // A handler for SIGHUP that causes us to break out of the |
| // main event loop when the user kill 1's us. This has the |
| // effect of triggered the main loop to deregister the |
| // current services and re-read the preferences. |
| { |
| assert(sigraised == SIGHUP); |
| gReceivedSigHup = mDNStrue; |
| } |
| |
| static void HandleSigInt(int sigraised) |
| // A handler for SIGINT that causes us to break out of the |
| // main event loop when the user types ^C. This has the |
| // effect of quitting the program. |
| { |
| assert(sigraised == SIGINT); |
| |
| if (gMDNSPlatformPosixVerboseLevel > 0) { |
| fprintf(stderr, "\nSIGINT\n"); |
| } |
| gStopNow = mDNStrue; |
| } |
| |
| static void HandleSigQuit(int sigraised) |
| // If we get a SIGQUIT the user is desperate and we |
| // just call mDNS_Close directly. This is definitely |
| // not safe (because it could reenter mDNS), but |
| // we presume that the user has already tried the safe |
| // alternatives. |
| { |
| assert(sigraised == SIGQUIT); |
| |
| if (gMDNSPlatformPosixVerboseLevel > 0) { |
| fprintf(stderr, "\nSIGQUIT\n"); |
| } |
| mDNS_Close(&mDNSStorage); |
| exit(0); |
| } |
| |
| #if COMPILER_LIKES_PRAGMA_MARK |
| #pragma mark ***** Parameter Checking |
| #endif |
| |
| static mDNSBool CheckThatRichTextNameIsUsable(const char *richTextName, mDNSBool printExplanation) |
| // Checks that richTextName is reasonable |
| // label and, if it isn't and printExplanation is true, prints |
| // an explanation of why not. |
| { |
| mDNSBool result = mDNStrue; |
| if (result && strlen(richTextName) > 63) { |
| if (printExplanation) { |
| fprintf(stderr, |
| "%s: Service name is too long (must be 63 characters or less)\n", |
| gProgramName); |
| } |
| result = mDNSfalse; |
| } |
| if (result && richTextName[0] == 0) { |
| if (printExplanation) { |
| fprintf(stderr, "%s: Service name can't be empty\n", gProgramName); |
| } |
| result = mDNSfalse; |
| } |
| return result; |
| } |
| |
| static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool printExplanation) |
| // Checks that serviceType is a reasonable service type |
| // label and, if it isn't and printExplanation is true, prints |
| // an explanation of why not. |
| { |
| mDNSBool result; |
| |
| result = mDNStrue; |
| if (result && strlen(serviceType) > 63) { |
| if (printExplanation) { |
| fprintf(stderr, |
| "%s: Service type is too long (must be 63 characters or less)\n", |
| gProgramName); |
| } |
| result = mDNSfalse; |
| } |
| if (result && serviceType[0] == 0) { |
| if (printExplanation) { |
| fprintf(stderr, |
| "%s: Service type can't be empty\n", |
| gProgramName); |
| } |
| result = mDNSfalse; |
| } |
| return result; |
| } |
| |
| static mDNSBool CheckThatPortNumberIsUsable(long portNumber, mDNSBool printExplanation) |
| // Checks that portNumber is a reasonable port number |
| // and, if it isn't and printExplanation is true, prints |
| // an explanation of why not. |
| { |
| mDNSBool result; |
| |
| result = mDNStrue; |
| if (result && (portNumber <= 0 || portNumber > 65535)) { |
| if (printExplanation) { |
| fprintf(stderr, |
| "%s: Port number specified by -p must be in range 1..65535\n", |
| gProgramName); |
| } |
| result = mDNSfalse; |
| } |
| return result; |
| } |
| |
| #if COMPILER_LIKES_PRAGMA_MARK |
| #pragma mark ***** Command Line Arguments |
| #endif |
| |
| static const char kDefaultPIDFile[] = "/var/run/mDNSResponder.pid"; |
| static const char kDefaultServiceType[] = "_afpovertcp._tcp."; |
| static const char kDefaultServiceDomain[] = "local."; |
| enum { |
| kDefaultPortNumber = 548 |
| }; |
| |
| static void PrintUsage() |
| { |
| fprintf(stderr, |
| "Usage: %s [-v level ] [-r] [-n name] [-t type] [-d domain] [-p port] [-f file] [-b] [-P pidfile] [-x name=val ...]\n", |
| gProgramName); |
| fprintf(stderr, " -v verbose mode, level is a number from 0 to 2\n"); |
| fprintf(stderr, " 0 = no debugging info (default)\n"); |
| fprintf(stderr, " 1 = standard debugging info\n"); |
| fprintf(stderr, " 2 = intense debugging info\n"); |
| fprintf(stderr, " can be cycled kill -USR1\n"); |
| fprintf(stderr, " -r also bind to port 53 (port 5353 is always bound)\n"); |
| fprintf(stderr, " -n uses 'name' as the service name (required)\n"); |
| fprintf(stderr, " -t uses 'type' as the service type (default is '%s')\n", kDefaultServiceType); |
| fprintf(stderr, " -d uses 'domain' as the service domain (default is '%s')\n", kDefaultServiceDomain); |
| fprintf(stderr, " -p uses 'port' as the port number (default is '%d')\n", kDefaultPortNumber); |
| fprintf(stderr, " -f reads a service list from 'file'\n"); |
| fprintf(stderr, " -b forces daemon (background) mode\n"); |
| fprintf(stderr, " -P uses 'pidfile' as the PID file\n"); |
| fprintf(stderr, " (default is '%s')\n", kDefaultPIDFile); |
| fprintf(stderr, " only meaningful if -b also specified\n"); |
| fprintf(stderr, " -x stores name=val in TXT record (default is empty).\n"); |
| fprintf(stderr, " MUST be the last command-line argument;\n"); |
| fprintf(stderr, " all subsequent arguments after -x are treated as name=val pairs.\n"); |
| } |
| |
| static mDNSBool gAvoidPort53 = mDNStrue; |
| static const char *gServiceName = ""; |
| static const char *gServiceType = kDefaultServiceType; |
| static const char *gServiceDomain = kDefaultServiceDomain; |
| static mDNSu8 gServiceText[sizeof(RDataBody)]; |
| static mDNSu16 gServiceTextLen = 0; |
| static int gPortNumber = kDefaultPortNumber; |
| static const char *gServiceFile = ""; |
| static mDNSBool gDaemon = mDNSfalse; |
| static const char *gPIDFile = kDefaultPIDFile; |
| |
| static void ParseArguments(int argc, char **argv) |
| // Parses our command line arguments into the global variables |
| // listed above. |
| { |
| int ch; |
| |
| // Set gProgramName to the last path component of argv[0] |
| |
| gProgramName = strrchr(argv[0], '/'); |
| if (gProgramName == NULL) { |
| gProgramName = argv[0]; |
| } else { |
| gProgramName += 1; |
| } |
| |
| // Parse command line options using getopt. |
| |
| do { |
| ch = getopt(argc, argv, "v:rn:t:d:p:f:dP:bx"); |
| if (ch != -1) { |
| switch (ch) { |
| case 'v': |
| gMDNSPlatformPosixVerboseLevel = atoi(optarg); |
| if (gMDNSPlatformPosixVerboseLevel < 0 || gMDNSPlatformPosixVerboseLevel > 2) { |
| fprintf(stderr, |
| "%s: Verbose mode must be in the range 0..2\n", |
| gProgramName); |
| exit(1); |
| } |
| break; |
| case 'r': |
| gAvoidPort53 = mDNSfalse; |
| break; |
| case 'n': |
| gServiceName = optarg; |
| if ( ! CheckThatRichTextNameIsUsable(gServiceName, mDNStrue) ) { |
| exit(1); |
| } |
| break; |
| case 't': |
| gServiceType = optarg; |
| if ( ! CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) { |
| exit(1); |
| } |
| break; |
| case 'd': |
| gServiceDomain = optarg; |
| break; |
| case 'p': |
| gPortNumber = atol(optarg); |
| if ( ! CheckThatPortNumberIsUsable(gPortNumber, mDNStrue) ) { |
| exit(1); |
| } |
| break; |
| case 'f': |
| gServiceFile = optarg; |
| break; |
| case 'b': |
| gDaemon = mDNStrue; |
| break; |
| case 'P': |
| gPIDFile = optarg; |
| break; |
| case 'x': |
| while (optind < argc) |
| { |
| gServiceText[gServiceTextLen] = strlen(argv[optind]); |
| mDNSPlatformMemCopy(gServiceText+gServiceTextLen+1, argv[optind], gServiceText[gServiceTextLen]); |
| gServiceTextLen += 1 + gServiceText[gServiceTextLen]; |
| optind++; |
| } |
| ch = -1; |
| break; |
| case '?': |
| default: |
| PrintUsage(); |
| exit(1); |
| break; |
| } |
| } |
| } while (ch != -1); |
| |
| // Check for any left over command line arguments. |
| |
| if (optind != argc) { |
| PrintUsage(); |
| fprintf(stderr, "%s: Unexpected argument '%s'\n", gProgramName, argv[optind]); |
| exit(1); |
| } |
| |
| // Check for inconsistency between the arguments. |
| |
| if ( (gServiceName[0] == 0) && (gServiceFile[0] == 0) ) { |
| PrintUsage(); |
| fprintf(stderr, "%s: You must specify a service name to register (-n) or a service file (-f).\n", gProgramName); |
| exit(1); |
| } |
| } |
| |
| #if COMPILER_LIKES_PRAGMA_MARK |
| #pragma mark ***** Registration |
| #endif |
| |
| typedef struct PosixService PosixService; |
| |
| struct PosixService { |
| ServiceRecordSet coreServ; |
| PosixService *next; |
| int serviceID; |
| }; |
| |
| static PosixService *gServiceList = NULL; |
| |
| static void RegistrationCallback(mDNS *const m, ServiceRecordSet *const thisRegistration, mStatus status) |
| // mDNS core calls this routine to tell us about the status of |
| // our registration. The appropriate action to take depends |
| // entirely on the value of status. |
| { |
| switch (status) { |
| |
| case mStatus_NoError: |
| debugf("Callback: %##s Name Registered", thisRegistration->RR_SRV.resrec.name->c); |
| // Do nothing; our name was successfully registered. We may |
| // get more call backs in the future. |
| break; |
| |
| case mStatus_NameConflict: |
| debugf("Callback: %##s Name Conflict", thisRegistration->RR_SRV.resrec.name->c); |
| |
| // In the event of a conflict, this sample RegistrationCallback |
| // just calls mDNS_RenameAndReregisterService to automatically |
| // pick a new unique name for the service. For a device such as a |
| // printer, this may be appropriate. For a device with a user |
| // interface, and a screen, and a keyboard, the appropriate response |
| // may be to prompt the user and ask them to choose a new name for |
| // the service. |
| // |
| // Also, what do we do if mDNS_RenameAndReregisterService returns an |
| // error. Right now I have no place to send that error to. |
| |
| status = mDNS_RenameAndReregisterService(m, thisRegistration, mDNSNULL); |
| assert(status == mStatus_NoError); |
| break; |
| |
| case mStatus_MemFree: |
| debugf("Callback: %##s Memory Free", thisRegistration->RR_SRV.resrec.name->c); |
| |
| // When debugging is enabled, make sure that thisRegistration |
| // is not on our gServiceList. |
| |
| #if !defined(NDEBUG) |
| { |
| PosixService *cursor; |
| |
| cursor = gServiceList; |
| while (cursor != NULL) { |
| assert(&cursor->coreServ != thisRegistration); |
| cursor = cursor->next; |
| } |
| } |
| #endif |
| free(thisRegistration); |
| break; |
| |
| default: |
| debugf("Callback: %##s Unknown Status %ld", thisRegistration->RR_SRV.resrec.name->c, status); |
| break; |
| } |
| } |
| |
| static int gServiceID = 0; |
| |
| static mStatus RegisterOneService(const char * richTextName, |
| const char * serviceType, |
| const char * serviceDomain, |
| const mDNSu8 text[], |
| mDNSu16 textLen, |
| long portNumber) |
| { |
| mStatus status; |
| PosixService * thisServ; |
| domainlabel name; |
| domainname type; |
| domainname domain; |
| |
| status = mStatus_NoError; |
| thisServ = (PosixService *) malloc(sizeof(*thisServ)); |
| if (thisServ == NULL) { |
| status = mStatus_NoMemoryErr; |
| } |
| if (status == mStatus_NoError) { |
| MakeDomainLabelFromLiteralString(&name, richTextName); |
| MakeDomainNameFromDNSNameString(&type, serviceType); |
| MakeDomainNameFromDNSNameString(&domain, serviceDomain); |
| status = mDNS_RegisterService(&mDNSStorage, &thisServ->coreServ, |
| &name, &type, &domain, // Name, type, domain |
| NULL, mDNSOpaque16fromIntVal(portNumber), |
| text, textLen, // TXT data, length |
| NULL, 0, // Subtypes |
| mDNSInterface_Any, // Interface ID |
| RegistrationCallback, thisServ, 0); // Callback, context, flags |
| } |
| if (status == mStatus_NoError) { |
| thisServ->serviceID = gServiceID; |
| gServiceID += 1; |
| |
| thisServ->next = gServiceList; |
| gServiceList = thisServ; |
| |
| if (gMDNSPlatformPosixVerboseLevel > 0) { |
| fprintf(stderr, |
| "%s: Registered service %d, name \"%s\", type \"%s\", domain \"%s\", port %ld\n", |
| gProgramName, |
| thisServ->serviceID, |
| richTextName, |
| serviceType, |
| serviceDomain, |
| portNumber); |
| } |
| } else { |
| if (thisServ != NULL) { |
| free(thisServ); |
| } |
| } |
| return status; |
| } |
| |
| static mDNSBool ReadALine(char *buf, size_t bufSize, FILE *fp, mDNSBool skipBlankLines) |
| { |
| size_t len; |
| mDNSBool readNextLine; |
| |
| do { |
| readNextLine = mDNSfalse; |
| |
| if (fgets(buf, bufSize, fp) == NULL) |
| return mDNSfalse; // encountered EOF or an error condition |
| |
| // These first characters indicate a blank line. |
| if (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\r' || buf[0] == '\n') { |
| if (!skipBlankLines) |
| return mDNSfalse; |
| readNextLine = mDNStrue; |
| } |
| // always skip comment lines |
| if (buf[0] == '#') |
| readNextLine = mDNStrue; |
| |
| } while (readNextLine); |
| |
| len = strlen( buf); |
| if ( buf[len - 1] == '\r' || buf[len - 1] == '\n') |
| buf[len - 1] = '\0'; |
| |
| return mDNStrue; |
| } |
| |
| static mStatus RegisterServicesInFile(const char *filePath) |
| { |
| mStatus status = mStatus_NoError; |
| FILE * fp = fopen(filePath, "r"); |
| |
| if (fp == NULL) { |
| return mStatus_UnknownErr; |
| } |
| |
| if (gMDNSPlatformPosixVerboseLevel > 1) |
| fprintf(stderr, "Parsing %s for services\n", filePath); |
| |
| do { |
| char nameBuf[256]; |
| char * name = nameBuf; |
| char type[256]; |
| const char *dom = kDefaultServiceDomain; |
| char rawText[1024]; |
| mDNSu8 text[sizeof(RDataBody)]; |
| unsigned int textLen = 0; |
| char port[256]; |
| char *p; |
| |
| // Read the service name, type, port, and optional text record fields. |
| // Skip blank lines while looking for the next service name. |
| if (! ReadALine(name, sizeof(nameBuf), fp, mDNStrue)) |
| break; |
| |
| // Special case that allows service name to begin with a '#' |
| // character by escaping it with a '\' to distiguish it from |
| // a comment line. Remove the leading '\' here before |
| // registering the service. |
| if (name[0] == '\\' && name[1] == '#') |
| name++; |
| |
| if (gMDNSPlatformPosixVerboseLevel > 1) |
| fprintf(stderr, "Service name: \"%s\"\n", name); |
| |
| // Don't skip blank lines in calls to ReadAline() after finding the |
| // service name since the next blank line indicates the end |
| // of this service record. |
| if (! ReadALine(type, sizeof(type), fp, mDNSfalse)) |
| break; |
| |
| // see if a domain name is specified |
| p = type; |
| while (*p && *p != ' ' && *p != '\t') p++; |
| if (*p) { |
| *p = 0; // NULL terminate the <type>.<protocol> string |
| // skip any leading whitespace before domain name |
| p++; |
| while (*p && (*p == ' ' || *p == '\t')) p++; |
| if (*p) |
| dom = p; |
| } |
| if (gMDNSPlatformPosixVerboseLevel > 1) { |
| fprintf(stderr, "Service type: \"%s\"\n", type); |
| fprintf(stderr, "Service domain: \"%s\"\n", dom); |
| } |
| |
| if (! ReadALine(port, sizeof(port), fp, mDNSfalse)) |
| break; |
| if (gMDNSPlatformPosixVerboseLevel > 1) |
| fprintf(stderr, "Service port: %s\n", port); |
| |
| if ( ! CheckThatRichTextNameIsUsable(name, mDNStrue) |
| || ! CheckThatServiceTypeIsUsable(type, mDNStrue) |
| || ! CheckThatPortNumberIsUsable(atol(port), mDNStrue)) |
| break; |
| |
| // read the TXT record fields |
| while (1) { |
| int len; |
| if (!ReadALine(rawText, sizeof(rawText), fp, mDNSfalse)) break; |
| if (gMDNSPlatformPosixVerboseLevel > 1) |
| fprintf(stderr, "Text string: \"%s\"\n", rawText); |
| len = strlen(rawText); |
| if (len <= 255) |
| { |
| unsigned int newlen = textLen + 1 + len; |
| if (len == 0 || newlen >= sizeof(text)) break; |
| text[textLen] = len; |
| mDNSPlatformMemCopy(text + textLen + 1, rawText, len); |
| textLen = newlen; |
| } |
| else |
| fprintf(stderr, "%s: TXT attribute too long for name = %s, type = %s, port = %s\n", |
| gProgramName, name, type, port); |
| } |
| |
| status = RegisterOneService(name, type, dom, text, textLen, atol(port)); |
| if (status != mStatus_NoError) { |
| // print error, but try to read and register other services in the file |
| fprintf(stderr, "%s: Failed to register service, name \"%s\", type \"%s\", domain \"%s\", port %s\n", |
| gProgramName, name, type, dom, port); |
| } |
| |
| } while (!feof(fp)); |
| |
| if (!feof(fp)) { |
| fprintf(stderr, "%s: Error reading service file %s\n", gProgramName, filePath); |
| status = mStatus_UnknownErr; |
| } |
| |
| { |
| // __ANDROID__ : replaced assert(fclose(..)) |
| int fp_closed = fclose(fp); |
| assert(0 == fp_closed); |
| } |
| |
| return status; |
| } |
| |
| static mStatus RegisterOurServices(void) |
| { |
| mStatus status; |
| |
| status = mStatus_NoError; |
| if (gServiceName[0] != 0) { |
| status = RegisterOneService(gServiceName, |
| gServiceType, |
| gServiceDomain, |
| gServiceText, gServiceTextLen, |
| gPortNumber); |
| } |
| if (status == mStatus_NoError && gServiceFile[0] != 0) { |
| status = RegisterServicesInFile(gServiceFile); |
| } |
| return status; |
| } |
| |
| static void DeregisterOurServices(void) |
| { |
| PosixService *thisServ; |
| int thisServID; |
| |
| while (gServiceList != NULL) { |
| thisServ = gServiceList; |
| gServiceList = thisServ->next; |
| |
| thisServID = thisServ->serviceID; |
| |
| mDNS_DeregisterService(&mDNSStorage, &thisServ->coreServ); |
| |
| if (gMDNSPlatformPosixVerboseLevel > 0) { |
| fprintf(stderr, |
| "%s: Deregistered service %d\n", |
| gProgramName, |
| thisServ->serviceID); |
| } |
| } |
| } |
| |
| #if COMPILER_LIKES_PRAGMA_MARK |
| #pragma mark **** Main |
| #endif |
| |
| int main(int argc, char **argv) |
| { |
| mStatus status; |
| int result; |
| |
| // Parse our command line arguments. This won't come back if there's an error. |
| |
| ParseArguments(argc, argv); |
| |
| // If we're told to run as a daemon, then do that straight away. |
| // Note that we don't treat the inability to create our PID |
| // file as an error. Also note that we assign getpid to a long |
| // because printf has no format specified for pid_t. |
| |
| if (gDaemon) { |
| int result; |
| if (gMDNSPlatformPosixVerboseLevel > 0) { |
| fprintf(stderr, "%s: Starting in daemon mode\n", gProgramName); |
| } |
| result = daemon(0,0); |
| if (result == 0) { |
| FILE *fp; |
| int junk; |
| |
| fp = fopen(gPIDFile, "w"); |
| if (fp != NULL) { |
| fprintf(fp, "%ld\n", (long) getpid()); |
| junk = fclose(fp); |
| assert(junk == 0); |
| } |
| } else { |
| fprintf(stderr, "%s: Could not run as daemon - exiting\n", gProgramName); |
| exit(result); |
| } |
| } else { |
| if (gMDNSPlatformPosixVerboseLevel > 0) { |
| fprintf(stderr, "%s: Starting in foreground mode, PID %ld\n", gProgramName, (long) getpid()); |
| } |
| } |
| |
| status = mDNS_Init(&mDNSStorage, &PlatformStorage, |
| mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, |
| mDNS_Init_AdvertiseLocalAddresses, |
| mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); |
| if (status != mStatus_NoError) return(2); |
| |
| status = RegisterOurServices(); |
| if (status != mStatus_NoError) return(2); |
| |
| signal(SIGHUP, HandleSigHup); // SIGHUP has to be sent by kill -HUP <pid> |
| signal(SIGINT, HandleSigInt); // SIGINT is what you get for a Ctrl-C |
| signal(SIGQUIT, HandleSigQuit); // SIGQUIT is what you get for a Ctrl-\ (indeed) |
| signal(SIGUSR1, HandleSigUsr1); // SIGUSR1 has to be sent by kill -USR1 <pid> |
| |
| while (!gStopNow) |
| { |
| int nfds = 0; |
| fd_set readfds; |
| struct timeval timeout; |
| int result; |
| |
| // 1. Set up the fd_set as usual here. |
| // This example client has no file descriptors of its own, |
| // but a real application would call FD_SET to add them to the set here |
| FD_ZERO(&readfds); |
| |
| // 2. Set up the timeout. |
| // This example client has no other work it needs to be doing, |
| // so we set an effectively infinite timeout |
| timeout.tv_sec = 0x3FFFFFFF; |
| timeout.tv_usec = 0; |
| |
| // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout |
| mDNSPosixGetFDSet(&mDNSStorage, &nfds, &readfds, &timeout); |
| |
| // 4. Call select as normal |
| verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec); |
| result = select(nfds, &readfds, NULL, NULL, &timeout); |
| |
| if (result < 0) |
| { |
| verbosedebugf("select() returned %d errno %d", result, errno); |
| if (errno != EINTR) gStopNow = mDNStrue; |
| else |
| { |
| if (gReceivedSigUsr1) |
| { |
| gReceivedSigUsr1 = mDNSfalse; |
| gMDNSPlatformPosixVerboseLevel += 1; |
| if (gMDNSPlatformPosixVerboseLevel > 2) |
| gMDNSPlatformPosixVerboseLevel = 0; |
| if ( gMDNSPlatformPosixVerboseLevel > 0 ) |
| fprintf(stderr, "\nVerbose level %d\n", gMDNSPlatformPosixVerboseLevel); |
| } |
| if (gReceivedSigHup) |
| { |
| if (gMDNSPlatformPosixVerboseLevel > 0) |
| fprintf(stderr, "\nSIGHUP\n"); |
| gReceivedSigHup = mDNSfalse; |
| DeregisterOurServices(); |
| status = mDNSPlatformPosixRefreshInterfaceList(&mDNSStorage); |
| if (status != mStatus_NoError) break; |
| status = RegisterOurServices(); |
| if (status != mStatus_NoError) break; |
| } |
| } |
| } |
| else |
| { |
| // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work |
| mDNSPosixProcessFDSet(&mDNSStorage, &readfds); |
| |
| // 6. This example client has no other work it needs to be doing, |
| // but a real client would do its work here |
| // ... (do work) ... |
| } |
| } |
| |
| debugf("Exiting"); |
| |
| DeregisterOurServices(); |
| mDNS_Close(&mDNSStorage); |
| |
| if (status == mStatus_NoError) { |
| result = 0; |
| } else { |
| result = 2; |
| } |
| if ( (result != 0) || (gMDNSPlatformPosixVerboseLevel > 0) ) { |
| fprintf(stderr, "%s: Finished with status %d, result %d\n", gProgramName, (int)status, result); |
| } |
| |
| return result; |
| } |