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