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