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