]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/sysman.c
<rdar://problem/14323704> J90 PreEVT/Schooner: HP Photosmart Prem C310 prevents syste...
[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
61893d0b 352 cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep.");
e6013cfa
MS
353 IOAllowPowerChange(LastSysEvent.powerKernelPort,
354 LastSysEvent.powerNotificationID);
355}
356
357
09ec0018 358/*
359 * 'cupsdStartSystemMonitor()' - Start monitoring for system change.
360 */
361
362void
363cupsdStartSystemMonitor(void)
364{
365 int flags; /* fcntl flags on pipe */
366
367
368 if (cupsdOpenPipe(SysEventPipes))
369 {
370 cupsdLogMessage(CUPSD_LOG_ERROR, "System event monitor pipe() failed - %s!",
371 strerror(errno));
372 return;
373 }
374
75bd9771 375 cupsdAddSelect(SysEventPipes[0], (cupsd_selfunc_t)sysUpdate, NULL, NULL);
09ec0018 376
377 /*
378 * Set non-blocking mode on the descriptor we will be receiving notification
379 * events on.
380 */
381
382 flags = fcntl(SysEventPipes[0], F_GETFL, 0);
383 fcntl(SysEventPipes[0], F_SETFL, flags | O_NONBLOCK);
384
385 /*
386 * Start the thread that runs the runloop...
387 */
388
389 pthread_mutex_init(&SysEventThreadMutex, NULL);
390 pthread_cond_init(&SysEventThreadCond, NULL);
391 pthread_create(&SysEventThread, NULL, (void *(*)())sysEventThreadEntry, NULL);
392}
393
394
395/*
396 * 'cupsdStopSystemMonitor()' - Stop monitoring for system change.
397 */
398
399void
400cupsdStopSystemMonitor(void)
401{
402 CFRunLoopRef rl; /* The event handler runloop */
403
404
405 if (SysEventThread)
406 {
407 /*
408 * Make sure the thread has completed it's initialization and
409 * stored it's runloop reference in the shared global.
410 */
411
412 pthread_mutex_lock(&SysEventThreadMutex);
413
414 if (!SysEventRunloop)
415 pthread_cond_wait(&SysEventThreadCond, &SysEventThreadMutex);
416
417 rl = SysEventRunloop;
418 SysEventRunloop = NULL;
419
420 pthread_mutex_unlock(&SysEventThreadMutex);
421
422 if (rl)
423 CFRunLoopStop(rl);
424
425 pthread_join(SysEventThread, NULL);
426 pthread_mutex_destroy(&SysEventThreadMutex);
427 pthread_cond_destroy(&SysEventThreadCond);
428 }
429
430 if (SysEventPipes[0] >= 0)
431 {
f7deaa1a 432 cupsdRemoveSelect(SysEventPipes[0]);
09ec0018 433 cupsdClosePipe(SysEventPipes);
434 }
435}
436
437
09ec0018 438/*
439 * 'sysEventThreadEntry()' - A thread to receive power and computer name
440 * change notifications.
441 */
442
443static void * /* O - Return status/value */
444sysEventThreadEntry(void)
445{
446 io_object_t powerNotifierObj;
447 /* Power notifier object */
448 IONotificationPortRef powerNotifierPort;
449 /* Power notifier port */
450 SCDynamicStoreRef store = NULL;/* System Config dynamic store */
451 CFRunLoopSourceRef powerRLS = NULL,/* Power runloop source */
452 storeRLS = NULL;/* System Config runloop source */
e07d4801 453 CFStringRef key[6], /* System Config keys */
411affcf 454 pattern[2]; /* System Config patterns */
09ec0018 455 CFArrayRef keys = NULL, /* System Config key array*/
456 patterns = NULL;/* System Config pattern array */
457 SCDynamicStoreContext storeContext; /* Dynamic store context */
458 CFRunLoopTimerContext timerContext; /* Timer context */
459 cupsd_thread_data_t threadData; /* Thread context data for the *
460 * runloop notifiers */
461
462
463 /*
464 * Register for power state change notifications
465 */
466
467 bzero(&threadData, sizeof(threadData));
468
469 threadData.sysevent.powerKernelPort =
470 IORegisterForSystemPower(&threadData, &powerNotifierPort,
471 sysEventPowerNotifier, &powerNotifierObj);
472
473 if (threadData.sysevent.powerKernelPort)
474 {
475 powerRLS = IONotificationPortGetRunLoopSource(powerNotifierPort);
476 CFRunLoopAddSource(CFRunLoopGetCurrent(), powerRLS, kCFRunLoopDefaultMode);
477 }
478 else
479 DEBUG_puts("sysEventThreadEntry: error registering for system power "
480 "notifications");
481
482 /*
483 * Register for system configuration change notifications
484 */
485
486 bzero(&storeContext, sizeof(storeContext));
487 storeContext.info = &threadData;
488
e07d4801 489 store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("cupsd"),
09ec0018 490 sysEventConfigurationNotifier, &storeContext);
491
492 if (!ComputerNameKey)
e07d4801
MS
493 ComputerNameKey = SCDynamicStoreKeyCreateComputerName(kCFAllocatorDefault);
494
495 if (!BTMMKey)
496 BTMMKey = SCDynamicStoreKeyCreate(kCFAllocatorDefault,
497 CFSTR("Setup:/Network/BackToMyMac"));
09ec0018 498
411affcf 499 if (!NetworkGlobalKeyIPv4)
500 NetworkGlobalKeyIPv4 =
e07d4801 501 SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault,
09ec0018 502 kSCDynamicStoreDomainState,
503 kSCEntNetIPv4);
504
411affcf 505 if (!NetworkGlobalKeyIPv6)
506 NetworkGlobalKeyIPv6 =
e07d4801 507 SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault,
411affcf 508 kSCDynamicStoreDomainState,
509 kSCEntNetIPv6);
510
511 if (!NetworkGlobalKeyDNS)
a2326b5b
MS
512 NetworkGlobalKeyDNS =
513 SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault,
411affcf 514 kSCDynamicStoreDomainState,
515 kSCEntNetDNS);
516
09ec0018 517 if (!HostNamesKey)
e07d4801 518 HostNamesKey = SCDynamicStoreKeyCreateHostNames(kCFAllocatorDefault);
09ec0018 519
411affcf 520 if (!NetworkInterfaceKeyIPv4)
521 NetworkInterfaceKeyIPv4 =
e07d4801 522 SCDynamicStoreKeyCreateNetworkInterfaceEntity(kCFAllocatorDefault,
09ec0018 523 kSCDynamicStoreDomainState,
524 kSCCompAnyRegex,
525 kSCEntNetIPv4);
526
411affcf 527 if (!NetworkInterfaceKeyIPv6)
528 NetworkInterfaceKeyIPv6 =
e07d4801 529 SCDynamicStoreKeyCreateNetworkInterfaceEntity(kCFAllocatorDefault,
411affcf 530 kSCDynamicStoreDomainState,
531 kSCCompAnyRegex,
532 kSCEntNetIPv6);
533
534 if (store && ComputerNameKey && HostNamesKey &&
535 NetworkGlobalKeyIPv4 && NetworkGlobalKeyIPv6 && NetworkGlobalKeyDNS &&
536 NetworkInterfaceKeyIPv4 && NetworkInterfaceKeyIPv6)
09ec0018 537 {
538 key[0] = ComputerNameKey;
e07d4801
MS
539 key[1] = BTMMKey;
540 key[2] = NetworkGlobalKeyIPv4;
541 key[3] = NetworkGlobalKeyIPv6;
542 key[4] = NetworkGlobalKeyDNS;
543 key[5] = HostNamesKey;
411affcf 544
545 pattern[0] = NetworkInterfaceKeyIPv4;
546 pattern[1] = NetworkInterfaceKeyIPv6;
09ec0018 547
e07d4801
MS
548 keys = CFArrayCreate(kCFAllocatorDefault, (const void **)key,
549 sizeof(key) / sizeof(key[0]),
550 &kCFTypeArrayCallBacks);
411affcf 551
e07d4801 552 patterns = CFArrayCreate(kCFAllocatorDefault, (const void **)pattern,
09ec0018 553 sizeof(pattern) / sizeof(pattern[0]),
554 &kCFTypeArrayCallBacks);
555
556 if (keys && patterns &&
557 SCDynamicStoreSetNotificationKeys(store, keys, patterns))
558 {
e07d4801
MS
559 if ((storeRLS = SCDynamicStoreCreateRunLoopSource(kCFAllocatorDefault,
560 store, 0)) != NULL)
09ec0018 561 {
562 CFRunLoopAddSource(CFRunLoopGetCurrent(), storeRLS,
563 kCFRunLoopDefaultMode);
564 }
565 else
566 DEBUG_printf(("sysEventThreadEntry: SCDynamicStoreCreateRunLoopSource "
567 "failed: %s\n", SCErrorString(SCError())));
568 }
569 else
570 DEBUG_printf(("sysEventThreadEntry: SCDynamicStoreSetNotificationKeys "
571 "failed: %s\n", SCErrorString(SCError())));
572 }
573 else
574 DEBUG_printf(("sysEventThreadEntry: SCDynamicStoreCreate failed: %s\n",
575 SCErrorString(SCError())));
576
577 if (keys)
578 CFRelease(keys);
579
580 if (patterns)
581 CFRelease(patterns);
582
583 /*
584 * Set up a timer to delay the wake change notifications.
585 *
586 * The initial time is set a decade or so into the future, we'll adjust
587 * this later.
588 */
589
590 bzero(&timerContext, sizeof(timerContext));
591 timerContext.info = &threadData;
592
593 threadData.timerRef =
e07d4801 594 CFRunLoopTimerCreate(kCFAllocatorDefault,
a2326b5b 595 CFAbsoluteTimeGetCurrent() + (86400L * 365L * 10L),
09ec0018 596 86400L * 365L * 10L, 0, 0, sysEventTimerNotifier,
597 &timerContext);
598 CFRunLoopAddTimer(CFRunLoopGetCurrent(), threadData.timerRef,
599 kCFRunLoopDefaultMode);
600
601 /*
602 * Store our runloop in a global so the main thread can use it to stop us.
603 */
604
605 pthread_mutex_lock(&SysEventThreadMutex);
606
607 SysEventRunloop = CFRunLoopGetCurrent();
608
609 pthread_cond_signal(&SysEventThreadCond);
610 pthread_mutex_unlock(&SysEventThreadMutex);
611
612 /*
613 * Disappear into the runloop until it's stopped by the main thread.
614 */
615
616 CFRunLoopRun();
617
618 /*
619 * Clean up before exiting.
620 */
621
622 if (threadData.timerRef)
623 {
624 CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), threadData.timerRef,
625 kCFRunLoopDefaultMode);
626 CFRelease(threadData.timerRef);
627 }
628
629 if (threadData.sysevent.powerKernelPort)
630 {
631 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), powerRLS,
632 kCFRunLoopDefaultMode);
633 IODeregisterForSystemPower(&powerNotifierObj);
634 IOServiceClose(threadData.sysevent.powerKernelPort);
635 IONotificationPortDestroy(powerNotifierPort);
636 }
637
638 if (storeRLS)
639 {
640 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), storeRLS,
641 kCFRunLoopDefaultMode);
642 CFRunLoopSourceInvalidate(storeRLS);
643 CFRelease(storeRLS);
644 }
645
646 if (store)
647 CFRelease(store);
648
649 pthread_exit(NULL);
650}
651
652
653/*
654 * 'sysEventPowerNotifier()' - Handle power notification events.
655 */
656
657static void
658sysEventPowerNotifier(
659 void *context, /* I - Thread context data */
660 io_service_t service, /* I - Unused service info */
661 natural_t messageType, /* I - Type of message */
662 void *messageArgument) /* I - Message data */
663{
664 int sendit = 1; /* Send event to main thread? *
665 * (0 = no, 1 = yes, 2 = delayed */
666 cupsd_thread_data_t *threadData; /* Thread context data */
667
668
669 threadData = (cupsd_thread_data_t *)context;
670
671 (void)service; /* anti-compiler-warning-code */
672
673 switch (messageType)
674 {
61893d0b
MS
675 case kIOMessageCanSystemPowerOff :
676 case kIOMessageCanSystemSleep :
09ec0018 677 threadData->sysevent.event |= SYSEVENT_CANSLEEP;
678 break;
679
61893d0b
MS
680 case kIOMessageSystemWillRestart :
681 case kIOMessageSystemWillPowerOff :
682 case kIOMessageSystemWillSleep :
09ec0018 683 threadData->sysevent.event |= SYSEVENT_WILLSLEEP;
684 break;
685
61893d0b 686 case kIOMessageSystemHasPoweredOn :
a2326b5b 687 /*
09ec0018 688 * Because powered on is followed by a net-changed event, delay
689 * before sending it.
690 */
691
692 sendit = 2;
693 threadData->sysevent.event |= SYSEVENT_WOKE;
694 break;
695
61893d0b
MS
696 case kIOMessageSystemWillNotPowerOff :
697 case kIOMessageSystemWillNotSleep :
e07d4801 698# ifdef kIOMessageSystemWillPowerOn
61893d0b 699 case kIOMessageSystemWillPowerOn :
e07d4801 700# endif /* kIOMessageSystemWillPowerOn */
09ec0018 701 default:
702 sendit = 0;
703 break;
704 }
705
706 if (sendit == 0)
707 IOAllowPowerChange(threadData->sysevent.powerKernelPort,
708 (long)messageArgument);
709 else
710 {
711 threadData->sysevent.powerNotificationID = (long)messageArgument;
712
713 if (sendit == 1)
714 {
a2326b5b 715 /*
09ec0018 716 * Send the event to the main thread now.
717 */
718
719 write(SysEventPipes[1], &threadData->sysevent,
720 sizeof(threadData->sysevent));
721 threadData->sysevent.event = 0;
722 }
723 else
724 {
a2326b5b 725 /*
09ec0018 726 * Send the event to the main thread after 1 to 2 seconds.
727 */
728
729 CFRunLoopTimerSetNextFireDate(threadData->timerRef,
730 CFAbsoluteTimeGetCurrent() + 2);
731 }
732 }
733}
734
735
736/*
e07d4801 737 * 'sysEventConfigurationNotifier()' - Network configuration change notification
09ec0018 738 * callback.
739 */
740
741static void
742sysEventConfigurationNotifier(
743 SCDynamicStoreRef store, /* I - System data (unused) */
744 CFArrayRef changedKeys, /* I - Changed data */
745 void *context) /* I - Thread context data */
746{
747 cupsd_thread_data_t *threadData; /* Thread context data */
748
749
750 threadData = (cupsd_thread_data_t *)context;
a2326b5b 751
09ec0018 752 (void)store; /* anti-compiler-warning-code */
753
754 CFRange range = CFRangeMake(0, CFArrayGetCount(changedKeys));
755
e07d4801
MS
756 if (CFArrayContainsValue(changedKeys, range, ComputerNameKey) ||
757 CFArrayContainsValue(changedKeys, range, BTMMKey))
09ec0018 758 threadData->sysevent.event |= SYSEVENT_NAMECHANGED;
411affcf 759 else
760 {
09ec0018 761 threadData->sysevent.event |= SYSEVENT_NETCHANGED;
762
411affcf 763 /*
764 * Indicate the network interface list needs updating...
765 */
766
767 NetIFUpdate = 1;
768 }
769
09ec0018 770 /*
a2326b5b
MS
771 * Because we registered for several different kinds of change notifications
772 * this callback usually gets called several times in a row. We use a timer to
09ec0018 773 * de-bounce these so we only end up generating one event for the main thread.
774 */
775
a2326b5b 776 CFRunLoopTimerSetNextFireDate(threadData->timerRef,
411affcf 777 CFAbsoluteTimeGetCurrent() + 5);
09ec0018 778}
779
780
781/*
782 * 'sysEventTimerNotifier()' - Handle delayed event notifications.
783 */
784
785static void
786sysEventTimerNotifier(
787 CFRunLoopTimerRef timer, /* I - Timer information */
788 void *context) /* I - Thread context data */
789{
790 cupsd_thread_data_t *threadData; /* Thread context data */
791
792
321d8d57
MS
793 (void)timer;
794
09ec0018 795 threadData = (cupsd_thread_data_t *)context;
796
797 /*
798 * If an event is still pending send it to the main thread.
799 */
800
801 if (threadData->sysevent.event)
802 {
803 write(SysEventPipes[1], &threadData->sysevent,
804 sizeof(threadData->sysevent));
805 threadData->sysevent.event = 0;
806 }
807}
75bd9771
MS
808
809
810/*
811 * 'sysUpdate()' - Update the current system state.
812 */
813
814static void
815sysUpdate(void)
816{
817 int i; /* Looping var */
818 cupsd_sysevent_t sysevent; /* The system event */
819 cupsd_printer_t *p; /* Printer information */
820
821
822 /*
823 * Drain the event pipe...
824 */
825
826 while (read((int)SysEventPipes[0], &sysevent, sizeof(sysevent))
827 == sizeof(sysevent))
828 {
829 if (sysevent.event & SYSEVENT_CANSLEEP)
830 {
831 /*
832 * If there are active printers that don't have the connecting-to-device
833 * printer-state-reason then cancel the sleep request (i.e. this reason
834 * indicates a job that is not yet connected to the printer)...
835 */
836
837 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
838 p;
839 p = (cupsd_printer_t *)cupsArrayNext(Printers))
840 {
841 if (p->job)
842 {
843 for (i = 0; i < p->num_reasons; i ++)
844 if (!strcmp(p->reasons[i], "connecting-to-device"))
845 break;
846
847 if (!p->num_reasons || i >= p->num_reasons)
848 break;
849 }
850 }
851
852 if (p)
853 {
854 cupsdLogMessage(CUPSD_LOG_INFO,
855 "System sleep canceled because printer %s is active",
856 p->name);
857 IOCancelPowerChange(sysevent.powerKernelPort,
858 sysevent.powerNotificationID);
859 }
860 else
861 {
862 cupsdLogMessage(CUPSD_LOG_DEBUG, "System wants to sleep");
863 IOAllowPowerChange(sysevent.powerKernelPort,
864 sysevent.powerNotificationID);
865 }
866 }
867
868 if (sysevent.event & SYSEVENT_WILLSLEEP)
869 {
870 cupsdLogMessage(CUPSD_LOG_DEBUG, "System going to sleep");
871
872 Sleeping = 1;
873
75bd9771
MS
874 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
875 p;
876 p = (cupsd_printer_t *)cupsArrayNext(Printers))
877 {
a2326b5b
MS
878 cupsdLogMessage(CUPSD_LOG_DEBUG,
879 "Deregistering local printer \"%s\"", p->name);
880 cupsdDeregisterPrinter(p, 0);
75bd9771
MS
881 }
882
883 cupsdCleanDirty();
884
88f9aafc
MS
885#ifdef kIOPMAssertionTypeDenySystemSleep
886 /*
dcb445bc
MS
887 * Remove our assertion as needed since the user wants the system to
888 * sleep (different than idle sleep)...
88f9aafc
MS
889 */
890
7bf18198 891 if (keep_awake)
dcb445bc
MS
892 {
893 cupsdLogMessage(CUPSD_LOG_DEBUG, "Releasing dark wake assertion.");
7bf18198
MS
894 IOPMAssertionRelease(keep_awake);
895 keep_awake = 0;
dcb445bc
MS
896 }
897#endif /* kIOPMAssertionTypeDenySystemSleep */
88f9aafc 898
e6013cfa
MS
899 /*
900 * If we have no printing jobs, allow the power change immediately.
238c3832 901 * Otherwise set the SleepJobs time to 15 seconds in the future when
e6013cfa
MS
902 * we'll take more drastic measures...
903 */
904
905 if (cupsArrayCount(PrintingJobs) == 0)
61893d0b
MS
906 {
907 cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep.");
e6013cfa
MS
908 IOAllowPowerChange(sysevent.powerKernelPort,
909 sysevent.powerNotificationID);
61893d0b 910 }
e6013cfa
MS
911 else
912 {
e60ec91f
MS
913 /*
914 * If there are active printers that don't have the connecting-to-device
915 * printer-state-reason then delay the sleep request (i.e. this reason
916 * indicates a job that is not yet connected to the printer)...
917 */
918
919 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
920 p;
921 p = (cupsd_printer_t *)cupsArrayNext(Printers))
922 {
923 if (p->job)
924 {
925 for (i = 0; i < p->num_reasons; i ++)
926 if (!strcmp(p->reasons[i], "connecting-to-device"))
927 break;
928
929 if (!p->num_reasons || i >= p->num_reasons)
930 break;
931 }
932 }
933
934 if (p)
935 {
936 LastSysEvent = sysevent;
937 SleepJobs = time(NULL) + 10;
938 }
939 else
940 {
61893d0b 941 cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep.");
e60ec91f
MS
942 IOAllowPowerChange(sysevent.powerKernelPort,
943 sysevent.powerNotificationID);
944 }
e6013cfa 945 }
75bd9771
MS
946 }
947
948 if (sysevent.event & SYSEVENT_WOKE)
949 {
950 cupsdLogMessage(CUPSD_LOG_DEBUG, "System woke from sleep");
951 IOAllowPowerChange(sysevent.powerKernelPort,
952 sysevent.powerNotificationID);
953 Sleeping = 0;
dcb445bc
MS
954
955#ifdef kIOPMAssertionTypeDenySystemSleep
7bf18198 956 if (cupsArrayCount(PrintingJobs) > 0 && !keep_awake)
dcb445bc
MS
957 {
958 cupsdLogMessage(CUPSD_LOG_DEBUG, "Asserting dark wake.");
959 IOPMAssertionCreateWithName(kIOPMAssertionTypeDenySystemSleep,
960 kIOPMAssertionLevelOn,
7bf18198 961 CFSTR("org.cups.cupsd"), &keep_awake);
dcb445bc
MS
962 }
963#endif /* kIOPMAssertionTypeDenySystemSleep */
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
75bd9771
MS
992 cupsdCheckJobs();
993 }
994
995 if (sysevent.event & SYSEVENT_NETCHANGED)
996 {
997 if (!Sleeping)
75bd9771
MS
998 cupsdLogMessage(CUPSD_LOG_DEBUG,
999 "System network configuration changed");
75bd9771
MS
1000 else
1001 cupsdLogMessage(CUPSD_LOG_DEBUG,
1002 "System network configuration changed; "
1003 "ignored while sleeping");
1004 }
1005
1006 if (sysevent.event & SYSEVENT_NAMECHANGED)
1007 {
1008 if (!Sleeping)
1009 {
e07d4801
MS
1010 cupsdLogMessage(CUPSD_LOG_DEBUG,
1011 "Computer name or BTMM domains changed");
75bd9771
MS
1012
1013 /*
1014 * De-register the individual printers...
1015 */
1016
1017 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
1018 p;
1019 p = (cupsd_printer_t *)cupsArrayNext(Printers))
1020 cupsdDeregisterPrinter(p, 1);
1021
a29fd7dd 1022# if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
75bd9771 1023 /*
e07d4801 1024 * Update the computer name and BTMM domain list...
75bd9771
MS
1025 */
1026
1027 cupsdUpdateDNSSDName();
a29fd7dd 1028# endif /* HAVE_DNSSD || HAVE_AVAHI */
75bd9771
MS
1029
1030 /*
1031 * Now re-register them...
1032 */
1033
1034 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
1035 p;
1036 p = (cupsd_printer_t *)cupsArrayNext(Printers))
75bd9771 1037 cupsdRegisterPrinter(p);
75bd9771
MS
1038 }
1039 else
1040 cupsdLogMessage(CUPSD_LOG_DEBUG,
e07d4801
MS
1041 "Computer name or BTMM domains changed; ignored while "
1042 "sleeping");
75bd9771
MS
1043 }
1044 }
1045}
a4d04587 1046#endif /* __APPLE__ */
09ec0018 1047
1048
1049/*
f2d18633 1050 * End of "$Id$".
09ec0018 1051 */