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