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