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