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