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