]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSPosix/Responder.c
mDNSResponder-171.4.tar.gz
[apple/mdnsresponder.git] / mDNSPosix / Responder.c
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 Change History (most recent first):
18
19 $Log: Responder.c,v $
20 Revision 1.33 2007/04/16 20:49:39 cheshire
21 Fix compile errors for mDNSPosix build
22
23 Revision 1.32 2006/08/14 23:24:46 cheshire
24 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
25
26 Revision 1.31 2006/06/12 18:22:42 cheshire
27 <rdar://problem/4580067> mDNSResponder building warnings under Red Hat 64-bit (LP64) Linux
28
29 Revision 1.30 2005/10/26 22:21:16 cheshire
30 <rdar://problem/4149841> Potential buffer overflow in mDNSResponderPosix
31
32 Revision 1.29 2005/03/04 21:35:33 cheshire
33 <rdar://problem/4037201> Services.txt file not parsed properly when it contains more than one service
34
35 Revision 1.28 2005/01/11 01:55:26 ksekar
36 Fix compile errors in Posix debug build
37
38 Revision 1.27 2004/12/01 04:28:43 cheshire
39 <rdar://problem/3872803> Darwin patches for Solaris and Suse
40 Use version of daemon() provided in mDNSUNP.c instead of local copy
41
42 Revision 1.26 2004/11/30 22:37:01 cheshire
43 Update copyright dates and add "Mode: C; tab-width: 4" headers
44
45 Revision 1.25 2004/11/11 02:00:51 cheshire
46 Minor fixes to getopt, error message
47
48 Revision 1.24 2004/11/09 19:32:10 rpantos
49 Suggestion from Ademar de Souza Reis Jr. to allow comments in services file
50
51 Revision 1.23 2004/09/17 01:08:54 cheshire
52 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
53 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
54 declared in that file are ONLY appropriate to single-address-space embedded applications.
55 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
56
57 Revision 1.22 2004/09/16 01:58:22 cheshire
58 Fix compiler warnings
59
60 Revision 1.21 2004/06/15 03:48:07 cheshire
61 Update mDNSResponderPosix to take multiple name=val arguments in a sane way
62
63 Revision 1.20 2004/05/18 23:51:26 cheshire
64 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
65
66 Revision 1.19 2004/03/12 08:03:14 cheshire
67 Update comments
68
69 Revision 1.18 2004/01/25 00:00:55 cheshire
70 Change to use mDNSOpaque16fromIntVal() instead of shifting and masking
71
72 Revision 1.17 2003/12/11 19:11:55 cheshire
73 Fix compiler warning
74
75 Revision 1.16 2003/08/14 02:19:55 cheshire
76 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
77
78 Revision 1.15 2003/08/12 19:56:26 cheshire
79 Update to APSL 2.0
80
81 Revision 1.14 2003/08/06 18:20:51 cheshire
82 Makefile cleanup
83
84 Revision 1.13 2003/07/23 00:00:04 cheshire
85 Add comments
86
87 Revision 1.12 2003/07/15 01:55:16 cheshire
88 <rdar://problem/3315777> Need to implement service registration with subtypes
89
90 Revision 1.11 2003/07/14 18:11:54 cheshire
91 Fix stricter compiler warnings
92
93 Revision 1.10 2003/07/10 20:27:31 cheshire
94 <rdar://problem/3318717> mDNSResponder Posix version is missing a 'b' in the getopt option string
95
96 Revision 1.9 2003/07/02 21:19:59 cheshire
97 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
98
99 Revision 1.8 2003/06/18 05:48:41 cheshire
100 Fix warnings
101
102 Revision 1.7 2003/05/06 00:00:50 cheshire
103 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
104
105 Revision 1.6 2003/03/08 00:35:56 cheshire
106 Switched to using new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt")
107
108 Revision 1.5 2003/02/20 06:48:36 cheshire
109 <rdar://problem/3169535> Xserve RAID needs to do interface-specific registrations
110 Reviewed by: Josh Graessley, Bob Bradley
111
112 Revision 1.4 2003/01/28 03:07:46 cheshire
113 Add extra parameter to mDNS_RenameAndReregisterService(),
114 and add support for specifying a domain other than dot-local.
115
116 Revision 1.3 2002/09/21 20:44:53 zarzycki
117 Added APSL info
118
119 Revision 1.2 2002/09/19 04:20:44 cheshire
120 Remove high-ascii characters that confuse some systems
121
122 Revision 1.1 2002/09/17 06:24:35 cheshire
123 First checkin
124
125 */
126
127 #include "mDNSEmbeddedAPI.h"// Defines the interface to the client layer above
128 #include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
129
130 #include <assert.h>
131 #include <stdio.h> // For printf()
132 #include <stdlib.h> // For exit() etc.
133 #include <string.h> // For strlen() etc.
134 #include <unistd.h> // For select()
135 #include <errno.h> // For errno, EINTR
136 #include <signal.h>
137 #include <fcntl.h>
138
139 #if COMPILER_LIKES_PRAGMA_MARK
140 #pragma mark ***** Globals
141 #endif
142
143 static mDNS mDNSStorage; // mDNS core uses this to store its globals
144 static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
145
146 mDNSexport const char ProgramName[] = "mDNSResponderPosix";
147
148 static const char *gProgramName = ProgramName;
149
150 #if COMPILER_LIKES_PRAGMA_MARK
151 #pragma mark ***** Signals
152 #endif
153
154 static volatile mDNSBool gReceivedSigUsr1;
155 static volatile mDNSBool gReceivedSigHup;
156 static volatile mDNSBool gStopNow;
157
158 // We support 4 signals.
159 //
160 // o SIGUSR1 toggles verbose mode on and off in debug builds
161 // o SIGHUP triggers the program to re-read its preferences.
162 // o SIGINT causes an orderly shutdown of the program.
163 // o SIGQUIT causes a somewhat orderly shutdown (direct but dangerous)
164 // o SIGKILL kills us dead (easy to implement :-)
165 //
166 // There are fatal race conditions in our signal handling, but there's not much
167 // we can do about them while remaining within the Posix space. Specifically,
168 // if a signal arrives after we test the globals its sets but before we call
169 // select, the signal will be dropped. The user will have to send the signal
170 // again. Unfortunately, Posix does not have a "sigselect" to atomically
171 // modify the signal mask and start a select.
172
173 static void HandleSigUsr1(int sigraised)
174 // If we get a SIGUSR1 we toggle the state of the
175 // verbose mode.
176 {
177 assert(sigraised == SIGUSR1);
178 gReceivedSigUsr1 = mDNStrue;
179 }
180
181 static void HandleSigHup(int sigraised)
182 // A handler for SIGHUP that causes us to break out of the
183 // main event loop when the user kill 1's us. This has the
184 // effect of triggered the main loop to deregister the
185 // current services and re-read the preferences.
186 {
187 assert(sigraised == SIGHUP);
188 gReceivedSigHup = mDNStrue;
189 }
190
191 static void HandleSigInt(int sigraised)
192 // A handler for SIGINT that causes us to break out of the
193 // main event loop when the user types ^C. This has the
194 // effect of quitting the program.
195 {
196 assert(sigraised == SIGINT);
197
198 if (gMDNSPlatformPosixVerboseLevel > 0) {
199 fprintf(stderr, "\nSIGINT\n");
200 }
201 gStopNow = mDNStrue;
202 }
203
204 static void HandleSigQuit(int sigraised)
205 // If we get a SIGQUIT the user is desperate and we
206 // just call mDNS_Close directly. This is definitely
207 // not safe (because it could reenter mDNS), but
208 // we presume that the user has already tried the safe
209 // alternatives.
210 {
211 assert(sigraised == SIGQUIT);
212
213 if (gMDNSPlatformPosixVerboseLevel > 0) {
214 fprintf(stderr, "\nSIGQUIT\n");
215 }
216 mDNS_Close(&mDNSStorage);
217 exit(0);
218 }
219
220 #if COMPILER_LIKES_PRAGMA_MARK
221 #pragma mark ***** Parameter Checking
222 #endif
223
224 static mDNSBool CheckThatRichTextNameIsUsable(const char *richTextName, mDNSBool printExplanation)
225 // Checks that richTextName is reasonable
226 // label and, if it isn't and printExplanation is true, prints
227 // an explanation of why not.
228 {
229 mDNSBool result = mDNStrue;
230 if (result && strlen(richTextName) > 63) {
231 if (printExplanation) {
232 fprintf(stderr,
233 "%s: Service name is too long (must be 63 characters or less)\n",
234 gProgramName);
235 }
236 result = mDNSfalse;
237 }
238 if (result && richTextName[0] == 0) {
239 if (printExplanation) {
240 fprintf(stderr, "%s: Service name can't be empty\n", gProgramName);
241 }
242 result = mDNSfalse;
243 }
244 return result;
245 }
246
247 static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool printExplanation)
248 // Checks that serviceType is a reasonable service type
249 // label and, if it isn't and printExplanation is true, prints
250 // an explanation of why not.
251 {
252 mDNSBool result;
253
254 result = mDNStrue;
255 if (result && strlen(serviceType) > 63) {
256 if (printExplanation) {
257 fprintf(stderr,
258 "%s: Service type is too long (must be 63 characters or less)\n",
259 gProgramName);
260 }
261 result = mDNSfalse;
262 }
263 if (result && serviceType[0] == 0) {
264 if (printExplanation) {
265 fprintf(stderr,
266 "%s: Service type can't be empty\n",
267 gProgramName);
268 }
269 result = mDNSfalse;
270 }
271 return result;
272 }
273
274 static mDNSBool CheckThatPortNumberIsUsable(long portNumber, mDNSBool printExplanation)
275 // Checks that portNumber is a reasonable port number
276 // and, if it isn't and printExplanation is true, prints
277 // an explanation of why not.
278 {
279 mDNSBool result;
280
281 result = mDNStrue;
282 if (result && (portNumber <= 0 || portNumber > 65535)) {
283 if (printExplanation) {
284 fprintf(stderr,
285 "%s: Port number specified by -p must be in range 1..65535\n",
286 gProgramName);
287 }
288 result = mDNSfalse;
289 }
290 return result;
291 }
292
293 #if COMPILER_LIKES_PRAGMA_MARK
294 #pragma mark ***** Command Line Arguments
295 #endif
296
297 static const char kDefaultPIDFile[] = "/var/run/mDNSResponder.pid";
298 static const char kDefaultServiceType[] = "_afpovertcp._tcp.";
299 static const char kDefaultServiceDomain[] = "local.";
300 enum {
301 kDefaultPortNumber = 548
302 };
303
304 static void PrintUsage()
305 {
306 fprintf(stderr,
307 "Usage: %s [-v level ] [-r] [-n name] [-t type] [-d domain] [-p port] [-f file] [-b] [-P pidfile] [-x name=val ...]\n",
308 gProgramName);
309 fprintf(stderr, " -v verbose mode, level is a number from 0 to 2\n");
310 fprintf(stderr, " 0 = no debugging info (default)\n");
311 fprintf(stderr, " 1 = standard debugging info\n");
312 fprintf(stderr, " 2 = intense debugging info\n");
313 fprintf(stderr, " can be cycled kill -USR1\n");
314 fprintf(stderr, " -r also bind to port 53 (port 5353 is always bound)\n");
315 fprintf(stderr, " -n uses 'name' as the service name (required)\n");
316 fprintf(stderr, " -t uses 'type' as the service type (default is '%s')\n", kDefaultServiceType);
317 fprintf(stderr, " -d uses 'domain' as the service domain (default is '%s')\n", kDefaultServiceDomain);
318 fprintf(stderr, " -p uses 'port' as the port number (default is '%d')\n", kDefaultPortNumber);
319 fprintf(stderr, " -f reads a service list from 'file'\n");
320 fprintf(stderr, " -b forces daemon (background) mode\n");
321 fprintf(stderr, " -P uses 'pidfile' as the PID file\n");
322 fprintf(stderr, " (default is '%s')\n", kDefaultPIDFile);
323 fprintf(stderr, " only meaningful if -b also specified\n");
324 fprintf(stderr, " -x stores name=val in TXT record (default is empty).\n");
325 fprintf(stderr, " MUST be the last command-line argument;\n");
326 fprintf(stderr, " all subsequent arguments after -x are treated as name=val pairs.\n");
327 }
328
329 static mDNSBool gAvoidPort53 = mDNStrue;
330 static const char *gServiceName = "";
331 static const char *gServiceType = kDefaultServiceType;
332 static const char *gServiceDomain = kDefaultServiceDomain;
333 static mDNSu8 gServiceText[sizeof(RDataBody)];
334 static mDNSu16 gServiceTextLen = 0;
335 static int gPortNumber = kDefaultPortNumber;
336 static const char *gServiceFile = "";
337 static mDNSBool gDaemon = mDNSfalse;
338 static const char *gPIDFile = kDefaultPIDFile;
339
340 static void ParseArguments(int argc, char **argv)
341 // Parses our command line arguments into the global variables
342 // listed above.
343 {
344 int ch;
345
346 // Set gProgramName to the last path component of argv[0]
347
348 gProgramName = strrchr(argv[0], '/');
349 if (gProgramName == NULL) {
350 gProgramName = argv[0];
351 } else {
352 gProgramName += 1;
353 }
354
355 // Parse command line options using getopt.
356
357 do {
358 ch = getopt(argc, argv, "v:rn:t:d:p:f:dP:bx");
359 if (ch != -1) {
360 switch (ch) {
361 case 'v':
362 gMDNSPlatformPosixVerboseLevel = atoi(optarg);
363 if (gMDNSPlatformPosixVerboseLevel < 0 || gMDNSPlatformPosixVerboseLevel > 2) {
364 fprintf(stderr,
365 "%s: Verbose mode must be in the range 0..2\n",
366 gProgramName);
367 exit(1);
368 }
369 break;
370 case 'r':
371 gAvoidPort53 = mDNSfalse;
372 break;
373 case 'n':
374 gServiceName = optarg;
375 if ( ! CheckThatRichTextNameIsUsable(gServiceName, mDNStrue) ) {
376 exit(1);
377 }
378 break;
379 case 't':
380 gServiceType = optarg;
381 if ( ! CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) {
382 exit(1);
383 }
384 break;
385 case 'd':
386 gServiceDomain = optarg;
387 break;
388 case 'p':
389 gPortNumber = atol(optarg);
390 if ( ! CheckThatPortNumberIsUsable(gPortNumber, mDNStrue) ) {
391 exit(1);
392 }
393 break;
394 case 'f':
395 gServiceFile = optarg;
396 break;
397 case 'b':
398 gDaemon = mDNStrue;
399 break;
400 case 'P':
401 gPIDFile = optarg;
402 break;
403 case 'x':
404 while (optind < argc)
405 {
406 gServiceText[gServiceTextLen] = strlen(argv[optind]);
407 memcpy(gServiceText+gServiceTextLen+1, argv[optind], gServiceText[gServiceTextLen]);
408 gServiceTextLen += 1 + gServiceText[gServiceTextLen];
409 optind++;
410 }
411 ch = -1;
412 break;
413 case '?':
414 default:
415 PrintUsage();
416 exit(1);
417 break;
418 }
419 }
420 } while (ch != -1);
421
422 // Check for any left over command line arguments.
423
424 if (optind != argc) {
425 PrintUsage();
426 fprintf(stderr, "%s: Unexpected argument '%s'\n", gProgramName, argv[optind]);
427 exit(1);
428 }
429
430 // Check for inconsistency between the arguments.
431
432 if ( (gServiceName[0] == 0) && (gServiceFile[0] == 0) ) {
433 PrintUsage();
434 fprintf(stderr, "%s: You must specify a service name to register (-n) or a service file (-f).\n", gProgramName);
435 exit(1);
436 }
437 }
438
439 #if COMPILER_LIKES_PRAGMA_MARK
440 #pragma mark ***** Registration
441 #endif
442
443 typedef struct PosixService PosixService;
444
445 struct PosixService {
446 ServiceRecordSet coreServ;
447 PosixService *next;
448 int serviceID;
449 };
450
451 static PosixService *gServiceList = NULL;
452
453 static void RegistrationCallback(mDNS *const m, ServiceRecordSet *const thisRegistration, mStatus status)
454 // mDNS core calls this routine to tell us about the status of
455 // our registration. The appropriate action to take depends
456 // entirely on the value of status.
457 {
458 switch (status) {
459
460 case mStatus_NoError:
461 debugf("Callback: %##s Name Registered", thisRegistration->RR_SRV.resrec.name->c);
462 // Do nothing; our name was successfully registered. We may
463 // get more call backs in the future.
464 break;
465
466 case mStatus_NameConflict:
467 debugf("Callback: %##s Name Conflict", thisRegistration->RR_SRV.resrec.name->c);
468
469 // In the event of a conflict, this sample RegistrationCallback
470 // just calls mDNS_RenameAndReregisterService to automatically
471 // pick a new unique name for the service. For a device such as a
472 // printer, this may be appropriate. For a device with a user
473 // interface, and a screen, and a keyboard, the appropriate response
474 // may be to prompt the user and ask them to choose a new name for
475 // the service.
476 //
477 // Also, what do we do if mDNS_RenameAndReregisterService returns an
478 // error. Right now I have no place to send that error to.
479
480 status = mDNS_RenameAndReregisterService(m, thisRegistration, mDNSNULL);
481 assert(status == mStatus_NoError);
482 break;
483
484 case mStatus_MemFree:
485 debugf("Callback: %##s Memory Free", thisRegistration->RR_SRV.resrec.name->c);
486
487 // When debugging is enabled, make sure that thisRegistration
488 // is not on our gServiceList.
489
490 #if !defined(NDEBUG)
491 {
492 PosixService *cursor;
493
494 cursor = gServiceList;
495 while (cursor != NULL) {
496 assert(&cursor->coreServ != thisRegistration);
497 cursor = cursor->next;
498 }
499 }
500 #endif
501 free(thisRegistration);
502 break;
503
504 default:
505 debugf("Callback: %##s Unknown Status %ld", thisRegistration->RR_SRV.resrec.name->c, status);
506 break;
507 }
508 }
509
510 static int gServiceID = 0;
511
512 static mStatus RegisterOneService(const char * richTextName,
513 const char * serviceType,
514 const char * serviceDomain,
515 const mDNSu8 text[],
516 mDNSu16 textLen,
517 long portNumber)
518 {
519 mStatus status;
520 PosixService * thisServ;
521 domainlabel name;
522 domainname type;
523 domainname domain;
524
525 status = mStatus_NoError;
526 thisServ = (PosixService *) malloc(sizeof(*thisServ));
527 if (thisServ == NULL) {
528 status = mStatus_NoMemoryErr;
529 }
530 if (status == mStatus_NoError) {
531 MakeDomainLabelFromLiteralString(&name, richTextName);
532 MakeDomainNameFromDNSNameString(&type, serviceType);
533 MakeDomainNameFromDNSNameString(&domain, serviceDomain);
534 status = mDNS_RegisterService(&mDNSStorage, &thisServ->coreServ,
535 &name, &type, &domain, // Name, type, domain
536 NULL, mDNSOpaque16fromIntVal(portNumber),
537 text, textLen, // TXT data, length
538 NULL, 0, // Subtypes
539 mDNSInterface_Any, // Interface ID
540 RegistrationCallback, thisServ); // Callback and context
541 }
542 if (status == mStatus_NoError) {
543 thisServ->serviceID = gServiceID;
544 gServiceID += 1;
545
546 thisServ->next = gServiceList;
547 gServiceList = thisServ;
548
549 if (gMDNSPlatformPosixVerboseLevel > 0) {
550 fprintf(stderr,
551 "%s: Registered service %d, name '%s', type '%s', port %ld\n",
552 gProgramName,
553 thisServ->serviceID,
554 richTextName,
555 serviceType,
556 portNumber);
557 }
558 } else {
559 if (thisServ != NULL) {
560 free(thisServ);
561 }
562 }
563 return status;
564 }
565
566 static mDNSBool ReadALine(char *buf, size_t bufSize, FILE *fp)
567 // Read a line, skipping over any blank lines or lines starting with '#'
568 {
569 mDNSBool good, skip;
570 do {
571 good = (fgets(buf, bufSize, fp) != NULL);
572 skip = (good && (buf[0] == '#'));
573 } while (good && skip);
574 if (good)
575 {
576 int len = strlen( buf);
577 if ( buf[len - 1] == '\r' || buf[len - 1] == '\n')
578 buf[len - 1] = '\0';
579 }
580 return good;
581 }
582
583 static mStatus RegisterServicesInFile(const char *filePath)
584 {
585 mStatus status = mStatus_NoError;
586 FILE * fp = fopen(filePath, "r");
587 int junk;
588
589 if (fp == NULL) {
590 status = mStatus_UnknownErr;
591 }
592 if (status == mStatus_NoError) {
593 mDNSBool good = mDNStrue;
594 do {
595 int ch;
596 char name[256];
597 char type[256];
598 const char *dom = kDefaultServiceDomain;
599 char rawText[1024];
600 mDNSu8 text[sizeof(RDataBody)];
601 unsigned int textLen = 0;
602 char port[256];
603
604 // Skip over any blank lines.
605 do ch = fgetc(fp); while ( ch == '\n' || ch == '\r' );
606 if (ch != EOF) good = (ungetc(ch, fp) == ch);
607
608 // Read three lines, check them for validity, and register the service.
609 good = ReadALine(name, sizeof(name), fp);
610 if (good) {
611 good = ReadALine(type, sizeof(type), fp);
612 }
613 if (good) {
614 char *p = type;
615 while (*p && *p != ' ') p++;
616 if (*p) {
617 *p = 0;
618 dom = p+1;
619 }
620 }
621 if (good) {
622 good = ReadALine(port, sizeof(port), fp);
623 }
624 if (good) {
625 good = CheckThatRichTextNameIsUsable(name, mDNSfalse)
626 && CheckThatServiceTypeIsUsable(type, mDNSfalse)
627 && CheckThatPortNumberIsUsable(atol(port), mDNSfalse);
628 }
629 if (good) {
630 while (1) {
631 int len;
632 if (!ReadALine(rawText, sizeof(rawText), fp)) break;
633 len = strlen(rawText);
634 if (len <= 255)
635 {
636 unsigned int newlen = textLen + 1 + len;
637 if (len == 0 || newlen >= sizeof(text)) break;
638 text[textLen] = len;
639 memcpy(text + textLen + 1, rawText, len);
640 textLen = newlen;
641 }
642 else
643 fprintf(stderr, "%s: TXT attribute too long for name = %s, type = %s, port = %s\n",
644 gProgramName, name, type, port);
645 }
646 }
647 if (good) {
648 status = RegisterOneService(name, type, dom, text, textLen, atol(port));
649 if (status != mStatus_NoError) {
650 fprintf(stderr, "%s: Failed to register service, name = %s, type = %s, port = %s\n",
651 gProgramName, name, type, port);
652 status = mStatus_NoError; // keep reading
653 }
654 }
655 } while (good && !feof(fp));
656
657 if ( ! good ) {
658 fprintf(stderr, "%s: Error reading service file %s\n", gProgramName, filePath);
659 }
660 }
661
662 if (fp != NULL) {
663 junk = fclose(fp);
664 assert(junk == 0);
665 }
666
667 return status;
668 }
669
670 static mStatus RegisterOurServices(void)
671 {
672 mStatus status;
673
674 status = mStatus_NoError;
675 if (gServiceName[0] != 0) {
676 status = RegisterOneService(gServiceName,
677 gServiceType,
678 gServiceDomain,
679 gServiceText, gServiceTextLen,
680 gPortNumber);
681 }
682 if (status == mStatus_NoError && gServiceFile[0] != 0) {
683 status = RegisterServicesInFile(gServiceFile);
684 }
685 return status;
686 }
687
688 static void DeregisterOurServices(void)
689 {
690 PosixService *thisServ;
691 int thisServID;
692
693 while (gServiceList != NULL) {
694 thisServ = gServiceList;
695 gServiceList = thisServ->next;
696
697 thisServID = thisServ->serviceID;
698
699 mDNS_DeregisterService(&mDNSStorage, &thisServ->coreServ);
700
701 if (gMDNSPlatformPosixVerboseLevel > 0) {
702 fprintf(stderr,
703 "%s: Deregistered service %d\n",
704 gProgramName,
705 thisServ->serviceID);
706 }
707 }
708 }
709
710 #if COMPILER_LIKES_PRAGMA_MARK
711 #pragma mark **** Main
712 #endif
713
714 int main(int argc, char **argv)
715 {
716 mStatus status;
717 int result;
718
719 // Parse our command line arguments. This won't come back if there's an error.
720
721 ParseArguments(argc, argv);
722
723 // If we're told to run as a daemon, then do that straight away.
724 // Note that we don't treat the inability to create our PID
725 // file as an error. Also note that we assign getpid to a long
726 // because printf has no format specified for pid_t.
727
728 if (gDaemon) {
729 if (gMDNSPlatformPosixVerboseLevel > 0) {
730 fprintf(stderr, "%s: Starting in daemon mode\n", gProgramName);
731 }
732 daemon(0,0);
733 {
734 FILE *fp;
735 int junk;
736
737 fp = fopen(gPIDFile, "w");
738 if (fp != NULL) {
739 fprintf(fp, "%ld\n", (long) getpid());
740 junk = fclose(fp);
741 assert(junk == 0);
742 }
743 }
744 } else {
745 if (gMDNSPlatformPosixVerboseLevel > 0) {
746 fprintf(stderr, "%s: Starting in foreground mode, PID %ld\n", gProgramName, (long) getpid());
747 }
748 }
749
750 status = mDNS_Init(&mDNSStorage, &PlatformStorage,
751 mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
752 mDNS_Init_AdvertiseLocalAddresses,
753 mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
754 if (status != mStatus_NoError) return(2);
755
756 status = RegisterOurServices();
757 if (status != mStatus_NoError) return(2);
758
759 signal(SIGHUP, HandleSigHup); // SIGHUP has to be sent by kill -HUP <pid>
760 signal(SIGINT, HandleSigInt); // SIGINT is what you get for a Ctrl-C
761 signal(SIGQUIT, HandleSigQuit); // SIGQUIT is what you get for a Ctrl-\ (indeed)
762 signal(SIGUSR1, HandleSigUsr1); // SIGUSR1 has to be sent by kill -USR1 <pid>
763
764 while (!gStopNow)
765 {
766 int nfds = 0;
767 fd_set readfds;
768 struct timeval timeout;
769 int result;
770
771 // 1. Set up the fd_set as usual here.
772 // This example client has no file descriptors of its own,
773 // but a real application would call FD_SET to add them to the set here
774 FD_ZERO(&readfds);
775
776 // 2. Set up the timeout.
777 // This example client has no other work it needs to be doing,
778 // so we set an effectively infinite timeout
779 timeout.tv_sec = 0x3FFFFFFF;
780 timeout.tv_usec = 0;
781
782 // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout
783 mDNSPosixGetFDSet(&mDNSStorage, &nfds, &readfds, &timeout);
784
785 // 4. Call select as normal
786 verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec);
787 result = select(nfds, &readfds, NULL, NULL, &timeout);
788
789 if (result < 0)
790 {
791 verbosedebugf("select() returned %d errno %d", result, errno);
792 if (errno != EINTR) gStopNow = mDNStrue;
793 else
794 {
795 if (gReceivedSigUsr1)
796 {
797 gReceivedSigUsr1 = mDNSfalse;
798 gMDNSPlatformPosixVerboseLevel += 1;
799 if (gMDNSPlatformPosixVerboseLevel > 2)
800 gMDNSPlatformPosixVerboseLevel = 0;
801 if ( gMDNSPlatformPosixVerboseLevel > 0 )
802 fprintf(stderr, "\nVerbose level %d\n", gMDNSPlatformPosixVerboseLevel);
803 }
804 if (gReceivedSigHup)
805 {
806 if (gMDNSPlatformPosixVerboseLevel > 0)
807 fprintf(stderr, "\nSIGHUP\n");
808 gReceivedSigHup = mDNSfalse;
809 DeregisterOurServices();
810 status = mDNSPlatformPosixRefreshInterfaceList(&mDNSStorage);
811 if (status != mStatus_NoError) break;
812 status = RegisterOurServices();
813 if (status != mStatus_NoError) break;
814 }
815 }
816 }
817 else
818 {
819 // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work
820 mDNSPosixProcessFDSet(&mDNSStorage, &readfds);
821
822 // 6. This example client has no other work it needs to be doing,
823 // but a real client would do its work here
824 // ... (do work) ...
825 }
826 }
827
828 debugf("Exiting");
829
830 DeregisterOurServices();
831 mDNS_Close(&mDNSStorage);
832
833 if (status == mStatus_NoError) {
834 result = 0;
835 } else {
836 result = 2;
837 }
838 if ( (result != 0) || (gMDNSPlatformPosixVerboseLevel > 0) ) {
839 fprintf(stderr, "%s: Finished with status %d, result %d\n", gProgramName, (int)status, result);
840 }
841
842 return result;
843 }