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