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