blob: 9c323439fb6eab76b360afa58178427c65062230 [file] [log] [blame]
/*
* "$Id: sysman.c 7928 2008-09-10 22:14:22Z mike $"
*
* System management functions for the CUPS scheduler.
*
* Copyright 2007-2011 by Apple Inc.
* Copyright 2006 by Easy Software Products.
*
* These coded instructions, statements, and computer programs are the
* property of Apple Inc. and are protected by Federal copyright
* law. Distribution and use rights are outlined in the file "LICENSE.txt"
* which should have been included with this file. If this file is
* file is missing or damaged, see the license at "http://www.cups.org/".
*
* Contents:
*
* cupsdCleanDirty() - Write dirty config and state files.
* cupsdMarkDirty() - Mark config or state files as needing a
* write.
* cupsdSetBusyState() - Let the system know when we are busy
* doing something.
* cupsdAllowSleep() - Tell the OS it is now OK to sleep.
* cupsdStartSystemMonitor() - Start monitoring for system change.
* cupsdStopSystemMonitor() - Stop monitoring for system change.
* sysEventThreadEntry() - A thread to receive power and computer
* name change notifications.
* sysEventPowerNotifier() - Handle power notification events.
* sysEventConfigurationNotifier() - Computer name changed notification
* callback.
* sysEventTimerNotifier() - Handle delayed event notifications.
* sysUpdate() - Update the current system state.
*/
/*
* Include necessary headers...
*/
#include "cupsd.h"
#ifdef HAVE_VPROC_TRANSACTION_BEGIN
# include <vproc.h>
#endif /* HAVE_VPROC_TRANSACTION_BEGIN */
#ifdef __APPLE__
# include <IOKit/pwr_mgt/IOPMLib.h>
# ifdef HAVE_IOKIT_PWR_MGT_IOPMLIBPRIVATE_H
# include <IOKit/pwr_mgt/IOPMLibPrivate.h>
# endif /* HAVE_IOKIT_PWR_MGT_IOPMLIBPRIVATE_H */
#endif /* __APPLE__ */
/*
* The system management functions cover disk and power management which
* are primarily used on 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.
*
* Power management support is currently only implemented on MacOS X, 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.
*
* Once put to sleep, we invalidate all remote printers since it is common
* to wake up in a new location/on a new wireless network.
*/
/*
* Local globals...
*/
#ifdef kIOPMAssertionTypeDenySystemSleep
static IOPMAssertionID dark_wake = 0; /* "Dark wake" assertion for sharing */
#endif /* kIOPMAssertionTypeDenySystemSleep */
/*
* '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_REMOTE)
cupsdSaveRemoteCache();
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();
}
/*
* '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%c)",
(what & CUPSD_DIRTY_PRINTERS) ? 'P' : '-',
(what & CUPSD_DIRTY_CLASSES) ? 'C' : '-',
(what & CUPSD_DIRTY_REMOTE) ? 'R' : '-',
(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();
}
/*
* 'cupsdSetBusyState()' - Let the system know when we are busy doing something.
*/
void
cupsdSetBusyState(void)
{
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 HAVE_VPROC_TRANSACTION_BEGIN
static vproc_transaction_t vtran = 0; /* Current busy transaction */
#endif /* HAVE_VPROC_TRANSACTION_BEGIN */
/*
* Figure out how busy we are...
*/
newbusy = (DirtyCleanTime ? 1 : 0) |
(cupsArrayCount(ActiveClients) ? 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;
/*
* Manage state changes...
*/
if (newbusy != busy)
{
busy = newbusy;
#ifdef HAVE_VPROC_TRANSACTION_BEGIN
if (busy && !vtran)
vtran = vproc_transaction_begin(NULL);
else if (!busy && vtran)
{
vproc_transaction_end(NULL, vtran);
vtran = 0;
}
#endif /* HAVE_VPROC_TRANSACTION_BEGIN */
#ifdef kIOPMAssertionTypeDenySystemSleep
if ((busy & 2) && !dark_wake)
IOPMAssertionCreateWithName(kIOPMAssertionTypeDenySystemSleep,
kIOPMAssertionLevelOn,
CFSTR("org.cups.cupsd"), &dark_wake);
else if (!(busy & 2) && dark_wake)
{
IOPMAssertionRelease(dark_wake);
dark_wake = 0;
}
#endif /* kIOPMAssertionTypeDenySystemSleep */
cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSetBusyState: %s", busy_text[busy]);
}
}
#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 <IOKit/IOKitLib.h>
# include <IOKit/IOMessage.h>
# include <IOKit/pwr_mgt/IOPMLib.h>
# include <SystemConfiguration/SystemConfiguration.h>
# include <pthread.h>
/*
* 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) */
/*
* 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);
/*
* 'cupsdAllowSleep()' - Tell the OS it is now OK to sleep.
*/
void
cupsdAllowSleep(void)
{
cupsdCleanDirty();
IOAllowPowerChange(LastSysEvent.powerKernelPort,
LastSysEvent.powerNotificationID);
}
/*
* 'cupsdStartSystemMonitor()' - Start monitoring for system change.
*/
void
cupsdStartSystemMonitor(void)
{
int flags; /* fcntl flags on pipe */
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 *(*)())sysEventThreadEntry, NULL);
}
/*
* 'cupsdStopSystemMonitor()' - Stop monitoring for system change.
*/
void
cupsdStopSystemMonitor(void)
{
CFRunLoopRef rl; /* The event handler runloop */
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);
}
}
/*
* '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);
}
else
DEBUG_puts("sysEventThreadEntry: error registering for system power "
"notifications");
/*
* 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);
}
else
DEBUG_printf(("sysEventThreadEntry: SCDynamicStoreCreateRunLoopSource "
"failed: %s\n", SCErrorString(SCError())));
}
else
DEBUG_printf(("sysEventThreadEntry: SCDynamicStoreSetNotificationKeys "
"failed: %s\n", SCErrorString(SCError())));
}
else
DEBUG_printf(("sysEventThreadEntry: SCDynamicStoreCreate failed: %s\n",
SCErrorString(SCError())));
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;
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;
}
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
* printer-state-reason then cancel the sleep request (i.e. this reason
* indicates a job that is not yet connected to the printer)...
*/
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"))
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;
for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
p;
p = (cupsd_printer_t *)cupsArrayNext(Printers))
{
if (p->type & CUPS_PRINTER_DISCOVERED)
{
cupsdLogMessage(CUPSD_LOG_DEBUG,
"Deleting remote destination \"%s\"", p->name);
cupsArraySave(Printers);
cupsdDeletePrinter(p, 0);
cupsArrayRestore(Printers);
}
else
{
cupsdLogMessage(CUPSD_LOG_DEBUG,
"Deregistering local printer \"%s\"", p->name);
cupsdDeregisterPrinter(p, 0);
}
}
cupsdCleanDirty();
/*
* If we have no printing jobs, allow the power change immediately.
* Otherwise set the SleepJobs time to 15 seconds in the future when
* we'll take more drastic measures...
*/
if (cupsArrayCount(PrintingJobs) == 0)
IOAllowPowerChange(sysevent.powerKernelPort,
sysevent.powerNotificationID);
else
{
/*
* If there are active printers that don't have the connecting-to-device
* printer-state-reason then delay the sleep request (i.e. this reason
* indicates a job that is not yet connected to the printer)...
*/
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"))
break;
if (!p->num_reasons || i >= p->num_reasons)
break;
}
}
if (p)
{
LastSysEvent = sysevent;
SleepJobs = time(NULL) + 10;
}
else
{
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;
cupsdCheckJobs();
}
if (sysevent.event & SYSEVENT_NETCHANGED)
{
if (!Sleeping)
{
cupsdLogMessage(CUPSD_LOG_DEBUG,
"System network configuration changed");
/*
* Resetting browse_time before calling cupsdSendBrowseList causes
* browse packets to be sent for local shared printers.
*/
for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
p;
p = (cupsd_printer_t *)cupsArrayNext(Printers))
p->browse_time = 0;
cupsdSendBrowseList();
cupsdRestartPolling();
}
else
cupsdLogMessage(CUPSD_LOG_DEBUG,
"System network configuration changed; "
"ignored while sleeping");
}
if (sysevent.event & SYSEVENT_NAMECHANGED)
{
if (!Sleeping)
{
cupsdLogMessage(CUPSD_LOG_DEBUG,
"Computer name or BTMM domains changed");
/*
* De-register the individual printers...
*/
for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
p;
p = (cupsd_printer_t *)cupsArrayNext(Printers))
cupsdDeregisterPrinter(p, 1);
/*
* Update the computer name and BTMM domain list...
*/
cupsdUpdateDNSSDName();
/*
* Now re-register them...
*/
for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
p;
p = (cupsd_printer_t *)cupsArrayNext(Printers))
{
p->browse_time = 0;
cupsdRegisterPrinter(p);
}
}
else
cupsdLogMessage(CUPSD_LOG_DEBUG,
"Computer name or BTMM domains changed; ignored while "
"sleeping");
}
}
}
#endif /* __APPLE__ */
/*
* End of "$Id: sysman.c 7928 2008-09-10 22:14:22Z mike $".
*/