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