Home | History | Annotate | Download | only in mDNSPosix
      1 /* -*- Mode: C; tab-width: 4 -*-
      2  *
      3  * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 #if __APPLE__
     19 // In Mac OS X 10.5 and later trying to use the daemon function gives a daemon is deprecated
     20 // error, which prevents compilation because we build with "-Werror".
     21 // Since this is supposed to be portable cross-platform code, we don't care that daemon is
     22 // deprecated on Mac OS X 10.5, so we use this preprocessor trick to eliminate the error message.
     23 #define daemon yes_we_know_that_daemon_is_deprecated_in_os_x_10_5_thankyou
     24 #endif
     25 
     26 #include <assert.h>
     27 #include <stdio.h>			// For printf()
     28 #include <stdlib.h>			// For exit() etc.
     29 #include <string.h>			// For strlen() etc.
     30 #include <unistd.h>			// For select()
     31 #include <errno.h>			// For errno, EINTR
     32 #include <signal.h>
     33 #include <fcntl.h>
     34 
     35 #if __APPLE__
     36 #undef daemon
     37 extern int daemon(int, int);
     38 #endif
     39 
     40 #include "mDNSEmbeddedAPI.h"// Defines the interface to the client layer above
     41 #include "mDNSPosix.h"		// Defines the specific types needed to run mDNS on this platform
     42 #include "mDNSUNP.h"		// For daemon()
     43 
     44 #if COMPILER_LIKES_PRAGMA_MARK
     45 #pragma mark ***** Globals
     46 #endif
     47 
     48 static mDNS mDNSStorage;       // mDNS core uses this to store its globals
     49 static mDNS_PlatformSupport PlatformStorage;  // Stores this platform's globals
     50 
     51 mDNSexport const char ProgramName[] = "mDNSResponderPosix";
     52 
     53 static const char *gProgramName = ProgramName;
     54 
     55 #if COMPILER_LIKES_PRAGMA_MARK
     56 #pragma mark ***** Signals
     57 #endif
     58 
     59 static volatile mDNSBool gReceivedSigUsr1;
     60 static volatile mDNSBool gReceivedSigHup;
     61 static volatile mDNSBool gStopNow;
     62 
     63 // We support 4 signals.
     64 //
     65 // o SIGUSR1 toggles verbose mode on and off in debug builds
     66 // o SIGHUP  triggers the program to re-read its preferences.
     67 // o SIGINT  causes an orderly shutdown of the program.
     68 // o SIGQUIT causes a somewhat orderly shutdown (direct but dangerous)
     69 // o SIGKILL kills us dead (easy to implement :-)
     70 //
     71 // There are fatal race conditions in our signal handling, but there's not much
     72 // we can do about them while remaining within the Posix space.  Specifically,
     73 // if a signal arrives after we test the globals its sets but before we call
     74 // select, the signal will be dropped.  The user will have to send the signal
     75 // again.  Unfortunately, Posix does not have a "sigselect" to atomically
     76 // modify the signal mask and start a select.
     77 
     78 static void HandleSigUsr1(int sigraised)
     79     // If we get a SIGUSR1 we toggle the state of the
     80     // verbose mode.
     81 {
     82     assert(sigraised == SIGUSR1);
     83     gReceivedSigUsr1 = mDNStrue;
     84 }
     85 
     86 static void HandleSigHup(int sigraised)
     87     // A handler for SIGHUP that causes us to break out of the
     88     // main event loop when the user kill 1's us.  This has the
     89     // effect of triggered the main loop to deregister the
     90     // current services and re-read the preferences.
     91 {
     92     assert(sigraised == SIGHUP);
     93 	gReceivedSigHup = mDNStrue;
     94 }
     95 
     96 static void HandleSigInt(int sigraised)
     97     // A handler for SIGINT that causes us to break out of the
     98     // main event loop when the user types ^C.  This has the
     99     // effect of quitting the program.
    100 {
    101     assert(sigraised == SIGINT);
    102 
    103     if (gMDNSPlatformPosixVerboseLevel > 0) {
    104         fprintf(stderr, "\nSIGINT\n");
    105     }
    106     gStopNow = mDNStrue;
    107 }
    108 
    109 static void HandleSigQuit(int sigraised)
    110     // If we get a SIGQUIT the user is desperate and we
    111     // just call mDNS_Close directly.  This is definitely
    112     // not safe (because it could reenter mDNS), but
    113     // we presume that the user has already tried the safe
    114     // alternatives.
    115 {
    116     assert(sigraised == SIGQUIT);
    117 
    118     if (gMDNSPlatformPosixVerboseLevel > 0) {
    119         fprintf(stderr, "\nSIGQUIT\n");
    120     }
    121     mDNS_Close(&mDNSStorage);
    122     exit(0);
    123 }
    124 
    125 #if COMPILER_LIKES_PRAGMA_MARK
    126 #pragma mark ***** Parameter Checking
    127 #endif
    128 
    129 static mDNSBool CheckThatRichTextNameIsUsable(const char *richTextName, mDNSBool printExplanation)
    130     // Checks that richTextName is reasonable
    131     // label and, if it isn't and printExplanation is true, prints
    132     // an explanation of why not.
    133 {
    134     mDNSBool result = mDNStrue;
    135     if (result && strlen(richTextName) > 63) {
    136         if (printExplanation) {
    137             fprintf(stderr,
    138                     "%s: Service name is too long (must be 63 characters or less)\n",
    139                     gProgramName);
    140         }
    141         result = mDNSfalse;
    142     }
    143     if (result && richTextName[0] == 0) {
    144         if (printExplanation) {
    145             fprintf(stderr, "%s: Service name can't be empty\n", gProgramName);
    146         }
    147         result = mDNSfalse;
    148     }
    149     return result;
    150 }
    151 
    152 static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool printExplanation)
    153     // Checks that serviceType is a reasonable service type
    154     // label and, if it isn't and printExplanation is true, prints
    155     // an explanation of why not.
    156 {
    157     mDNSBool result;
    158 
    159     result = mDNStrue;
    160     if (result && strlen(serviceType) > 63) {
    161         if (printExplanation) {
    162             fprintf(stderr,
    163                     "%s: Service type is too long (must be 63 characters or less)\n",
    164                     gProgramName);
    165         }
    166         result = mDNSfalse;
    167     }
    168     if (result && serviceType[0] == 0) {
    169         if (printExplanation) {
    170             fprintf(stderr,
    171                     "%s: Service type can't be empty\n",
    172                     gProgramName);
    173         }
    174         result = mDNSfalse;
    175     }
    176     return result;
    177 }
    178 
    179 static mDNSBool CheckThatPortNumberIsUsable(long portNumber, mDNSBool printExplanation)
    180     // Checks that portNumber is a reasonable port number
    181     // and, if it isn't and printExplanation is true, prints
    182     // an explanation of why not.
    183 {
    184     mDNSBool result;
    185 
    186     result = mDNStrue;
    187     if (result && (portNumber <= 0 || portNumber > 65535)) {
    188         if (printExplanation) {
    189             fprintf(stderr,
    190                     "%s: Port number specified by -p must be in range 1..65535\n",
    191                     gProgramName);
    192         }
    193         result = mDNSfalse;
    194     }
    195     return result;
    196 }
    197 
    198 #if COMPILER_LIKES_PRAGMA_MARK
    199 #pragma mark ***** Command Line Arguments
    200 #endif
    201 
    202 static const char kDefaultPIDFile[]     = "/var/run/mDNSResponder.pid";
    203 static const char kDefaultServiceType[] = "_afpovertcp._tcp.";
    204 static const char kDefaultServiceDomain[] = "local.";
    205 enum {
    206     kDefaultPortNumber = 548
    207 };
    208 
    209 static void PrintUsage()
    210 {
    211     fprintf(stderr,
    212             "Usage: %s [-v level ] [-r] [-n name] [-t type] [-d domain] [-p port] [-f file] [-b] [-P pidfile] [-x name=val ...]\n",
    213             gProgramName);
    214     fprintf(stderr, "          -v verbose mode, level is a number from 0 to 2\n");
    215     fprintf(stderr, "             0 = no debugging info (default)\n");
    216     fprintf(stderr, "             1 = standard debugging info\n");
    217     fprintf(stderr, "             2 = intense debugging info\n");
    218     fprintf(stderr, "             can be cycled kill -USR1\n");
    219     fprintf(stderr, "          -r also bind to port 53 (port 5353 is always bound)\n");
    220     fprintf(stderr, "          -n uses 'name' as the service name (required)\n");
    221     fprintf(stderr, "          -t uses 'type' as the service type (default is '%s')\n", kDefaultServiceType);
    222     fprintf(stderr, "          -d uses 'domain' as the service domain (default is '%s')\n", kDefaultServiceDomain);
    223     fprintf(stderr, "          -p uses 'port' as the port number (default is '%d')\n",  kDefaultPortNumber);
    224     fprintf(stderr, "          -f reads a service list from 'file'\n");
    225     fprintf(stderr, "          -b forces daemon (background) mode\n");
    226     fprintf(stderr, "          -P uses 'pidfile' as the PID file\n");
    227     fprintf(stderr, "             (default is '%s')\n",  kDefaultPIDFile);
    228     fprintf(stderr, "             only meaningful if -b also specified\n");
    229     fprintf(stderr, "          -x stores name=val in TXT record (default is empty).\n");
    230     fprintf(stderr, "             MUST be the last command-line argument;\n");
    231     fprintf(stderr, "             all subsequent arguments after -x are treated as name=val pairs.\n");
    232 }
    233 
    234 static   mDNSBool  gAvoidPort53      = mDNStrue;
    235 static const char *gServiceName      = "";
    236 static const char *gServiceType      = kDefaultServiceType;
    237 static const char *gServiceDomain    = kDefaultServiceDomain;
    238 static mDNSu8      gServiceText[sizeof(RDataBody)];
    239 static mDNSu16     gServiceTextLen   = 0;
    240 static        int  gPortNumber       = kDefaultPortNumber;
    241 static const char *gServiceFile      = "";
    242 static   mDNSBool  gDaemon           = mDNSfalse;
    243 static const char *gPIDFile          = kDefaultPIDFile;
    244 
    245 static void ParseArguments(int argc, char **argv)
    246     // Parses our command line arguments into the global variables
    247     // listed above.
    248 {
    249     int ch;
    250 
    251     // Set gProgramName to the last path component of argv[0]
    252 
    253     gProgramName = strrchr(argv[0], '/');
    254     if (gProgramName == NULL) {
    255         gProgramName = argv[0];
    256     } else {
    257         gProgramName += 1;
    258     }
    259 
    260     // Parse command line options using getopt.
    261 
    262     do {
    263         ch = getopt(argc, argv, "v:rn:t:d:p:f:dP:bx");
    264         if (ch != -1) {
    265             switch (ch) {
    266                 case 'v':
    267                     gMDNSPlatformPosixVerboseLevel = atoi(optarg);
    268                     if (gMDNSPlatformPosixVerboseLevel < 0 || gMDNSPlatformPosixVerboseLevel > 2) {
    269                         fprintf(stderr,
    270                                 "%s: Verbose mode must be in the range 0..2\n",
    271                                 gProgramName);
    272                         exit(1);
    273                     }
    274                     break;
    275                 case 'r':
    276                     gAvoidPort53 = mDNSfalse;
    277                     break;
    278                 case 'n':
    279                     gServiceName = optarg;
    280                     if ( ! CheckThatRichTextNameIsUsable(gServiceName, mDNStrue) ) {
    281                         exit(1);
    282                     }
    283                     break;
    284                 case 't':
    285                     gServiceType = optarg;
    286                     if ( ! CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) {
    287                         exit(1);
    288                     }
    289                     break;
    290                 case 'd':
    291                     gServiceDomain = optarg;
    292                     break;
    293                 case 'p':
    294                     gPortNumber = atol(optarg);
    295                     if ( ! CheckThatPortNumberIsUsable(gPortNumber, mDNStrue) ) {
    296                         exit(1);
    297                     }
    298                     break;
    299                 case 'f':
    300                     gServiceFile = optarg;
    301                     break;
    302                 case 'b':
    303                     gDaemon = mDNStrue;
    304                     break;
    305                 case 'P':
    306                     gPIDFile = optarg;
    307                     break;
    308                 case 'x':
    309                 	while (optind < argc)
    310                 		{
    311                 		gServiceText[gServiceTextLen] = strlen(argv[optind]);
    312                 		mDNSPlatformMemCopy(gServiceText+gServiceTextLen+1, argv[optind], gServiceText[gServiceTextLen]);
    313                 		gServiceTextLen += 1 + gServiceText[gServiceTextLen];
    314                 		optind++;
    315                 		}
    316                 	ch = -1;
    317                 	break;
    318                 case '?':
    319                 default:
    320                     PrintUsage();
    321                     exit(1);
    322                     break;
    323             }
    324         }
    325     } while (ch != -1);
    326 
    327     // Check for any left over command line arguments.
    328 
    329     if (optind != argc) {
    330 	    PrintUsage();
    331         fprintf(stderr, "%s: Unexpected argument '%s'\n", gProgramName, argv[optind]);
    332         exit(1);
    333     }
    334 
    335     // Check for inconsistency between the arguments.
    336 
    337     if ( (gServiceName[0] == 0) && (gServiceFile[0] == 0) ) {
    338     	PrintUsage();
    339         fprintf(stderr, "%s: You must specify a service name to register (-n) or a service file (-f).\n", gProgramName);
    340         exit(1);
    341     }
    342 }
    343 
    344 #if COMPILER_LIKES_PRAGMA_MARK
    345 #pragma mark ***** Registration
    346 #endif
    347 
    348 typedef struct PosixService PosixService;
    349 
    350 struct PosixService {
    351     ServiceRecordSet coreServ;
    352     PosixService *next;
    353     int serviceID;
    354 };
    355 
    356 static PosixService *gServiceList = NULL;
    357 
    358 static void RegistrationCallback(mDNS *const m, ServiceRecordSet *const thisRegistration, mStatus status)
    359     // mDNS core calls this routine to tell us about the status of
    360     // our registration.  The appropriate action to take depends
    361     // entirely on the value of status.
    362 {
    363     switch (status) {
    364 
    365         case mStatus_NoError:
    366             debugf("Callback: %##s Name Registered",   thisRegistration->RR_SRV.resrec.name->c);
    367             // Do nothing; our name was successfully registered.  We may
    368             // get more call backs in the future.
    369             break;
    370 
    371         case mStatus_NameConflict:
    372             debugf("Callback: %##s Name Conflict",     thisRegistration->RR_SRV.resrec.name->c);
    373 
    374             // In the event of a conflict, this sample RegistrationCallback
    375             // just calls mDNS_RenameAndReregisterService to automatically
    376             // pick a new unique name for the service. For a device such as a
    377             // printer, this may be appropriate.  For a device with a user
    378             // interface, and a screen, and a keyboard, the appropriate response
    379             // may be to prompt the user and ask them to choose a new name for
    380             // the service.
    381             //
    382             // Also, what do we do if mDNS_RenameAndReregisterService returns an
    383             // error.  Right now I have no place to send that error to.
    384 
    385             status = mDNS_RenameAndReregisterService(m, thisRegistration, mDNSNULL);
    386             assert(status == mStatus_NoError);
    387             break;
    388 
    389         case mStatus_MemFree:
    390             debugf("Callback: %##s Memory Free",       thisRegistration->RR_SRV.resrec.name->c);
    391 
    392             // When debugging is enabled, make sure that thisRegistration
    393             // is not on our gServiceList.
    394 
    395             #if !defined(NDEBUG)
    396                 {
    397                     PosixService *cursor;
    398 
    399                     cursor = gServiceList;
    400                     while (cursor != NULL) {
    401                         assert(&cursor->coreServ != thisRegistration);
    402                         cursor = cursor->next;
    403                     }
    404                 }
    405             #endif
    406             free(thisRegistration);
    407             break;
    408 
    409         default:
    410             debugf("Callback: %##s Unknown Status %ld", thisRegistration->RR_SRV.resrec.name->c, status);
    411             break;
    412     }
    413 }
    414 
    415 static int gServiceID = 0;
    416 
    417 static mStatus RegisterOneService(const char *  richTextName,
    418                                   const char *  serviceType,
    419                                   const char *  serviceDomain,
    420                                   const mDNSu8  text[],
    421                                   mDNSu16       textLen,
    422                                   long          portNumber)
    423 {
    424     mStatus             status;
    425     PosixService *      thisServ;
    426     domainlabel         name;
    427     domainname          type;
    428     domainname          domain;
    429 
    430     status = mStatus_NoError;
    431     thisServ = (PosixService *) malloc(sizeof(*thisServ));
    432     if (thisServ == NULL) {
    433         status = mStatus_NoMemoryErr;
    434     }
    435     if (status == mStatus_NoError) {
    436         MakeDomainLabelFromLiteralString(&name,  richTextName);
    437         MakeDomainNameFromDNSNameString(&type, serviceType);
    438         MakeDomainNameFromDNSNameString(&domain, serviceDomain);
    439         status = mDNS_RegisterService(&mDNSStorage, &thisServ->coreServ,
    440                 &name, &type, &domain,				// Name, type, domain
    441                 NULL, mDNSOpaque16fromIntVal(portNumber),
    442                 text, textLen,						// TXT data, length
    443                 NULL, 0,							// Subtypes
    444                 mDNSInterface_Any,					// Interface ID
    445                 RegistrationCallback, thisServ, 0);	// Callback, context, flags
    446     }
    447     if (status == mStatus_NoError) {
    448         thisServ->serviceID = gServiceID;
    449         gServiceID += 1;
    450 
    451         thisServ->next = gServiceList;
    452         gServiceList = thisServ;
    453 
    454         if (gMDNSPlatformPosixVerboseLevel > 0) {
    455             fprintf(stderr,
    456                     "%s: Registered service %d, name \"%s\", type \"%s\", domain \"%s\",  port %ld\n",
    457                     gProgramName,
    458                     thisServ->serviceID,
    459                     richTextName,
    460                     serviceType,
    461                     serviceDomain,
    462                     portNumber);
    463         }
    464     } else {
    465         if (thisServ != NULL) {
    466             free(thisServ);
    467         }
    468     }
    469     return status;
    470 }
    471 
    472 static mDNSBool ReadALine(char *buf, size_t bufSize, FILE *fp, mDNSBool skipBlankLines)
    473 {
    474 	size_t	len;
    475 	mDNSBool readNextLine;
    476 
    477 	do {
    478 		readNextLine = mDNSfalse;
    479 
    480 		if (fgets(buf, bufSize, fp) == NULL)
    481 			return mDNSfalse;	// encountered EOF or an error condition
    482 
    483 		// These first characters indicate a blank line.
    484 		if (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\r' || buf[0] == '\n') {
    485 			if (!skipBlankLines)
    486 				return mDNSfalse;
    487 			readNextLine = mDNStrue;
    488 		}
    489 		// always skip comment lines
    490 		if (buf[0] == '#')
    491 			readNextLine = mDNStrue;
    492 
    493 	} while (readNextLine);
    494 
    495 	len = strlen( buf);
    496 	if ( buf[len - 1] == '\r' || buf[len - 1] == '\n')
    497 		buf[len - 1] = '\0';
    498 
    499     return mDNStrue;
    500 }
    501 
    502 static mStatus RegisterServicesInFile(const char *filePath)
    503 {
    504     mStatus     status = mStatus_NoError;
    505     FILE *      fp = fopen(filePath, "r");
    506 
    507     if (fp == NULL) {
    508         return mStatus_UnknownErr;
    509     }
    510 
    511 	if (gMDNSPlatformPosixVerboseLevel > 1)
    512 		fprintf(stderr, "Parsing %s for services\n", filePath);
    513 
    514 	do {
    515 		char nameBuf[256];
    516 		char * name = nameBuf;
    517 		char type[256];
    518 		const char *dom = kDefaultServiceDomain;
    519 		char rawText[1024];
    520 		mDNSu8  text[sizeof(RDataBody)];
    521 		unsigned int textLen = 0;
    522 		char port[256];
    523 		char *p;
    524 
    525 		// Read the service name, type, port, and optional text record fields.
    526 		// Skip blank lines while looking for the next service name.
    527 		if (! ReadALine(name, sizeof(nameBuf), fp, mDNStrue))
    528 			break;
    529 
    530 		// Special case that allows service name to begin with a '#'
    531 		// character by escaping it with a '\' to distiguish it from
    532 		// a comment line.  Remove the leading '\' here before
    533 		// registering the service.
    534 		if (name[0] == '\\' && name[1] == '#')
    535 			name++;
    536 
    537 		if (gMDNSPlatformPosixVerboseLevel > 1)
    538 			fprintf(stderr, "Service name: \"%s\"\n", name);
    539 
    540 		// Don't skip blank lines in calls to ReadAline() after finding the
    541 		// service name since the next blank line indicates the end
    542 		// of this service record.
    543 		if (! ReadALine(type, sizeof(type), fp, mDNSfalse))
    544 			break;
    545 
    546 		// see if a domain name is specified
    547 		p = type;
    548 		while (*p && *p != ' ' && *p != '\t') p++;
    549 		if (*p) {
    550 			*p = 0;	// NULL terminate the <type>.<protocol> string
    551 			// skip any leading whitespace before domain name
    552 			p++;
    553 			while (*p && (*p == ' ' || *p == '\t')) p++;
    554 			if (*p)
    555 				dom = p;
    556 		}
    557 		if (gMDNSPlatformPosixVerboseLevel > 1) {
    558 			fprintf(stderr, "Service type: \"%s\"\n", type);
    559 			fprintf(stderr, "Service domain: \"%s\"\n", dom);
    560 		}
    561 
    562 		if (! ReadALine(port, sizeof(port), fp, mDNSfalse))
    563 			break;
    564 		if (gMDNSPlatformPosixVerboseLevel > 1)
    565 			fprintf(stderr, "Service port: %s\n", port);
    566 
    567 		if (   ! CheckThatRichTextNameIsUsable(name, mDNStrue)
    568 			|| ! CheckThatServiceTypeIsUsable(type, mDNStrue)
    569 			|| ! CheckThatPortNumberIsUsable(atol(port), mDNStrue))
    570 			break;
    571 
    572 		// read the TXT record fields
    573 		while (1) {
    574 			int len;
    575 			if (!ReadALine(rawText, sizeof(rawText), fp, mDNSfalse)) break;
    576 			if (gMDNSPlatformPosixVerboseLevel > 1)
    577 				fprintf(stderr, "Text string: \"%s\"\n", rawText);
    578 			len = strlen(rawText);
    579 			if (len <= 255)
    580 				{
    581 				unsigned int newlen = textLen + 1 + len;
    582 				if (len == 0 || newlen >= sizeof(text)) break;
    583 				text[textLen] = len;
    584 				mDNSPlatformMemCopy(text + textLen + 1, rawText, len);
    585 				textLen = newlen;
    586 				}
    587 			else
    588 				fprintf(stderr, "%s: TXT attribute too long for name = %s, type = %s, port = %s\n",
    589 					gProgramName, name, type, port);
    590 		}
    591 
    592 		status = RegisterOneService(name, type, dom, text, textLen, atol(port));
    593 		if (status != mStatus_NoError) {
    594 			// print error, but try to read and register other services in the file
    595 			fprintf(stderr, "%s: Failed to register service, name \"%s\", type \"%s\", domain \"%s\", port %s\n",
    596 					gProgramName, name, type, dom, port);
    597 		}
    598 
    599 	} while (!feof(fp));
    600 
    601 	if (!feof(fp)) {
    602 		fprintf(stderr, "%s: Error reading service file %s\n", gProgramName, filePath);
    603 		status = mStatus_UnknownErr;
    604 	}
    605 
    606 	{
    607 // __ANDROID__ : replaced assert(fclose(..))
    608 		int fp_closed = fclose(fp);
    609 		assert(0 == fp_closed);
    610 	}
    611 
    612 	return status;
    613 }
    614 
    615 static mStatus RegisterOurServices(void)
    616 {
    617     mStatus status;
    618 
    619     status = mStatus_NoError;
    620     if (gServiceName[0] != 0) {
    621         status = RegisterOneService(gServiceName,
    622                                     gServiceType,
    623                                     gServiceDomain,
    624                                     gServiceText, gServiceTextLen,
    625                                     gPortNumber);
    626     }
    627     if (status == mStatus_NoError && gServiceFile[0] != 0) {
    628         status = RegisterServicesInFile(gServiceFile);
    629     }
    630     return status;
    631 }
    632 
    633 static void DeregisterOurServices(void)
    634 {
    635     PosixService *thisServ;
    636     int thisServID;
    637 
    638     while (gServiceList != NULL) {
    639         thisServ = gServiceList;
    640         gServiceList = thisServ->next;
    641 
    642         thisServID = thisServ->serviceID;
    643 
    644         mDNS_DeregisterService(&mDNSStorage, &thisServ->coreServ);
    645 
    646         if (gMDNSPlatformPosixVerboseLevel > 0) {
    647             fprintf(stderr,
    648                     "%s: Deregistered service %d\n",
    649                     gProgramName,
    650                     thisServ->serviceID);
    651         }
    652     }
    653 }
    654 
    655 #if COMPILER_LIKES_PRAGMA_MARK
    656 #pragma mark **** Main
    657 #endif
    658 
    659 int main(int argc, char **argv)
    660 {
    661     mStatus status;
    662     int     result;
    663 
    664     // Parse our command line arguments.  This won't come back if there's an error.
    665 
    666     ParseArguments(argc, argv);
    667 
    668     // If we're told to run as a daemon, then do that straight away.
    669     // Note that we don't treat the inability to create our PID
    670     // file as an error.  Also note that we assign getpid to a long
    671     // because printf has no format specified for pid_t.
    672 
    673     if (gDaemon) {
    674     	int result;
    675         if (gMDNSPlatformPosixVerboseLevel > 0) {
    676             fprintf(stderr, "%s: Starting in daemon mode\n", gProgramName);
    677         }
    678         result = daemon(0,0);
    679         if (result == 0) {
    680             FILE *fp;
    681             int  junk;
    682 
    683             fp = fopen(gPIDFile, "w");
    684             if (fp != NULL) {
    685                 fprintf(fp, "%ld\n", (long) getpid());
    686                 junk = fclose(fp);
    687                 assert(junk == 0);
    688             }
    689         } else {
    690             fprintf(stderr, "%s: Could not run as daemon - exiting\n", gProgramName);
    691             exit(result);
    692         }
    693     } else {
    694         if (gMDNSPlatformPosixVerboseLevel > 0) {
    695             fprintf(stderr, "%s: Starting in foreground mode, PID %ld\n", gProgramName, (long) getpid());
    696         }
    697     }
    698 
    699     status = mDNS_Init(&mDNSStorage, &PlatformStorage,
    700     	mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
    701     	mDNS_Init_AdvertiseLocalAddresses,
    702     	mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
    703     if (status != mStatus_NoError) return(2);
    704 
    705 	status = RegisterOurServices();
    706     if (status != mStatus_NoError) return(2);
    707 
    708     signal(SIGHUP,  HandleSigHup);      // SIGHUP has to be sent by kill -HUP <pid>
    709     signal(SIGINT,  HandleSigInt);      // SIGINT is what you get for a Ctrl-C
    710     signal(SIGQUIT, HandleSigQuit);     // SIGQUIT is what you get for a Ctrl-\ (indeed)
    711     signal(SIGUSR1, HandleSigUsr1);     // SIGUSR1 has to be sent by kill -USR1 <pid>
    712 
    713 	while (!gStopNow)
    714 		{
    715 		int nfds = 0;
    716 		fd_set readfds;
    717 		struct timeval timeout;
    718 		int result;
    719 
    720 		// 1. Set up the fd_set as usual here.
    721 		// This example client has no file descriptors of its own,
    722 		// but a real application would call FD_SET to add them to the set here
    723 		FD_ZERO(&readfds);
    724 
    725 		// 2. Set up the timeout.
    726 		// This example client has no other work it needs to be doing,
    727 		// so we set an effectively infinite timeout
    728 		timeout.tv_sec = 0x3FFFFFFF;
    729 		timeout.tv_usec = 0;
    730 
    731 		// 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout
    732 		mDNSPosixGetFDSet(&mDNSStorage, &nfds, &readfds, &timeout);
    733 
    734 		// 4. Call select as normal
    735 		verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec);
    736 		result = select(nfds, &readfds, NULL, NULL, &timeout);
    737 
    738 		if (result < 0)
    739 			{
    740 			verbosedebugf("select() returned %d errno %d", result, errno);
    741 			if (errno != EINTR) gStopNow = mDNStrue;
    742 			else
    743 				{
    744 				if (gReceivedSigUsr1)
    745 					{
    746 					gReceivedSigUsr1 = mDNSfalse;
    747 					gMDNSPlatformPosixVerboseLevel += 1;
    748 					if (gMDNSPlatformPosixVerboseLevel > 2)
    749 						gMDNSPlatformPosixVerboseLevel = 0;
    750 					if ( gMDNSPlatformPosixVerboseLevel > 0 )
    751 						fprintf(stderr, "\nVerbose level %d\n", gMDNSPlatformPosixVerboseLevel);
    752 					}
    753 				if (gReceivedSigHup)
    754 					{
    755 					if (gMDNSPlatformPosixVerboseLevel > 0)
    756 						fprintf(stderr, "\nSIGHUP\n");
    757 					gReceivedSigHup = mDNSfalse;
    758 					DeregisterOurServices();
    759 					status = mDNSPlatformPosixRefreshInterfaceList(&mDNSStorage);
    760 					if (status != mStatus_NoError) break;
    761 					status = RegisterOurServices();
    762 					if (status != mStatus_NoError) break;
    763 					}
    764 				}
    765 			}
    766 		else
    767 			{
    768 			// 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work
    769 			mDNSPosixProcessFDSet(&mDNSStorage, &readfds);
    770 
    771 			// 6. This example client has no other work it needs to be doing,
    772 			// but a real client would do its work here
    773 			// ... (do work) ...
    774 			}
    775 		}
    776 
    777 	debugf("Exiting");
    778 
    779 	DeregisterOurServices();
    780 	mDNS_Close(&mDNSStorage);
    781 
    782     if (status == mStatus_NoError) {
    783         result = 0;
    784     } else {
    785         result = 2;
    786     }
    787     if ( (result != 0) || (gMDNSPlatformPosixVerboseLevel > 0) ) {
    788         fprintf(stderr, "%s: Finished with status %d, result %d\n", gProgramName, (int)status, result);
    789     }
    790 
    791     return result;
    792 }
    793