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