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