/* * System management functions for the CUPS scheduler. * * Copyright 2007-2018 by Apple Inc. * Copyright 2006 by Easy Software Products. * * Licensed under Apache License v2.0. See the file "LICENSE" for more information. */ /* * Include necessary headers... */ #include "cupsd.h" #ifdef __APPLE__ # include #endif /* __APPLE__ */ /* * The system management functions cover disk and power management which * are primarily used for portable computers. * * Disk management involves delaying the write of certain configuration * and state files to minimize the number of times the disk has to spin * up or flash to be written to. * * Power management support is currently only implemented on macOS, but * essentially we use four functions to let the OS know when it is OK to * put the system to sleep, typically when we are not in the middle of * printing a job. And on macOS we can also "sleep print" - basically the * system only wakes up long enough to service network requests and process * print jobs. */ /* * 'cupsdCleanDirty()' - Write dirty config and state files. */ void cupsdCleanDirty(void) { if (DirtyFiles & CUPSD_DIRTY_PRINTERS) cupsdSaveAllPrinters(); if (DirtyFiles & CUPSD_DIRTY_CLASSES) cupsdSaveAllClasses(); if (DirtyFiles & CUPSD_DIRTY_PRINTCAP) cupsdWritePrintcap(); if (DirtyFiles & CUPSD_DIRTY_JOBS) { cupsd_job_t *job; /* Current job */ cupsdSaveAllJobs(); for (job = (cupsd_job_t *)cupsArrayFirst(Jobs); job; job = (cupsd_job_t *)cupsArrayNext(Jobs)) if (job->dirty) cupsdSaveJob(job); } if (DirtyFiles & CUPSD_DIRTY_SUBSCRIPTIONS) cupsdSaveAllSubscriptions(); DirtyFiles = CUPSD_DIRTY_NONE; DirtyCleanTime = 0; cupsdSetBusyState(0); } /* * 'cupsdMarkDirty()' - Mark config or state files as needing a write. */ void cupsdMarkDirty(int what) /* I - What file(s) are dirty? */ { cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdMarkDirty(%c%c%c%c%c)", (what & CUPSD_DIRTY_PRINTERS) ? 'P' : '-', (what & CUPSD_DIRTY_CLASSES) ? 'C' : '-', (what & CUPSD_DIRTY_PRINTCAP) ? 'p' : '-', (what & CUPSD_DIRTY_JOBS) ? 'J' : '-', (what & CUPSD_DIRTY_SUBSCRIPTIONS) ? 'S' : '-'); if (what == CUPSD_DIRTY_PRINTCAP && !Printcap) return; DirtyFiles |= what; if (!DirtyCleanTime) DirtyCleanTime = time(NULL) + DirtyCleanInterval; cupsdSetBusyState(0); } /* * 'cupsdSetBusyState()' - Let the system know when we are busy doing something. */ void cupsdSetBusyState(int working) /* I - Doing significant work? */ { int i; /* Looping var */ cupsd_job_t *job; /* Current job */ cupsd_printer_t *p; /* Current printer */ int newbusy; /* New busy state */ static int busy = 0; /* Current busy state */ static const char * const busy_text[] = { /* Text for busy states */ "Not busy", "Dirty files", "Printing jobs", "Printing jobs and dirty files", "Active clients", "Active clients and dirty files", "Active clients and printing jobs", "Active clients, printing jobs, and dirty files" }; #ifdef __APPLE__ static IOPMAssertionID keep_awake = 0;/* Keep the system awake while printing */ #endif /* __APPLE__ */ /* * Figure out how busy we are... */ newbusy = (DirtyCleanTime ? 1 : 0) | ((working || cupsArrayCount(ActiveClients) > 0) ? 4 : 0); for (job = (cupsd_job_t *)cupsArrayFirst(PrintingJobs); job; job = (cupsd_job_t *)cupsArrayNext(PrintingJobs)) { if ((p = job->printer) != NULL) { for (i = 0; i < p->num_reasons; i ++) if (!strcmp(p->reasons[i], "connecting-to-device")) break; if (!p->num_reasons || i >= p->num_reasons) break; } } if (job) newbusy |= 2; cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSetBusyState: newbusy=\"%s\", busy=\"%s\"", busy_text[newbusy], busy_text[busy]); /* * Manage state changes... */ if (newbusy != busy) busy = newbusy; #ifdef __APPLE__ if (cupsArrayCount(PrintingJobs) > 0 && !keep_awake) { cupsdLogMessage(CUPSD_LOG_DEBUG, "Asserting NetworkClientActive."); IOPMAssertionCreateWithName(kIOPMAssertNetworkClientActive, kIOPMAssertionLevelOn, CFSTR("org.cups.cupsd"), &keep_awake); } else if (cupsArrayCount(PrintingJobs) == 0 && keep_awake) { cupsdLogMessage(CUPSD_LOG_DEBUG, "Releasing power assertion."); IOPMAssertionRelease(keep_awake); keep_awake = 0; } #endif /* __APPLE__ */ } #ifdef __APPLE__ /* * This is the Apple-specific system event code. It works by creating * a worker thread that waits for events from the OS and relays them * to the main thread via a traditional pipe. */ /* * Include MacOS-specific headers... */ # include # include # include # include # include # include # include /* * Constants... */ # define SYSEVENT_CANSLEEP 0x1 /* Decide whether to allow sleep or not */ # define SYSEVENT_WILLSLEEP 0x2 /* Computer will go to sleep */ # define SYSEVENT_WOKE 0x4 /* Computer woke from sleep */ # define SYSEVENT_NETCHANGED 0x8 /* Network changed */ # define SYSEVENT_NAMECHANGED 0x10 /* Computer name changed */ /* * Structures... */ typedef struct cupsd_sysevent_s /*** System event data ****/ { unsigned char event; /* Event bit field */ io_connect_t powerKernelPort; /* Power context data */ long powerNotificationID; /* Power event data */ } cupsd_sysevent_t; typedef struct cupsd_thread_data_s /*** Thread context data ****/ { cupsd_sysevent_t sysevent; /* System event */ CFRunLoopTimerRef timerRef; /* Timer to delay some change * * notifications */ } cupsd_thread_data_t; /* * Local globals... */ static pthread_t SysEventThread = NULL; /* Thread to host a runloop */ static pthread_mutex_t SysEventThreadMutex = { 0 }; /* Coordinates access to shared gloabals */ static pthread_cond_t SysEventThreadCond = { 0 }; /* Thread initialization complete condition */ static CFRunLoopRef SysEventRunloop = NULL; /* The runloop. Access must be protected! */ static CFStringRef ComputerNameKey = NULL, /* Computer name key */ BTMMKey = NULL, /* Back to My Mac key */ NetworkGlobalKeyIPv4 = NULL, /* Network global IPv4 key */ NetworkGlobalKeyIPv6 = NULL, /* Network global IPv6 key */ NetworkGlobalKeyDNS = NULL, /* Network global DNS key */ HostNamesKey = NULL, /* Host name key */ NetworkInterfaceKeyIPv4 = NULL, /* Netowrk interface key */ NetworkInterfaceKeyIPv6 = NULL; /* Netowrk interface key */ static cupsd_sysevent_t LastSysEvent; /* Last system event (for delayed sleep) */ static int NameChanged = 0;/* Did we get a 'name changed' event during sleep? */ static int PSToken = 0; /* Power source notifications */ /* * Local functions... */ static void *sysEventThreadEntry(void); static void sysEventPowerNotifier(void *context, io_service_t service, natural_t messageType, void *messageArgument); static void sysEventConfigurationNotifier(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context); static void sysEventTimerNotifier(CFRunLoopTimerRef timer, void *context); static void sysUpdate(void); static void sysUpdateNames(void); /* * 'cupsdAllowSleep()' - Tell the OS it is now OK to sleep. */ void cupsdAllowSleep(void) { cupsdCleanDirty(); cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep."); IOAllowPowerChange(LastSysEvent.powerKernelPort, LastSysEvent.powerNotificationID); } /* * 'cupsdStartSystemMonitor()' - Start monitoring for system change. */ void cupsdStartSystemMonitor(void) { int flags; /* fcntl flags on pipe */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartSystemMonitor()"); if (cupsdOpenPipe(SysEventPipes)) { cupsdLogMessage(CUPSD_LOG_ERROR, "System event monitor pipe() failed - %s!", strerror(errno)); return; } cupsdAddSelect(SysEventPipes[0], (cupsd_selfunc_t)sysUpdate, NULL, NULL); /* * Set non-blocking mode on the descriptor we will be receiving notification * events on. */ flags = fcntl(SysEventPipes[0], F_GETFL, 0); fcntl(SysEventPipes[0], F_SETFL, flags | O_NONBLOCK); /* * Start the thread that runs the runloop... */ pthread_mutex_init(&SysEventThreadMutex, NULL); pthread_cond_init(&SysEventThreadCond, NULL); pthread_create(&SysEventThread, NULL, (void *(*)(void *))sysEventThreadEntry, NULL); /* * Monitor for power source changes via dispatch queue... */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartSystemMonitor: IOPSGetTimeRemainingEstimate=%f", IOPSGetTimeRemainingEstimate()); ACPower = IOPSGetTimeRemainingEstimate() == kIOPSTimeRemainingUnlimited; notify_register_dispatch(kIOPSNotifyPowerSource, &PSToken, dispatch_get_main_queue(), ^(int t) { (void)t; ACPower = IOPSGetTimeRemainingEstimate() == kIOPSTimeRemainingUnlimited; }); } /* * 'cupsdStopSystemMonitor()' - Stop monitoring for system change. */ void cupsdStopSystemMonitor(void) { CFRunLoopRef rl; /* The event handler runloop */ cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStopSystemMonitor()"); if (SysEventThread) { /* * Make sure the thread has completed it's initialization and * stored it's runloop reference in the shared global. */ pthread_mutex_lock(&SysEventThreadMutex); if (!SysEventRunloop) pthread_cond_wait(&SysEventThreadCond, &SysEventThreadMutex); rl = SysEventRunloop; SysEventRunloop = NULL; pthread_mutex_unlock(&SysEventThreadMutex); if (rl) CFRunLoopStop(rl); pthread_join(SysEventThread, NULL); pthread_mutex_destroy(&SysEventThreadMutex); pthread_cond_destroy(&SysEventThreadCond); } if (SysEventPipes[0] >= 0) { cupsdRemoveSelect(SysEventPipes[0]); cupsdClosePipe(SysEventPipes); } if (PSToken != 0) { notify_cancel(PSToken); PSToken = 0; } } /* * 'sysEventThreadEntry()' - A thread to receive power and computer name * change notifications. */ static void * /* O - Return status/value */ sysEventThreadEntry(void) { io_object_t powerNotifierObj; /* Power notifier object */ IONotificationPortRef powerNotifierPort; /* Power notifier port */ SCDynamicStoreRef store = NULL;/* System Config dynamic store */ CFRunLoopSourceRef powerRLS = NULL,/* Power runloop source */ storeRLS = NULL;/* System Config runloop source */ CFStringRef key[6], /* System Config keys */ pattern[2]; /* System Config patterns */ CFArrayRef keys = NULL, /* System Config key array*/ patterns = NULL;/* System Config pattern array */ SCDynamicStoreContext storeContext; /* Dynamic store context */ CFRunLoopTimerContext timerContext; /* Timer context */ cupsd_thread_data_t threadData; /* Thread context data for the * * runloop notifiers */ /* * Register for power state change notifications */ bzero(&threadData, sizeof(threadData)); threadData.sysevent.powerKernelPort = IORegisterForSystemPower(&threadData, &powerNotifierPort, sysEventPowerNotifier, &powerNotifierObj); if (threadData.sysevent.powerKernelPort) { powerRLS = IONotificationPortGetRunLoopSource(powerNotifierPort); CFRunLoopAddSource(CFRunLoopGetCurrent(), powerRLS, kCFRunLoopDefaultMode); } /* * Register for system configuration change notifications */ bzero(&storeContext, sizeof(storeContext)); storeContext.info = &threadData; store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("cupsd"), sysEventConfigurationNotifier, &storeContext); if (!ComputerNameKey) ComputerNameKey = SCDynamicStoreKeyCreateComputerName(kCFAllocatorDefault); if (!BTMMKey) BTMMKey = SCDynamicStoreKeyCreate(kCFAllocatorDefault, CFSTR("Setup:/Network/BackToMyMac")); if (!NetworkGlobalKeyIPv4) NetworkGlobalKeyIPv4 = SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault, kSCDynamicStoreDomainState, kSCEntNetIPv4); if (!NetworkGlobalKeyIPv6) NetworkGlobalKeyIPv6 = SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault, kSCDynamicStoreDomainState, kSCEntNetIPv6); if (!NetworkGlobalKeyDNS) NetworkGlobalKeyDNS = SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault, kSCDynamicStoreDomainState, kSCEntNetDNS); if (!HostNamesKey) HostNamesKey = SCDynamicStoreKeyCreateHostNames(kCFAllocatorDefault); if (!NetworkInterfaceKeyIPv4) NetworkInterfaceKeyIPv4 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(kCFAllocatorDefault, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4); if (!NetworkInterfaceKeyIPv6) NetworkInterfaceKeyIPv6 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(kCFAllocatorDefault, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6); if (store && ComputerNameKey && HostNamesKey && NetworkGlobalKeyIPv4 && NetworkGlobalKeyIPv6 && NetworkGlobalKeyDNS && NetworkInterfaceKeyIPv4 && NetworkInterfaceKeyIPv6) { key[0] = ComputerNameKey; key[1] = BTMMKey; key[2] = NetworkGlobalKeyIPv4; key[3] = NetworkGlobalKeyIPv6; key[4] = NetworkGlobalKeyDNS; key[5] = HostNamesKey; pattern[0] = NetworkInterfaceKeyIPv4; pattern[1] = NetworkInterfaceKeyIPv6; keys = CFArrayCreate(kCFAllocatorDefault, (const void **)key, sizeof(key) / sizeof(key[0]), &kCFTypeArrayCallBacks); patterns = CFArrayCreate(kCFAllocatorDefault, (const void **)pattern, sizeof(pattern) / sizeof(pattern[0]), &kCFTypeArrayCallBacks); if (keys && patterns && SCDynamicStoreSetNotificationKeys(store, keys, patterns)) { if ((storeRLS = SCDynamicStoreCreateRunLoopSource(kCFAllocatorDefault, store, 0)) != NULL) { CFRunLoopAddSource(CFRunLoopGetCurrent(), storeRLS, kCFRunLoopDefaultMode); } } } if (keys) CFRelease(keys); if (patterns) CFRelease(patterns); /* * Set up a timer to delay the wake change notifications. * * The initial time is set a decade or so into the future, we'll adjust * this later. */ bzero(&timerContext, sizeof(timerContext)); timerContext.info = &threadData; threadData.timerRef = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + (86400L * 365L * 10L), 86400L * 365L * 10L, 0, 0, sysEventTimerNotifier, &timerContext); CFRunLoopAddTimer(CFRunLoopGetCurrent(), threadData.timerRef, kCFRunLoopDefaultMode); /* * Store our runloop in a global so the main thread can use it to stop us. */ pthread_mutex_lock(&SysEventThreadMutex); SysEventRunloop = CFRunLoopGetCurrent(); pthread_cond_signal(&SysEventThreadCond); pthread_mutex_unlock(&SysEventThreadMutex); /* * Disappear into the runloop until it's stopped by the main thread. */ CFRunLoopRun(); /* * Clean up before exiting. */ if (threadData.timerRef) { CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), threadData.timerRef, kCFRunLoopDefaultMode); CFRelease(threadData.timerRef); } if (threadData.sysevent.powerKernelPort) { CFRunLoopRemoveSource(CFRunLoopGetCurrent(), powerRLS, kCFRunLoopDefaultMode); IODeregisterForSystemPower(&powerNotifierObj); IOServiceClose(threadData.sysevent.powerKernelPort); IONotificationPortDestroy(powerNotifierPort); } if (storeRLS) { CFRunLoopRemoveSource(CFRunLoopGetCurrent(), storeRLS, kCFRunLoopDefaultMode); CFRunLoopSourceInvalidate(storeRLS); CFRelease(storeRLS); } if (store) CFRelease(store); pthread_exit(NULL); } /* * 'sysEventPowerNotifier()' - Handle power notification events. */ static void sysEventPowerNotifier( void *context, /* I - Thread context data */ io_service_t service, /* I - Unused service info */ natural_t messageType, /* I - Type of message */ void *messageArgument) /* I - Message data */ { int sendit = 1; /* Send event to main thread? * * (0 = no, 1 = yes, 2 = delayed */ cupsd_thread_data_t *threadData; /* Thread context data */ threadData = (cupsd_thread_data_t *)context; (void)service; /* anti-compiler-warning-code */ switch (messageType) { case kIOMessageCanSystemPowerOff : case kIOMessageCanSystemSleep : threadData->sysevent.event |= SYSEVENT_CANSLEEP; break; case kIOMessageSystemWillRestart : case kIOMessageSystemWillPowerOff : case kIOMessageSystemWillSleep : threadData->sysevent.event |= SYSEVENT_WILLSLEEP; threadData->sysevent.event &= ~SYSEVENT_WOKE; break; case kIOMessageSystemHasPoweredOn : /* * Because powered on is followed by a net-changed event, delay * before sending it. */ sendit = 2; threadData->sysevent.event |= SYSEVENT_WOKE; break; case kIOMessageSystemWillNotPowerOff : case kIOMessageSystemWillNotSleep : # ifdef kIOMessageSystemWillPowerOn case kIOMessageSystemWillPowerOn : # endif /* kIOMessageSystemWillPowerOn */ default: sendit = 0; break; } switch (messageType) { case kIOMessageCanSystemPowerOff : cupsdLogMessage(CUPSD_LOG_DEBUG, "Got kIOMessageCanSystemPowerOff message."); break; case kIOMessageCanSystemSleep : cupsdLogMessage(CUPSD_LOG_DEBUG, "Got kIOMessageCannSystemSleep message."); break; case kIOMessageSystemWillRestart : cupsdLogMessage(CUPSD_LOG_DEBUG, "Got kIOMessageSystemWillRestart message."); break; case kIOMessageSystemWillPowerOff : cupsdLogMessage(CUPSD_LOG_DEBUG, "Got kIOMessageSystemWillPowerOff message."); break; case kIOMessageSystemWillSleep : cupsdLogMessage(CUPSD_LOG_DEBUG, "Got kIOMessageSystemWillSleep message."); break; case kIOMessageSystemHasPoweredOn : cupsdLogMessage(CUPSD_LOG_DEBUG, "Got kIOMessageSystemHasPoweredOn message."); break; case kIOMessageSystemWillNotPowerOff : cupsdLogMessage(CUPSD_LOG_DEBUG, "Got kIOMessageSystemWillNotPowerOff message."); break; case kIOMessageSystemWillNotSleep : cupsdLogMessage(CUPSD_LOG_DEBUG, "Got kIOMessageSystemWillNotSleep message."); break; # ifdef kIOMessageSystemWillPowerOn case kIOMessageSystemWillPowerOn : cupsdLogMessage(CUPSD_LOG_DEBUG, "Got kIOMessageSystemWillPowerOn message."); break; # endif /* kIOMessageSystemWillPowerOn */ default: cupsdLogMessage(CUPSD_LOG_DEBUG, "Got unknown power message %d.", (int)messageType); break; } if (sendit == 0) IOAllowPowerChange(threadData->sysevent.powerKernelPort, (long)messageArgument); else { threadData->sysevent.powerNotificationID = (long)messageArgument; if (sendit == 1) { /* * Send the event to the main thread now. */ write(SysEventPipes[1], &threadData->sysevent, sizeof(threadData->sysevent)); threadData->sysevent.event = 0; } else { /* * Send the event to the main thread after 1 to 2 seconds. */ CFRunLoopTimerSetNextFireDate(threadData->timerRef, CFAbsoluteTimeGetCurrent() + 2); } } } /* * 'sysEventConfigurationNotifier()' - Network configuration change notification * callback. */ static void sysEventConfigurationNotifier( SCDynamicStoreRef store, /* I - System data (unused) */ CFArrayRef changedKeys, /* I - Changed data */ void *context) /* I - Thread context data */ { cupsd_thread_data_t *threadData; /* Thread context data */ threadData = (cupsd_thread_data_t *)context; (void)store; /* anti-compiler-warning-code */ CFRange range = CFRangeMake(0, CFArrayGetCount(changedKeys)); if (CFArrayContainsValue(changedKeys, range, ComputerNameKey) || CFArrayContainsValue(changedKeys, range, BTMMKey)) threadData->sysevent.event |= SYSEVENT_NAMECHANGED; else { threadData->sysevent.event |= SYSEVENT_NETCHANGED; /* * Indicate the network interface list needs updating... */ NetIFUpdate = 1; } /* * Because we registered for several different kinds of change notifications * this callback usually gets called several times in a row. We use a timer to * de-bounce these so we only end up generating one event for the main thread. */ CFRunLoopTimerSetNextFireDate(threadData->timerRef, CFAbsoluteTimeGetCurrent() + 5); } /* * 'sysEventTimerNotifier()' - Handle delayed event notifications. */ static void sysEventTimerNotifier( CFRunLoopTimerRef timer, /* I - Timer information */ void *context) /* I - Thread context data */ { cupsd_thread_data_t *threadData; /* Thread context data */ (void)timer; threadData = (cupsd_thread_data_t *)context; /* * If an event is still pending send it to the main thread. */ if (threadData->sysevent.event) { write(SysEventPipes[1], &threadData->sysevent, sizeof(threadData->sysevent)); threadData->sysevent.event = 0; } } /* * 'sysUpdate()' - Update the current system state. */ static void sysUpdate(void) { int i; /* Looping var */ cupsd_sysevent_t sysevent; /* The system event */ cupsd_printer_t *p; /* Printer information */ /* * Drain the event pipe... */ while (read((int)SysEventPipes[0], &sysevent, sizeof(sysevent)) == sizeof(sysevent)) { if (sysevent.event & SYSEVENT_CANSLEEP) { /* * If there are active printers that don't have the connecting-to-device * or cups-waiting-for-job-completed printer-state-reason then cancel the * sleep request, i.e., these reasons indicate a job that is not actively * doing anything... */ for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); p; p = (cupsd_printer_t *)cupsArrayNext(Printers)) { if (p->job) { for (i = 0; i < p->num_reasons; i ++) if (!strcmp(p->reasons[i], "connecting-to-device") || !strcmp(p->reasons[i], "cups-waiting-for-job-completed")) break; if (!p->num_reasons || i >= p->num_reasons) break; } } if (p) { cupsdLogMessage(CUPSD_LOG_INFO, "System sleep canceled because printer %s is active.", p->name); IOCancelPowerChange(sysevent.powerKernelPort, sysevent.powerNotificationID); } else { cupsdLogMessage(CUPSD_LOG_DEBUG, "System wants to sleep."); IOAllowPowerChange(sysevent.powerKernelPort, sysevent.powerNotificationID); } } if (sysevent.event & SYSEVENT_WILLSLEEP) { cupsdLogMessage(CUPSD_LOG_DEBUG, "System going to sleep."); Sleeping = 1; cupsdCleanDirty(); /* * If we have no printing jobs, allow the power change immediately. * Otherwise set the SleepJobs time to 10 seconds in the future when * we'll take more drastic measures... */ if (cupsArrayCount(PrintingJobs) == 0) { cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep."); IOAllowPowerChange(sysevent.powerKernelPort, sysevent.powerNotificationID); } else { /* * If there are active printers that don't have the connecting-to-device * or cups-waiting-for-job-completed printer-state-reasons then delay the * sleep request, i.e., these reasons indicate a job is active... */ for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); p; p = (cupsd_printer_t *)cupsArrayNext(Printers)) { if (p->job) { for (i = 0; i < p->num_reasons; i ++) if (!strcmp(p->reasons[i], "connecting-to-device") || !strcmp(p->reasons[i], "cups-waiting-for-job-completed")) break; if (!p->num_reasons || i >= p->num_reasons) break; } } if (p) { cupsdLogMessage(CUPSD_LOG_INFO, "System sleep delayed because printer %s is active.", p->name); LastSysEvent = sysevent; SleepJobs = time(NULL) + 10; } else { cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep."); IOAllowPowerChange(sysevent.powerKernelPort, sysevent.powerNotificationID); } } } if (sysevent.event & SYSEVENT_WOKE) { cupsdLogMessage(CUPSD_LOG_DEBUG, "System woke from sleep."); IOAllowPowerChange(sysevent.powerKernelPort, sysevent.powerNotificationID); Sleeping = 0; /* * Make sure jobs that were queued prior to the system going to sleep don't * get canceled right away... */ if (MaxJobTime > 0) { cupsd_job_t *job; /* Current job */ for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs); job; job = (cupsd_job_t *)cupsArrayNext(ActiveJobs)) { if (job->cancel_time) { ipp_attribute_t *cancel_after = ippFindAttribute(job->attrs, "job-cancel-after", IPP_TAG_INTEGER); if (cancel_after) job->cancel_time = time(NULL) + ippGetInteger(cancel_after, 0); else job->cancel_time = time(NULL) + MaxJobTime; } } } if (NameChanged) sysUpdateNames(); cupsdCheckJobs(); } if (sysevent.event & SYSEVENT_NETCHANGED) { if (Sleeping) cupsdLogMessage(CUPSD_LOG_DEBUG, "System network configuration changed - " "ignored while sleeping."); else cupsdLogMessage(CUPSD_LOG_DEBUG, "System network configuration changed."); } if (sysevent.event & SYSEVENT_NAMECHANGED) { if (Sleeping) { NameChanged = 1; cupsdLogMessage(CUPSD_LOG_DEBUG, "Computer name or BTMM domains changed - ignored while " "sleeping."); } else { cupsdLogMessage(CUPSD_LOG_DEBUG, "Computer name or BTMM domains changed."); sysUpdateNames(); } } } } /* * 'sysUpdateNames()' - Update computer and/or BTMM domains. */ static void sysUpdateNames(void) { cupsd_printer_t *p; /* Current printer */ NameChanged = 0; /* * De-register the individual printers... */ for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); p; p = (cupsd_printer_t *)cupsArrayNext(Printers)) cupsdDeregisterPrinter(p, 1); # if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) /* * Update the computer name and BTMM domain list... */ cupsdUpdateDNSSDName(); # endif /* HAVE_DNSSD || HAVE_AVAHI */ /* * Now re-register them... */ for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); p; p = (cupsd_printer_t *)cupsArrayNext(Printers)) cupsdRegisterPrinter(p); } #endif /* __APPLE__ */