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