• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
HandleSigUsr1(int sigraised)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 
HandleSigHup(int sigraised)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 
HandleSigInt(int sigraised)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 
HandleSigQuit(int sigraised)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 
CheckThatRichTextNameIsUsable(const char * richTextName,mDNSBool printExplanation)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 
CheckThatServiceTypeIsUsable(const char * serviceType,mDNSBool printExplanation)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 
CheckThatPortNumberIsUsable(long portNumber,mDNSBool printExplanation)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 
PrintUsage()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 
ParseArguments(int argc,char ** argv)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 
RegistrationCallback(mDNS * const m,ServiceRecordSet * const thisRegistration,mStatus status)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 
RegisterOneService(const char * richTextName,const char * serviceType,const char * serviceDomain,const mDNSu8 text[],mDNSu16 textLen,long portNumber)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 
ReadALine(char * buf,size_t bufSize,FILE * fp,mDNSBool skipBlankLines)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 
RegisterServicesInFile(const char * filePath)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 
RegisterOurServices(void)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 
DeregisterOurServices(void)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 
main(int argc,char ** argv)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