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