2 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
25 Change History (most recent first):
28 Revision 1.20 2004/05/18 23:51:26 cheshire
29 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
31 Revision 1.19 2004/03/12 08:03:14 cheshire
34 Revision 1.18 2004/01/25 00:00:55 cheshire
35 Change to use mDNSOpaque16fromIntVal() instead of shifting and masking
37 Revision 1.17 2003/12/11 19:11:55 cheshire
40 Revision 1.16 2003/08/14 02:19:55 cheshire
41 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
43 Revision 1.15 2003/08/12 19:56:26 cheshire
46 Revision 1.14 2003/08/06 18:20:51 cheshire
49 Revision 1.13 2003/07/23 00:00:04 cheshire
52 Revision 1.12 2003/07/15 01:55:16 cheshire
53 <rdar://problem/3315777> Need to implement service registration with subtypes
55 Revision 1.11 2003/07/14 18:11:54 cheshire
56 Fix stricter compiler warnings
58 Revision 1.10 2003/07/10 20:27:31 cheshire
59 <rdar://problem/3318717> mDNSResponder Posix version is missing a 'b' in the getopt option string
61 Revision 1.9 2003/07/02 21:19:59 cheshire
62 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
64 Revision 1.8 2003/06/18 05:48:41 cheshire
67 Revision 1.7 2003/05/06 00:00:50 cheshire
68 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
70 Revision 1.6 2003/03/08 00:35:56 cheshire
71 Switched to using new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt")
73 Revision 1.5 2003/02/20 06:48:36 cheshire
74 <rdar://problem/3169535> Xserve RAID needs to do interface-specific registrations
75 Reviewed by: Josh Graessley, Bob Bradley
77 Revision 1.4 2003/01/28 03:07:46 cheshire
78 Add extra parameter to mDNS_RenameAndReregisterService(),
79 and add support for specifying a domain other than dot-local.
81 Revision 1.3 2002/09/21 20:44:53 zarzycki
84 Revision 1.2 2002/09/19 04:20:44 cheshire
85 Remove high-ascii characters that confuse some systems
87 Revision 1.1 2002/09/17 06:24:35 cheshire
92 #include "mDNSClientAPI.h"// Defines the interface to the client layer above
93 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
96 #include <stdio.h> // For printf()
97 #include <stdlib.h> // For exit() etc.
98 #include <string.h> // For strlen() etc.
99 #include <unistd.h> // For select()
100 #include <errno.h> // For errno, EINTR
104 #if COMPILER_LIKES_PRAGMA_MARK
105 #pragma mark ***** Globals
108 static mDNS mDNSStorage
; // mDNS core uses this to store its globals
109 static mDNS_PlatformSupport PlatformStorage
; // Stores this platform's globals
111 static const char *gProgramName
= "mDNSResponderPosix";
113 #if COMPILER_LIKES_PRAGMA_MARK
114 #pragma mark ***** Signals
117 static volatile mDNSBool gReceivedSigUsr1
;
118 static volatile mDNSBool gReceivedSigHup
;
119 static volatile mDNSBool gStopNow
;
121 // We support 4 signals.
123 // o SIGUSR1 toggles verbose mode on and off in debug builds
124 // o SIGHUP triggers the program to re-read its preferences.
125 // o SIGINT causes an orderly shutdown of the program.
126 // o SIGQUIT causes a somewhat orderly shutdown (direct but dangerous)
127 // o SIGKILL kills us dead (easy to implement :-)
129 // There are fatal race conditions in our signal handling, but there's not much
130 // we can do about them while remaining within the Posix space. Specifically,
131 // if a signal arrives after we test the globals its sets but before we call
132 // select, the signal will be dropped. The user will have to send the signal
133 // again. Unfortunately, Posix does not have a "sigselect" to atomically
134 // modify the signal mask and start a select.
136 static void HandleSigUsr1(int sigraised
)
137 // If we get a SIGUSR1 we toggle the state of the
140 assert(sigraised
== SIGUSR1
);
141 gReceivedSigUsr1
= mDNStrue
;
144 static void HandleSigHup(int sigraised
)
145 // A handler for SIGHUP that causes us to break out of the
146 // main event loop when the user kill 1's us. This has the
147 // effect of triggered the main loop to deregister the
148 // current services and re-read the preferences.
150 assert(sigraised
== SIGHUP
);
151 gReceivedSigHup
= mDNStrue
;
154 static void HandleSigInt(int sigraised
)
155 // A handler for SIGINT that causes us to break out of the
156 // main event loop when the user types ^C. This has the
157 // effect of quitting the program.
159 assert(sigraised
== SIGINT
);
161 if (gMDNSPlatformPosixVerboseLevel
> 0) {
162 fprintf(stderr
, "\nSIGINT\n");
167 static void HandleSigQuit(int sigraised
)
168 // If we get a SIGQUIT the user is desperate and we
169 // just call mDNS_Close directly. This is definitely
170 // not safe (because it could reenter mDNS), but
171 // we presume that the user has already tried the safe
174 assert(sigraised
== SIGQUIT
);
176 if (gMDNSPlatformPosixVerboseLevel
> 0) {
177 fprintf(stderr
, "\nSIGQUIT\n");
179 mDNS_Close(&mDNSStorage
);
183 #if COMPILER_LIKES_PRAGMA_MARK
184 #pragma mark ***** Parameter Checking
187 static mDNSBool
CheckThatRichTextHostNameIsUsable(const char *richTextHostName
, mDNSBool printExplanation
)
188 // Checks that richTextHostName is a reasonable host name
189 // label and, if it isn't and printExplanation is true, prints
190 // an explanation of why not.
193 domainlabel richLabel
;
194 domainlabel poorLabel
;
197 if (result
&& strlen(richTextHostName
) > 63) {
198 if (printExplanation
) {
200 "%s: Host name is too long (must be 63 characters or less)\n",
205 if (result
&& richTextHostName
[0] == 0) {
206 if (printExplanation
) {
207 fprintf(stderr
, "%s: Host name can't be empty\n", gProgramName
);
212 MakeDomainLabelFromLiteralString(&richLabel
, richTextHostName
);
213 ConvertUTF8PstringToRFC1034HostLabel(richLabel
.c
, &poorLabel
);
214 if (poorLabel
.c
[0] == 0) {
215 if (printExplanation
) {
217 "%s: Host name doesn't produce a usable RFC-1034 name\n",
226 static mDNSBool
CheckThatServiceTypeIsUsable(const char *serviceType
, mDNSBool printExplanation
)
227 // Checks that serviceType is a reasonable service type
228 // label and, if it isn't and printExplanation is true, prints
229 // an explanation of why not.
234 if (result
&& strlen(serviceType
) > 63) {
235 if (printExplanation
) {
237 "%s: Service type is too long (must be 63 characters or less)\n",
242 if (result
&& serviceType
[0] == 0) {
243 if (printExplanation
) {
245 "%s: Service type can't be empty\n",
253 static mDNSBool
CheckThatServiceTextIsUsable(const char *serviceText
, mDNSBool printExplanation
,
254 mDNSu8
*pStringList
, mDNSu16
*pStringListLen
)
255 // Checks that serviceText is a reasonable service text record
256 // and, if it isn't and printExplanation is true, prints
257 // an explanation of why not. Also parse the text into
258 // the packed PString buffer denoted by pStringList and
259 // return the length of that buffer in *pStringListLen.
260 // Note that this routine assumes that the buffer is
261 // sizeof(RDataBody) bytes long.
264 size_t serviceTextLen
;
266 // Note that parsing a C string into a PString list always
267 // expands the data by one character, so the following
268 // compare is ">=", not ">". Here's the logic:
270 // #1 For a string with not ^A's, the PString length is one
271 // greater than the C string length because we add a length
273 // #2 For every regular (not ^A) character you add to the C
274 // string, you add a regular character to the PString list.
275 // This does not affect the equivalence stated in #1.
276 // #3 For every ^A you add to the C string, you add a length
277 // byte to the PString list but you also eliminate the ^A,
278 // which again does not affect the equivalence stated in #1.
281 serviceTextLen
= strlen(serviceText
);
282 if (result
&& strlen(serviceText
) >= sizeof(RDataBody
)) {
283 if (printExplanation
) {
285 "%s: Service text record is too long (must be less than %d characters)\n",
287 (int) sizeof(RDataBody
) );
292 // Now break the string up into PStrings delimited by ^A.
293 // We know the data will fit so we can ignore buffer overrun concerns.
294 // However, we still have to treat runs long than 255 characters as
298 int lastPStringOffset
;
302 // This algorithm is a little tricky. We start by copying
303 // the string directly into the output buffer, shifted up by
304 // one byte. We then fill in the first byte with a ^A.
305 // We then walk backwards through the buffer and, for each
306 // ^A that we find, we replace it with the difference between
307 // its offset and the offset of the last ^A that we found
308 // (ie lastPStringOffset).
310 memcpy(&pStringList
[1], serviceText
, serviceTextLen
);
312 lastPStringOffset
= serviceTextLen
+ 1;
313 for (i
= serviceTextLen
; i
>= 0; i
--) {
314 if ( pStringList
[i
] == 1 ) {
315 thisPStringLen
= (lastPStringOffset
- i
- 1);
316 assert(thisPStringLen
>= 0);
317 if (thisPStringLen
> 255) {
319 if (printExplanation
) {
321 "%s: Each component of the service text record must be 255 characters or less\n",
326 pStringList
[i
] = thisPStringLen
;
327 lastPStringOffset
= i
;
332 *pStringListLen
= serviceTextLen
+ 1;
338 static mDNSBool
CheckThatPortNumberIsUsable(long portNumber
, mDNSBool printExplanation
)
339 // Checks that portNumber is a reasonable port number
340 // and, if it isn't and printExplanation is true, prints
341 // an explanation of why not.
346 if (result
&& (portNumber
<= 0 || portNumber
> 65535)) {
347 if (printExplanation
) {
349 "%s: Port number specified by -p must be in range 1..65535\n",
357 #if COMPILER_LIKES_PRAGMA_MARK
358 #pragma mark ***** Command Line Arguments
361 static const char kDefaultPIDFile
[] = "/var/run/mDNSResponder.pid";
362 static const char kDefaultServiceType
[] = "_afpovertcp._tcp.";
363 static const char kDefaultServiceDomain
[] = "local.";
365 kDefaultPortNumber
= 548
368 static void PrintUsage()
371 "Usage: %s [-v level ] [-r] [-n name] [-t type] [-d domain] [-x TXT] [-p port] [-f file] [-b] [-P pidfile]\n",
373 fprintf(stderr
, " -v verbose mode, level is a number from 0 to 2\n");
374 fprintf(stderr
, " 0 = no debugging info (default)\n");
375 fprintf(stderr
, " 1 = standard debugging info\n");
376 fprintf(stderr
, " 2 = intense debugging info\n");
377 fprintf(stderr
, " can be cycled kill -USR1\n");
378 fprintf(stderr
, " -r also bind to port 53 (port 5353 is always bound)\n");
379 fprintf(stderr
, " -n uses 'name' as the host name (default is none)\n");
380 fprintf(stderr
, " -t uses 'type' as the service type (default is '%s')\n", kDefaultServiceType
);
381 fprintf(stderr
, " -d uses 'domain' as the service domain (default is '%s')\n", kDefaultServiceDomain
);
382 fprintf(stderr
, " -x uses 'TXT' as the service TXT record (default is empty)\n");
383 fprintf(stderr
, " -p uses 'port' as the port number (default is '%d')\n", kDefaultPortNumber
);
384 fprintf(stderr
, " -f reads a service list from 'file'\n");
385 fprintf(stderr
, " -b forces daemon (background) mode\n");
386 fprintf(stderr
, " -P uses 'pidfile' as the PID file\n");
387 fprintf(stderr
, " (default is '%s')\n", kDefaultPIDFile
);
388 fprintf(stderr
, " only meaningful if -b also specified\n");
391 static mDNSBool gAvoidPort53
= mDNStrue
;
392 static const char *gRichTextHostName
= "";
393 static const char *gServiceType
= kDefaultServiceType
;
394 static const char *gServiceDomain
= kDefaultServiceDomain
;
395 static mDNSu8 gServiceText
[sizeof(RDataBody
)];
396 static mDNSu16 gServiceTextLen
= 0;
397 static int gPortNumber
= kDefaultPortNumber
;
398 static const char *gServiceFile
= "";
399 static mDNSBool gDaemon
= mDNSfalse
;
400 static const char *gPIDFile
= kDefaultPIDFile
;
402 static void ParseArguments(int argc
, char **argv
)
403 // Parses our command line arguments into the global variables
408 // Set gProgramName to the last path component of argv[0]
410 gProgramName
= strrchr(argv
[0], '/');
411 if (gProgramName
== NULL
) {
412 gProgramName
= argv
[0];
417 // Parse command line options using getopt.
420 ch
= getopt(argc
, argv
, "v:rn:t:d:x:p:f:dPb");
424 gMDNSPlatformPosixVerboseLevel
= atoi(optarg
);
425 if (gMDNSPlatformPosixVerboseLevel
< 0 || gMDNSPlatformPosixVerboseLevel
> 2) {
427 "%s: Verbose mode must be in the range 0..2\n",
433 gAvoidPort53
= mDNSfalse
;
436 gRichTextHostName
= optarg
;
437 if ( ! CheckThatRichTextHostNameIsUsable(gRichTextHostName
, mDNStrue
) ) {
442 gServiceType
= optarg
;
443 if ( ! CheckThatServiceTypeIsUsable(gServiceType
, mDNStrue
) ) {
448 gServiceDomain
= optarg
;
451 if ( ! CheckThatServiceTextIsUsable(optarg
, mDNStrue
, gServiceText
, &gServiceTextLen
) ) {
456 gPortNumber
= atol(optarg
);
457 if ( ! CheckThatPortNumberIsUsable(gPortNumber
, mDNStrue
) ) {
462 gServiceFile
= optarg
;
479 // Check for any left over command line arguments.
481 if (optind
!= argc
) {
483 fprintf(stderr
, "%s: Unexpected argument '%s'\n", gProgramName
, argv
[optind
]);
487 // Check for inconsistency between the arguments.
489 if ( (gRichTextHostName
[0] == 0) && (gServiceFile
[0] == 0) ) {
491 fprintf(stderr
, "%s: You must specify a service to register (-n) or a service file (-f).\n", gProgramName
);
496 #if COMPILER_LIKES_PRAGMA_MARK
497 #pragma mark ***** Registration
500 typedef struct PosixService PosixService
;
502 struct PosixService
{
503 ServiceRecordSet coreServ
;
508 static PosixService
*gServiceList
= NULL
;
510 static void RegistrationCallback(mDNS
*const m
, ServiceRecordSet
*const thisRegistration
, mStatus status
)
511 // mDNS core calls this routine to tell us about the status of
512 // our registration. The appropriate action to take depends
513 // entirely on the value of status.
517 case mStatus_NoError
:
518 debugf("Callback: %##s Name Registered", thisRegistration
->RR_SRV
.resrec
.name
.c
);
519 // Do nothing; our name was successfully registered. We may
520 // get more call backs in the future.
523 case mStatus_NameConflict
:
524 debugf("Callback: %##s Name Conflict", thisRegistration
->RR_SRV
.resrec
.name
.c
);
526 // In the event of a conflict, this sample RegistrationCallback
527 // just calls mDNS_RenameAndReregisterService to automatically
528 // pick a new unique name for the service. For a device such as a
529 // printer, this may be appropriate. For a device with a user
530 // interface, and a screen, and a keyboard, the appropriate response
531 // may be to prompt the user and ask them to choose a new name for
534 // Also, what do we do if mDNS_RenameAndReregisterService returns an
535 // error. Right now I have no place to send that error to.
537 status
= mDNS_RenameAndReregisterService(m
, thisRegistration
, mDNSNULL
);
538 assert(status
== mStatus_NoError
);
541 case mStatus_MemFree
:
542 debugf("Callback: %##s Memory Free", thisRegistration
->RR_SRV
.resrec
.name
.c
);
544 // When debugging is enabled, make sure that thisRegistration
545 // is not on our gServiceList.
549 PosixService
*cursor
;
551 cursor
= gServiceList
;
552 while (cursor
!= NULL
) {
553 assert(&cursor
->coreServ
!= thisRegistration
);
554 cursor
= cursor
->next
;
558 free(thisRegistration
);
562 debugf("Callback: %##s Unknown Status %d", thisRegistration
->RR_SRV
.resrec
.name
.c
, status
);
567 static int gServiceID
= 0;
569 static mStatus
RegisterOneService(const char * richTextHostName
,
570 const char * serviceType
,
571 const char * serviceDomain
,
577 PosixService
* thisServ
;
582 status
= mStatus_NoError
;
583 thisServ
= (PosixService
*) malloc(sizeof(*thisServ
));
584 if (thisServ
== NULL
) {
585 status
= mStatus_NoMemoryErr
;
587 if (status
== mStatus_NoError
) {
588 MakeDomainLabelFromLiteralString(&name
, richTextHostName
);
589 MakeDomainNameFromDNSNameString(&type
, serviceType
);
590 MakeDomainNameFromDNSNameString(&domain
, serviceDomain
);
591 status
= mDNS_RegisterService(&mDNSStorage
, &thisServ
->coreServ
,
592 &name
, &type
, &domain
, // Name, type, domain
593 NULL
, mDNSOpaque16fromIntVal(portNumber
),
594 text
, textLen
, // TXT data, length
596 mDNSInterface_Any
, // Interface ID
597 RegistrationCallback
, thisServ
); // Callback and context
599 if (status
== mStatus_NoError
) {
600 thisServ
->serviceID
= gServiceID
;
603 thisServ
->next
= gServiceList
;
604 gServiceList
= thisServ
;
606 if (gMDNSPlatformPosixVerboseLevel
> 0) {
608 "%s: Registered service %d, name '%s', type '%s', port %ld\n",
616 if (thisServ
!= NULL
) {
623 static mDNSBool
ReadALine(char *buf
, size_t bufSize
, FILE *fp
)
625 mDNSBool good
= (fgets(buf
, bufSize
, fp
) != NULL
);
628 size_t len
= strlen(buf
);
629 good
= (len
> 0 && buf
[len
- 1] == '\n');
630 if (good
) buf
[len
- 1] = 0;
635 static mStatus
RegisterServicesInFile(const char *filePath
)
644 const char *dom
= kDefaultServiceDomain
;
646 mDNSu8 text
[sizeof(RDataBody
)];
650 status
= mStatus_NoError
;
651 fp
= fopen(filePath
, "r");
653 status
= mStatus_UnknownErr
;
655 if (status
== mStatus_NoError
) {
658 // Skip over any blank lines.
661 } while ( ch
== '\n' || ch
== '\r' );
663 good
= (ungetc(ch
, fp
) == ch
);
666 // Read three lines, check them for validity, and register the service.
667 if ( good
&& ! feof(fp
) ) {
668 good
= ReadALine(name
, sizeof(name
), fp
);
670 good
= ReadALine(type
, sizeof(type
), fp
);
674 while (*p
&& *p
!= ' ') p
++;
681 good
= ReadALine(rawText
, sizeof(rawText
), fp
);
684 good
= ReadALine(port
, sizeof(port
), fp
);
687 good
= CheckThatRichTextHostNameIsUsable(name
, mDNSfalse
)
688 && CheckThatServiceTypeIsUsable(type
, mDNSfalse
)
689 && CheckThatServiceTextIsUsable(rawText
, mDNSfalse
, text
, &textLen
)
690 && CheckThatPortNumberIsUsable(atol(port
), mDNSfalse
);
693 status
= RegisterOneService(name
, type
, dom
, text
, textLen
, atol(port
));
694 if (status
!= mStatus_NoError
) {
696 "%s: Failed to register service, name = %s, type = %s, port = %s\n",
701 status
= mStatus_NoError
; // keep reading
705 } while (good
&& !feof(fp
));
708 fprintf(stderr
, "%s: Error reading service file %s\n", gProgramName
, gServiceFile
);
720 static mStatus
RegisterOurServices(void)
724 status
= mStatus_NoError
;
725 if (gRichTextHostName
[0] != 0) {
726 status
= RegisterOneService(gRichTextHostName
,
729 gServiceText
, gServiceTextLen
,
732 if (status
== mStatus_NoError
&& gServiceFile
[0] != 0) {
733 status
= RegisterServicesInFile(gServiceFile
);
738 static void DeregisterOurServices(void)
740 PosixService
*thisServ
;
743 while (gServiceList
!= NULL
) {
744 thisServ
= gServiceList
;
745 gServiceList
= thisServ
->next
;
747 thisServID
= thisServ
->serviceID
;
749 mDNS_DeregisterService(&mDNSStorage
, &thisServ
->coreServ
);
751 if (gMDNSPlatformPosixVerboseLevel
> 0) {
753 "%s: Deregistered service %d\n",
755 thisServ
->serviceID
);
760 #if COMPILER_LIKES_PRAGMA_MARK
761 #pragma mark **** Main
764 #ifdef NOT_HAVE_DAEMON
766 // The version of Solaris that I tested on didn't have the daemon
767 // call. This implementation was basically stolen from the
768 // Mac OS X standard C library.
770 static int daemon(int nochdir
, int noclose
)
789 if (!noclose
&& (fd
= _open("/dev/null", O_RDWR
, 0)) != -1) {
790 (void)dup2(fd
, STDIN_FILENO
);
791 (void)dup2(fd
, STDOUT_FILENO
);
792 (void)dup2(fd
, STDERR_FILENO
);
799 #endif /* NOT_HAVE_DAEMON */
801 int main(int argc
, char **argv
)
806 // Parse our command line arguments. This won't come back if there's an error.
808 ParseArguments(argc
, argv
);
810 // If we're told to run as a daemon, then do that straight away.
811 // Note that we don't treat the inability to create our PID
812 // file as an error. Also note that we assign getpid to a long
813 // because printf has no format specified for pid_t.
816 if (gMDNSPlatformPosixVerboseLevel
> 0) {
817 fprintf(stderr
, "%s: Starting in daemon mode\n", gProgramName
);
824 fp
= fopen(gPIDFile
, "w");
826 fprintf(fp
, "%ld\n", (long) getpid());
832 if (gMDNSPlatformPosixVerboseLevel
> 0) {
833 fprintf(stderr
, "%s: Starting in foreground mode, PID %ld\n", gProgramName
, (long) getpid());
837 status
= mDNS_Init(&mDNSStorage
, &PlatformStorage
,
838 mDNS_Init_NoCache
, mDNS_Init_ZeroCacheSize
,
839 mDNS_Init_AdvertiseLocalAddresses
,
840 mDNS_Init_NoInitCallback
, mDNS_Init_NoInitCallbackContext
);
841 if (status
!= mStatus_NoError
) return(2);
843 status
= RegisterOurServices();
844 if (status
!= mStatus_NoError
) return(2);
846 signal(SIGHUP
, HandleSigHup
); // SIGHUP has to be sent by kill -HUP <pid>
847 signal(SIGINT
, HandleSigInt
); // SIGINT is what you get for a Ctrl-C
848 signal(SIGQUIT
, HandleSigQuit
); // SIGQUIT is what you get for a Ctrl-\ (indeed)
849 signal(SIGUSR1
, HandleSigUsr1
); // SIGUSR1 has to be sent by kill -USR1 <pid>
855 struct timeval timeout
;
858 // 1. Set up the fd_set as usual here.
859 // This example client has no file descriptors of its own,
860 // but a real application would call FD_SET to add them to the set here
863 // 2. Set up the timeout.
864 // This example client has no other work it needs to be doing,
865 // so we set an effectively infinite timeout
866 timeout
.tv_sec
= 0x3FFFFFFF;
869 // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout
870 mDNSPosixGetFDSet(&mDNSStorage
, &nfds
, &readfds
, &timeout
);
872 // 4. Call select as normal
873 verbosedebugf("select(%d, %d.%06d)", nfds
, timeout
.tv_sec
, timeout
.tv_usec
);
874 result
= select(nfds
, &readfds
, NULL
, NULL
, &timeout
);
878 verbosedebugf("select() returned %d errno %d", result
, errno
);
879 if (errno
!= EINTR
) gStopNow
= mDNStrue
;
882 if (gReceivedSigUsr1
)
884 gReceivedSigUsr1
= mDNSfalse
;
885 gMDNSPlatformPosixVerboseLevel
+= 1;
886 if (gMDNSPlatformPosixVerboseLevel
> 2)
887 gMDNSPlatformPosixVerboseLevel
= 0;
888 if ( gMDNSPlatformPosixVerboseLevel
> 0 )
889 fprintf(stderr
, "\nVerbose level %d\n", gMDNSPlatformPosixVerboseLevel
);
893 if (gMDNSPlatformPosixVerboseLevel
> 0)
894 fprintf(stderr
, "\nSIGHUP\n");
895 gReceivedSigHup
= mDNSfalse
;
896 DeregisterOurServices();
897 status
= mDNSPlatformPosixRefreshInterfaceList(&mDNSStorage
);
898 if (status
!= mStatus_NoError
) break;
899 status
= RegisterOurServices();
900 if (status
!= mStatus_NoError
) break;
906 // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work
907 mDNSPosixProcessFDSet(&mDNSStorage
, &readfds
);
909 // 6. This example client has no other work it needs to be doing,
910 // but a real client would do its work here
917 DeregisterOurServices();
918 mDNS_Close(&mDNSStorage
);
920 if (status
== mStatus_NoError
) {
925 if ( (result
!= 0) || (gMDNSPlatformPosixVerboseLevel
> 0) ) {
926 fprintf(stderr
, "%s: Finished with status %ld, result %d\n", gProgramName
, status
, result
);