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
cupsdCleanDirty(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
cupsdMarkDirty(int what)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
cupsdSetBusyState(int working)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
cupsdAllowSleep(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
cupsdStartSystemMonitor(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
cupsdStopSystemMonitor(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 */
sysEventThreadEntry(void)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
440 /*
441 * Register for system configuration change notifications
442 */
443
444 bzero(&storeContext, sizeof(storeContext));
445 storeContext.info = &threadData;
446
447 store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("cupsd"),
448 sysEventConfigurationNotifier, &storeContext);
449
450 if (!ComputerNameKey)
451 ComputerNameKey = SCDynamicStoreKeyCreateComputerName(kCFAllocatorDefault);
452
453 if (!BTMMKey)
454 BTMMKey = SCDynamicStoreKeyCreate(kCFAllocatorDefault,
455 CFSTR("Setup:/Network/BackToMyMac"));
456
457 if (!NetworkGlobalKeyIPv4)
458 NetworkGlobalKeyIPv4 =
459 SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault,
460 kSCDynamicStoreDomainState,
461 kSCEntNetIPv4);
462
463 if (!NetworkGlobalKeyIPv6)
464 NetworkGlobalKeyIPv6 =
465 SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault,
466 kSCDynamicStoreDomainState,
467 kSCEntNetIPv6);
468
469 if (!NetworkGlobalKeyDNS)
470 NetworkGlobalKeyDNS =
471 SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault,
472 kSCDynamicStoreDomainState,
473 kSCEntNetDNS);
474
475 if (!HostNamesKey)
476 HostNamesKey = SCDynamicStoreKeyCreateHostNames(kCFAllocatorDefault);
477
478 if (!NetworkInterfaceKeyIPv4)
479 NetworkInterfaceKeyIPv4 =
480 SCDynamicStoreKeyCreateNetworkInterfaceEntity(kCFAllocatorDefault,
481 kSCDynamicStoreDomainState,
482 kSCCompAnyRegex,
483 kSCEntNetIPv4);
484
485 if (!NetworkInterfaceKeyIPv6)
486 NetworkInterfaceKeyIPv6 =
487 SCDynamicStoreKeyCreateNetworkInterfaceEntity(kCFAllocatorDefault,
488 kSCDynamicStoreDomainState,
489 kSCCompAnyRegex,
490 kSCEntNetIPv6);
491
492 if (store && ComputerNameKey && HostNamesKey &&
493 NetworkGlobalKeyIPv4 && NetworkGlobalKeyIPv6 && NetworkGlobalKeyDNS &&
494 NetworkInterfaceKeyIPv4 && NetworkInterfaceKeyIPv6)
495 {
496 key[0] = ComputerNameKey;
497 key[1] = BTMMKey;
498 key[2] = NetworkGlobalKeyIPv4;
499 key[3] = NetworkGlobalKeyIPv6;
500 key[4] = NetworkGlobalKeyDNS;
501 key[5] = HostNamesKey;
502
503 pattern[0] = NetworkInterfaceKeyIPv4;
504 pattern[1] = NetworkInterfaceKeyIPv6;
505
506 keys = CFArrayCreate(kCFAllocatorDefault, (const void **)key,
507 sizeof(key) / sizeof(key[0]),
508 &kCFTypeArrayCallBacks);
509
510 patterns = CFArrayCreate(kCFAllocatorDefault, (const void **)pattern,
511 sizeof(pattern) / sizeof(pattern[0]),
512 &kCFTypeArrayCallBacks);
513
514 if (keys && patterns &&
515 SCDynamicStoreSetNotificationKeys(store, keys, patterns))
516 {
517 if ((storeRLS = SCDynamicStoreCreateRunLoopSource(kCFAllocatorDefault,
518 store, 0)) != NULL)
519 {
520 CFRunLoopAddSource(CFRunLoopGetCurrent(), storeRLS,
521 kCFRunLoopDefaultMode);
522 }
523 }
524 }
525
526 if (keys)
527 CFRelease(keys);
528
529 if (patterns)
530 CFRelease(patterns);
531
532 /*
533 * Set up a timer to delay the wake change notifications.
534 *
535 * The initial time is set a decade or so into the future, we'll adjust
536 * this later.
537 */
538
539 bzero(&timerContext, sizeof(timerContext));
540 timerContext.info = &threadData;
541
542 threadData.timerRef =
543 CFRunLoopTimerCreate(kCFAllocatorDefault,
544 CFAbsoluteTimeGetCurrent() + (86400L * 365L * 10L),
545 86400L * 365L * 10L, 0, 0, sysEventTimerNotifier,
546 &timerContext);
547 CFRunLoopAddTimer(CFRunLoopGetCurrent(), threadData.timerRef,
548 kCFRunLoopDefaultMode);
549
550 /*
551 * Store our runloop in a global so the main thread can use it to stop us.
552 */
553
554 pthread_mutex_lock(&SysEventThreadMutex);
555
556 SysEventRunloop = CFRunLoopGetCurrent();
557
558 pthread_cond_signal(&SysEventThreadCond);
559 pthread_mutex_unlock(&SysEventThreadMutex);
560
561 /*
562 * Disappear into the runloop until it's stopped by the main thread.
563 */
564
565 CFRunLoopRun();
566
567 /*
568 * Clean up before exiting.
569 */
570
571 if (threadData.timerRef)
572 {
573 CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), threadData.timerRef,
574 kCFRunLoopDefaultMode);
575 CFRelease(threadData.timerRef);
576 }
577
578 if (threadData.sysevent.powerKernelPort)
579 {
580 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), powerRLS,
581 kCFRunLoopDefaultMode);
582 IODeregisterForSystemPower(&powerNotifierObj);
583 IOServiceClose(threadData.sysevent.powerKernelPort);
584 IONotificationPortDestroy(powerNotifierPort);
585 }
586
587 if (storeRLS)
588 {
589 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), storeRLS,
590 kCFRunLoopDefaultMode);
591 CFRunLoopSourceInvalidate(storeRLS);
592 CFRelease(storeRLS);
593 }
594
595 if (store)
596 CFRelease(store);
597
598 pthread_exit(NULL);
599 }
600
601
602 /*
603 * 'sysEventPowerNotifier()' - Handle power notification events.
604 */
605
606 static void
sysEventPowerNotifier(void * context,io_service_t service,natural_t messageType,void * messageArgument)607 sysEventPowerNotifier(
608 void *context, /* I - Thread context data */
609 io_service_t service, /* I - Unused service info */
610 natural_t messageType, /* I - Type of message */
611 void *messageArgument) /* I - Message data */
612 {
613 int sendit = 1; /* Send event to main thread? *
614 * (0 = no, 1 = yes, 2 = delayed */
615 cupsd_thread_data_t *threadData; /* Thread context data */
616
617
618 threadData = (cupsd_thread_data_t *)context;
619
620 (void)service; /* anti-compiler-warning-code */
621
622 switch (messageType)
623 {
624 case kIOMessageCanSystemPowerOff :
625 case kIOMessageCanSystemSleep :
626 threadData->sysevent.event |= SYSEVENT_CANSLEEP;
627 break;
628
629 case kIOMessageSystemWillRestart :
630 case kIOMessageSystemWillPowerOff :
631 case kIOMessageSystemWillSleep :
632 threadData->sysevent.event |= SYSEVENT_WILLSLEEP;
633 threadData->sysevent.event &= ~SYSEVENT_WOKE;
634 break;
635
636 case kIOMessageSystemHasPoweredOn :
637 /*
638 * Because powered on is followed by a net-changed event, delay
639 * before sending it.
640 */
641
642 sendit = 2;
643 threadData->sysevent.event |= SYSEVENT_WOKE;
644 break;
645
646 case kIOMessageSystemWillNotPowerOff :
647 case kIOMessageSystemWillNotSleep :
648 # ifdef kIOMessageSystemWillPowerOn
649 case kIOMessageSystemWillPowerOn :
650 # endif /* kIOMessageSystemWillPowerOn */
651 default:
652 sendit = 0;
653 break;
654 }
655
656 switch (messageType)
657 {
658 case kIOMessageCanSystemPowerOff :
659 cupsdLogMessage(CUPSD_LOG_DEBUG,
660 "Got kIOMessageCanSystemPowerOff message.");
661 break;
662 case kIOMessageCanSystemSleep :
663 cupsdLogMessage(CUPSD_LOG_DEBUG,
664 "Got kIOMessageCannSystemSleep message.");
665 break;
666 case kIOMessageSystemWillRestart :
667 cupsdLogMessage(CUPSD_LOG_DEBUG,
668 "Got kIOMessageSystemWillRestart message.");
669 break;
670 case kIOMessageSystemWillPowerOff :
671 cupsdLogMessage(CUPSD_LOG_DEBUG,
672 "Got kIOMessageSystemWillPowerOff message.");
673 break;
674 case kIOMessageSystemWillSleep :
675 cupsdLogMessage(CUPSD_LOG_DEBUG,
676 "Got kIOMessageSystemWillSleep message.");
677 break;
678 case kIOMessageSystemHasPoweredOn :
679 cupsdLogMessage(CUPSD_LOG_DEBUG,
680 "Got kIOMessageSystemHasPoweredOn message.");
681 break;
682 case kIOMessageSystemWillNotPowerOff :
683 cupsdLogMessage(CUPSD_LOG_DEBUG,
684 "Got kIOMessageSystemWillNotPowerOff message.");
685 break;
686 case kIOMessageSystemWillNotSleep :
687 cupsdLogMessage(CUPSD_LOG_DEBUG,
688 "Got kIOMessageSystemWillNotSleep message.");
689 break;
690 # ifdef kIOMessageSystemWillPowerOn
691 case kIOMessageSystemWillPowerOn :
692 cupsdLogMessage(CUPSD_LOG_DEBUG,
693 "Got kIOMessageSystemWillPowerOn message.");
694 break;
695 # endif /* kIOMessageSystemWillPowerOn */
696 default:
697 cupsdLogMessage(CUPSD_LOG_DEBUG, "Got unknown power message %d.",
698 (int)messageType);
699 break;
700 }
701
702 if (sendit == 0)
703 IOAllowPowerChange(threadData->sysevent.powerKernelPort,
704 (long)messageArgument);
705 else
706 {
707 threadData->sysevent.powerNotificationID = (long)messageArgument;
708
709 if (sendit == 1)
710 {
711 /*
712 * Send the event to the main thread now.
713 */
714
715 write(SysEventPipes[1], &threadData->sysevent,
716 sizeof(threadData->sysevent));
717 threadData->sysevent.event = 0;
718 }
719 else
720 {
721 /*
722 * Send the event to the main thread after 1 to 2 seconds.
723 */
724
725 CFRunLoopTimerSetNextFireDate(threadData->timerRef,
726 CFAbsoluteTimeGetCurrent() + 2);
727 }
728 }
729 }
730
731
732 /*
733 * 'sysEventConfigurationNotifier()' - Network configuration change notification
734 * callback.
735 */
736
737 static void
sysEventConfigurationNotifier(SCDynamicStoreRef store,CFArrayRef changedKeys,void * context)738 sysEventConfigurationNotifier(
739 SCDynamicStoreRef store, /* I - System data (unused) */
740 CFArrayRef changedKeys, /* I - Changed data */
741 void *context) /* I - Thread context data */
742 {
743 cupsd_thread_data_t *threadData; /* Thread context data */
744
745
746 threadData = (cupsd_thread_data_t *)context;
747
748 (void)store; /* anti-compiler-warning-code */
749
750 CFRange range = CFRangeMake(0, CFArrayGetCount(changedKeys));
751
752 if (CFArrayContainsValue(changedKeys, range, ComputerNameKey) ||
753 CFArrayContainsValue(changedKeys, range, BTMMKey))
754 threadData->sysevent.event |= SYSEVENT_NAMECHANGED;
755 else
756 {
757 threadData->sysevent.event |= SYSEVENT_NETCHANGED;
758
759 /*
760 * Indicate the network interface list needs updating...
761 */
762
763 NetIFUpdate = 1;
764 }
765
766 /*
767 * Because we registered for several different kinds of change notifications
768 * this callback usually gets called several times in a row. We use a timer to
769 * de-bounce these so we only end up generating one event for the main thread.
770 */
771
772 CFRunLoopTimerSetNextFireDate(threadData->timerRef,
773 CFAbsoluteTimeGetCurrent() + 5);
774 }
775
776
777 /*
778 * 'sysEventTimerNotifier()' - Handle delayed event notifications.
779 */
780
781 static void
sysEventTimerNotifier(CFRunLoopTimerRef timer,void * context)782 sysEventTimerNotifier(
783 CFRunLoopTimerRef timer, /* I - Timer information */
784 void *context) /* I - Thread context data */
785 {
786 cupsd_thread_data_t *threadData; /* Thread context data */
787
788
789 (void)timer;
790
791 threadData = (cupsd_thread_data_t *)context;
792
793 /*
794 * If an event is still pending send it to the main thread.
795 */
796
797 if (threadData->sysevent.event)
798 {
799 write(SysEventPipes[1], &threadData->sysevent,
800 sizeof(threadData->sysevent));
801 threadData->sysevent.event = 0;
802 }
803 }
804
805
806 /*
807 * 'sysUpdate()' - Update the current system state.
808 */
809
810 static void
sysUpdate(void)811 sysUpdate(void)
812 {
813 int i; /* Looping var */
814 cupsd_sysevent_t sysevent; /* The system event */
815 cupsd_printer_t *p; /* Printer information */
816
817
818 /*
819 * Drain the event pipe...
820 */
821
822 while (read((int)SysEventPipes[0], &sysevent, sizeof(sysevent))
823 == sizeof(sysevent))
824 {
825 if (sysevent.event & SYSEVENT_CANSLEEP)
826 {
827 /*
828 * If there are active printers that don't have the connecting-to-device
829 * or cups-waiting-for-job-completed printer-state-reason then cancel the
830 * sleep request, i.e., these reasons indicate a job that is not actively
831 * doing anything...
832 */
833
834 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
835 p;
836 p = (cupsd_printer_t *)cupsArrayNext(Printers))
837 {
838 if (p->job)
839 {
840 for (i = 0; i < p->num_reasons; i ++)
841 if (!strcmp(p->reasons[i], "connecting-to-device") ||
842 !strcmp(p->reasons[i], "cups-waiting-for-job-completed"))
843 break;
844
845 if (!p->num_reasons || i >= p->num_reasons)
846 break;
847 }
848 }
849
850 if (p)
851 {
852 cupsdLogMessage(CUPSD_LOG_INFO,
853 "System sleep canceled because printer %s is active.",
854 p->name);
855 IOCancelPowerChange(sysevent.powerKernelPort,
856 sysevent.powerNotificationID);
857 }
858 else
859 {
860 cupsdLogMessage(CUPSD_LOG_DEBUG, "System wants to sleep.");
861 IOAllowPowerChange(sysevent.powerKernelPort,
862 sysevent.powerNotificationID);
863 }
864 }
865
866 if (sysevent.event & SYSEVENT_WILLSLEEP)
867 {
868 cupsdLogMessage(CUPSD_LOG_DEBUG, "System going to sleep.");
869
870 Sleeping = 1;
871
872 cupsdCleanDirty();
873
874 /*
875 * If we have no printing jobs, allow the power change immediately.
876 * Otherwise set the SleepJobs time to 10 seconds in the future when
877 * we'll take more drastic measures...
878 */
879
880 if (cupsArrayCount(PrintingJobs) == 0)
881 {
882 cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep.");
883 IOAllowPowerChange(sysevent.powerKernelPort,
884 sysevent.powerNotificationID);
885 }
886 else
887 {
888 /*
889 * If there are active printers that don't have the connecting-to-device
890 * or cups-waiting-for-job-completed printer-state-reasons then delay the
891 * sleep request, i.e., these reasons indicate a job is active...
892 */
893
894 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
895 p;
896 p = (cupsd_printer_t *)cupsArrayNext(Printers))
897 {
898 if (p->job)
899 {
900 for (i = 0; i < p->num_reasons; i ++)
901 if (!strcmp(p->reasons[i], "connecting-to-device") ||
902 !strcmp(p->reasons[i], "cups-waiting-for-job-completed"))
903 break;
904
905 if (!p->num_reasons || i >= p->num_reasons)
906 break;
907 }
908 }
909
910 if (p)
911 {
912 cupsdLogMessage(CUPSD_LOG_INFO,
913 "System sleep delayed because printer %s is active.",
914 p->name);
915
916 LastSysEvent = sysevent;
917 SleepJobs = time(NULL) + 10;
918 }
919 else
920 {
921 cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep.");
922 IOAllowPowerChange(sysevent.powerKernelPort,
923 sysevent.powerNotificationID);
924 }
925 }
926 }
927
928 if (sysevent.event & SYSEVENT_WOKE)
929 {
930 cupsdLogMessage(CUPSD_LOG_DEBUG, "System woke from sleep.");
931 IOAllowPowerChange(sysevent.powerKernelPort,
932 sysevent.powerNotificationID);
933 Sleeping = 0;
934
935 /*
936 * Make sure jobs that were queued prior to the system going to sleep don't
937 * get canceled right away...
938 */
939
940 if (MaxJobTime > 0)
941 {
942 cupsd_job_t *job; /* Current job */
943
944 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
945 job;
946 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
947 {
948 if (job->cancel_time)
949 {
950 ipp_attribute_t *cancel_after = ippFindAttribute(job->attrs,
951 "job-cancel-after",
952 IPP_TAG_INTEGER);
953
954 if (cancel_after)
955 job->cancel_time = time(NULL) + ippGetInteger(cancel_after, 0);
956 else
957 job->cancel_time = time(NULL) + MaxJobTime;
958 }
959 }
960 }
961
962 if (NameChanged)
963 sysUpdateNames();
964
965 cupsdCheckJobs();
966 }
967
968 if (sysevent.event & SYSEVENT_NETCHANGED)
969 {
970 if (Sleeping)
971 cupsdLogMessage(CUPSD_LOG_DEBUG,
972 "System network configuration changed - "
973 "ignored while sleeping.");
974 else
975 cupsdLogMessage(CUPSD_LOG_DEBUG,
976 "System network configuration changed.");
977 }
978
979 if (sysevent.event & SYSEVENT_NAMECHANGED)
980 {
981 if (Sleeping)
982 {
983 NameChanged = 1;
984
985 cupsdLogMessage(CUPSD_LOG_DEBUG,
986 "Computer name or BTMM domains changed - ignored while "
987 "sleeping.");
988 }
989 else
990 {
991 cupsdLogMessage(CUPSD_LOG_DEBUG,
992 "Computer name or BTMM domains changed.");
993
994 sysUpdateNames();
995 }
996 }
997 }
998 }
999
1000
1001 /*
1002 * 'sysUpdateNames()' - Update computer and/or BTMM domains.
1003 */
1004
1005 static void
sysUpdateNames(void)1006 sysUpdateNames(void)
1007 {
1008 cupsd_printer_t *p; /* Current printer */
1009
1010
1011 NameChanged = 0;
1012
1013 /*
1014 * De-register the individual printers...
1015 */
1016
1017 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
1018 p;
1019 p = (cupsd_printer_t *)cupsArrayNext(Printers))
1020 cupsdDeregisterPrinter(p, 1);
1021
1022 # if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
1023 /*
1024 * Update the computer name and BTMM domain list...
1025 */
1026
1027 cupsdUpdateDNSSDName();
1028 # endif /* HAVE_DNSSD || HAVE_AVAHI */
1029
1030 /*
1031 * Now re-register them...
1032 */
1033
1034 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
1035 p;
1036 p = (cupsd_printer_t *)cupsArrayNext(Printers))
1037 cupsdRegisterPrinter(p);
1038 }
1039 #endif /* __APPLE__ */
1040