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