]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/sysman.c
Fix source file header text duplication text duplication.
[thirdparty/cups.git] / scheduler / sysman.c
CommitLineData
09ec0018 1/*
581dae2d 2 * System management functions for the CUPS scheduler.
09ec0018 3 *
1deb86fd 4 * Copyright 2007-2014 by Apple Inc.
581dae2d 5 * Copyright 2006 by Easy Software Products.
09ec0018 6 *
581dae2d
MS
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
57b7b66b 11 * missing or damaged, see the license at "http://www.cups.org/".
09ec0018 12 */
13
14
15/*
16 * Include necessary headers...
17 */
18
19#include "cupsd.h"
84315f46 20#ifdef __APPLE__
1deb86fd 21# include <vproc.h>
84315f46 22# include <IOKit/pwr_mgt/IOPMLib.h>
84315f46 23#endif /* __APPLE__ */
09ec0018 24
25
26/*
3dfe78b3 27 * The system management functions cover disk and power management which
1deb86fd 28 * are primarily used for portable computers.
09ec0018 29 *
3dfe78b3
MS
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
1deb86fd 32 * up or flash to be written to.
3dfe78b3 33 *
8072030b 34 * Power management support is currently only implemented on macOS, but
3dfe78b3 35 * essentially we use four functions to let the OS know when it is OK to
10d09e33 36 * put the system to sleep, typically when we are not in the middle of
8072030b 37 * printing a job. And on macOS we can also "sleep print" - basically the
1deb86fd
MS
38 * system only wakes up long enough to service network requests and process
39 * print jobs.
3dfe78b3
MS
40 */
41
1deb86fd 42
3dfe78b3
MS
43/*
44 * 'cupsdCleanDirty()' - Write dirty config and state files.
09ec0018 45 */
46
3dfe78b3
MS
47void
48cupsdCleanDirty(void)
49{
50 if (DirtyFiles & CUPSD_DIRTY_PRINTERS)
51 cupsdSaveAllPrinters();
52
53 if (DirtyFiles & CUPSD_DIRTY_CLASSES)
54 cupsdSaveAllClasses();
55
3dfe78b3
MS
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;
49d87452
MS
77
78 cupsdSetBusyState();
3dfe78b3
MS
79}
80
81
82/*
83 * 'cupsdMarkDirty()' - Mark config or state files as needing a write.
84 */
85
86void
87cupsdMarkDirty(int what) /* I - What file(s) are dirty? */
88{
a2326b5b 89 cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdMarkDirty(%c%c%c%c%c)",
68b10830
MS
90 (what & CUPSD_DIRTY_PRINTERS) ? 'P' : '-',
91 (what & CUPSD_DIRTY_CLASSES) ? 'C' : '-',
68b10830
MS
92 (what & CUPSD_DIRTY_PRINTCAP) ? 'p' : '-',
93 (what & CUPSD_DIRTY_JOBS) ? 'J' : '-',
94 (what & CUPSD_DIRTY_SUBSCRIPTIONS) ? 'S' : '-');
95
49d87452
MS
96 if (what == CUPSD_DIRTY_PRINTCAP && !Printcap)
97 return;
98
3dfe78b3
MS
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
112void
113cupsdSetBusyState(void)
114{
84315f46
MS
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 */
49d87452
MS
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 };
1deb86fd 131#ifdef __APPLE__
178cb736 132 static vproc_transaction_t vtran = 0; /* Current busy transaction */
1deb86fd
MS
133 static IOPMAssertionID keep_awake = 0;/* Keep the system awake while printing */
134#endif /* __APPLE__ */
49d87452
MS
135
136
84315f46
MS
137 /*
138 * Figure out how busy we are...
139 */
140
49d87452 141 newbusy = (DirtyCleanTime ? 1 : 0) |
49d87452 142 (cupsArrayCount(ActiveClients) ? 4 : 0);
3dfe78b3 143
84315f46
MS
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
f99f3698
MS
162 cupsdLogMessage(CUPSD_LOG_DEBUG,
163 "cupsdSetBusyState: newbusy=\"%s\", busy=\"%s\"",
164 busy_text[newbusy], busy_text[busy]);
165
84315f46
MS
166 /*
167 * Manage state changes...
168 */
169
3dfe78b3
MS
170 if (newbusy != busy)
171 {
172 busy = newbusy;
173
1deb86fd 174#ifdef __APPLE__
178cb736
MS
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 }
1deb86fd
MS
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);
f99f3698 193 }
1deb86fd
MS
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__ */
3dfe78b3
MS
201}
202
203
09ec0018 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
1deb86fd 215# include <notify.h>
09ec0018 216# include <IOKit/IOKitLib.h>
217# include <IOKit/IOMessage.h>
1deb86fd 218# include <IOKit/ps/IOPowerSources.h>
09ec0018 219# include <IOKit/pwr_mgt/IOPMLib.h>
220# include <SystemConfiguration/SystemConfiguration.h>
221# include <pthread.h>
222
223
224/*
225 * Constants...
226 */
227
a4d04587 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 */
09ec0018 233
234
a2326b5b
MS
235/*
236 * Structures...
09ec0018 237 */
238
239typedef 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
247typedef 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
a2326b5b
MS
255/*
256 * Local globals...
09ec0018 257 */
258
259static pthread_t SysEventThread = NULL;
260 /* Thread to host a runloop */
261static pthread_mutex_t SysEventThreadMutex = { 0 };
a2326b5b 262 /* Coordinates access to shared gloabals */
09ec0018 263static pthread_cond_t SysEventThreadCond = { 0 };
264 /* Thread initialization complete condition */
265static CFRunLoopRef SysEventRunloop = NULL;
266 /* The runloop. Access must be protected! */
267static CFStringRef ComputerNameKey = NULL,
268 /* Computer name key */
e07d4801 269 BTMMKey = NULL, /* Back to My Mac key */
411affcf 270 NetworkGlobalKeyIPv4 = NULL,
271 /* Network global IPv4 key */
272 NetworkGlobalKeyIPv6 = NULL,
273 /* Network global IPv6 key */
274 NetworkGlobalKeyDNS = NULL,
275 /* Network global DNS key */
09ec0018 276 HostNamesKey = NULL,
277 /* Host name key */
411affcf 278 NetworkInterfaceKeyIPv4 = NULL,
279 /* Netowrk interface key */
280 NetworkInterfaceKeyIPv6 = NULL;
09ec0018 281 /* Netowrk interface key */
e6013cfa 282static cupsd_sysevent_t LastSysEvent; /* Last system event (for delayed sleep) */
581dae2d 283static int NameChanged = 0;/* Did we get a 'name changed' event during sleep? */
1deb86fd 284static int PSToken = 0; /* Power source notifications */
09ec0018 285
286
a2326b5b
MS
287/*
288 * Local functions...
09ec0018 289 */
290
291static void *sysEventThreadEntry(void);
292static void sysEventPowerNotifier(void *context, io_service_t service,
293 natural_t messageType,
294 void *messageArgument);
295static void sysEventConfigurationNotifier(SCDynamicStoreRef store,
296 CFArrayRef changedKeys,
297 void *context);
298static void sysEventTimerNotifier(CFRunLoopTimerRef timer, void *context);
75bd9771 299static void sysUpdate(void);
581dae2d 300static void sysUpdateNames(void);
09ec0018 301
302
e6013cfa
MS
303/*
304 * 'cupsdAllowSleep()' - Tell the OS it is now OK to sleep.
305 */
306
307void
308cupsdAllowSleep(void)
309{
310 cupsdCleanDirty();
311
61893d0b 312 cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep.");
e6013cfa
MS
313 IOAllowPowerChange(LastSysEvent.powerKernelPort,
314 LastSysEvent.powerNotificationID);
315}
316
317
09ec0018 318/*
319 * 'cupsdStartSystemMonitor()' - Start monitoring for system change.
320 */
321
322void
323cupsdStartSystemMonitor(void)
324{
325 int flags; /* fcntl flags on pipe */
326
327
1deb86fd
MS
328 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartSystemMonitor()");
329
09ec0018 330 if (cupsdOpenPipe(SysEventPipes))
331 {
332 cupsdLogMessage(CUPSD_LOG_ERROR, "System event monitor pipe() failed - %s!",
333 strerror(errno));
334 return;
335 }
336
75bd9771 337 cupsdAddSelect(SysEventPipes[0], (cupsd_selfunc_t)sysUpdate, NULL, NULL);
09ec0018 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);
1deb86fd
MS
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;
49c81c17
MS
361 notify_register_dispatch(kIOPSNotifyPowerSource, &PSToken, dispatch_get_main_queue(), ^(int t) { (void)t;
362 ACPower = IOPSGetTimeRemainingEstimate() == kIOPSTimeRemainingUnlimited; });
09ec0018 363}
364
365
366/*
367 * 'cupsdStopSystemMonitor()' - Stop monitoring for system change.
368 */
369
370void
371cupsdStopSystemMonitor(void)
372{
373 CFRunLoopRef rl; /* The event handler runloop */
374
375
1deb86fd
MS
376 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStopSystemMonitor()");
377
09ec0018 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 {
f7deaa1a 405 cupsdRemoveSelect(SysEventPipes[0]);
09ec0018 406 cupsdClosePipe(SysEventPipes);
407 }
1deb86fd
MS
408
409 if (PSToken != 0)
410 {
411 notify_cancel(PSToken);
412 PSToken = 0;
413 }
09ec0018 414}
415
416
09ec0018 417/*
418 * 'sysEventThreadEntry()' - A thread to receive power and computer name
419 * change notifications.
420 */
421
422static void * /* O - Return status/value */
423sysEventThreadEntry(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 */
e07d4801 432 CFStringRef key[6], /* System Config keys */
411affcf 433 pattern[2]; /* System Config patterns */
09ec0018 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
e07d4801 468 store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("cupsd"),
09ec0018 469 sysEventConfigurationNotifier, &storeContext);
470
471 if (!ComputerNameKey)
e07d4801
MS
472 ComputerNameKey = SCDynamicStoreKeyCreateComputerName(kCFAllocatorDefault);
473
474 if (!BTMMKey)
475 BTMMKey = SCDynamicStoreKeyCreate(kCFAllocatorDefault,
476 CFSTR("Setup:/Network/BackToMyMac"));
09ec0018 477
411affcf 478 if (!NetworkGlobalKeyIPv4)
479 NetworkGlobalKeyIPv4 =
e07d4801 480 SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault,
09ec0018 481 kSCDynamicStoreDomainState,
482 kSCEntNetIPv4);
483
411affcf 484 if (!NetworkGlobalKeyIPv6)
485 NetworkGlobalKeyIPv6 =
e07d4801 486 SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault,
411affcf 487 kSCDynamicStoreDomainState,
488 kSCEntNetIPv6);
489
490 if (!NetworkGlobalKeyDNS)
a2326b5b
MS
491 NetworkGlobalKeyDNS =
492 SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault,
411affcf 493 kSCDynamicStoreDomainState,
494 kSCEntNetDNS);
495
09ec0018 496 if (!HostNamesKey)
e07d4801 497 HostNamesKey = SCDynamicStoreKeyCreateHostNames(kCFAllocatorDefault);
09ec0018 498
411affcf 499 if (!NetworkInterfaceKeyIPv4)
500 NetworkInterfaceKeyIPv4 =
e07d4801 501 SCDynamicStoreKeyCreateNetworkInterfaceEntity(kCFAllocatorDefault,
09ec0018 502 kSCDynamicStoreDomainState,
503 kSCCompAnyRegex,
504 kSCEntNetIPv4);
505
411affcf 506 if (!NetworkInterfaceKeyIPv6)
507 NetworkInterfaceKeyIPv6 =
e07d4801 508 SCDynamicStoreKeyCreateNetworkInterfaceEntity(kCFAllocatorDefault,
411affcf 509 kSCDynamicStoreDomainState,
510 kSCCompAnyRegex,
511 kSCEntNetIPv6);
512
513 if (store && ComputerNameKey && HostNamesKey &&
514 NetworkGlobalKeyIPv4 && NetworkGlobalKeyIPv6 && NetworkGlobalKeyDNS &&
515 NetworkInterfaceKeyIPv4 && NetworkInterfaceKeyIPv6)
09ec0018 516 {
517 key[0] = ComputerNameKey;
e07d4801
MS
518 key[1] = BTMMKey;
519 key[2] = NetworkGlobalKeyIPv4;
520 key[3] = NetworkGlobalKeyIPv6;
521 key[4] = NetworkGlobalKeyDNS;
522 key[5] = HostNamesKey;
411affcf 523
524 pattern[0] = NetworkInterfaceKeyIPv4;
525 pattern[1] = NetworkInterfaceKeyIPv6;
09ec0018 526
e07d4801
MS
527 keys = CFArrayCreate(kCFAllocatorDefault, (const void **)key,
528 sizeof(key) / sizeof(key[0]),
529 &kCFTypeArrayCallBacks);
411affcf 530
e07d4801 531 patterns = CFArrayCreate(kCFAllocatorDefault, (const void **)pattern,
09ec0018 532 sizeof(pattern) / sizeof(pattern[0]),
533 &kCFTypeArrayCallBacks);
534
535 if (keys && patterns &&
536 SCDynamicStoreSetNotificationKeys(store, keys, patterns))
537 {
e07d4801
MS
538 if ((storeRLS = SCDynamicStoreCreateRunLoopSource(kCFAllocatorDefault,
539 store, 0)) != NULL)
09ec0018 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 =
e07d4801 573 CFRunLoopTimerCreate(kCFAllocatorDefault,
a2326b5b 574 CFAbsoluteTimeGetCurrent() + (86400L * 365L * 10L),
09ec0018 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
636static void
637sysEventPowerNotifier(
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 {
61893d0b
MS
654 case kIOMessageCanSystemPowerOff :
655 case kIOMessageCanSystemSleep :
09ec0018 656 threadData->sysevent.event |= SYSEVENT_CANSLEEP;
657 break;
658
61893d0b
MS
659 case kIOMessageSystemWillRestart :
660 case kIOMessageSystemWillPowerOff :
661 case kIOMessageSystemWillSleep :
09ec0018 662 threadData->sysevent.event |= SYSEVENT_WILLSLEEP;
581dae2d 663 threadData->sysevent.event &= ~SYSEVENT_WOKE;
09ec0018 664 break;
665
61893d0b 666 case kIOMessageSystemHasPoweredOn :
a2326b5b 667 /*
09ec0018 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
61893d0b
MS
676 case kIOMessageSystemWillNotPowerOff :
677 case kIOMessageSystemWillNotSleep :
e07d4801 678# ifdef kIOMessageSystemWillPowerOn
61893d0b 679 case kIOMessageSystemWillPowerOn :
e07d4801 680# endif /* kIOMessageSystemWillPowerOn */
09ec0018 681 default:
682 sendit = 0;
683 break;
684 }
685
28a463e0
MS
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
09ec0018 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 {
a2326b5b 741 /*
09ec0018 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 {
a2326b5b 751 /*
09ec0018 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/*
e07d4801 763 * 'sysEventConfigurationNotifier()' - Network configuration change notification
09ec0018 764 * callback.
765 */
766
767static void
768sysEventConfigurationNotifier(
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;
a2326b5b 777
09ec0018 778 (void)store; /* anti-compiler-warning-code */
779
780 CFRange range = CFRangeMake(0, CFArrayGetCount(changedKeys));
781
e07d4801
MS
782 if (CFArrayContainsValue(changedKeys, range, ComputerNameKey) ||
783 CFArrayContainsValue(changedKeys, range, BTMMKey))
09ec0018 784 threadData->sysevent.event |= SYSEVENT_NAMECHANGED;
411affcf 785 else
786 {
09ec0018 787 threadData->sysevent.event |= SYSEVENT_NETCHANGED;
788
411affcf 789 /*
790 * Indicate the network interface list needs updating...
791 */
792
793 NetIFUpdate = 1;
794 }
795
09ec0018 796 /*
a2326b5b
MS
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
09ec0018 799 * de-bounce these so we only end up generating one event for the main thread.
800 */
801
a2326b5b 802 CFRunLoopTimerSetNextFireDate(threadData->timerRef,
411affcf 803 CFAbsoluteTimeGetCurrent() + 5);
09ec0018 804}
805
806
807/*
808 * 'sysEventTimerNotifier()' - Handle delayed event notifications.
809 */
810
811static void
812sysEventTimerNotifier(
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
321d8d57
MS
819 (void)timer;
820
09ec0018 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}
75bd9771
MS
834
835
836/*
837 * 'sysUpdate()' - Update the current system state.
838 */
839
840static void
841sysUpdate(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
1e3e80bb
MS
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...
75bd9771
MS
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 ++)
581dae2d 871 if (!strcmp(p->reasons[i], "connecting-to-device") ||
1e3e80bb 872 !strcmp(p->reasons[i], "cups-waiting-for-job-completed"))
75bd9771
MS
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,
581dae2d 883 "System sleep canceled because printer %s is active.",
75bd9771
MS
884 p->name);
885 IOCancelPowerChange(sysevent.powerKernelPort,
886 sysevent.powerNotificationID);
887 }
888 else
889 {
581dae2d 890 cupsdLogMessage(CUPSD_LOG_DEBUG, "System wants to sleep.");
75bd9771
MS
891 IOAllowPowerChange(sysevent.powerKernelPort,
892 sysevent.powerNotificationID);
893 }
894 }
895
896 if (sysevent.event & SYSEVENT_WILLSLEEP)
897 {
581dae2d 898 cupsdLogMessage(CUPSD_LOG_DEBUG, "System going to sleep.");
75bd9771
MS
899
900 Sleeping = 1;
901
75bd9771
MS
902 cupsdCleanDirty();
903
e6013cfa
MS
904 /*
905 * If we have no printing jobs, allow the power change immediately.
581dae2d 906 * Otherwise set the SleepJobs time to 10 seconds in the future when
e6013cfa
MS
907 * we'll take more drastic measures...
908 */
909
910 if (cupsArrayCount(PrintingJobs) == 0)
61893d0b
MS
911 {
912 cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep.");
e6013cfa
MS
913 IOAllowPowerChange(sysevent.powerKernelPort,
914 sysevent.powerNotificationID);
61893d0b 915 }
e6013cfa
MS
916 else
917 {
e60ec91f
MS
918 /*
919 * If there are active printers that don't have the connecting-to-device
1e3e80bb 920 * or cups-waiting-for-job-completed printer-state-reasons then delay the
581dae2d 921 * sleep request, i.e., these reasons indicate a job is active...
e60ec91f
MS
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 ++)
581dae2d 931 if (!strcmp(p->reasons[i], "connecting-to-device") ||
1e3e80bb 932 !strcmp(p->reasons[i], "cups-waiting-for-job-completed"))
e60ec91f
MS
933 break;
934
935 if (!p->num_reasons || i >= p->num_reasons)
936 break;
937 }
938 }
939
940 if (p)
941 {
581dae2d
MS
942 cupsdLogMessage(CUPSD_LOG_INFO,
943 "System sleep delayed because printer %s is active.",
944 p->name);
945
e60ec91f
MS
946 LastSysEvent = sysevent;
947 SleepJobs = time(NULL) + 10;
948 }
949 else
950 {
61893d0b 951 cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep.");
e60ec91f
MS
952 IOAllowPowerChange(sysevent.powerKernelPort,
953 sysevent.powerNotificationID);
954 }
e6013cfa 955 }
75bd9771
MS
956 }
957
958 if (sysevent.event & SYSEVENT_WOKE)
959 {
581dae2d 960 cupsdLogMessage(CUPSD_LOG_DEBUG, "System woke from sleep.");
75bd9771
MS
961 IOAllowPowerChange(sysevent.powerKernelPort,
962 sysevent.powerNotificationID);
963 Sleeping = 0;
dcb445bc 964
982069db
MS
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
581dae2d
MS
992 if (NameChanged)
993 sysUpdateNames();
994
75bd9771
MS
995 cupsdCheckJobs();
996 }
997
998 if (sysevent.event & SYSEVENT_NETCHANGED)
999 {
581dae2d 1000 if (Sleeping)
75bd9771 1001 cupsdLogMessage(CUPSD_LOG_DEBUG,
581dae2d
MS
1002 "System network configuration changed - "
1003 "ignored while sleeping.");
75bd9771
MS
1004 else
1005 cupsdLogMessage(CUPSD_LOG_DEBUG,
581dae2d 1006 "System network configuration changed.");
75bd9771
MS
1007 }
1008
1009 if (sysevent.event & SYSEVENT_NAMECHANGED)
1010 {
581dae2d
MS
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
75bd9771 1020 {
e07d4801 1021 cupsdLogMessage(CUPSD_LOG_DEBUG,
581dae2d 1022 "Computer name or BTMM domains changed.");
75bd9771 1023
581dae2d
MS
1024 sysUpdateNames();
1025 }
1026 }
1027 }
1028}
75bd9771 1029
581dae2d
MS
1030
1031/*
1032 * 'sysUpdateNames()' - Update computer and/or BTMM domains.
1033 */
1034
1035static void
1036sysUpdateNames(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);
75bd9771 1051
a29fd7dd 1052# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
581dae2d
MS
1053 /*
1054 * Update the computer name and BTMM domain list...
1055 */
75bd9771 1056
581dae2d 1057 cupsdUpdateDNSSDName();
a29fd7dd 1058# endif /* HAVE_DNSSD || HAVE_AVAHI */
75bd9771 1059
581dae2d
MS
1060 /*
1061 * Now re-register them...
1062 */
75bd9771 1063
581dae2d
MS
1064 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
1065 p;
1066 p = (cupsd_printer_t *)cupsArrayNext(Printers))
1067 cupsdRegisterPrinter(p);
75bd9771 1068}
a4d04587 1069#endif /* __APPLE__ */