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