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