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