4 * System management functions for the CUPS scheduler.
6 * Copyright 2007-2013 by Apple Inc.
7 * Copyright 2006 by Easy Software Products.
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/".
17 * cupsdCleanDirty() - Write dirty config and state files.
18 * cupsdMarkDirty() - Mark config or state files as needing a
20 * cupsdSetBusyState() - Let the system know when we are busy
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
30 * sysEventTimerNotifier() - Handle delayed event notifications.
31 * sysUpdate() - Update the current system state.
36 * Include necessary headers...
40 #ifdef HAVE_VPROC_TRANSACTION_BEGIN
42 #endif /* HAVE_VPROC_TRANSACTION_BEGIN */
44 # include <IOKit/pwr_mgt/IOPMLib.h>
45 # ifdef HAVE_IOKIT_PWR_MGT_IOPMLIBPRIVATE_H
46 # include <IOKit/pwr_mgt/IOPMLibPrivate.h>
48 # define kIOPMAssertionTypeDenySystemSleep CFSTR("DenySystemSleep")
49 # endif /* HAVE_IOKIT_PWR_MGT_IOPMLIBPRIVATE_H */
50 #endif /* __APPLE__ */
54 * The system management functions cover disk and power management which
55 * are primarily used on portable computers.
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
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
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.
74 #if defined(kIOPMAssertionTypeDenySystemSleep) || defined(kIOPMAssertNetworkClientActive)
75 static IOPMAssertionID keep_awake
= 0; /* Keep the system awake while printing */
76 #endif /* kIOPMAssertionTypeDenySystemSleep || kIOPMAssertNetworkClientActive */
80 * 'cupsdCleanDirty()' - Write dirty config and state files.
86 if (DirtyFiles
& CUPSD_DIRTY_PRINTERS
)
87 cupsdSaveAllPrinters();
89 if (DirtyFiles
& CUPSD_DIRTY_CLASSES
)
90 cupsdSaveAllClasses();
92 if (DirtyFiles
& CUPSD_DIRTY_PRINTCAP
)
95 if (DirtyFiles
& CUPSD_DIRTY_JOBS
)
97 cupsd_job_t
*job
; /* Current job */
101 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
103 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
108 if (DirtyFiles
& CUPSD_DIRTY_SUBSCRIPTIONS
)
109 cupsdSaveAllSubscriptions();
111 DirtyFiles
= CUPSD_DIRTY_NONE
;
119 * 'cupsdMarkDirty()' - Mark config or state files as needing a write.
123 cupsdMarkDirty(int what
) /* I - What file(s) are dirty? */
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' : '-');
132 if (what
== CUPSD_DIRTY_PRINTCAP
&& !Printcap
)
138 DirtyCleanTime
= time(NULL
) + DirtyCleanInterval
;
145 * 'cupsdSetBusyState()' - Let the system know when we are busy doing something.
149 cupsdSetBusyState(void)
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 */
161 "Printing jobs and dirty files",
163 "Active clients and dirty files",
164 "Active clients and printing jobs",
165 "Active clients, printing jobs, and dirty files"
167 #ifdef HAVE_VPROC_TRANSACTION_BEGIN
168 static vproc_transaction_t vtran
= 0; /* Current busy transaction */
169 #endif /* HAVE_VPROC_TRANSACTION_BEGIN */
173 * Figure out how busy we are...
176 newbusy
= (DirtyCleanTime
? 1 : 0) |
177 (cupsArrayCount(ActiveClients
) ? 4 : 0);
179 for (job
= (cupsd_job_t
*)cupsArrayFirst(PrintingJobs
);
181 job
= (cupsd_job_t
*)cupsArrayNext(PrintingJobs
))
183 if ((p
= job
->printer
) != NULL
)
185 for (i
= 0; i
< p
->num_reasons
; i
++)
186 if (!strcmp(p
->reasons
[i
], "connecting-to-device"))
189 if (!p
->num_reasons
|| i
>= p
->num_reasons
)
197 cupsdLogMessage(CUPSD_LOG_DEBUG
,
198 "cupsdSetBusyState: newbusy=\"%s\", busy=\"%s\"",
199 busy_text
[newbusy
], busy_text
[busy
]);
202 * Manage state changes...
209 #ifdef HAVE_VPROC_TRANSACTION_BEGIN
211 vtran
= vproc_transaction_begin(NULL
);
212 else if (!busy
&& vtran
)
214 vproc_transaction_end(NULL
, vtran
);
217 #endif /* HAVE_VPROC_TRANSACTION_BEGIN */
220 #if defined(kIOPMAssertionTypeDenySystemSleep) || defined(kIOPMAssertNetworkClientActive)
221 if (cupsArrayCount(PrintingJobs
) > 0 && !keep_awake
)
223 # ifdef kIOPMAssertNetworkClientActive
224 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Asserting NetworkClientActive.");
226 IOPMAssertionCreateWithName(kIOPMAssertNetworkClientActive
,
227 kIOPMAssertionLevelOn
,
228 CFSTR("org.cups.cupsd"), &keep_awake
);
231 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Asserting DenySystemSleep.");
233 IOPMAssertionCreateWithName(kIOPMAssertionTypeDenySystemSleep
,
234 kIOPMAssertionLevelOn
,
235 CFSTR("org.cups.cupsd"), &keep_awake
);
237 # endif /* kIOPMAssertNetworkClientActive */
239 else if (cupsArrayCount(PrintingJobs
) == 0 && keep_awake
)
241 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Releasing power assertion.");
242 IOPMAssertionRelease(keep_awake
);
245 #endif /* kIOPMAssertionTypeDenySystemSleep || kIOPMAssertNetworkClientActive */
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.
257 * Include MacOS-specific headers...
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>
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 */
282 typedef struct cupsd_sysevent_s
/*** System event data ****/
284 unsigned char event
; /* Event bit field */
285 io_connect_t powerKernelPort
; /* Power context data */
286 long powerNotificationID
; /* Power event data */
290 typedef struct cupsd_thread_data_s
/*** Thread context data ****/
292 cupsd_sysevent_t sysevent
; /* System event */
293 CFRunLoopTimerRef timerRef
; /* Timer to delay some change *
295 } cupsd_thread_data_t
;
302 static pthread_t SysEventThread
= NULL
;
303 /* Thread to host a runloop */
304 static pthread_mutex_t SysEventThreadMutex
= { 0 };
305 /* Coordinates access to shared gloabals */
306 static pthread_cond_t SysEventThreadCond
= { 0 };
307 /* Thread initialization complete condition */
308 static CFRunLoopRef SysEventRunloop
= NULL
;
309 /* The runloop. Access must be protected! */
310 static 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 */
321 NetworkInterfaceKeyIPv4
= NULL
,
322 /* Netowrk interface key */
323 NetworkInterfaceKeyIPv6
= NULL
;
324 /* Netowrk interface key */
325 static cupsd_sysevent_t LastSysEvent
; /* Last system event (for delayed sleep) */
332 static void *sysEventThreadEntry(void);
333 static void sysEventPowerNotifier(void *context
, io_service_t service
,
334 natural_t messageType
,
335 void *messageArgument
);
336 static void sysEventConfigurationNotifier(SCDynamicStoreRef store
,
337 CFArrayRef changedKeys
,
339 static void sysEventTimerNotifier(CFRunLoopTimerRef timer
, void *context
);
340 static void sysUpdate(void);
344 * 'cupsdAllowSleep()' - Tell the OS it is now OK to sleep.
348 cupsdAllowSleep(void)
352 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Allowing system sleep.");
353 IOAllowPowerChange(LastSysEvent
.powerKernelPort
,
354 LastSysEvent
.powerNotificationID
);
359 * 'cupsdStartSystemMonitor()' - Start monitoring for system change.
363 cupsdStartSystemMonitor(void)
365 int flags
; /* fcntl flags on pipe */
368 if (cupsdOpenPipe(SysEventPipes
))
370 cupsdLogMessage(CUPSD_LOG_ERROR
, "System event monitor pipe() failed - %s!",
375 cupsdAddSelect(SysEventPipes
[0], (cupsd_selfunc_t
)sysUpdate
, NULL
, NULL
);
378 * Set non-blocking mode on the descriptor we will be receiving notification
382 flags
= fcntl(SysEventPipes
[0], F_GETFL
, 0);
383 fcntl(SysEventPipes
[0], F_SETFL
, flags
| O_NONBLOCK
);
386 * Start the thread that runs the runloop...
389 pthread_mutex_init(&SysEventThreadMutex
, NULL
);
390 pthread_cond_init(&SysEventThreadCond
, NULL
);
391 pthread_create(&SysEventThread
, NULL
, (void *(*)())sysEventThreadEntry
, NULL
);
396 * 'cupsdStopSystemMonitor()' - Stop monitoring for system change.
400 cupsdStopSystemMonitor(void)
402 CFRunLoopRef rl
; /* The event handler runloop */
408 * Make sure the thread has completed it's initialization and
409 * stored it's runloop reference in the shared global.
412 pthread_mutex_lock(&SysEventThreadMutex
);
414 if (!SysEventRunloop
)
415 pthread_cond_wait(&SysEventThreadCond
, &SysEventThreadMutex
);
417 rl
= SysEventRunloop
;
418 SysEventRunloop
= NULL
;
420 pthread_mutex_unlock(&SysEventThreadMutex
);
425 pthread_join(SysEventThread
, NULL
);
426 pthread_mutex_destroy(&SysEventThreadMutex
);
427 pthread_cond_destroy(&SysEventThreadCond
);
430 if (SysEventPipes
[0] >= 0)
432 cupsdRemoveSelect(SysEventPipes
[0]);
433 cupsdClosePipe(SysEventPipes
);
439 * 'sysEventThreadEntry()' - A thread to receive power and computer name
440 * change notifications.
443 static void * /* O - Return status/value */
444 sysEventThreadEntry(void)
446 io_object_t powerNotifierObj
;
447 /* Power notifier object */
448 IONotificationPortRef powerNotifierPort
;
449 /* Power notifier port */
450 SCDynamicStoreRef store
= NULL
;/* System Config dynamic store */
451 CFRunLoopSourceRef powerRLS
= NULL
,/* Power runloop source */
452 storeRLS
= NULL
;/* System Config runloop source */
453 CFStringRef key
[6], /* System Config keys */
454 pattern
[2]; /* System Config patterns */
455 CFArrayRef keys
= NULL
, /* System Config key array*/
456 patterns
= NULL
;/* System Config pattern array */
457 SCDynamicStoreContext storeContext
; /* Dynamic store context */
458 CFRunLoopTimerContext timerContext
; /* Timer context */
459 cupsd_thread_data_t threadData
; /* Thread context data for the *
460 * runloop notifiers */
464 * Register for power state change notifications
467 bzero(&threadData
, sizeof(threadData
));
469 threadData
.sysevent
.powerKernelPort
=
470 IORegisterForSystemPower(&threadData
, &powerNotifierPort
,
471 sysEventPowerNotifier
, &powerNotifierObj
);
473 if (threadData
.sysevent
.powerKernelPort
)
475 powerRLS
= IONotificationPortGetRunLoopSource(powerNotifierPort
);
476 CFRunLoopAddSource(CFRunLoopGetCurrent(), powerRLS
, kCFRunLoopDefaultMode
);
479 DEBUG_puts("sysEventThreadEntry: error registering for system power "
483 * Register for system configuration change notifications
486 bzero(&storeContext
, sizeof(storeContext
));
487 storeContext
.info
= &threadData
;
489 store
= SCDynamicStoreCreate(kCFAllocatorDefault
, CFSTR("cupsd"),
490 sysEventConfigurationNotifier
, &storeContext
);
492 if (!ComputerNameKey
)
493 ComputerNameKey
= SCDynamicStoreKeyCreateComputerName(kCFAllocatorDefault
);
496 BTMMKey
= SCDynamicStoreKeyCreate(kCFAllocatorDefault
,
497 CFSTR("Setup:/Network/BackToMyMac"));
499 if (!NetworkGlobalKeyIPv4
)
500 NetworkGlobalKeyIPv4
=
501 SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault
,
502 kSCDynamicStoreDomainState
,
505 if (!NetworkGlobalKeyIPv6
)
506 NetworkGlobalKeyIPv6
=
507 SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault
,
508 kSCDynamicStoreDomainState
,
511 if (!NetworkGlobalKeyDNS
)
512 NetworkGlobalKeyDNS
=
513 SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault
,
514 kSCDynamicStoreDomainState
,
518 HostNamesKey
= SCDynamicStoreKeyCreateHostNames(kCFAllocatorDefault
);
520 if (!NetworkInterfaceKeyIPv4
)
521 NetworkInterfaceKeyIPv4
=
522 SCDynamicStoreKeyCreateNetworkInterfaceEntity(kCFAllocatorDefault
,
523 kSCDynamicStoreDomainState
,
527 if (!NetworkInterfaceKeyIPv6
)
528 NetworkInterfaceKeyIPv6
=
529 SCDynamicStoreKeyCreateNetworkInterfaceEntity(kCFAllocatorDefault
,
530 kSCDynamicStoreDomainState
,
534 if (store
&& ComputerNameKey
&& HostNamesKey
&&
535 NetworkGlobalKeyIPv4
&& NetworkGlobalKeyIPv6
&& NetworkGlobalKeyDNS
&&
536 NetworkInterfaceKeyIPv4
&& NetworkInterfaceKeyIPv6
)
538 key
[0] = ComputerNameKey
;
540 key
[2] = NetworkGlobalKeyIPv4
;
541 key
[3] = NetworkGlobalKeyIPv6
;
542 key
[4] = NetworkGlobalKeyDNS
;
543 key
[5] = HostNamesKey
;
545 pattern
[0] = NetworkInterfaceKeyIPv4
;
546 pattern
[1] = NetworkInterfaceKeyIPv6
;
548 keys
= CFArrayCreate(kCFAllocatorDefault
, (const void **)key
,
549 sizeof(key
) / sizeof(key
[0]),
550 &kCFTypeArrayCallBacks
);
552 patterns
= CFArrayCreate(kCFAllocatorDefault
, (const void **)pattern
,
553 sizeof(pattern
) / sizeof(pattern
[0]),
554 &kCFTypeArrayCallBacks
);
556 if (keys
&& patterns
&&
557 SCDynamicStoreSetNotificationKeys(store
, keys
, patterns
))
559 if ((storeRLS
= SCDynamicStoreCreateRunLoopSource(kCFAllocatorDefault
,
562 CFRunLoopAddSource(CFRunLoopGetCurrent(), storeRLS
,
563 kCFRunLoopDefaultMode
);
566 DEBUG_printf(("sysEventThreadEntry: SCDynamicStoreCreateRunLoopSource "
567 "failed: %s\n", SCErrorString(SCError())));
570 DEBUG_printf(("sysEventThreadEntry: SCDynamicStoreSetNotificationKeys "
571 "failed: %s\n", SCErrorString(SCError())));
574 DEBUG_printf(("sysEventThreadEntry: SCDynamicStoreCreate failed: %s\n",
575 SCErrorString(SCError())));
584 * Set up a timer to delay the wake change notifications.
586 * The initial time is set a decade or so into the future, we'll adjust
590 bzero(&timerContext
, sizeof(timerContext
));
591 timerContext
.info
= &threadData
;
593 threadData
.timerRef
=
594 CFRunLoopTimerCreate(kCFAllocatorDefault
,
595 CFAbsoluteTimeGetCurrent() + (86400L * 365L * 10L),
596 86400L * 365L * 10L, 0, 0, sysEventTimerNotifier
,
598 CFRunLoopAddTimer(CFRunLoopGetCurrent(), threadData
.timerRef
,
599 kCFRunLoopDefaultMode
);
602 * Store our runloop in a global so the main thread can use it to stop us.
605 pthread_mutex_lock(&SysEventThreadMutex
);
607 SysEventRunloop
= CFRunLoopGetCurrent();
609 pthread_cond_signal(&SysEventThreadCond
);
610 pthread_mutex_unlock(&SysEventThreadMutex
);
613 * Disappear into the runloop until it's stopped by the main thread.
619 * Clean up before exiting.
622 if (threadData
.timerRef
)
624 CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), threadData
.timerRef
,
625 kCFRunLoopDefaultMode
);
626 CFRelease(threadData
.timerRef
);
629 if (threadData
.sysevent
.powerKernelPort
)
631 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), powerRLS
,
632 kCFRunLoopDefaultMode
);
633 IODeregisterForSystemPower(&powerNotifierObj
);
634 IOServiceClose(threadData
.sysevent
.powerKernelPort
);
635 IONotificationPortDestroy(powerNotifierPort
);
640 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), storeRLS
,
641 kCFRunLoopDefaultMode
);
642 CFRunLoopSourceInvalidate(storeRLS
);
654 * 'sysEventPowerNotifier()' - Handle power notification events.
658 sysEventPowerNotifier(
659 void *context
, /* I - Thread context data */
660 io_service_t service
, /* I - Unused service info */
661 natural_t messageType
, /* I - Type of message */
662 void *messageArgument
) /* I - Message data */
664 int sendit
= 1; /* Send event to main thread? *
665 * (0 = no, 1 = yes, 2 = delayed */
666 cupsd_thread_data_t
*threadData
; /* Thread context data */
669 threadData
= (cupsd_thread_data_t
*)context
;
671 (void)service
; /* anti-compiler-warning-code */
675 case kIOMessageCanSystemPowerOff
:
676 case kIOMessageCanSystemSleep
:
677 threadData
->sysevent
.event
|= SYSEVENT_CANSLEEP
;
680 case kIOMessageSystemWillRestart
:
681 case kIOMessageSystemWillPowerOff
:
682 case kIOMessageSystemWillSleep
:
683 threadData
->sysevent
.event
|= SYSEVENT_WILLSLEEP
;
686 case kIOMessageSystemHasPoweredOn
:
688 * Because powered on is followed by a net-changed event, delay
693 threadData
->sysevent
.event
|= SYSEVENT_WOKE
;
696 case kIOMessageSystemWillNotPowerOff
:
697 case kIOMessageSystemWillNotSleep
:
698 # ifdef kIOMessageSystemWillPowerOn
699 case kIOMessageSystemWillPowerOn
:
700 # endif /* kIOMessageSystemWillPowerOn */
708 case kIOMessageCanSystemPowerOff
:
709 cupsdLogMessage(CUPSD_LOG_DEBUG
,
710 "Got kIOMessageCanSystemPowerOff message.");
712 case kIOMessageCanSystemSleep
:
713 cupsdLogMessage(CUPSD_LOG_DEBUG
,
714 "Got kIOMessageCannSystemSleep message.");
716 case kIOMessageSystemWillRestart
:
717 cupsdLogMessage(CUPSD_LOG_DEBUG
,
718 "Got kIOMessageSystemWillRestart message.");
720 case kIOMessageSystemWillPowerOff
:
721 cupsdLogMessage(CUPSD_LOG_DEBUG
,
722 "Got kIOMessageSystemWillPowerOff message.");
724 case kIOMessageSystemWillSleep
:
725 cupsdLogMessage(CUPSD_LOG_DEBUG
,
726 "Got kIOMessageSystemWillSleep message.");
728 case kIOMessageSystemHasPoweredOn
:
729 cupsdLogMessage(CUPSD_LOG_DEBUG
,
730 "Got kIOMessageSystemHasPoweredOn message.");
732 case kIOMessageSystemWillNotPowerOff
:
733 cupsdLogMessage(CUPSD_LOG_DEBUG
,
734 "Got kIOMessageSystemWillNotPowerOff message.");
736 case kIOMessageSystemWillNotSleep
:
737 cupsdLogMessage(CUPSD_LOG_DEBUG
,
738 "Got kIOMessageSystemWillNotSleep message.");
740 # ifdef kIOMessageSystemWillPowerOn
741 case kIOMessageSystemWillPowerOn
:
742 cupsdLogMessage(CUPSD_LOG_DEBUG
,
743 "Got kIOMessageSystemWillPowerOn message.");
745 # endif /* kIOMessageSystemWillPowerOn */
747 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Got unknown power message %d.",
753 IOAllowPowerChange(threadData
->sysevent
.powerKernelPort
,
754 (long)messageArgument
);
757 threadData
->sysevent
.powerNotificationID
= (long)messageArgument
;
762 * Send the event to the main thread now.
765 write(SysEventPipes
[1], &threadData
->sysevent
,
766 sizeof(threadData
->sysevent
));
767 threadData
->sysevent
.event
= 0;
772 * Send the event to the main thread after 1 to 2 seconds.
775 CFRunLoopTimerSetNextFireDate(threadData
->timerRef
,
776 CFAbsoluteTimeGetCurrent() + 2);
783 * 'sysEventConfigurationNotifier()' - Network configuration change notification
788 sysEventConfigurationNotifier(
789 SCDynamicStoreRef store
, /* I - System data (unused) */
790 CFArrayRef changedKeys
, /* I - Changed data */
791 void *context
) /* I - Thread context data */
793 cupsd_thread_data_t
*threadData
; /* Thread context data */
796 threadData
= (cupsd_thread_data_t
*)context
;
798 (void)store
; /* anti-compiler-warning-code */
800 CFRange range
= CFRangeMake(0, CFArrayGetCount(changedKeys
));
802 if (CFArrayContainsValue(changedKeys
, range
, ComputerNameKey
) ||
803 CFArrayContainsValue(changedKeys
, range
, BTMMKey
))
804 threadData
->sysevent
.event
|= SYSEVENT_NAMECHANGED
;
807 threadData
->sysevent
.event
|= SYSEVENT_NETCHANGED
;
810 * Indicate the network interface list needs updating...
817 * Because we registered for several different kinds of change notifications
818 * this callback usually gets called several times in a row. We use a timer to
819 * de-bounce these so we only end up generating one event for the main thread.
822 CFRunLoopTimerSetNextFireDate(threadData
->timerRef
,
823 CFAbsoluteTimeGetCurrent() + 5);
828 * 'sysEventTimerNotifier()' - Handle delayed event notifications.
832 sysEventTimerNotifier(
833 CFRunLoopTimerRef timer
, /* I - Timer information */
834 void *context
) /* I - Thread context data */
836 cupsd_thread_data_t
*threadData
; /* Thread context data */
841 threadData
= (cupsd_thread_data_t
*)context
;
844 * If an event is still pending send it to the main thread.
847 if (threadData
->sysevent
.event
)
849 write(SysEventPipes
[1], &threadData
->sysevent
,
850 sizeof(threadData
->sysevent
));
851 threadData
->sysevent
.event
= 0;
857 * 'sysUpdate()' - Update the current system state.
863 int i
; /* Looping var */
864 cupsd_sysevent_t sysevent
; /* The system event */
865 cupsd_printer_t
*p
; /* Printer information */
869 * Drain the event pipe...
872 while (read((int)SysEventPipes
[0], &sysevent
, sizeof(sysevent
))
875 if (sysevent
.event
& SYSEVENT_CANSLEEP
)
878 * If there are active printers that don't have the connecting-to-device
879 * printer-state-reason then cancel the sleep request (i.e. this reason
880 * indicates a job that is not yet connected to the printer)...
883 for (p
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
885 p
= (cupsd_printer_t
*)cupsArrayNext(Printers
))
889 for (i
= 0; i
< p
->num_reasons
; i
++)
890 if (!strcmp(p
->reasons
[i
], "connecting-to-device"))
893 if (!p
->num_reasons
|| i
>= p
->num_reasons
)
900 cupsdLogMessage(CUPSD_LOG_INFO
,
901 "System sleep canceled because printer %s is active",
903 IOCancelPowerChange(sysevent
.powerKernelPort
,
904 sysevent
.powerNotificationID
);
908 cupsdLogMessage(CUPSD_LOG_DEBUG
, "System wants to sleep");
909 IOAllowPowerChange(sysevent
.powerKernelPort
,
910 sysevent
.powerNotificationID
);
914 if (sysevent
.event
& SYSEVENT_WILLSLEEP
)
916 cupsdLogMessage(CUPSD_LOG_DEBUG
, "System going to sleep");
920 for (p
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
922 p
= (cupsd_printer_t
*)cupsArrayNext(Printers
))
924 cupsdLogMessage(CUPSD_LOG_DEBUG
,
925 "Deregistering local printer \"%s\"", p
->name
);
926 cupsdDeregisterPrinter(p
, 0);
931 #ifdef kIOPMAssertionTypeDenySystemSleep
933 * Remove our assertion as needed since the user wants the system to
934 * sleep (different than idle sleep)...
939 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Releasing dark wake assertion.");
940 IOPMAssertionRelease(keep_awake
);
943 #endif /* kIOPMAssertionTypeDenySystemSleep */
946 * If we have no printing jobs, allow the power change immediately.
947 * Otherwise set the SleepJobs time to 15 seconds in the future when
948 * we'll take more drastic measures...
951 if (cupsArrayCount(PrintingJobs
) == 0)
953 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Allowing system sleep.");
954 IOAllowPowerChange(sysevent
.powerKernelPort
,
955 sysevent
.powerNotificationID
);
960 * If there are active printers that don't have the connecting-to-device
961 * printer-state-reason then delay the sleep request (i.e. this reason
962 * indicates a job that is not yet connected to the printer)...
965 for (p
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
967 p
= (cupsd_printer_t
*)cupsArrayNext(Printers
))
971 for (i
= 0; i
< p
->num_reasons
; i
++)
972 if (!strcmp(p
->reasons
[i
], "connecting-to-device"))
975 if (!p
->num_reasons
|| i
>= p
->num_reasons
)
982 LastSysEvent
= sysevent
;
983 SleepJobs
= time(NULL
) + 10;
987 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Allowing system sleep.");
988 IOAllowPowerChange(sysevent
.powerKernelPort
,
989 sysevent
.powerNotificationID
);
994 if (sysevent
.event
& SYSEVENT_WOKE
)
996 cupsdLogMessage(CUPSD_LOG_DEBUG
, "System woke from sleep");
997 IOAllowPowerChange(sysevent
.powerKernelPort
,
998 sysevent
.powerNotificationID
);
1001 #ifdef kIOPMAssertionTypeDenySystemSleep
1002 if (cupsArrayCount(PrintingJobs
) > 0 && !keep_awake
)
1004 cupsdLogMessage(CUPSD_LOG_DEBUG
, "Asserting dark wake.");
1005 IOPMAssertionCreateWithName(kIOPMAssertionTypeDenySystemSleep
,
1006 kIOPMAssertionLevelOn
,
1007 CFSTR("org.cups.cupsd"), &keep_awake
);
1009 #endif /* kIOPMAssertionTypeDenySystemSleep */
1012 * Make sure jobs that were queued prior to the system going to sleep don't
1013 * get canceled right away...
1018 cupsd_job_t
*job
; /* Current job */
1020 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
1022 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
1024 if (job
->cancel_time
)
1026 ipp_attribute_t
*cancel_after
= ippFindAttribute(job
->attrs
,
1031 job
->cancel_time
= time(NULL
) + ippGetInteger(cancel_after
, 0);
1033 job
->cancel_time
= time(NULL
) + MaxJobTime
;
1041 if (sysevent
.event
& SYSEVENT_NETCHANGED
)
1044 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1045 "System network configuration changed");
1047 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1048 "System network configuration changed; "
1049 "ignored while sleeping");
1052 if (sysevent
.event
& SYSEVENT_NAMECHANGED
)
1056 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1057 "Computer name or BTMM domains changed");
1060 * De-register the individual printers...
1063 for (p
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
1065 p
= (cupsd_printer_t
*)cupsArrayNext(Printers
))
1066 cupsdDeregisterPrinter(p
, 1);
1068 # if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
1070 * Update the computer name and BTMM domain list...
1073 cupsdUpdateDNSSDName();
1074 # endif /* HAVE_DNSSD || HAVE_AVAHI */
1077 * Now re-register them...
1080 for (p
= (cupsd_printer_t
*)cupsArrayFirst(Printers
);
1082 p
= (cupsd_printer_t
*)cupsArrayNext(Printers
))
1083 cupsdRegisterPrinter(p
);
1086 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1087 "Computer name or BTMM domains changed; ignored while "
1092 #endif /* __APPLE__ */