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