]> git.saurik.com Git - apple/mdnsresponder.git/blame - mDNSPosix/Responder.c
mDNSResponder-333.10.tar.gz
[apple/mdnsresponder.git] / mDNSPosix / Responder.c
CommitLineData
7f0064bd
A
1/* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
c9b9ae52 4 *
67c8f8a1
A
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
c9b9ae52 8 *
67c8f8a1 9 * http://www.apache.org/licenses/LICENSE-2.0
c9b9ae52 10 *
67c8f8a1
A
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
c9b9ae52 15 * limitations under the License.
263eeeab
A
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
c9b9ae52
A
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
263eeeab
A
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
c9b9ae52
A
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
67c8f8a1
A
51mDNSexport const char ProgramName[] = "mDNSResponderPosix";
52
53static const char *gProgramName = ProgramName;
c9b9ae52
A
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
7cb34e5c
A
129static mDNSBool CheckThatRichTextNameIsUsable(const char *richTextName, mDNSBool printExplanation)
130 // Checks that richTextName is reasonable
c9b9ae52
A
131 // label and, if it isn't and printExplanation is true, prints
132 // an explanation of why not.
133{
7cb34e5c
A
134 mDNSBool result = mDNStrue;
135 if (result && strlen(richTextName) > 63) {
c9b9ae52
A
136 if (printExplanation) {
137 fprintf(stderr,
7cb34e5c 138 "%s: Service name is too long (must be 63 characters or less)\n",
c9b9ae52
A
139 gProgramName);
140 }
141 result = mDNSfalse;
142 }
7cb34e5c 143 if (result && richTextName[0] == 0) {
c9b9ae52 144 if (printExplanation) {
7cb34e5c 145 fprintf(stderr, "%s: Service name can't be empty\n", gProgramName);
c9b9ae52
A
146 }
147 result = mDNSfalse;
148 }
c9b9ae52
A
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
c9b9ae52
A
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,
7f0064bd 212 "Usage: %s [-v level ] [-r] [-n name] [-t type] [-d domain] [-p port] [-f file] [-b] [-P pidfile] [-x name=val ...]\n",
c9b9ae52
A
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");
8e92c31c 219 fprintf(stderr, " -r also bind to port 53 (port 5353 is always bound)\n");
7cb34e5c 220 fprintf(stderr, " -n uses 'name' as the service name (required)\n");
c9b9ae52
A
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);
c9b9ae52
A
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");
7f0064bd
A
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");
c9b9ae52
A
232}
233
8e92c31c 234static mDNSBool gAvoidPort53 = mDNStrue;
7cb34e5c 235static const char *gServiceName = "";
c9b9ae52
A
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 {
7f0064bd 263 ch = getopt(argc, argv, "v:rn:t:d:p:f:dP:bx");
c9b9ae52
A
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;
8e92c31c
A
275 case 'r':
276 gAvoidPort53 = mDNSfalse;
277 break;
c9b9ae52 278 case 'n':
7cb34e5c
A
279 gServiceName = optarg;
280 if ( ! CheckThatRichTextNameIsUsable(gServiceName, mDNStrue) ) {
c9b9ae52
A
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;
c9b9ae52
A
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;
7f0064bd
A
308 case 'x':
309 while (optind < argc)
310 {
311 gServiceText[gServiceTextLen] = strlen(argv[optind]);
32bb7e43 312 mDNSPlatformMemCopy(gServiceText+gServiceTextLen+1, argv[optind], gServiceText[gServiceTextLen]);
7f0064bd
A
313 gServiceTextLen += 1 + gServiceText[gServiceTextLen];
314 optind++;
315 }
316 ch = -1;
317 break;
c9b9ae52
A
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
7cb34e5c 337 if ( (gServiceName[0] == 0) && (gServiceFile[0] == 0) ) {
c9b9ae52 338 PrintUsage();
7cb34e5c 339 fprintf(stderr, "%s: You must specify a service name to register (-n) or a service file (-f).\n", gProgramName);
c9b9ae52
A
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:
283ee3ff 366 debugf("Callback: %##s Name Registered", thisRegistration->RR_SRV.resrec.name->c);
c9b9ae52
A
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:
283ee3ff 372 debugf("Callback: %##s Name Conflict", thisRegistration->RR_SRV.resrec.name->c);
c9b9ae52
A
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:
283ee3ff 390 debugf("Callback: %##s Memory Free", thisRegistration->RR_SRV.resrec.name->c);
c9b9ae52
A
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:
283ee3ff 410 debugf("Callback: %##s Unknown Status %ld", thisRegistration->RR_SRV.resrec.name->c, status);
c9b9ae52
A
411 break;
412 }
413}
414
415static int gServiceID = 0;
416
7cb34e5c 417static mStatus RegisterOneService(const char * richTextName,
c9b9ae52
A
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;
c9b9ae52
A
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) {
7cb34e5c 436 MakeDomainLabelFromLiteralString(&name, richTextName);
c9b9ae52
A
437 MakeDomainNameFromDNSNameString(&type, serviceType);
438 MakeDomainNameFromDNSNameString(&domain, serviceDomain);
c9b9ae52
A
439 status = mDNS_RegisterService(&mDNSStorage, &thisServ->coreServ,
440 &name, &type, &domain, // Name, type, domain
8e92c31c 441 NULL, mDNSOpaque16fromIntVal(portNumber),
c9b9ae52
A
442 text, textLen, // TXT data, length
443 NULL, 0, // Subtypes
8e92c31c 444 mDNSInterface_Any, // Interface ID
294beb6e 445 RegistrationCallback, thisServ, 0); // Callback, context, flags
c9b9ae52
A
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,
8b5f5b69 456 "%s: Registered service %d, name '%s', type '%s', port %ld\n",
c9b9ae52
A
457 gProgramName,
458 thisServ->serviceID,
7cb34e5c 459 richTextName,
c9b9ae52
A
460 serviceType,
461 portNumber);
462 }
463 } else {
464 if (thisServ != NULL) {
465 free(thisServ);
466 }
467 }
468 return status;
469}
470
8b5f5b69
A
471static mDNSBool ReadALine(char *buf, size_t bufSize, FILE *fp)
472// Read a line, skipping over any blank lines or lines starting with '#'
7f0064bd 473{
8b5f5b69 474 mDNSBool good, skip;
7f0064bd 475 do {
8b5f5b69
A
476 good = (fgets(buf, bufSize, fp) != NULL);
477 skip = (good && (buf[0] == '#'));
478 } while (good && skip);
479 if (good)
480 {
481 int len = strlen( buf);
482 if ( buf[len - 1] == '\r' || buf[len - 1] == '\n')
483 buf[len - 1] = '\0';
484 }
485 return good;
7f0064bd 486}
c9b9ae52
A
487
488static mStatus RegisterServicesInFile(const char *filePath)
489{
7f0064bd
A
490 mStatus status = mStatus_NoError;
491 FILE * fp = fopen(filePath, "r");
8b5f5b69 492 int junk;
c9b9ae52 493
c9b9ae52 494 if (fp == NULL) {
8b5f5b69 495 status = mStatus_UnknownErr;
c9b9ae52 496 }
8b5f5b69
A
497 if (status == mStatus_NoError) {
498 mDNSBool good = mDNStrue;
499 do {
500 int ch;
501 char name[256];
502 char type[256];
503 const char *dom = kDefaultServiceDomain;
504 char rawText[1024];
505 mDNSu8 text[sizeof(RDataBody)];
506 unsigned int textLen = 0;
507 char port[256];
508
509 // Skip over any blank lines.
510 do ch = fgetc(fp); while ( ch == '\n' || ch == '\r' );
511 if (ch != EOF) good = (ungetc(ch, fp) == ch);
512
513 // Read three lines, check them for validity, and register the service.
514 good = ReadALine(name, sizeof(name), fp);
515 if (good) {
516 good = ReadALine(type, sizeof(type), fp);
517 }
518 if (good) {
519 char *p = type;
520 while (*p && *p != ' ') p++;
521 if (*p) {
522 *p = 0;
523 dom = p+1;
8ec81f6c 524 }
8b5f5b69
A
525 }
526 if (good) {
527 good = ReadALine(port, sizeof(port), fp);
528 }
529 if (good) {
530 good = CheckThatRichTextNameIsUsable(name, mDNSfalse)
531 && CheckThatServiceTypeIsUsable(type, mDNSfalse)
532 && CheckThatPortNumberIsUsable(atol(port), mDNSfalse);
533 }
534 if (good) {
535 while (1) {
536 int len;
537 if (!ReadALine(rawText, sizeof(rawText), fp)) break;
538 len = strlen(rawText);
539 if (len <= 255)
540 {
541 unsigned int newlen = textLen + 1 + len;
542 if (len == 0 || newlen >= sizeof(text)) break;
543 text[textLen] = len;
544 mDNSPlatformMemCopy(text + textLen + 1, rawText, len);
545 textLen = newlen;
546 }
547 else
548 fprintf(stderr, "%s: TXT attribute too long for name = %s, type = %s, port = %s\n",
549 gProgramName, name, type, port);
550 }
551 }
552 if (good) {
553 status = RegisterOneService(name, type, dom, text, textLen, atol(port));
554 if (status != mStatus_NoError) {
555 fprintf(stderr, "%s: Failed to register service, name = %s, type = %s, port = %s\n",
556 gProgramName, name, type, port);
557 status = mStatus_NoError; // keep reading
558 }
559 }
560 } while (good && !feof(fp));
561
562 if ( ! good ) {
563 fprintf(stderr, "%s: Error reading service file %s\n", gProgramName, filePath);
564 }
565 }
c9b9ae52 566
8b5f5b69
A
567 if (fp != NULL) {
568 junk = fclose(fp);
569 assert(junk == 0);
570 }
c9b9ae52 571
8b5f5b69 572 return status;
c9b9ae52
A
573}
574
575static mStatus RegisterOurServices(void)
576{
577 mStatus status;
578
579 status = mStatus_NoError;
7cb34e5c
A
580 if (gServiceName[0] != 0) {
581 status = RegisterOneService(gServiceName,
c9b9ae52
A
582 gServiceType,
583 gServiceDomain,
584 gServiceText, gServiceTextLen,
585 gPortNumber);
586 }
587 if (status == mStatus_NoError && gServiceFile[0] != 0) {
588 status = RegisterServicesInFile(gServiceFile);
589 }
590 return status;
591}
592
593static void DeregisterOurServices(void)
594{
595 PosixService *thisServ;
596 int thisServID;
597
598 while (gServiceList != NULL) {
599 thisServ = gServiceList;
600 gServiceList = thisServ->next;
601
602 thisServID = thisServ->serviceID;
603
604 mDNS_DeregisterService(&mDNSStorage, &thisServ->coreServ);
605
606 if (gMDNSPlatformPosixVerboseLevel > 0) {
607 fprintf(stderr,
608 "%s: Deregistered service %d\n",
609 gProgramName,
610 thisServ->serviceID);
611 }
612 }
613}
614
615#if COMPILER_LIKES_PRAGMA_MARK
616#pragma mark **** Main
617#endif
618
c9b9ae52
A
619int main(int argc, char **argv)
620{
621 mStatus status;
622 int result;
623
624 // Parse our command line arguments. This won't come back if there's an error.
625
626 ParseArguments(argc, argv);
627
628 // If we're told to run as a daemon, then do that straight away.
629 // Note that we don't treat the inability to create our PID
630 // file as an error. Also note that we assign getpid to a long
631 // because printf has no format specified for pid_t.
632
633 if (gDaemon) {
32bb7e43 634 int result;
c9b9ae52
A
635 if (gMDNSPlatformPosixVerboseLevel > 0) {
636 fprintf(stderr, "%s: Starting in daemon mode\n", gProgramName);
637 }
32bb7e43
A
638 result = daemon(0,0);
639 if (result == 0) {
c9b9ae52
A
640 FILE *fp;
641 int junk;
642
643 fp = fopen(gPIDFile, "w");
644 if (fp != NULL) {
645 fprintf(fp, "%ld\n", (long) getpid());
646 junk = fclose(fp);
647 assert(junk == 0);
648 }
32bb7e43
A
649 } else {
650 fprintf(stderr, "%s: Could not run as daemon - exiting\n", gProgramName);
651 exit(result);
c9b9ae52
A
652 }
653 } else {
654 if (gMDNSPlatformPosixVerboseLevel > 0) {
655 fprintf(stderr, "%s: Starting in foreground mode, PID %ld\n", gProgramName, (long) getpid());
656 }
657 }
658
659 status = mDNS_Init(&mDNSStorage, &PlatformStorage,
660 mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
661 mDNS_Init_AdvertiseLocalAddresses,
662 mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
663 if (status != mStatus_NoError) return(2);
664
665 status = RegisterOurServices();
666 if (status != mStatus_NoError) return(2);
667
668 signal(SIGHUP, HandleSigHup); // SIGHUP has to be sent by kill -HUP <pid>
669 signal(SIGINT, HandleSigInt); // SIGINT is what you get for a Ctrl-C
670 signal(SIGQUIT, HandleSigQuit); // SIGQUIT is what you get for a Ctrl-\ (indeed)
671 signal(SIGUSR1, HandleSigUsr1); // SIGUSR1 has to be sent by kill -USR1 <pid>
672
673 while (!gStopNow)
674 {
675 int nfds = 0;
676 fd_set readfds;
677 struct timeval timeout;
678 int result;
679
680 // 1. Set up the fd_set as usual here.
681 // This example client has no file descriptors of its own,
682 // but a real application would call FD_SET to add them to the set here
683 FD_ZERO(&readfds);
684
685 // 2. Set up the timeout.
686 // This example client has no other work it needs to be doing,
687 // so we set an effectively infinite timeout
688 timeout.tv_sec = 0x3FFFFFFF;
689 timeout.tv_usec = 0;
690
691 // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout
692 mDNSPosixGetFDSet(&mDNSStorage, &nfds, &readfds, &timeout);
693
694 // 4. Call select as normal
695 verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec);
696 result = select(nfds, &readfds, NULL, NULL, &timeout);
697
698 if (result < 0)
699 {
700 verbosedebugf("select() returned %d errno %d", result, errno);
701 if (errno != EINTR) gStopNow = mDNStrue;
702 else
703 {
704 if (gReceivedSigUsr1)
705 {
706 gReceivedSigUsr1 = mDNSfalse;
707 gMDNSPlatformPosixVerboseLevel += 1;
708 if (gMDNSPlatformPosixVerboseLevel > 2)
709 gMDNSPlatformPosixVerboseLevel = 0;
710 if ( gMDNSPlatformPosixVerboseLevel > 0 )
711 fprintf(stderr, "\nVerbose level %d\n", gMDNSPlatformPosixVerboseLevel);
712 }
713 if (gReceivedSigHup)
714 {
715 if (gMDNSPlatformPosixVerboseLevel > 0)
716 fprintf(stderr, "\nSIGHUP\n");
717 gReceivedSigHup = mDNSfalse;
718 DeregisterOurServices();
719 status = mDNSPlatformPosixRefreshInterfaceList(&mDNSStorage);
720 if (status != mStatus_NoError) break;
721 status = RegisterOurServices();
722 if (status != mStatus_NoError) break;
723 }
724 }
725 }
726 else
727 {
728 // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work
729 mDNSPosixProcessFDSet(&mDNSStorage, &readfds);
730
731 // 6. This example client has no other work it needs to be doing,
732 // but a real client would do its work here
733 // ... (do work) ...
734 }
735 }
736
737 debugf("Exiting");
738
739 DeregisterOurServices();
740 mDNS_Close(&mDNSStorage);
741
742 if (status == mStatus_NoError) {
743 result = 0;
744 } else {
745 result = 2;
746 }
747 if ( (result != 0) || (gMDNSPlatformPosixVerboseLevel > 0) ) {
67c8f8a1 748 fprintf(stderr, "%s: Finished with status %d, result %d\n", gProgramName, (int)status, result);
c9b9ae52
A
749 }
750
751 return result;
752}