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