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