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