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
37extern 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
48static mDNS mDNSStorage;       // mDNS core uses this to store its globals
49static mDNS_PlatformSupport PlatformStorage;  // Stores this platform's globals
50
51mDNSexport const char ProgramName[] = "mDNSResponderPosix";
52
53static const char *gProgramName = ProgramName;
54
55#if COMPILER_LIKES_PRAGMA_MARK
56#pragma mark ***** Signals
57#endif
58
59static volatile mDNSBool gReceivedSigUsr1;
60static volatile mDNSBool gReceivedSigHup;
61static 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
78static 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
86static 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
96static 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
109static 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
129static 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
152static 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
179static 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
202static const char kDefaultPIDFile[]     = "/var/run/mDNSResponder.pid";
203static const char kDefaultServiceType[] = "_afpovertcp._tcp.";
204static const char kDefaultServiceDomain[] = "local.";
205enum {
206    kDefaultPortNumber = 548
207};
208
209static 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
234static   mDNSBool  gAvoidPort53      = mDNStrue;
235static const char *gServiceName      = "";
236static const char *gServiceType      = kDefaultServiceType;
237static const char *gServiceDomain    = kDefaultServiceDomain;
238static mDNSu8      gServiceText[sizeof(RDataBody)];
239static mDNSu16     gServiceTextLen   = 0;
240static        int  gPortNumber       = kDefaultPortNumber;
241static const char *gServiceFile      = "";
242static   mDNSBool  gDaemon           = mDNSfalse;
243static const char *gPIDFile          = kDefaultPIDFile;
244
245static 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
348typedef struct PosixService PosixService;
349
350struct PosixService {
351    ServiceRecordSet coreServ;
352    PosixService *next;
353    int serviceID;
354};
355
356static PosixService *gServiceList = NULL;
357
358static 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
415static int gServiceID = 0;
416
417static 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
472static 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
502static 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
615static 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
633static 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
659int 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