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 37extern 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 48static mDNS mDNSStorage; // mDNS core uses this to store its globals 49static mDNS_PlatformSupport PlatformStorage; // Stores this platform's globals 50 51mDNSexport const char ProgramName[] = "mDNSResponderPosix"; 52 53static const char *gProgramName = ProgramName; 54 55#if COMPILER_LIKES_PRAGMA_MARK 56#pragma mark ***** Signals 57#endif 58 59static volatile mDNSBool gReceivedSigUsr1; 60static volatile mDNSBool gReceivedSigHup; 61static 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 78static 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 86static 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 96static 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 109static 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 129static 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 152static 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 179static 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 202static const char kDefaultPIDFile[] = "/var/run/mDNSResponder.pid"; 203static const char kDefaultServiceType[] = "_afpovertcp._tcp."; 204static const char kDefaultServiceDomain[] = "local."; 205enum { 206 kDefaultPortNumber = 548 207}; 208 209static 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 234static mDNSBool gAvoidPort53 = mDNStrue; 235static const char *gServiceName = ""; 236static const char *gServiceType = kDefaultServiceType; 237static const char *gServiceDomain = kDefaultServiceDomain; 238static mDNSu8 gServiceText[sizeof(RDataBody)]; 239static mDNSu16 gServiceTextLen = 0; 240static int gPortNumber = kDefaultPortNumber; 241static const char *gServiceFile = ""; 242static mDNSBool gDaemon = mDNSfalse; 243static const char *gPIDFile = kDefaultPIDFile; 244 245static 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 348typedef struct PosixService PosixService; 349 350struct PosixService { 351 ServiceRecordSet coreServ; 352 PosixService *next; 353 int serviceID; 354}; 355 356static PosixService *gServiceList = NULL; 357 358static 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 415static int gServiceID = 0; 416 417static 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 472static 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 502static 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 { 607// __ANDROID__ : replaced assert(fclose(..)) 608 int fp_closed = fclose(fp); 609 assert(0 == fp_closed); 610 } 611 612 return status; 613} 614 615static mStatus RegisterOurServices(void) 616{ 617 mStatus status; 618 619 status = mStatus_NoError; 620 if (gServiceName[0] != 0) { 621 status = RegisterOneService(gServiceName, 622 gServiceType, 623 gServiceDomain, 624 gServiceText, gServiceTextLen, 625 gPortNumber); 626 } 627 if (status == mStatus_NoError && gServiceFile[0] != 0) { 628 status = RegisterServicesInFile(gServiceFile); 629 } 630 return status; 631} 632 633static void DeregisterOurServices(void) 634{ 635 PosixService *thisServ; 636 int thisServID; 637 638 while (gServiceList != NULL) { 639 thisServ = gServiceList; 640 gServiceList = thisServ->next; 641 642 thisServID = thisServ->serviceID; 643 644 mDNS_DeregisterService(&mDNSStorage, &thisServ->coreServ); 645 646 if (gMDNSPlatformPosixVerboseLevel > 0) { 647 fprintf(stderr, 648 "%s: Deregistered service %d\n", 649 gProgramName, 650 thisServ->serviceID); 651 } 652 } 653} 654 655#if COMPILER_LIKES_PRAGMA_MARK 656#pragma mark **** Main 657#endif 658 659int main(int argc, char **argv) 660{ 661 mStatus status; 662 int result; 663 664 // Parse our command line arguments. This won't come back if there's an error. 665 666 ParseArguments(argc, argv); 667 668 // If we're told to run as a daemon, then do that straight away. 669 // Note that we don't treat the inability to create our PID 670 // file as an error. Also note that we assign getpid to a long 671 // because printf has no format specified for pid_t. 672 673 if (gDaemon) { 674 int result; 675 if (gMDNSPlatformPosixVerboseLevel > 0) { 676 fprintf(stderr, "%s: Starting in daemon mode\n", gProgramName); 677 } 678 result = daemon(0,0); 679 if (result == 0) { 680 FILE *fp; 681 int junk; 682 683 fp = fopen(gPIDFile, "w"); 684 if (fp != NULL) { 685 fprintf(fp, "%ld\n", (long) getpid()); 686 junk = fclose(fp); 687 assert(junk == 0); 688 } 689 } else { 690 fprintf(stderr, "%s: Could not run as daemon - exiting\n", gProgramName); 691 exit(result); 692 } 693 } else { 694 if (gMDNSPlatformPosixVerboseLevel > 0) { 695 fprintf(stderr, "%s: Starting in foreground mode, PID %ld\n", gProgramName, (long) getpid()); 696 } 697 } 698 699 status = mDNS_Init(&mDNSStorage, &PlatformStorage, 700 mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, 701 mDNS_Init_AdvertiseLocalAddresses, 702 mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); 703 if (status != mStatus_NoError) return(2); 704 705 status = RegisterOurServices(); 706 if (status != mStatus_NoError) return(2); 707 708 signal(SIGHUP, HandleSigHup); // SIGHUP has to be sent by kill -HUP <pid> 709 signal(SIGINT, HandleSigInt); // SIGINT is what you get for a Ctrl-C 710 signal(SIGQUIT, HandleSigQuit); // SIGQUIT is what you get for a Ctrl-\ (indeed) 711 signal(SIGUSR1, HandleSigUsr1); // SIGUSR1 has to be sent by kill -USR1 <pid> 712 713 while (!gStopNow) 714 { 715 int nfds = 0; 716 fd_set readfds; 717 struct timeval timeout; 718 int result; 719 720 // 1. Set up the fd_set as usual here. 721 // This example client has no file descriptors of its own, 722 // but a real application would call FD_SET to add them to the set here 723 FD_ZERO(&readfds); 724 725 // 2. Set up the timeout. 726 // This example client has no other work it needs to be doing, 727 // so we set an effectively infinite timeout 728 timeout.tv_sec = 0x3FFFFFFF; 729 timeout.tv_usec = 0; 730 731 // 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout 732 mDNSPosixGetFDSet(&mDNSStorage, &nfds, &readfds, &timeout); 733 734 // 4. Call select as normal 735 verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec); 736 result = select(nfds, &readfds, NULL, NULL, &timeout); 737 738 if (result < 0) 739 { 740 verbosedebugf("select() returned %d errno %d", result, errno); 741 if (errno != EINTR) gStopNow = mDNStrue; 742 else 743 { 744 if (gReceivedSigUsr1) 745 { 746 gReceivedSigUsr1 = mDNSfalse; 747 gMDNSPlatformPosixVerboseLevel += 1; 748 if (gMDNSPlatformPosixVerboseLevel > 2) 749 gMDNSPlatformPosixVerboseLevel = 0; 750 if ( gMDNSPlatformPosixVerboseLevel > 0 ) 751 fprintf(stderr, "\nVerbose level %d\n", gMDNSPlatformPosixVerboseLevel); 752 } 753 if (gReceivedSigHup) 754 { 755 if (gMDNSPlatformPosixVerboseLevel > 0) 756 fprintf(stderr, "\nSIGHUP\n"); 757 gReceivedSigHup = mDNSfalse; 758 DeregisterOurServices(); 759 status = mDNSPlatformPosixRefreshInterfaceList(&mDNSStorage); 760 if (status != mStatus_NoError) break; 761 status = RegisterOurServices(); 762 if (status != mStatus_NoError) break; 763 } 764 } 765 } 766 else 767 { 768 // 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work 769 mDNSPosixProcessFDSet(&mDNSStorage, &readfds); 770 771 // 6. This example client has no other work it needs to be doing, 772 // but a real client would do its work here 773 // ... (do work) ... 774 } 775 } 776 777 debugf("Exiting"); 778 779 DeregisterOurServices(); 780 mDNS_Close(&mDNSStorage); 781 782 if (status == mStatus_NoError) { 783 result = 0; 784 } else { 785 result = 2; 786 } 787 if ( (result != 0) || (gMDNSPlatformPosixVerboseLevel > 0) ) { 788 fprintf(stderr, "%s: Finished with status %d, result %d\n", gProgramName, (int)status, result); 789 } 790 791 return result; 792} 793