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