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