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