1/* 2 * "$Id: sysman.c 12142 2014-08-30 02:35:43Z msweet $" 3 * 4 * System management functions for the CUPS scheduler. 5 * 6 * Copyright 2007-2014 by Apple Inc. 7 * Copyright 2006 by Easy Software Products. 8 * 9 * These coded instructions, statements, and computer programs are the 10 * property of Apple Inc. and are protected by Federal copyright 11 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 12 * which should have been included with this file. If this file is 13 * file is missing or damaged, see the license at "http://www.cups.org/". 14 */ 15 16 17/* 18 * Include necessary headers... 19 */ 20 21#include "cupsd.h" 22#ifdef __APPLE__ 23# include <vproc.h> 24# include <IOKit/pwr_mgt/IOPMLib.h> 25#endif /* __APPLE__ */ 26 27 28/* 29 * The system management functions cover disk and power management which 30 * are primarily used for portable computers. 31 * 32 * Disk management involves delaying the write of certain configuration 33 * and state files to minimize the number of times the disk has to spin 34 * up or flash to be written to. 35 * 36 * Power management support is currently only implemented on OS X, but 37 * essentially we use four functions to let the OS know when it is OK to 38 * put the system to sleep, typically when we are not in the middle of 39 * printing a job. And on OS X we can also "sleep print" - basically the 40 * system only wakes up long enough to service network requests and process 41 * print jobs. 42 */ 43 44 45/* 46 * 'cupsdCleanDirty()' - Write dirty config and state files. 47 */ 48 49void 50cupsdCleanDirty(void) 51{ 52 if (DirtyFiles & CUPSD_DIRTY_PRINTERS) 53 cupsdSaveAllPrinters(); 54 55 if (DirtyFiles & CUPSD_DIRTY_CLASSES) 56 cupsdSaveAllClasses(); 57 58 if (DirtyFiles & CUPSD_DIRTY_PRINTCAP) 59 cupsdWritePrintcap(); 60 61 if (DirtyFiles & CUPSD_DIRTY_JOBS) 62 { 63 cupsd_job_t *job; /* Current job */ 64 65 cupsdSaveAllJobs(); 66 67 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs); 68 job; 69 job = (cupsd_job_t *)cupsArrayNext(Jobs)) 70 if (job->dirty) 71 cupsdSaveJob(job); 72 } 73 74 if (DirtyFiles & CUPSD_DIRTY_SUBSCRIPTIONS) 75 cupsdSaveAllSubscriptions(); 76 77 DirtyFiles = CUPSD_DIRTY_NONE; 78 DirtyCleanTime = 0; 79 80 cupsdSetBusyState(); 81} 82 83 84/* 85 * 'cupsdMarkDirty()' - Mark config or state files as needing a write. 86 */ 87 88void 89cupsdMarkDirty(int what) /* I - What file(s) are dirty? */ 90{ 91 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdMarkDirty(%c%c%c%c%c)", 92 (what & CUPSD_DIRTY_PRINTERS) ? 'P' : '-', 93 (what & CUPSD_DIRTY_CLASSES) ? 'C' : '-', 94 (what & CUPSD_DIRTY_PRINTCAP) ? 'p' : '-', 95 (what & CUPSD_DIRTY_JOBS) ? 'J' : '-', 96 (what & CUPSD_DIRTY_SUBSCRIPTIONS) ? 'S' : '-'); 97 98 if (what == CUPSD_DIRTY_PRINTCAP && !Printcap) 99 return; 100 101 DirtyFiles |= what; 102 103 if (!DirtyCleanTime) 104 DirtyCleanTime = time(NULL) + DirtyCleanInterval; 105 106 cupsdSetBusyState(); 107} 108 109 110/* 111 * 'cupsdSetBusyState()' - Let the system know when we are busy doing something. 112 */ 113 114void 115cupsdSetBusyState(void) 116{ 117 int i; /* Looping var */ 118 cupsd_job_t *job; /* Current job */ 119 cupsd_printer_t *p; /* Current printer */ 120 int newbusy; /* New busy state */ 121 static int busy = 0; /* Current busy state */ 122 static const char * const busy_text[] = 123 { /* Text for busy states */ 124 "Not busy", 125 "Dirty files", 126 "Printing jobs", 127 "Printing jobs and dirty files", 128 "Active clients", 129 "Active clients and dirty files", 130 "Active clients and printing jobs", 131 "Active clients, printing jobs, and dirty files" 132 }; 133#ifdef __APPLE__ 134 static vproc_transaction_t vtran = 0; /* Current busy transaction */ 135 static IOPMAssertionID keep_awake = 0;/* Keep the system awake while printing */ 136#endif /* __APPLE__ */ 137 138 139 /* 140 * Figure out how busy we are... 141 */ 142 143 newbusy = (DirtyCleanTime ? 1 : 0) | 144 (cupsArrayCount(ActiveClients) ? 4 : 0); 145 146 for (job = (cupsd_job_t *)cupsArrayFirst(PrintingJobs); 147 job; 148 job = (cupsd_job_t *)cupsArrayNext(PrintingJobs)) 149 { 150 if ((p = job->printer) != NULL) 151 { 152 for (i = 0; i < p->num_reasons; i ++) 153 if (!strcmp(p->reasons[i], "connecting-to-device")) 154 break; 155 156 if (!p->num_reasons || i >= p->num_reasons) 157 break; 158 } 159 } 160 161 if (job) 162 newbusy |= 2; 163 164 cupsdLogMessage(CUPSD_LOG_DEBUG, 165 "cupsdSetBusyState: newbusy=\"%s\", busy=\"%s\"", 166 busy_text[newbusy], busy_text[busy]); 167 168 /* 169 * Manage state changes... 170 */ 171 172 if (newbusy != busy) 173 { 174 busy = newbusy; 175 176#ifdef __APPLE__ 177 if (busy && !vtran) 178 vtran = vproc_transaction_begin(NULL); 179 else if (!busy && vtran) 180 { 181 vproc_transaction_end(NULL, vtran); 182 vtran = 0; 183 } 184#endif /* __APPLE__ */ 185 } 186 187#ifdef __APPLE__ 188 if (cupsArrayCount(PrintingJobs) > 0 && !keep_awake) 189 { 190 cupsdLogMessage(CUPSD_LOG_DEBUG, "Asserting NetworkClientActive."); 191 192 IOPMAssertionCreateWithName(kIOPMAssertNetworkClientActive, 193 kIOPMAssertionLevelOn, 194 CFSTR("org.cups.cupsd"), &keep_awake); 195 } 196 else if (cupsArrayCount(PrintingJobs) == 0 && keep_awake) 197 { 198 cupsdLogMessage(CUPSD_LOG_DEBUG, "Releasing power assertion."); 199 IOPMAssertionRelease(keep_awake); 200 keep_awake = 0; 201 } 202#endif /* __APPLE__ */ 203} 204 205 206#ifdef __APPLE__ 207/* 208 * This is the Apple-specific system event code. It works by creating 209 * a worker thread that waits for events from the OS and relays them 210 * to the main thread via a traditional pipe. 211 */ 212 213/* 214 * Include MacOS-specific headers... 215 */ 216 217# include <notify.h> 218# include <IOKit/IOKitLib.h> 219# include <IOKit/IOMessage.h> 220# include <IOKit/ps/IOPowerSources.h> 221# include <IOKit/pwr_mgt/IOPMLib.h> 222# include <SystemConfiguration/SystemConfiguration.h> 223# include <pthread.h> 224 225 226/* 227 * Constants... 228 */ 229 230# define SYSEVENT_CANSLEEP 0x1 /* Decide whether to allow sleep or not */ 231# define SYSEVENT_WILLSLEEP 0x2 /* Computer will go to sleep */ 232# define SYSEVENT_WOKE 0x4 /* Computer woke from sleep */ 233# define SYSEVENT_NETCHANGED 0x8 /* Network changed */ 234# define SYSEVENT_NAMECHANGED 0x10 /* Computer name changed */ 235 236 237/* 238 * Structures... 239 */ 240 241typedef struct cupsd_sysevent_s /*** System event data ****/ 242{ 243 unsigned char event; /* Event bit field */ 244 io_connect_t powerKernelPort; /* Power context data */ 245 long powerNotificationID; /* Power event data */ 246} cupsd_sysevent_t; 247 248 249typedef struct cupsd_thread_data_s /*** Thread context data ****/ 250{ 251 cupsd_sysevent_t sysevent; /* System event */ 252 CFRunLoopTimerRef timerRef; /* Timer to delay some change * 253 * notifications */ 254} cupsd_thread_data_t; 255 256 257/* 258 * Local globals... 259 */ 260 261static pthread_t SysEventThread = NULL; 262 /* Thread to host a runloop */ 263static pthread_mutex_t SysEventThreadMutex = { 0 }; 264 /* Coordinates access to shared gloabals */ 265static pthread_cond_t SysEventThreadCond = { 0 }; 266 /* Thread initialization complete condition */ 267static CFRunLoopRef SysEventRunloop = NULL; 268 /* The runloop. Access must be protected! */ 269static CFStringRef ComputerNameKey = NULL, 270 /* Computer name key */ 271 BTMMKey = NULL, /* Back to My Mac key */ 272 NetworkGlobalKeyIPv4 = NULL, 273 /* Network global IPv4 key */ 274 NetworkGlobalKeyIPv6 = NULL, 275 /* Network global IPv6 key */ 276 NetworkGlobalKeyDNS = NULL, 277 /* Network global DNS key */ 278 HostNamesKey = NULL, 279 /* Host name key */ 280 NetworkInterfaceKeyIPv4 = NULL, 281 /* Netowrk interface key */ 282 NetworkInterfaceKeyIPv6 = NULL; 283 /* Netowrk interface key */ 284static cupsd_sysevent_t LastSysEvent; /* Last system event (for delayed sleep) */ 285static int NameChanged = 0;/* Did we get a 'name changed' event during sleep? */ 286static int PSToken = 0; /* Power source notifications */ 287 288 289/* 290 * Local functions... 291 */ 292 293static void *sysEventThreadEntry(void); 294static void sysEventPowerNotifier(void *context, io_service_t service, 295 natural_t messageType, 296 void *messageArgument); 297static void sysEventConfigurationNotifier(SCDynamicStoreRef store, 298 CFArrayRef changedKeys, 299 void *context); 300static void sysEventTimerNotifier(CFRunLoopTimerRef timer, void *context); 301static void sysUpdate(void); 302static void sysUpdateNames(void); 303 304 305/* 306 * 'cupsdAllowSleep()' - Tell the OS it is now OK to sleep. 307 */ 308 309void 310cupsdAllowSleep(void) 311{ 312 cupsdCleanDirty(); 313 314 cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep."); 315 IOAllowPowerChange(LastSysEvent.powerKernelPort, 316 LastSysEvent.powerNotificationID); 317} 318 319 320/* 321 * 'cupsdStartSystemMonitor()' - Start monitoring for system change. 322 */ 323 324void 325cupsdStartSystemMonitor(void) 326{ 327 int flags; /* fcntl flags on pipe */ 328 329 330 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartSystemMonitor()"); 331 332 if (cupsdOpenPipe(SysEventPipes)) 333 { 334 cupsdLogMessage(CUPSD_LOG_ERROR, "System event monitor pipe() failed - %s!", 335 strerror(errno)); 336 return; 337 } 338 339 cupsdAddSelect(SysEventPipes[0], (cupsd_selfunc_t)sysUpdate, NULL, NULL); 340 341 /* 342 * Set non-blocking mode on the descriptor we will be receiving notification 343 * events on. 344 */ 345 346 flags = fcntl(SysEventPipes[0], F_GETFL, 0); 347 fcntl(SysEventPipes[0], F_SETFL, flags | O_NONBLOCK); 348 349 /* 350 * Start the thread that runs the runloop... 351 */ 352 353 pthread_mutex_init(&SysEventThreadMutex, NULL); 354 pthread_cond_init(&SysEventThreadCond, NULL); 355 pthread_create(&SysEventThread, NULL, (void *(*)())sysEventThreadEntry, NULL); 356 357 /* 358 * Monitor for power source changes via dispatch queue... 359 */ 360 361 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartSystemMonitor: IOPSGetTimeRemainingEstimate=%f", IOPSGetTimeRemainingEstimate()); 362 ACPower = IOPSGetTimeRemainingEstimate() == kIOPSTimeRemainingUnlimited; 363 notify_register_dispatch(kIOPSNotifyPowerSource, &PSToken, dispatch_get_main_queue(), ^(int t) { ACPower = IOPSGetTimeRemainingEstimate() == kIOPSTimeRemainingUnlimited; }); 364} 365 366 367/* 368 * 'cupsdStopSystemMonitor()' - Stop monitoring for system change. 369 */ 370 371void 372cupsdStopSystemMonitor(void) 373{ 374 CFRunLoopRef rl; /* The event handler runloop */ 375 376 377 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStopSystemMonitor()"); 378 379 if (SysEventThread) 380 { 381 /* 382 * Make sure the thread has completed it's initialization and 383 * stored it's runloop reference in the shared global. 384 */ 385 386 pthread_mutex_lock(&SysEventThreadMutex); 387 388 if (!SysEventRunloop) 389 pthread_cond_wait(&SysEventThreadCond, &SysEventThreadMutex); 390 391 rl = SysEventRunloop; 392 SysEventRunloop = NULL; 393 394 pthread_mutex_unlock(&SysEventThreadMutex); 395 396 if (rl) 397 CFRunLoopStop(rl); 398 399 pthread_join(SysEventThread, NULL); 400 pthread_mutex_destroy(&SysEventThreadMutex); 401 pthread_cond_destroy(&SysEventThreadCond); 402 } 403 404 if (SysEventPipes[0] >= 0) 405 { 406 cupsdRemoveSelect(SysEventPipes[0]); 407 cupsdClosePipe(SysEventPipes); 408 } 409 410 if (PSToken != 0) 411 { 412 notify_cancel(PSToken); 413 PSToken = 0; 414 } 415} 416 417 418/* 419 * 'sysEventThreadEntry()' - A thread to receive power and computer name 420 * change notifications. 421 */ 422 423static void * /* O - Return status/value */ 424sysEventThreadEntry(void) 425{ 426 io_object_t powerNotifierObj; 427 /* Power notifier object */ 428 IONotificationPortRef powerNotifierPort; 429 /* Power notifier port */ 430 SCDynamicStoreRef store = NULL;/* System Config dynamic store */ 431 CFRunLoopSourceRef powerRLS = NULL,/* Power runloop source */ 432 storeRLS = NULL;/* System Config runloop source */ 433 CFStringRef key[6], /* System Config keys */ 434 pattern[2]; /* System Config patterns */ 435 CFArrayRef keys = NULL, /* System Config key array*/ 436 patterns = NULL;/* System Config pattern array */ 437 SCDynamicStoreContext storeContext; /* Dynamic store context */ 438 CFRunLoopTimerContext timerContext; /* Timer context */ 439 cupsd_thread_data_t threadData; /* Thread context data for the * 440 * runloop notifiers */ 441 442 443 /* 444 * Register for power state change notifications 445 */ 446 447 bzero(&threadData, sizeof(threadData)); 448 449 threadData.sysevent.powerKernelPort = 450 IORegisterForSystemPower(&threadData, &powerNotifierPort, 451 sysEventPowerNotifier, &powerNotifierObj); 452 453 if (threadData.sysevent.powerKernelPort) 454 { 455 powerRLS = IONotificationPortGetRunLoopSource(powerNotifierPort); 456 CFRunLoopAddSource(CFRunLoopGetCurrent(), powerRLS, kCFRunLoopDefaultMode); 457 } 458 else 459 DEBUG_puts("sysEventThreadEntry: error registering for system power " 460 "notifications"); 461 462 /* 463 * Register for system configuration change notifications 464 */ 465 466 bzero(&storeContext, sizeof(storeContext)); 467 storeContext.info = &threadData; 468 469 store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("cupsd"), 470 sysEventConfigurationNotifier, &storeContext); 471 472 if (!ComputerNameKey) 473 ComputerNameKey = SCDynamicStoreKeyCreateComputerName(kCFAllocatorDefault); 474 475 if (!BTMMKey) 476 BTMMKey = SCDynamicStoreKeyCreate(kCFAllocatorDefault, 477 CFSTR("Setup:/Network/BackToMyMac")); 478 479 if (!NetworkGlobalKeyIPv4) 480 NetworkGlobalKeyIPv4 = 481 SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault, 482 kSCDynamicStoreDomainState, 483 kSCEntNetIPv4); 484 485 if (!NetworkGlobalKeyIPv6) 486 NetworkGlobalKeyIPv6 = 487 SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault, 488 kSCDynamicStoreDomainState, 489 kSCEntNetIPv6); 490 491 if (!NetworkGlobalKeyDNS) 492 NetworkGlobalKeyDNS = 493 SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault, 494 kSCDynamicStoreDomainState, 495 kSCEntNetDNS); 496 497 if (!HostNamesKey) 498 HostNamesKey = SCDynamicStoreKeyCreateHostNames(kCFAllocatorDefault); 499 500 if (!NetworkInterfaceKeyIPv4) 501 NetworkInterfaceKeyIPv4 = 502 SCDynamicStoreKeyCreateNetworkInterfaceEntity(kCFAllocatorDefault, 503 kSCDynamicStoreDomainState, 504 kSCCompAnyRegex, 505 kSCEntNetIPv4); 506 507 if (!NetworkInterfaceKeyIPv6) 508 NetworkInterfaceKeyIPv6 = 509 SCDynamicStoreKeyCreateNetworkInterfaceEntity(kCFAllocatorDefault, 510 kSCDynamicStoreDomainState, 511 kSCCompAnyRegex, 512 kSCEntNetIPv6); 513 514 if (store && ComputerNameKey && HostNamesKey && 515 NetworkGlobalKeyIPv4 && NetworkGlobalKeyIPv6 && NetworkGlobalKeyDNS && 516 NetworkInterfaceKeyIPv4 && NetworkInterfaceKeyIPv6) 517 { 518 key[0] = ComputerNameKey; 519 key[1] = BTMMKey; 520 key[2] = NetworkGlobalKeyIPv4; 521 key[3] = NetworkGlobalKeyIPv6; 522 key[4] = NetworkGlobalKeyDNS; 523 key[5] = HostNamesKey; 524 525 pattern[0] = NetworkInterfaceKeyIPv4; 526 pattern[1] = NetworkInterfaceKeyIPv6; 527 528 keys = CFArrayCreate(kCFAllocatorDefault, (const void **)key, 529 sizeof(key) / sizeof(key[0]), 530 &kCFTypeArrayCallBacks); 531 532 patterns = CFArrayCreate(kCFAllocatorDefault, (const void **)pattern, 533 sizeof(pattern) / sizeof(pattern[0]), 534 &kCFTypeArrayCallBacks); 535 536 if (keys && patterns && 537 SCDynamicStoreSetNotificationKeys(store, keys, patterns)) 538 { 539 if ((storeRLS = SCDynamicStoreCreateRunLoopSource(kCFAllocatorDefault, 540 store, 0)) != NULL) 541 { 542 CFRunLoopAddSource(CFRunLoopGetCurrent(), storeRLS, 543 kCFRunLoopDefaultMode); 544 } 545 else 546 DEBUG_printf(("sysEventThreadEntry: SCDynamicStoreCreateRunLoopSource " 547 "failed: %s\n", SCErrorString(SCError()))); 548 } 549 else 550 DEBUG_printf(("sysEventThreadEntry: SCDynamicStoreSetNotificationKeys " 551 "failed: %s\n", SCErrorString(SCError()))); 552 } 553 else 554 DEBUG_printf(("sysEventThreadEntry: SCDynamicStoreCreate failed: %s\n", 555 SCErrorString(SCError()))); 556 557 if (keys) 558 CFRelease(keys); 559 560 if (patterns) 561 CFRelease(patterns); 562 563 /* 564 * Set up a timer to delay the wake change notifications. 565 * 566 * The initial time is set a decade or so into the future, we'll adjust 567 * this later. 568 */ 569 570 bzero(&timerContext, sizeof(timerContext)); 571 timerContext.info = &threadData; 572 573 threadData.timerRef = 574 CFRunLoopTimerCreate(kCFAllocatorDefault, 575 CFAbsoluteTimeGetCurrent() + (86400L * 365L * 10L), 576 86400L * 365L * 10L, 0, 0, sysEventTimerNotifier, 577 &timerContext); 578 CFRunLoopAddTimer(CFRunLoopGetCurrent(), threadData.timerRef, 579 kCFRunLoopDefaultMode); 580 581 /* 582 * Store our runloop in a global so the main thread can use it to stop us. 583 */ 584 585 pthread_mutex_lock(&SysEventThreadMutex); 586 587 SysEventRunloop = CFRunLoopGetCurrent(); 588 589 pthread_cond_signal(&SysEventThreadCond); 590 pthread_mutex_unlock(&SysEventThreadMutex); 591 592 /* 593 * Disappear into the runloop until it's stopped by the main thread. 594 */ 595 596 CFRunLoopRun(); 597 598 /* 599 * Clean up before exiting. 600 */ 601 602 if (threadData.timerRef) 603 { 604 CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), threadData.timerRef, 605 kCFRunLoopDefaultMode); 606 CFRelease(threadData.timerRef); 607 } 608 609 if (threadData.sysevent.powerKernelPort) 610 { 611 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), powerRLS, 612 kCFRunLoopDefaultMode); 613 IODeregisterForSystemPower(&powerNotifierObj); 614 IOServiceClose(threadData.sysevent.powerKernelPort); 615 IONotificationPortDestroy(powerNotifierPort); 616 } 617 618 if (storeRLS) 619 { 620 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), storeRLS, 621 kCFRunLoopDefaultMode); 622 CFRunLoopSourceInvalidate(storeRLS); 623 CFRelease(storeRLS); 624 } 625 626 if (store) 627 CFRelease(store); 628 629 pthread_exit(NULL); 630} 631 632 633/* 634 * 'sysEventPowerNotifier()' - Handle power notification events. 635 */ 636 637static void 638sysEventPowerNotifier( 639 void *context, /* I - Thread context data */ 640 io_service_t service, /* I - Unused service info */ 641 natural_t messageType, /* I - Type of message */ 642 void *messageArgument) /* I - Message data */ 643{ 644 int sendit = 1; /* Send event to main thread? * 645 * (0 = no, 1 = yes, 2 = delayed */ 646 cupsd_thread_data_t *threadData; /* Thread context data */ 647 648 649 threadData = (cupsd_thread_data_t *)context; 650 651 (void)service; /* anti-compiler-warning-code */ 652 653 switch (messageType) 654 { 655 case kIOMessageCanSystemPowerOff : 656 case kIOMessageCanSystemSleep : 657 threadData->sysevent.event |= SYSEVENT_CANSLEEP; 658 break; 659 660 case kIOMessageSystemWillRestart : 661 case kIOMessageSystemWillPowerOff : 662 case kIOMessageSystemWillSleep : 663 threadData->sysevent.event |= SYSEVENT_WILLSLEEP; 664 threadData->sysevent.event &= ~SYSEVENT_WOKE; 665 break; 666 667 case kIOMessageSystemHasPoweredOn : 668 /* 669 * Because powered on is followed by a net-changed event, delay 670 * before sending it. 671 */ 672 673 sendit = 2; 674 threadData->sysevent.event |= SYSEVENT_WOKE; 675 break; 676 677 case kIOMessageSystemWillNotPowerOff : 678 case kIOMessageSystemWillNotSleep : 679# ifdef kIOMessageSystemWillPowerOn 680 case kIOMessageSystemWillPowerOn : 681# endif /* kIOMessageSystemWillPowerOn */ 682 default: 683 sendit = 0; 684 break; 685 } 686 687 switch (messageType) 688 { 689 case kIOMessageCanSystemPowerOff : 690 cupsdLogMessage(CUPSD_LOG_DEBUG, 691 "Got kIOMessageCanSystemPowerOff message."); 692 break; 693 case kIOMessageCanSystemSleep : 694 cupsdLogMessage(CUPSD_LOG_DEBUG, 695 "Got kIOMessageCannSystemSleep message."); 696 break; 697 case kIOMessageSystemWillRestart : 698 cupsdLogMessage(CUPSD_LOG_DEBUG, 699 "Got kIOMessageSystemWillRestart message."); 700 break; 701 case kIOMessageSystemWillPowerOff : 702 cupsdLogMessage(CUPSD_LOG_DEBUG, 703 "Got kIOMessageSystemWillPowerOff message."); 704 break; 705 case kIOMessageSystemWillSleep : 706 cupsdLogMessage(CUPSD_LOG_DEBUG, 707 "Got kIOMessageSystemWillSleep message."); 708 break; 709 case kIOMessageSystemHasPoweredOn : 710 cupsdLogMessage(CUPSD_LOG_DEBUG, 711 "Got kIOMessageSystemHasPoweredOn message."); 712 break; 713 case kIOMessageSystemWillNotPowerOff : 714 cupsdLogMessage(CUPSD_LOG_DEBUG, 715 "Got kIOMessageSystemWillNotPowerOff message."); 716 break; 717 case kIOMessageSystemWillNotSleep : 718 cupsdLogMessage(CUPSD_LOG_DEBUG, 719 "Got kIOMessageSystemWillNotSleep message."); 720 break; 721# ifdef kIOMessageSystemWillPowerOn 722 case kIOMessageSystemWillPowerOn : 723 cupsdLogMessage(CUPSD_LOG_DEBUG, 724 "Got kIOMessageSystemWillPowerOn message."); 725 break; 726# endif /* kIOMessageSystemWillPowerOn */ 727 default: 728 cupsdLogMessage(CUPSD_LOG_DEBUG, "Got unknown power message %d.", 729 (int)messageType); 730 break; 731 } 732 733 if (sendit == 0) 734 IOAllowPowerChange(threadData->sysevent.powerKernelPort, 735 (long)messageArgument); 736 else 737 { 738 threadData->sysevent.powerNotificationID = (long)messageArgument; 739 740 if (sendit == 1) 741 { 742 /* 743 * Send the event to the main thread now. 744 */ 745 746 write(SysEventPipes[1], &threadData->sysevent, 747 sizeof(threadData->sysevent)); 748 threadData->sysevent.event = 0; 749 } 750 else 751 { 752 /* 753 * Send the event to the main thread after 1 to 2 seconds. 754 */ 755 756 CFRunLoopTimerSetNextFireDate(threadData->timerRef, 757 CFAbsoluteTimeGetCurrent() + 2); 758 } 759 } 760} 761 762 763/* 764 * 'sysEventConfigurationNotifier()' - Network configuration change notification 765 * callback. 766 */ 767 768static void 769sysEventConfigurationNotifier( 770 SCDynamicStoreRef store, /* I - System data (unused) */ 771 CFArrayRef changedKeys, /* I - Changed data */ 772 void *context) /* I - Thread context data */ 773{ 774 cupsd_thread_data_t *threadData; /* Thread context data */ 775 776 777 threadData = (cupsd_thread_data_t *)context; 778 779 (void)store; /* anti-compiler-warning-code */ 780 781 CFRange range = CFRangeMake(0, CFArrayGetCount(changedKeys)); 782 783 if (CFArrayContainsValue(changedKeys, range, ComputerNameKey) || 784 CFArrayContainsValue(changedKeys, range, BTMMKey)) 785 threadData->sysevent.event |= SYSEVENT_NAMECHANGED; 786 else 787 { 788 threadData->sysevent.event |= SYSEVENT_NETCHANGED; 789 790 /* 791 * Indicate the network interface list needs updating... 792 */ 793 794 NetIFUpdate = 1; 795 } 796 797 /* 798 * Because we registered for several different kinds of change notifications 799 * this callback usually gets called several times in a row. We use a timer to 800 * de-bounce these so we only end up generating one event for the main thread. 801 */ 802 803 CFRunLoopTimerSetNextFireDate(threadData->timerRef, 804 CFAbsoluteTimeGetCurrent() + 5); 805} 806 807 808/* 809 * 'sysEventTimerNotifier()' - Handle delayed event notifications. 810 */ 811 812static void 813sysEventTimerNotifier( 814 CFRunLoopTimerRef timer, /* I - Timer information */ 815 void *context) /* I - Thread context data */ 816{ 817 cupsd_thread_data_t *threadData; /* Thread context data */ 818 819 820 (void)timer; 821 822 threadData = (cupsd_thread_data_t *)context; 823 824 /* 825 * If an event is still pending send it to the main thread. 826 */ 827 828 if (threadData->sysevent.event) 829 { 830 write(SysEventPipes[1], &threadData->sysevent, 831 sizeof(threadData->sysevent)); 832 threadData->sysevent.event = 0; 833 } 834} 835 836 837/* 838 * 'sysUpdate()' - Update the current system state. 839 */ 840 841static void 842sysUpdate(void) 843{ 844 int i; /* Looping var */ 845 cupsd_sysevent_t sysevent; /* The system event */ 846 cupsd_printer_t *p; /* Printer information */ 847 848 849 /* 850 * Drain the event pipe... 851 */ 852 853 while (read((int)SysEventPipes[0], &sysevent, sizeof(sysevent)) 854 == sizeof(sysevent)) 855 { 856 if (sysevent.event & SYSEVENT_CANSLEEP) 857 { 858 /* 859 * If there are active printers that don't have the connecting-to-device 860 * or cups-waiting-for-job-completed printer-state-reason then cancel the 861 * sleep request, i.e., these reasons indicate a job that is not actively 862 * doing anything... 863 */ 864 865 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); 866 p; 867 p = (cupsd_printer_t *)cupsArrayNext(Printers)) 868 { 869 if (p->job) 870 { 871 for (i = 0; i < p->num_reasons; i ++) 872 if (!strcmp(p->reasons[i], "connecting-to-device") || 873 !strcmp(p->reasons[i], "cups-waiting-for-job-completed")) 874 break; 875 876 if (!p->num_reasons || i >= p->num_reasons) 877 break; 878 } 879 } 880 881 if (p) 882 { 883 cupsdLogMessage(CUPSD_LOG_INFO, 884 "System sleep canceled because printer %s is active.", 885 p->name); 886 IOCancelPowerChange(sysevent.powerKernelPort, 887 sysevent.powerNotificationID); 888 } 889 else 890 { 891 cupsdLogMessage(CUPSD_LOG_DEBUG, "System wants to sleep."); 892 IOAllowPowerChange(sysevent.powerKernelPort, 893 sysevent.powerNotificationID); 894 } 895 } 896 897 if (sysevent.event & SYSEVENT_WILLSLEEP) 898 { 899 cupsdLogMessage(CUPSD_LOG_DEBUG, "System going to sleep."); 900 901 Sleeping = 1; 902 903 cupsdCleanDirty(); 904 905 /* 906 * If we have no printing jobs, allow the power change immediately. 907 * Otherwise set the SleepJobs time to 10 seconds in the future when 908 * we'll take more drastic measures... 909 */ 910 911 if (cupsArrayCount(PrintingJobs) == 0) 912 { 913 cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep."); 914 IOAllowPowerChange(sysevent.powerKernelPort, 915 sysevent.powerNotificationID); 916 } 917 else 918 { 919 /* 920 * If there are active printers that don't have the connecting-to-device 921 * or cups-waiting-for-job-completed printer-state-reasons then delay the 922 * sleep request, i.e., these reasons indicate a job is active... 923 */ 924 925 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); 926 p; 927 p = (cupsd_printer_t *)cupsArrayNext(Printers)) 928 { 929 if (p->job) 930 { 931 for (i = 0; i < p->num_reasons; i ++) 932 if (!strcmp(p->reasons[i], "connecting-to-device") || 933 !strcmp(p->reasons[i], "cups-waiting-for-job-completed")) 934 break; 935 936 if (!p->num_reasons || i >= p->num_reasons) 937 break; 938 } 939 } 940 941 if (p) 942 { 943 cupsdLogMessage(CUPSD_LOG_INFO, 944 "System sleep delayed because printer %s is active.", 945 p->name); 946 947 LastSysEvent = sysevent; 948 SleepJobs = time(NULL) + 10; 949 } 950 else 951 { 952 cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep."); 953 IOAllowPowerChange(sysevent.powerKernelPort, 954 sysevent.powerNotificationID); 955 } 956 } 957 } 958 959 if (sysevent.event & SYSEVENT_WOKE) 960 { 961 cupsdLogMessage(CUPSD_LOG_DEBUG, "System woke from sleep."); 962 IOAllowPowerChange(sysevent.powerKernelPort, 963 sysevent.powerNotificationID); 964 Sleeping = 0; 965 966 /* 967 * Make sure jobs that were queued prior to the system going to sleep don't 968 * get canceled right away... 969 */ 970 971 if (MaxJobTime > 0) 972 { 973 cupsd_job_t *job; /* Current job */ 974 975 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs); 976 job; 977 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs)) 978 { 979 if (job->cancel_time) 980 { 981 ipp_attribute_t *cancel_after = ippFindAttribute(job->attrs, 982 "job-cancel-after", 983 IPP_TAG_INTEGER); 984 985 if (cancel_after) 986 job->cancel_time = time(NULL) + ippGetInteger(cancel_after, 0); 987 else 988 job->cancel_time = time(NULL) + MaxJobTime; 989 } 990 } 991 } 992 993 if (NameChanged) 994 sysUpdateNames(); 995 996 cupsdCheckJobs(); 997 } 998 999 if (sysevent.event & SYSEVENT_NETCHANGED) 1000 { 1001 if (Sleeping) 1002 cupsdLogMessage(CUPSD_LOG_DEBUG, 1003 "System network configuration changed - " 1004 "ignored while sleeping."); 1005 else 1006 cupsdLogMessage(CUPSD_LOG_DEBUG, 1007 "System network configuration changed."); 1008 } 1009 1010 if (sysevent.event & SYSEVENT_NAMECHANGED) 1011 { 1012 if (Sleeping) 1013 { 1014 NameChanged = 1; 1015 1016 cupsdLogMessage(CUPSD_LOG_DEBUG, 1017 "Computer name or BTMM domains changed - ignored while " 1018 "sleeping."); 1019 } 1020 else 1021 { 1022 cupsdLogMessage(CUPSD_LOG_DEBUG, 1023 "Computer name or BTMM domains changed."); 1024 1025 sysUpdateNames(); 1026 } 1027 } 1028 } 1029} 1030 1031 1032/* 1033 * 'sysUpdateNames()' - Update computer and/or BTMM domains. 1034 */ 1035 1036static void 1037sysUpdateNames(void) 1038{ 1039 cupsd_printer_t *p; /* Current printer */ 1040 1041 1042 NameChanged = 0; 1043 1044 /* 1045 * De-register the individual printers... 1046 */ 1047 1048 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); 1049 p; 1050 p = (cupsd_printer_t *)cupsArrayNext(Printers)) 1051 cupsdDeregisterPrinter(p, 1); 1052 1053# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) 1054 /* 1055 * Update the computer name and BTMM domain list... 1056 */ 1057 1058 cupsdUpdateDNSSDName(); 1059# endif /* HAVE_DNSSD || HAVE_AVAHI */ 1060 1061 /* 1062 * Now re-register them... 1063 */ 1064 1065 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); 1066 p; 1067 p = (cupsd_printer_t *)cupsArrayNext(Printers)) 1068 cupsdRegisterPrinter(p); 1069} 1070#endif /* __APPLE__ */ 1071 1072 1073/* 1074 * End of "$Id: sysman.c 12142 2014-08-30 02:35:43Z msweet $". 1075 */ 1076