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