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