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