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