]> git.saurik.com Git - apple/mdnsresponder.git/blame - mDNSPosix/Responder.c
mDNSResponder-1096.100.3.tar.gz
[apple/mdnsresponder.git] / mDNSPosix / Responder.c
CommitLineData
7f0064bd
A
1/* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
c9b9ae52 4 *
67c8f8a1
A
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
83fb1e36 8 *
67c8f8a1 9 * http://www.apache.org/licenses/LICENSE-2.0
83fb1e36 10 *
67c8f8a1
A
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
c9b9ae52 15 * limitations under the License.
263eeeab
A
16 */
17
18#if __APPLE__
19// In Mac OS X 10.5 and later trying to use the daemon function gives a “‘daemon’ is deprecated”
20// error, which prevents compilation because we build with "-Werror".
21// Since this is supposed to be portable cross-platform code, we don't care that daemon is
22// deprecated on Mac OS X 10.5, so we use this preprocessor trick to eliminate the error message.
23#define daemon yes_we_know_that_daemon_is_deprecated_in_os_x_10_5_thankyou
24#endif
c9b9ae52
A
25
26#include <assert.h>
83fb1e36
A
27#include <stdio.h> // For printf()
28#include <stdlib.h> // For exit() etc.
29#include <string.h> // For strlen() etc.
30#include <unistd.h> // For select()
31#include <errno.h> // For errno, EINTR
c9b9ae52
A
32#include <signal.h>
33#include <fcntl.h>
f0cc3e7b 34#include <sys/socket.h>
c9b9ae52 35
263eeeab
A
36#if __APPLE__
37#undef daemon
38extern int daemon(int, int);
39#endif
40
83fb1e36
A
41#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above
42#include "mDNSPosix.h" // Defines the specific types needed to run mDNS on this platform
43#include "mDNSUNP.h" // For daemon()
263eeeab 44
c9b9ae52
A
45#if COMPILER_LIKES_PRAGMA_MARK
46#pragma mark ***** Globals
47#endif
48
f0cc3e7b 49mDNS mDNSStorage; // mDNS core uses this to store its globals
c9b9ae52
A
50static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals
51
67c8f8a1
A
52mDNSexport const char ProgramName[] = "mDNSResponderPosix";
53
54static const char *gProgramName = ProgramName;
c9b9ae52
A
55
56#if COMPILER_LIKES_PRAGMA_MARK
57#pragma mark ***** Signals
58#endif
59
60static volatile mDNSBool gReceivedSigUsr1;
61static volatile mDNSBool gReceivedSigHup;
62static volatile mDNSBool gStopNow;
63
64// We support 4 signals.
65//
66// o SIGUSR1 toggles verbose mode on and off in debug builds
67// o SIGHUP triggers the program to re-read its preferences.
68// o SIGINT causes an orderly shutdown of the program.
69// o SIGQUIT causes a somewhat orderly shutdown (direct but dangerous)
70// o SIGKILL kills us dead (easy to implement :-)
71//
83fb1e36
A
72// There are fatal race conditions in our signal handling, but there's not much
73// we can do about them while remaining within the Posix space. Specifically,
74// if a signal arrives after we test the globals its sets but before we call
75// select, the signal will be dropped. The user will have to send the signal
76// again. Unfortunately, Posix does not have a "sigselect" to atomically
c9b9ae52
A
77// modify the signal mask and start a select.
78
79static void HandleSigUsr1(int sigraised)
83fb1e36
A
80// If we get a SIGUSR1 we toggle the state of the
81// verbose mode.
c9b9ae52
A
82{
83 assert(sigraised == SIGUSR1);
84 gReceivedSigUsr1 = mDNStrue;
85}
86
87static void HandleSigHup(int sigraised)
83fb1e36
A
88// A handler for SIGHUP that causes us to break out of the
89// main event loop when the user kill 1's us. This has the
90// effect of triggered the main loop to deregister the
91// current services and re-read the preferences.
c9b9ae52
A
92{
93 assert(sigraised == SIGHUP);
83fb1e36 94 gReceivedSigHup = mDNStrue;
c9b9ae52
A
95}
96
97static void HandleSigInt(int sigraised)
83fb1e36
A
98// A handler for SIGINT that causes us to break out of the
99// main event loop when the user types ^C. This has the
100// effect of quitting the program.
c9b9ae52
A
101{
102 assert(sigraised == SIGINT);
83fb1e36 103
c9b9ae52
A
104 if (gMDNSPlatformPosixVerboseLevel > 0) {
105 fprintf(stderr, "\nSIGINT\n");
106 }
107 gStopNow = mDNStrue;
108}
109
110static void HandleSigQuit(int sigraised)
83fb1e36
A
111// If we get a SIGQUIT the user is desperate and we
112// just call mDNS_Close directly. This is definitely
113// not safe (because it could reenter mDNS), but
114// we presume that the user has already tried the safe
115// alternatives.
c9b9ae52
A
116{
117 assert(sigraised == SIGQUIT);
118
119 if (gMDNSPlatformPosixVerboseLevel > 0) {
120 fprintf(stderr, "\nSIGQUIT\n");
121 }
122 mDNS_Close(&mDNSStorage);
123 exit(0);
124}
125
126#if COMPILER_LIKES_PRAGMA_MARK
127#pragma mark ***** Parameter Checking
128#endif
129
7cb34e5c 130static mDNSBool CheckThatRichTextNameIsUsable(const char *richTextName, mDNSBool printExplanation)
83fb1e36
A
131// Checks that richTextName is reasonable
132// label and, if it isn't and printExplanation is true, prints
133// an explanation of why not.
c9b9ae52 134{
7cb34e5c
A
135 mDNSBool result = mDNStrue;
136 if (result && strlen(richTextName) > 63) {
c9b9ae52 137 if (printExplanation) {
83fb1e36
A
138 fprintf(stderr,
139 "%s: Service name is too long (must be 63 characters or less)\n",
c9b9ae52
A
140 gProgramName);
141 }
142 result = mDNSfalse;
143 }
7cb34e5c 144 if (result && richTextName[0] == 0) {
c9b9ae52 145 if (printExplanation) {
7cb34e5c 146 fprintf(stderr, "%s: Service name can't be empty\n", gProgramName);
c9b9ae52
A
147 }
148 result = mDNSfalse;
149 }
c9b9ae52
A
150 return result;
151}
152
153static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool printExplanation)
83fb1e36
A
154// Checks that serviceType is a reasonable service type
155// label and, if it isn't and printExplanation is true, prints
156// an explanation of why not.
c9b9ae52
A
157{
158 mDNSBool result;
83fb1e36 159
c9b9ae52
A
160 result = mDNStrue;
161 if (result && strlen(serviceType) > 63) {
162 if (printExplanation) {
83fb1e36
A
163 fprintf(stderr,
164 "%s: Service type is too long (must be 63 characters or less)\n",
c9b9ae52
A
165 gProgramName);
166 }
167 result = mDNSfalse;
168 }
169 if (result && serviceType[0] == 0) {
170 if (printExplanation) {
83fb1e36
A
171 fprintf(stderr,
172 "%s: Service type can't be empty\n",
c9b9ae52
A
173 gProgramName);
174 }
175 result = mDNSfalse;
176 }
177 return result;
178}
179
c9b9ae52 180static mDNSBool CheckThatPortNumberIsUsable(long portNumber, mDNSBool printExplanation)
83fb1e36
A
181// Checks that portNumber is a reasonable port number
182// and, if it isn't and printExplanation is true, prints
183// an explanation of why not.
c9b9ae52
A
184{
185 mDNSBool result;
83fb1e36 186
c9b9ae52
A
187 result = mDNStrue;
188 if (result && (portNumber <= 0 || portNumber > 65535)) {
189 if (printExplanation) {
83fb1e36
A
190 fprintf(stderr,
191 "%s: Port number specified by -p must be in range 1..65535\n",
c9b9ae52
A
192 gProgramName);
193 }
194 result = mDNSfalse;
195 }
196 return result;
197}
198
199#if COMPILER_LIKES_PRAGMA_MARK
200#pragma mark ***** Command Line Arguments
201#endif
202
203static const char kDefaultPIDFile[] = "/var/run/mDNSResponder.pid";
204static const char kDefaultServiceType[] = "_afpovertcp._tcp.";
205static const char kDefaultServiceDomain[] = "local.";
206enum {
207 kDefaultPortNumber = 548
208};
209
210static void PrintUsage()
211{
83fb1e36
A
212 fprintf(stderr,
213 "Usage: %s [-v level ] [-r] [-n name] [-t type] [-d domain] [-p port] [-f file] [-b] [-P pidfile] [-x name=val ...]\n",
c9b9ae52
A
214 gProgramName);
215 fprintf(stderr, " -v verbose mode, level is a number from 0 to 2\n");
216 fprintf(stderr, " 0 = no debugging info (default)\n");
217 fprintf(stderr, " 1 = standard debugging info\n");
218 fprintf(stderr, " 2 = intense debugging info\n");
219 fprintf(stderr, " can be cycled kill -USR1\n");
8e92c31c 220 fprintf(stderr, " -r also bind to port 53 (port 5353 is always bound)\n");
7cb34e5c 221 fprintf(stderr, " -n uses 'name' as the service name (required)\n");
c9b9ae52
A
222 fprintf(stderr, " -t uses 'type' as the service type (default is '%s')\n", kDefaultServiceType);
223 fprintf(stderr, " -d uses 'domain' as the service domain (default is '%s')\n", kDefaultServiceDomain);
c9b9ae52
A
224 fprintf(stderr, " -p uses 'port' as the port number (default is '%d')\n", kDefaultPortNumber);
225 fprintf(stderr, " -f reads a service list from 'file'\n");
226 fprintf(stderr, " -b forces daemon (background) mode\n");
227 fprintf(stderr, " -P uses 'pidfile' as the PID file\n");
228 fprintf(stderr, " (default is '%s')\n", kDefaultPIDFile);
229 fprintf(stderr, " only meaningful if -b also specified\n");
7f0064bd
A
230 fprintf(stderr, " -x stores name=val in TXT record (default is empty).\n");
231 fprintf(stderr, " MUST be the last command-line argument;\n");
232 fprintf(stderr, " all subsequent arguments after -x are treated as name=val pairs.\n");
c9b9ae52
A
233}
234
83fb1e36 235static mDNSBool gAvoidPort53 = mDNStrue;
7cb34e5c 236static const char *gServiceName = "";
c9b9ae52
A
237static const char *gServiceType = kDefaultServiceType;
238static const char *gServiceDomain = kDefaultServiceDomain;
83fb1e36
A
239static mDNSu8 gServiceText[sizeof(RDataBody)];
240static mDNSu16 gServiceTextLen = 0;
241static int gPortNumber = kDefaultPortNumber;
c9b9ae52 242static const char *gServiceFile = "";
83fb1e36 243static mDNSBool gDaemon = mDNSfalse;
c9b9ae52
A
244static const char *gPIDFile = kDefaultPIDFile;
245
246static void ParseArguments(int argc, char **argv)
83fb1e36
A
247// Parses our command line arguments into the global variables
248// listed above.
c9b9ae52
A
249{
250 int ch;
83fb1e36 251
c9b9ae52 252 // Set gProgramName to the last path component of argv[0]
83fb1e36 253
c9b9ae52
A
254 gProgramName = strrchr(argv[0], '/');
255 if (gProgramName == NULL) {
256 gProgramName = argv[0];
257 } else {
258 gProgramName += 1;
259 }
83fb1e36 260
c9b9ae52 261 // Parse command line options using getopt.
83fb1e36 262
c9b9ae52 263 do {
7f0064bd 264 ch = getopt(argc, argv, "v:rn:t:d:p:f:dP:bx");
c9b9ae52
A
265 if (ch != -1) {
266 switch (ch) {
83fb1e36
A
267 case 'v':
268 gMDNSPlatformPosixVerboseLevel = atoi(optarg);
269 if (gMDNSPlatformPosixVerboseLevel < 0 || gMDNSPlatformPosixVerboseLevel > 2) {
270 fprintf(stderr,
271 "%s: Verbose mode must be in the range 0..2\n",
272 gProgramName);
c9b9ae52 273 exit(1);
83fb1e36
A
274 }
275 break;
276 case 'r':
277 gAvoidPort53 = mDNSfalse;
278 break;
279 case 'n':
280 gServiceName = optarg;
281 if ( !CheckThatRichTextNameIsUsable(gServiceName, mDNStrue) ) {
282 exit(1);
283 }
284 break;
285 case 't':
286 gServiceType = optarg;
287 if ( !CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) {
288 exit(1);
289 }
290 break;
291 case 'd':
292 gServiceDomain = optarg;
293 break;
294 case 'p':
295 gPortNumber = atol(optarg);
296 if ( !CheckThatPortNumberIsUsable(gPortNumber, mDNStrue) ) {
297 exit(1);
298 }
299 break;
300 case 'f':
301 gServiceFile = optarg;
302 break;
303 case 'b':
304 gDaemon = mDNStrue;
305 break;
306 case 'P':
307 gPIDFile = optarg;
308 break;
309 case 'x':
310 while (optind < argc)
311 {
312 gServiceText[gServiceTextLen] = strlen(argv[optind]);
313 mDNSPlatformMemCopy(gServiceText+gServiceTextLen+1, argv[optind], gServiceText[gServiceTextLen]);
314 gServiceTextLen += 1 + gServiceText[gServiceTextLen];
315 optind++;
316 }
317 ch = -1;
318 break;
319 case '?':
320 default:
321 PrintUsage();
322 exit(1);
323 break;
c9b9ae52
A
324 }
325 }
326 } while (ch != -1);
327
328 // Check for any left over command line arguments.
83fb1e36 329
c9b9ae52 330 if (optind != argc) {
83fb1e36 331 PrintUsage();
c9b9ae52
A
332 fprintf(stderr, "%s: Unexpected argument '%s'\n", gProgramName, argv[optind]);
333 exit(1);
334 }
83fb1e36 335
c9b9ae52 336 // Check for inconsistency between the arguments.
83fb1e36 337
7cb34e5c 338 if ( (gServiceName[0] == 0) && (gServiceFile[0] == 0) ) {
83fb1e36 339 PrintUsage();
7cb34e5c 340 fprintf(stderr, "%s: You must specify a service name to register (-n) or a service file (-f).\n", gProgramName);
c9b9ae52
A
341 exit(1);
342 }
343}
344
345#if COMPILER_LIKES_PRAGMA_MARK
346#pragma mark ***** Registration
347#endif
348
349typedef struct PosixService PosixService;
350
351struct PosixService {
352 ServiceRecordSet coreServ;
353 PosixService *next;
354 int serviceID;
355};
356
357static PosixService *gServiceList = NULL;
358
359static void RegistrationCallback(mDNS *const m, ServiceRecordSet *const thisRegistration, mStatus status)
83fb1e36
A
360// mDNS core calls this routine to tell us about the status of
361// our registration. The appropriate action to take depends
362// entirely on the value of status.
c9b9ae52
A
363{
364 switch (status) {
365
83fb1e36
A
366 case mStatus_NoError:
367 debugf("Callback: %##s Name Registered", thisRegistration->RR_SRV.resrec.name->c);
368 // Do nothing; our name was successfully registered. We may
369 // get more call backs in the future.
370 break;
c9b9ae52 371
83fb1e36
A
372 case mStatus_NameConflict:
373 debugf("Callback: %##s Name Conflict", thisRegistration->RR_SRV.resrec.name->c);
374
375 // In the event of a conflict, this sample RegistrationCallback
376 // just calls mDNS_RenameAndReregisterService to automatically
377 // pick a new unique name for the service. For a device such as a
378 // printer, this may be appropriate. For a device with a user
379 // interface, and a screen, and a keyboard, the appropriate response
380 // may be to prompt the user and ask them to choose a new name for
381 // the service.
382 //
383 // Also, what do we do if mDNS_RenameAndReregisterService returns an
384 // error. Right now I have no place to send that error to.
385
386 status = mDNS_RenameAndReregisterService(m, thisRegistration, mDNSNULL);
387 assert(status == mStatus_NoError);
388 break;
389
390 case mStatus_MemFree:
391 debugf("Callback: %##s Memory Free", thisRegistration->RR_SRV.resrec.name->c);
392
393 // When debugging is enabled, make sure that thisRegistration
394 // is not on our gServiceList.
c9b9ae52 395
c9b9ae52 396 #if !defined(NDEBUG)
83fb1e36
A
397 {
398 PosixService *cursor;
399
400 cursor = gServiceList;
401 while (cursor != NULL) {
402 assert(&cursor->coreServ != thisRegistration);
403 cursor = cursor->next;
404 }
405 }
c9b9ae52 406 #endif
83fb1e36
A
407 free(thisRegistration);
408 break;
c9b9ae52 409
83fb1e36
A
410 default:
411 debugf("Callback: %##s Unknown Status %ld", thisRegistration->RR_SRV.resrec.name->c, status);
412 break;
c9b9ae52
A
413 }
414}
415
416static int gServiceID = 0;
417
83fb1e36
A
418static mStatus RegisterOneService(const char * richTextName,
419 const char * serviceType,
420 const char * serviceDomain,
421 const mDNSu8 text[],
422 mDNSu16 textLen,
423 long portNumber)
c9b9ae52 424{
83fb1e36 425 mStatus status;
c9b9ae52 426 PosixService * thisServ;
83fb1e36
A
427 domainlabel name;
428 domainname type;
429 domainname domain;
430
c9b9ae52 431 status = mStatus_NoError;
f0cc3e7b 432 thisServ = (PosixService *) calloc(sizeof(*thisServ), 1);
c9b9ae52
A
433 if (thisServ == NULL) {
434 status = mStatus_NoMemoryErr;
435 }
436 if (status == mStatus_NoError) {
7cb34e5c 437 MakeDomainLabelFromLiteralString(&name, richTextName);
c9b9ae52
A
438 MakeDomainNameFromDNSNameString(&type, serviceType);
439 MakeDomainNameFromDNSNameString(&domain, serviceDomain);
c9b9ae52 440 status = mDNS_RegisterService(&mDNSStorage, &thisServ->coreServ,
83fb1e36
A
441 &name, &type, &domain, // Name, type, domain
442 NULL, mDNSOpaque16fromIntVal(portNumber),
2682e09e 443 NULL, text, textLen, // TXT data, length
83fb1e36
A
444 NULL, 0, // Subtypes
445 mDNSInterface_Any, // Interface ID
446 RegistrationCallback, thisServ, 0); // Callback, context, flags
c9b9ae52
A
447 }
448 if (status == mStatus_NoError) {
449 thisServ->serviceID = gServiceID;
450 gServiceID += 1;
451
452 thisServ->next = gServiceList;
453 gServiceList = thisServ;
454
455 if (gMDNSPlatformPosixVerboseLevel > 0) {
83fb1e36
A
456 fprintf(stderr,
457 "%s: Registered service %d, name \"%s\", type \"%s\", domain \"%s\", port %ld\n",
458 gProgramName,
459 thisServ->serviceID,
7cb34e5c 460 richTextName,
c9b9ae52 461 serviceType,
83fb1e36 462 serviceDomain,
c9b9ae52
A
463 portNumber);
464 }
465 } else {
466 if (thisServ != NULL) {
467 free(thisServ);
468 }
469 }
470 return status;
471}
472
83fb1e36 473static mDNSBool ReadALine(char *buf, size_t bufSize, FILE *fp, mDNSBool skipBlankLines)
7f0064bd 474{
83fb1e36
A
475 size_t len;
476 mDNSBool readNextLine;
477
478 do {
479 readNextLine = mDNSfalse;
480
481 if (fgets(buf, bufSize, fp) == NULL)
482 return mDNSfalse; // encountered EOF or an error condition
483
484 // These first characters indicate a blank line.
485 if (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\r' || buf[0] == '\n') {
486 if (!skipBlankLines)
487 return mDNSfalse;
488 readNextLine = mDNStrue;
489 }
490 // always skip comment lines
491 if (buf[0] == '#')
492 readNextLine = mDNStrue;
493
494 } while (readNextLine);
495
496 len = strlen( buf);
497 if ( buf[len - 1] == '\r' || buf[len - 1] == '\n')
498 buf[len - 1] = '\0';
499
500 return mDNStrue;
7f0064bd 501}
c9b9ae52
A
502
503static mStatus RegisterServicesInFile(const char *filePath)
504{
83fb1e36 505 mStatus status = mStatus_NoError;
7f0064bd 506 FILE * fp = fopen(filePath, "r");
e0815622 507 int rv;
83fb1e36 508
c9b9ae52 509 if (fp == NULL) {
83fb1e36 510 return mStatus_UnknownErr;
c9b9ae52 511 }
83fb1e36
A
512
513 if (gMDNSPlatformPosixVerboseLevel > 1)
514 fprintf(stderr, "Parsing %s for services\n", filePath);
515
516 do {
517 char nameBuf[256];
518 char * name = nameBuf;
519 char type[256];
520 const char *dom = kDefaultServiceDomain;
521 char rawText[1024];
522 mDNSu8 text[sizeof(RDataBody)];
523 unsigned int textLen = 0;
524 char port[256];
525 char *p;
526
527 // Read the service name, type, port, and optional text record fields.
528 // Skip blank lines while looking for the next service name.
529 if (!ReadALine(name, sizeof(nameBuf), fp, mDNStrue))
530 break;
531
532 // Special case that allows service name to begin with a '#'
533 // character by escaping it with a '\' to distiguish it from
534 // a comment line. Remove the leading '\' here before
535 // registering the service.
536 if (name[0] == '\\' && name[1] == '#')
537 name++;
538
539 if (gMDNSPlatformPosixVerboseLevel > 1)
540 fprintf(stderr, "Service name: \"%s\"\n", name);
541
542 // Don't skip blank lines in calls to ReadAline() after finding the
543 // service name since the next blank line indicates the end
544 // of this service record.
545 if (!ReadALine(type, sizeof(type), fp, mDNSfalse))
546 break;
547
548 // see if a domain name is specified
549 p = type;
550 while (*p && *p != ' ' && *p != '\t') p++;
551 if (*p) {
552 *p = 0; // NULL terminate the <type>.<protocol> string
553 // skip any leading whitespace before domain name
554 p++;
555 while (*p && (*p == ' ' || *p == '\t')) p++;
556 if (*p)
557 dom = p;
8b5f5b69 558 }
83fb1e36
A
559 if (gMDNSPlatformPosixVerboseLevel > 1) {
560 fprintf(stderr, "Service type: \"%s\"\n", type);
561 fprintf(stderr, "Service domain: \"%s\"\n", dom);
562 }
563
564 if (!ReadALine(port, sizeof(port), fp, mDNSfalse))
565 break;
566 if (gMDNSPlatformPosixVerboseLevel > 1)
567 fprintf(stderr, "Service port: %s\n", port);
568
569 if ( !CheckThatRichTextNameIsUsable(name, mDNStrue)
570 || !CheckThatServiceTypeIsUsable(type, mDNStrue)
571 || !CheckThatPortNumberIsUsable(atol(port), mDNStrue))
572 break;
573
574 // read the TXT record fields
575 while (1) {
576 int len;
577 if (!ReadALine(rawText, sizeof(rawText), fp, mDNSfalse)) break;
578 if (gMDNSPlatformPosixVerboseLevel > 1)
579 fprintf(stderr, "Text string: \"%s\"\n", rawText);
580 len = strlen(rawText);
581 if (len <= 255)
582 {
583 unsigned int newlen = textLen + 1 + len;
584 if (len == 0 || newlen >= sizeof(text)) break;
585 text[textLen] = len;
586 mDNSPlatformMemCopy(text + textLen + 1, rawText, len);
587 textLen = newlen;
588 }
589 else
590 fprintf(stderr, "%s: TXT attribute too long for name = %s, type = %s, port = %s\n",
591 gProgramName, name, type, port);
592 }
593
594 status = RegisterOneService(name, type, dom, text, textLen, atol(port));
595 if (status != mStatus_NoError) {
596 // print error, but try to read and register other services in the file
597 fprintf(stderr, "%s: Failed to register service, name \"%s\", type \"%s\", domain \"%s\", port %s\n",
598 gProgramName, name, type, dom, port);
599 }
600
601 } while (!feof(fp));
602
603 if (!feof(fp)) {
604 fprintf(stderr, "%s: Error reading service file %s\n", gProgramName, filePath);
605 status = mStatus_UnknownErr;
8b5f5b69 606 }
83fb1e36 607
e0815622
A
608 rv = fclose(fp);
609 assert(rv == 0);
83fb1e36 610
8b5f5b69 611 return status;
c9b9ae52
A
612}
613
614static mStatus RegisterOurServices(void)
615{
616 mStatus status;
83fb1e36 617
c9b9ae52 618 status = mStatus_NoError;
7cb34e5c 619 if (gServiceName[0] != 0) {
83fb1e36
A
620 status = RegisterOneService(gServiceName,
621 gServiceType,
622 gServiceDomain,
623 gServiceText, gServiceTextLen,
c9b9ae52
A
624 gPortNumber);
625 }
626 if (status == mStatus_NoError && gServiceFile[0] != 0) {
627 status = RegisterServicesInFile(gServiceFile);
628 }
629 return status;
630}
631
632static void DeregisterOurServices(void)
633{
634 PosixService *thisServ;
83fb1e36 635
c9b9ae52
A
636 while (gServiceList != NULL) {
637 thisServ = gServiceList;
638 gServiceList = thisServ->next;
639
c9b9ae52
A
640 mDNS_DeregisterService(&mDNSStorage, &thisServ->coreServ);
641
642 if (gMDNSPlatformPosixVerboseLevel > 0) {
83fb1e36 643 fprintf(stderr,
c9b9ae52 644 "%s: Deregistered service %d\n",
83fb1e36 645 gProgramName,
c9b9ae52
A
646 thisServ->serviceID);
647 }
648 }
649}
650
651#if COMPILER_LIKES_PRAGMA_MARK
652#pragma mark **** Main
653#endif
654
c9b9ae52
A
655int main(int argc, char **argv)
656{
657 mStatus status;
83fb1e36 658 int result;
c9b9ae52
A
659
660 // Parse our command line arguments. This won't come back if there's an error.
83fb1e36 661
c9b9ae52
A
662 ParseArguments(argc, argv);
663
664 // If we're told to run as a daemon, then do that straight away.
83fb1e36
A
665 // Note that we don't treat the inability to create our PID
666 // file as an error. Also note that we assign getpid to a long
c9b9ae52 667 // because printf has no format specified for pid_t.
83fb1e36 668
c9b9ae52 669 if (gDaemon) {
83fb1e36 670 int result;
c9b9ae52
A
671 if (gMDNSPlatformPosixVerboseLevel > 0) {
672 fprintf(stderr, "%s: Starting in daemon mode\n", gProgramName);
673 }
32bb7e43
A
674 result = daemon(0,0);
675 if (result == 0) {
c9b9ae52 676 FILE *fp;
83fb1e36
A
677 int junk;
678
c9b9ae52
A
679 fp = fopen(gPIDFile, "w");
680 if (fp != NULL) {
681 fprintf(fp, "%ld\n", (long) getpid());
682 junk = fclose(fp);
683 assert(junk == 0);
684 }
32bb7e43
A
685 } else {
686 fprintf(stderr, "%s: Could not run as daemon - exiting\n", gProgramName);
687 exit(result);
c9b9ae52
A
688 }
689 } else {
690 if (gMDNSPlatformPosixVerboseLevel > 0) {
691 fprintf(stderr, "%s: Starting in foreground mode, PID %ld\n", gProgramName, (long) getpid());
692 }
693 }
694
695 status = mDNS_Init(&mDNSStorage, &PlatformStorage,
83fb1e36
A
696 mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
697 mDNS_Init_AdvertiseLocalAddresses,
698 mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
c9b9ae52
A
699 if (status != mStatus_NoError) return(2);
700
83fb1e36 701 status = RegisterOurServices();
c9b9ae52 702 if (status != mStatus_NoError) return(2);
83fb1e36 703
c9b9ae52
A
704 signal(SIGHUP, HandleSigHup); // SIGHUP has to be sent by kill -HUP <pid>
705 signal(SIGINT, HandleSigInt); // SIGINT is what you get for a Ctrl-C
706 signal(SIGQUIT, HandleSigQuit); // SIGQUIT is what you get for a Ctrl-\ (indeed)
707 signal(SIGUSR1, HandleSigUsr1); // SIGUSR1 has to be sent by kill -USR1 <pid>
708
83fb1e36
A
709 while (!gStopNow)
710 {
711 int nfds = 0;
f0cc3e7b 712 fd_set readfds, writefds;
83fb1e36
A
713 struct timeval timeout;
714 int result;
715
716 // 1. Set up the fd_set as usual here.
717 // This example client has no file descriptors of its own,
718 // but a real application would call FD_SET to add them to the set here
719 FD_ZERO(&readfds);
f0cc3e7b 720 FD_ZERO(&writefds);
83fb1e36
A
721
722 // 2. Set up the timeout.
723 // This example client has no other work it needs to be doing,
724 // so we set an effectively infinite timeout
12c5fa7a 725 timeout.tv_sec = FutureTime;
83fb1e36
A
726 timeout.tv_usec = 0;
727
728 // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout
f0cc3e7b 729 mDNSPosixGetFDSet(&mDNSStorage, &nfds, &readfds, &writefds, &timeout);
83fb1e36
A
730
731 // 4. Call select as normal
732 verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec);
733 result = select(nfds, &readfds, NULL, NULL, &timeout);
734
735 if (result < 0)
736 {
737 verbosedebugf("select() returned %d errno %d", result, errno);
738 if (errno != EINTR) gStopNow = mDNStrue;
739 else
740 {
741 if (gReceivedSigUsr1)
742 {
743 gReceivedSigUsr1 = mDNSfalse;
744 gMDNSPlatformPosixVerboseLevel += 1;
745 if (gMDNSPlatformPosixVerboseLevel > 2)
746 gMDNSPlatformPosixVerboseLevel = 0;
747 if ( gMDNSPlatformPosixVerboseLevel > 0 )
748 fprintf(stderr, "\nVerbose level %d\n", gMDNSPlatformPosixVerboseLevel);
749 }
750 if (gReceivedSigHup)
751 {
752 if (gMDNSPlatformPosixVerboseLevel > 0)
753 fprintf(stderr, "\nSIGHUP\n");
754 gReceivedSigHup = mDNSfalse;
755 DeregisterOurServices();
756 status = mDNSPlatformPosixRefreshInterfaceList(&mDNSStorage);
757 if (status != mStatus_NoError) break;
758 status = RegisterOurServices();
759 if (status != mStatus_NoError) break;
760 }
761 }
762 }
763 else
764 {
765 // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work
f0cc3e7b 766 mDNSPosixProcessFDSet(&mDNSStorage, &readfds, &writefds);
83fb1e36
A
767
768 // 6. This example client has no other work it needs to be doing,
769 // but a real client would do its work here
770 // ... (do work) ...
771 }
772 }
773
774 debugf("Exiting");
775
776 DeregisterOurServices();
777 mDNS_Close(&mDNSStorage);
c9b9ae52
A
778
779 if (status == mStatus_NoError) {
780 result = 0;
781 } else {
782 result = 2;
783 }
784 if ( (result != 0) || (gMDNSPlatformPosixVerboseLevel > 0) ) {
67c8f8a1 785 fprintf(stderr, "%s: Finished with status %d, result %d\n", gProgramName, (int)status, result);
c9b9ae52 786 }
83fb1e36 787
c9b9ae52
A
788 return result;
789}