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