]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/sysman.c
Log power messages.
[thirdparty/cups.git] / scheduler / sysman.c
1 /*
2 * "$Id$"
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)
75 static 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
83 void
84 cupsdCleanDirty(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
122 void
123 cupsdMarkDirty(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
148 void
149 cupsdSetBusyState(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
282 typedef 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
290 typedef 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
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 */
319 HostNamesKey = NULL,
320 /* Host name 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) */
326
327
328 /*
329 * Local functions...
330 */
331
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,
338 void *context);
339 static void sysEventTimerNotifier(CFRunLoopTimerRef timer, void *context);
340 static void sysUpdate(void);
341
342
343 /*
344 * 'cupsdAllowSleep()' - Tell the OS it is now OK to sleep.
345 */
346
347 void
348 cupsdAllowSleep(void)
349 {
350 cupsdCleanDirty();
351
352 cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep.");
353 IOAllowPowerChange(LastSysEvent.powerKernelPort,
354 LastSysEvent.powerNotificationID);
355 }
356
357
358 /*
359 * 'cupsdStartSystemMonitor()' - Start monitoring for system change.
360 */
361
362 void
363 cupsdStartSystemMonitor(void)
364 {
365 int flags; /* fcntl flags on pipe */
366
367
368 if (cupsdOpenPipe(SysEventPipes))
369 {
370 cupsdLogMessage(CUPSD_LOG_ERROR, "System event monitor pipe() failed - %s!",
371 strerror(errno));
372 return;
373 }
374
375 cupsdAddSelect(SysEventPipes[0], (cupsd_selfunc_t)sysUpdate, NULL, NULL);
376
377 /*
378 * Set non-blocking mode on the descriptor we will be receiving notification
379 * events on.
380 */
381
382 flags = fcntl(SysEventPipes[0], F_GETFL, 0);
383 fcntl(SysEventPipes[0], F_SETFL, flags | O_NONBLOCK);
384
385 /*
386 * Start the thread that runs the runloop...
387 */
388
389 pthread_mutex_init(&SysEventThreadMutex, NULL);
390 pthread_cond_init(&SysEventThreadCond, NULL);
391 pthread_create(&SysEventThread, NULL, (void *(*)())sysEventThreadEntry, NULL);
392 }
393
394
395 /*
396 * 'cupsdStopSystemMonitor()' - Stop monitoring for system change.
397 */
398
399 void
400 cupsdStopSystemMonitor(void)
401 {
402 CFRunLoopRef rl; /* The event handler runloop */
403
404
405 if (SysEventThread)
406 {
407 /*
408 * Make sure the thread has completed it's initialization and
409 * stored it's runloop reference in the shared global.
410 */
411
412 pthread_mutex_lock(&SysEventThreadMutex);
413
414 if (!SysEventRunloop)
415 pthread_cond_wait(&SysEventThreadCond, &SysEventThreadMutex);
416
417 rl = SysEventRunloop;
418 SysEventRunloop = NULL;
419
420 pthread_mutex_unlock(&SysEventThreadMutex);
421
422 if (rl)
423 CFRunLoopStop(rl);
424
425 pthread_join(SysEventThread, NULL);
426 pthread_mutex_destroy(&SysEventThreadMutex);
427 pthread_cond_destroy(&SysEventThreadCond);
428 }
429
430 if (SysEventPipes[0] >= 0)
431 {
432 cupsdRemoveSelect(SysEventPipes[0]);
433 cupsdClosePipe(SysEventPipes);
434 }
435 }
436
437
438 /*
439 * 'sysEventThreadEntry()' - A thread to receive power and computer name
440 * change notifications.
441 */
442
443 static void * /* O - Return status/value */
444 sysEventThreadEntry(void)
445 {
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 */
461
462
463 /*
464 * Register for power state change notifications
465 */
466
467 bzero(&threadData, sizeof(threadData));
468
469 threadData.sysevent.powerKernelPort =
470 IORegisterForSystemPower(&threadData, &powerNotifierPort,
471 sysEventPowerNotifier, &powerNotifierObj);
472
473 if (threadData.sysevent.powerKernelPort)
474 {
475 powerRLS = IONotificationPortGetRunLoopSource(powerNotifierPort);
476 CFRunLoopAddSource(CFRunLoopGetCurrent(), powerRLS, kCFRunLoopDefaultMode);
477 }
478 else
479 DEBUG_puts("sysEventThreadEntry: error registering for system power "
480 "notifications");
481
482 /*
483 * Register for system configuration change notifications
484 */
485
486 bzero(&storeContext, sizeof(storeContext));
487 storeContext.info = &threadData;
488
489 store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("cupsd"),
490 sysEventConfigurationNotifier, &storeContext);
491
492 if (!ComputerNameKey)
493 ComputerNameKey = SCDynamicStoreKeyCreateComputerName(kCFAllocatorDefault);
494
495 if (!BTMMKey)
496 BTMMKey = SCDynamicStoreKeyCreate(kCFAllocatorDefault,
497 CFSTR("Setup:/Network/BackToMyMac"));
498
499 if (!NetworkGlobalKeyIPv4)
500 NetworkGlobalKeyIPv4 =
501 SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault,
502 kSCDynamicStoreDomainState,
503 kSCEntNetIPv4);
504
505 if (!NetworkGlobalKeyIPv6)
506 NetworkGlobalKeyIPv6 =
507 SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault,
508 kSCDynamicStoreDomainState,
509 kSCEntNetIPv6);
510
511 if (!NetworkGlobalKeyDNS)
512 NetworkGlobalKeyDNS =
513 SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault,
514 kSCDynamicStoreDomainState,
515 kSCEntNetDNS);
516
517 if (!HostNamesKey)
518 HostNamesKey = SCDynamicStoreKeyCreateHostNames(kCFAllocatorDefault);
519
520 if (!NetworkInterfaceKeyIPv4)
521 NetworkInterfaceKeyIPv4 =
522 SCDynamicStoreKeyCreateNetworkInterfaceEntity(kCFAllocatorDefault,
523 kSCDynamicStoreDomainState,
524 kSCCompAnyRegex,
525 kSCEntNetIPv4);
526
527 if (!NetworkInterfaceKeyIPv6)
528 NetworkInterfaceKeyIPv6 =
529 SCDynamicStoreKeyCreateNetworkInterfaceEntity(kCFAllocatorDefault,
530 kSCDynamicStoreDomainState,
531 kSCCompAnyRegex,
532 kSCEntNetIPv6);
533
534 if (store && ComputerNameKey && HostNamesKey &&
535 NetworkGlobalKeyIPv4 && NetworkGlobalKeyIPv6 && NetworkGlobalKeyDNS &&
536 NetworkInterfaceKeyIPv4 && NetworkInterfaceKeyIPv6)
537 {
538 key[0] = ComputerNameKey;
539 key[1] = BTMMKey;
540 key[2] = NetworkGlobalKeyIPv4;
541 key[3] = NetworkGlobalKeyIPv6;
542 key[4] = NetworkGlobalKeyDNS;
543 key[5] = HostNamesKey;
544
545 pattern[0] = NetworkInterfaceKeyIPv4;
546 pattern[1] = NetworkInterfaceKeyIPv6;
547
548 keys = CFArrayCreate(kCFAllocatorDefault, (const void **)key,
549 sizeof(key) / sizeof(key[0]),
550 &kCFTypeArrayCallBacks);
551
552 patterns = CFArrayCreate(kCFAllocatorDefault, (const void **)pattern,
553 sizeof(pattern) / sizeof(pattern[0]),
554 &kCFTypeArrayCallBacks);
555
556 if (keys && patterns &&
557 SCDynamicStoreSetNotificationKeys(store, keys, patterns))
558 {
559 if ((storeRLS = SCDynamicStoreCreateRunLoopSource(kCFAllocatorDefault,
560 store, 0)) != NULL)
561 {
562 CFRunLoopAddSource(CFRunLoopGetCurrent(), storeRLS,
563 kCFRunLoopDefaultMode);
564 }
565 else
566 DEBUG_printf(("sysEventThreadEntry: SCDynamicStoreCreateRunLoopSource "
567 "failed: %s\n", SCErrorString(SCError())));
568 }
569 else
570 DEBUG_printf(("sysEventThreadEntry: SCDynamicStoreSetNotificationKeys "
571 "failed: %s\n", SCErrorString(SCError())));
572 }
573 else
574 DEBUG_printf(("sysEventThreadEntry: SCDynamicStoreCreate failed: %s\n",
575 SCErrorString(SCError())));
576
577 if (keys)
578 CFRelease(keys);
579
580 if (patterns)
581 CFRelease(patterns);
582
583 /*
584 * Set up a timer to delay the wake change notifications.
585 *
586 * The initial time is set a decade or so into the future, we'll adjust
587 * this later.
588 */
589
590 bzero(&timerContext, sizeof(timerContext));
591 timerContext.info = &threadData;
592
593 threadData.timerRef =
594 CFRunLoopTimerCreate(kCFAllocatorDefault,
595 CFAbsoluteTimeGetCurrent() + (86400L * 365L * 10L),
596 86400L * 365L * 10L, 0, 0, sysEventTimerNotifier,
597 &timerContext);
598 CFRunLoopAddTimer(CFRunLoopGetCurrent(), threadData.timerRef,
599 kCFRunLoopDefaultMode);
600
601 /*
602 * Store our runloop in a global so the main thread can use it to stop us.
603 */
604
605 pthread_mutex_lock(&SysEventThreadMutex);
606
607 SysEventRunloop = CFRunLoopGetCurrent();
608
609 pthread_cond_signal(&SysEventThreadCond);
610 pthread_mutex_unlock(&SysEventThreadMutex);
611
612 /*
613 * Disappear into the runloop until it's stopped by the main thread.
614 */
615
616 CFRunLoopRun();
617
618 /*
619 * Clean up before exiting.
620 */
621
622 if (threadData.timerRef)
623 {
624 CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), threadData.timerRef,
625 kCFRunLoopDefaultMode);
626 CFRelease(threadData.timerRef);
627 }
628
629 if (threadData.sysevent.powerKernelPort)
630 {
631 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), powerRLS,
632 kCFRunLoopDefaultMode);
633 IODeregisterForSystemPower(&powerNotifierObj);
634 IOServiceClose(threadData.sysevent.powerKernelPort);
635 IONotificationPortDestroy(powerNotifierPort);
636 }
637
638 if (storeRLS)
639 {
640 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), storeRLS,
641 kCFRunLoopDefaultMode);
642 CFRunLoopSourceInvalidate(storeRLS);
643 CFRelease(storeRLS);
644 }
645
646 if (store)
647 CFRelease(store);
648
649 pthread_exit(NULL);
650 }
651
652
653 /*
654 * 'sysEventPowerNotifier()' - Handle power notification events.
655 */
656
657 static void
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 */
663 {
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 */
667
668
669 threadData = (cupsd_thread_data_t *)context;
670
671 (void)service; /* anti-compiler-warning-code */
672
673 switch (messageType)
674 {
675 case kIOMessageCanSystemPowerOff :
676 case kIOMessageCanSystemSleep :
677 threadData->sysevent.event |= SYSEVENT_CANSLEEP;
678 break;
679
680 case kIOMessageSystemWillRestart :
681 case kIOMessageSystemWillPowerOff :
682 case kIOMessageSystemWillSleep :
683 threadData->sysevent.event |= SYSEVENT_WILLSLEEP;
684 break;
685
686 case kIOMessageSystemHasPoweredOn :
687 /*
688 * Because powered on is followed by a net-changed event, delay
689 * before sending it.
690 */
691
692 sendit = 2;
693 threadData->sysevent.event |= SYSEVENT_WOKE;
694 break;
695
696 case kIOMessageSystemWillNotPowerOff :
697 case kIOMessageSystemWillNotSleep :
698 # ifdef kIOMessageSystemWillPowerOn
699 case kIOMessageSystemWillPowerOn :
700 # endif /* kIOMessageSystemWillPowerOn */
701 default:
702 sendit = 0;
703 break;
704 }
705
706 switch (messageType)
707 {
708 case kIOMessageCanSystemPowerOff :
709 cupsdLogMessage(CUPSD_LOG_DEBUG,
710 "Got kIOMessageCanSystemPowerOff message.");
711 break;
712 case kIOMessageCanSystemSleep :
713 cupsdLogMessage(CUPSD_LOG_DEBUG,
714 "Got kIOMessageCannSystemSleep message.");
715 break;
716 case kIOMessageSystemWillRestart :
717 cupsdLogMessage(CUPSD_LOG_DEBUG,
718 "Got kIOMessageSystemWillRestart message.");
719 break;
720 case kIOMessageSystemWillPowerOff :
721 cupsdLogMessage(CUPSD_LOG_DEBUG,
722 "Got kIOMessageSystemWillPowerOff message.");
723 break;
724 case kIOMessageSystemWillSleep :
725 cupsdLogMessage(CUPSD_LOG_DEBUG,
726 "Got kIOMessageSystemWillSleep message.");
727 break;
728 case kIOMessageSystemHasPoweredOn :
729 cupsdLogMessage(CUPSD_LOG_DEBUG,
730 "Got kIOMessageSystemHasPoweredOn message.");
731 break;
732 case kIOMessageSystemWillNotPowerOff :
733 cupsdLogMessage(CUPSD_LOG_DEBUG,
734 "Got kIOMessageSystemWillNotPowerOff message.");
735 break;
736 case kIOMessageSystemWillNotSleep :
737 cupsdLogMessage(CUPSD_LOG_DEBUG,
738 "Got kIOMessageSystemWillNotSleep message.");
739 break;
740 # ifdef kIOMessageSystemWillPowerOn
741 case kIOMessageSystemWillPowerOn :
742 cupsdLogMessage(CUPSD_LOG_DEBUG,
743 "Got kIOMessageSystemWillPowerOn message.");
744 break;
745 # endif /* kIOMessageSystemWillPowerOn */
746 default:
747 cupsdLogMessage(CUPSD_LOG_DEBUG, "Got unknown power message %d.",
748 (int)messageType);
749 break;
750 }
751
752 if (sendit == 0)
753 IOAllowPowerChange(threadData->sysevent.powerKernelPort,
754 (long)messageArgument);
755 else
756 {
757 threadData->sysevent.powerNotificationID = (long)messageArgument;
758
759 if (sendit == 1)
760 {
761 /*
762 * Send the event to the main thread now.
763 */
764
765 write(SysEventPipes[1], &threadData->sysevent,
766 sizeof(threadData->sysevent));
767 threadData->sysevent.event = 0;
768 }
769 else
770 {
771 /*
772 * Send the event to the main thread after 1 to 2 seconds.
773 */
774
775 CFRunLoopTimerSetNextFireDate(threadData->timerRef,
776 CFAbsoluteTimeGetCurrent() + 2);
777 }
778 }
779 }
780
781
782 /*
783 * 'sysEventConfigurationNotifier()' - Network configuration change notification
784 * callback.
785 */
786
787 static void
788 sysEventConfigurationNotifier(
789 SCDynamicStoreRef store, /* I - System data (unused) */
790 CFArrayRef changedKeys, /* I - Changed data */
791 void *context) /* I - Thread context data */
792 {
793 cupsd_thread_data_t *threadData; /* Thread context data */
794
795
796 threadData = (cupsd_thread_data_t *)context;
797
798 (void)store; /* anti-compiler-warning-code */
799
800 CFRange range = CFRangeMake(0, CFArrayGetCount(changedKeys));
801
802 if (CFArrayContainsValue(changedKeys, range, ComputerNameKey) ||
803 CFArrayContainsValue(changedKeys, range, BTMMKey))
804 threadData->sysevent.event |= SYSEVENT_NAMECHANGED;
805 else
806 {
807 threadData->sysevent.event |= SYSEVENT_NETCHANGED;
808
809 /*
810 * Indicate the network interface list needs updating...
811 */
812
813 NetIFUpdate = 1;
814 }
815
816 /*
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.
820 */
821
822 CFRunLoopTimerSetNextFireDate(threadData->timerRef,
823 CFAbsoluteTimeGetCurrent() + 5);
824 }
825
826
827 /*
828 * 'sysEventTimerNotifier()' - Handle delayed event notifications.
829 */
830
831 static void
832 sysEventTimerNotifier(
833 CFRunLoopTimerRef timer, /* I - Timer information */
834 void *context) /* I - Thread context data */
835 {
836 cupsd_thread_data_t *threadData; /* Thread context data */
837
838
839 (void)timer;
840
841 threadData = (cupsd_thread_data_t *)context;
842
843 /*
844 * If an event is still pending send it to the main thread.
845 */
846
847 if (threadData->sysevent.event)
848 {
849 write(SysEventPipes[1], &threadData->sysevent,
850 sizeof(threadData->sysevent));
851 threadData->sysevent.event = 0;
852 }
853 }
854
855
856 /*
857 * 'sysUpdate()' - Update the current system state.
858 */
859
860 static void
861 sysUpdate(void)
862 {
863 int i; /* Looping var */
864 cupsd_sysevent_t sysevent; /* The system event */
865 cupsd_printer_t *p; /* Printer information */
866
867
868 /*
869 * Drain the event pipe...
870 */
871
872 while (read((int)SysEventPipes[0], &sysevent, sizeof(sysevent))
873 == sizeof(sysevent))
874 {
875 if (sysevent.event & SYSEVENT_CANSLEEP)
876 {
877 /*
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)...
881 */
882
883 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
884 p;
885 p = (cupsd_printer_t *)cupsArrayNext(Printers))
886 {
887 if (p->job)
888 {
889 for (i = 0; i < p->num_reasons; i ++)
890 if (!strcmp(p->reasons[i], "connecting-to-device"))
891 break;
892
893 if (!p->num_reasons || i >= p->num_reasons)
894 break;
895 }
896 }
897
898 if (p)
899 {
900 cupsdLogMessage(CUPSD_LOG_INFO,
901 "System sleep canceled because printer %s is active",
902 p->name);
903 IOCancelPowerChange(sysevent.powerKernelPort,
904 sysevent.powerNotificationID);
905 }
906 else
907 {
908 cupsdLogMessage(CUPSD_LOG_DEBUG, "System wants to sleep");
909 IOAllowPowerChange(sysevent.powerKernelPort,
910 sysevent.powerNotificationID);
911 }
912 }
913
914 if (sysevent.event & SYSEVENT_WILLSLEEP)
915 {
916 cupsdLogMessage(CUPSD_LOG_DEBUG, "System going to sleep");
917
918 Sleeping = 1;
919
920 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
921 p;
922 p = (cupsd_printer_t *)cupsArrayNext(Printers))
923 {
924 cupsdLogMessage(CUPSD_LOG_DEBUG,
925 "Deregistering local printer \"%s\"", p->name);
926 cupsdDeregisterPrinter(p, 0);
927 }
928
929 cupsdCleanDirty();
930
931 #ifdef kIOPMAssertionTypeDenySystemSleep
932 /*
933 * Remove our assertion as needed since the user wants the system to
934 * sleep (different than idle sleep)...
935 */
936
937 if (keep_awake)
938 {
939 cupsdLogMessage(CUPSD_LOG_DEBUG, "Releasing dark wake assertion.");
940 IOPMAssertionRelease(keep_awake);
941 keep_awake = 0;
942 }
943 #endif /* kIOPMAssertionTypeDenySystemSleep */
944
945 /*
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...
949 */
950
951 if (cupsArrayCount(PrintingJobs) == 0)
952 {
953 cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep.");
954 IOAllowPowerChange(sysevent.powerKernelPort,
955 sysevent.powerNotificationID);
956 }
957 else
958 {
959 /*
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)...
963 */
964
965 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
966 p;
967 p = (cupsd_printer_t *)cupsArrayNext(Printers))
968 {
969 if (p->job)
970 {
971 for (i = 0; i < p->num_reasons; i ++)
972 if (!strcmp(p->reasons[i], "connecting-to-device"))
973 break;
974
975 if (!p->num_reasons || i >= p->num_reasons)
976 break;
977 }
978 }
979
980 if (p)
981 {
982 LastSysEvent = sysevent;
983 SleepJobs = time(NULL) + 10;
984 }
985 else
986 {
987 cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep.");
988 IOAllowPowerChange(sysevent.powerKernelPort,
989 sysevent.powerNotificationID);
990 }
991 }
992 }
993
994 if (sysevent.event & SYSEVENT_WOKE)
995 {
996 cupsdLogMessage(CUPSD_LOG_DEBUG, "System woke from sleep");
997 IOAllowPowerChange(sysevent.powerKernelPort,
998 sysevent.powerNotificationID);
999 Sleeping = 0;
1000
1001 #ifdef kIOPMAssertionTypeDenySystemSleep
1002 if (cupsArrayCount(PrintingJobs) > 0 && !keep_awake)
1003 {
1004 cupsdLogMessage(CUPSD_LOG_DEBUG, "Asserting dark wake.");
1005 IOPMAssertionCreateWithName(kIOPMAssertionTypeDenySystemSleep,
1006 kIOPMAssertionLevelOn,
1007 CFSTR("org.cups.cupsd"), &keep_awake);
1008 }
1009 #endif /* kIOPMAssertionTypeDenySystemSleep */
1010
1011 /*
1012 * Make sure jobs that were queued prior to the system going to sleep don't
1013 * get canceled right away...
1014 */
1015
1016 if (MaxJobTime > 0)
1017 {
1018 cupsd_job_t *job; /* Current job */
1019
1020 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
1021 job;
1022 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
1023 {
1024 if (job->cancel_time)
1025 {
1026 ipp_attribute_t *cancel_after = ippFindAttribute(job->attrs,
1027 "job-cancel-after",
1028 IPP_TAG_INTEGER);
1029
1030 if (cancel_after)
1031 job->cancel_time = time(NULL) + ippGetInteger(cancel_after, 0);
1032 else
1033 job->cancel_time = time(NULL) + MaxJobTime;
1034 }
1035 }
1036 }
1037
1038 cupsdCheckJobs();
1039 }
1040
1041 if (sysevent.event & SYSEVENT_NETCHANGED)
1042 {
1043 if (!Sleeping)
1044 cupsdLogMessage(CUPSD_LOG_DEBUG,
1045 "System network configuration changed");
1046 else
1047 cupsdLogMessage(CUPSD_LOG_DEBUG,
1048 "System network configuration changed; "
1049 "ignored while sleeping");
1050 }
1051
1052 if (sysevent.event & SYSEVENT_NAMECHANGED)
1053 {
1054 if (!Sleeping)
1055 {
1056 cupsdLogMessage(CUPSD_LOG_DEBUG,
1057 "Computer name or BTMM domains changed");
1058
1059 /*
1060 * De-register the individual printers...
1061 */
1062
1063 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
1064 p;
1065 p = (cupsd_printer_t *)cupsArrayNext(Printers))
1066 cupsdDeregisterPrinter(p, 1);
1067
1068 # if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
1069 /*
1070 * Update the computer name and BTMM domain list...
1071 */
1072
1073 cupsdUpdateDNSSDName();
1074 # endif /* HAVE_DNSSD || HAVE_AVAHI */
1075
1076 /*
1077 * Now re-register them...
1078 */
1079
1080 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
1081 p;
1082 p = (cupsd_printer_t *)cupsArrayNext(Printers))
1083 cupsdRegisterPrinter(p);
1084 }
1085 else
1086 cupsdLogMessage(CUPSD_LOG_DEBUG,
1087 "Computer name or BTMM domains changed; ignored while "
1088 "sleeping");
1089 }
1090 }
1091 }
1092 #endif /* __APPLE__ */
1093
1094
1095 /*
1096 * End of "$Id$".
1097 */