Bump to cups 2.3.3
[platform/upstream/cups.git] / scheduler / sysman.c
1 /*
2  * System management functions for the CUPS scheduler.
3  *
4  * Copyright 2007-2018 by Apple Inc.
5  * Copyright 2006 by Easy Software Products.
6  *
7  * Licensed under Apache License v2.0.  See the file "LICENSE" for more information.
8  */
9
10
11 /*
12  * Include necessary headers...
13  */
14
15 #include "cupsd.h"
16 #ifdef __APPLE__
17 #  include <IOKit/pwr_mgt/IOPMLib.h>
18 #endif /* __APPLE__ */
19
20
21 /*
22  * The system management functions cover disk and power management which
23  * are primarily used for portable computers.
24  *
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
27  * up or flash to be written to.
28  *
29  * Power management support is currently only implemented on macOS, but
30  * essentially we use four functions to let the OS know when it is OK to
31  * put the system to sleep, typically when we are not in the middle of
32  * printing a job.  And on macOS we can also "sleep print" - basically the
33  * system only wakes up long enough to service network requests and process
34  * print jobs.
35  */
36
37
38 /*
39  * 'cupsdCleanDirty()' - Write dirty config and state files.
40  */
41
42 void
43 cupsdCleanDirty(void)
44 {
45   if (DirtyFiles & CUPSD_DIRTY_PRINTERS)
46     cupsdSaveAllPrinters();
47
48   if (DirtyFiles & CUPSD_DIRTY_CLASSES)
49     cupsdSaveAllClasses();
50
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;
72
73   cupsdSetBusyState(0);
74 }
75
76
77 /*
78  * 'cupsdMarkDirty()' - Mark config or state files as needing a write.
79  */
80
81 void
82 cupsdMarkDirty(int what)                /* I - What file(s) are dirty? */
83 {
84   cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdMarkDirty(%c%c%c%c%c)",
85                   (what & CUPSD_DIRTY_PRINTERS) ? 'P' : '-',
86                   (what & CUPSD_DIRTY_CLASSES) ? 'C' : '-',
87                   (what & CUPSD_DIRTY_PRINTCAP) ? 'p' : '-',
88                   (what & CUPSD_DIRTY_JOBS) ? 'J' : '-',
89                   (what & CUPSD_DIRTY_SUBSCRIPTIONS) ? 'S' : '-');
90
91   if (what == CUPSD_DIRTY_PRINTCAP && !Printcap)
92     return;
93
94   DirtyFiles |= what;
95
96   if (!DirtyCleanTime)
97     DirtyCleanTime = time(NULL) + DirtyCleanInterval;
98
99   cupsdSetBusyState(0);
100 }
101
102
103 /*
104  * 'cupsdSetBusyState()' - Let the system know when we are busy doing something.
105  */
106
107 void
108 cupsdSetBusyState(int working)          /* I - Doing significant work? */
109 {
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 */
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   };
126 #ifdef __APPLE__
127   static IOPMAssertionID keep_awake = 0;/* Keep the system awake while printing */
128 #endif /* __APPLE__ */
129
130
131  /*
132   * Figure out how busy we are...
133   */
134
135   newbusy = (DirtyCleanTime ? 1 : 0) |
136             ((working || cupsArrayCount(ActiveClients) > 0) ? 4 : 0);
137
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
156   cupsdLogMessage(CUPSD_LOG_DEBUG,
157                   "cupsdSetBusyState: newbusy=\"%s\", busy=\"%s\"",
158                   busy_text[newbusy], busy_text[busy]);
159
160  /*
161   * Manage state changes...
162   */
163
164   if (newbusy != busy)
165     busy = newbusy;
166
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);
175   }
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__ */
183 }
184
185
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
197 #  include <notify.h>
198 #  include <IOKit/IOKitLib.h>
199 #  include <IOKit/IOMessage.h>
200 #  include <IOKit/ps/IOPowerSources.h>
201 #  include <IOKit/pwr_mgt/IOPMLib.h>
202 #  include <SystemConfiguration/SystemConfiguration.h>
203 #  include <pthread.h>
204
205
206 /*
207  * Constants...
208  */
209
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 */
215
216
217 /*
218  * Structures...
219  */
220
221 typedef 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
229 typedef 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
237 /*
238  * Local globals...
239  */
240
241 static pthread_t        SysEventThread = NULL;
242                                         /* Thread to host a runloop */
243 static pthread_mutex_t  SysEventThreadMutex = { 0 };
244                                         /* Coordinates access to shared gloabals */
245 static pthread_cond_t   SysEventThreadCond = { 0 };
246                                         /* Thread initialization complete condition */
247 static CFRunLoopRef     SysEventRunloop = NULL;
248                                         /* The runloop. Access must be protected! */
249 static CFStringRef      ComputerNameKey = NULL,
250                                         /* Computer name key */
251                         BTMMKey = NULL, /* Back to My Mac key */
252                         NetworkGlobalKeyIPv4 = NULL,
253                                         /* Network global IPv4 key */
254                         NetworkGlobalKeyIPv6 = NULL,
255                                         /* Network global IPv6 key */
256                         NetworkGlobalKeyDNS = NULL,
257                                         /* Network global DNS key */
258                         HostNamesKey = NULL,
259                                         /* Host name key */
260                         NetworkInterfaceKeyIPv4 = NULL,
261                                         /* Netowrk interface key */
262                         NetworkInterfaceKeyIPv6 = NULL;
263                                         /* Netowrk interface key */
264 static cupsd_sysevent_t LastSysEvent;   /* Last system event (for delayed sleep) */
265 static int              NameChanged = 0;/* Did we get a 'name changed' event during sleep? */
266 static int              PSToken = 0;    /* Power source notifications */
267
268
269 /*
270  * Local functions...
271  */
272
273 static void     *sysEventThreadEntry(void);
274 static void     sysEventPowerNotifier(void *context, io_service_t service,
275                                       natural_t messageType,
276                                       void *messageArgument);
277 static void     sysEventConfigurationNotifier(SCDynamicStoreRef store,
278                                               CFArrayRef changedKeys,
279                                               void *context);
280 static void     sysEventTimerNotifier(CFRunLoopTimerRef timer, void *context);
281 static void     sysUpdate(void);
282 static void     sysUpdateNames(void);
283
284
285 /*
286  * 'cupsdAllowSleep()' - Tell the OS it is now OK to sleep.
287  */
288
289 void
290 cupsdAllowSleep(void)
291 {
292   cupsdCleanDirty();
293
294   cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep.");
295   IOAllowPowerChange(LastSysEvent.powerKernelPort,
296                      LastSysEvent.powerNotificationID);
297 }
298
299
300 /*
301  * 'cupsdStartSystemMonitor()' - Start monitoring for system change.
302  */
303
304 void
305 cupsdStartSystemMonitor(void)
306 {
307   int   flags;                          /* fcntl flags on pipe */
308
309
310   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartSystemMonitor()");
311
312   if (cupsdOpenPipe(SysEventPipes))
313   {
314     cupsdLogMessage(CUPSD_LOG_ERROR, "System event monitor pipe() failed - %s!",
315                     strerror(errno));
316     return;
317   }
318
319   cupsdAddSelect(SysEventPipes[0], (cupsd_selfunc_t)sysUpdate, NULL, NULL);
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);
335   pthread_create(&SysEventThread, NULL, (void *(*)(void *))sysEventThreadEntry, NULL);
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;
343   notify_register_dispatch(kIOPSNotifyPowerSource, &PSToken, dispatch_get_main_queue(), ^(int t) { (void)t;
344       ACPower = IOPSGetTimeRemainingEstimate() == kIOPSTimeRemainingUnlimited; });
345 }
346
347
348 /*
349  * 'cupsdStopSystemMonitor()' - Stop monitoring for system change.
350  */
351
352 void
353 cupsdStopSystemMonitor(void)
354 {
355   CFRunLoopRef  rl;                     /* The event handler runloop */
356
357
358   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStopSystemMonitor()");
359
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   {
387     cupsdRemoveSelect(SysEventPipes[0]);
388     cupsdClosePipe(SysEventPipes);
389   }
390
391   if (PSToken != 0)
392   {
393     notify_cancel(PSToken);
394     PSToken = 0;
395   }
396 }
397
398
399 /*
400  * 'sysEventThreadEntry()' - A thread to receive power and computer name
401  *                           change notifications.
402  */
403
404 static void *                           /* O - Return status/value */
405 sysEventThreadEntry(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 */
414   CFStringRef           key[6],         /* System Config keys */
415                         pattern[2];     /* System Config patterns */
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
440  /*
441   * Register for system configuration change notifications
442   */
443
444   bzero(&storeContext, sizeof(storeContext));
445   storeContext.info = &threadData;
446
447   store = SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("cupsd"),
448                                sysEventConfigurationNotifier, &storeContext);
449
450   if (!ComputerNameKey)
451     ComputerNameKey = SCDynamicStoreKeyCreateComputerName(kCFAllocatorDefault);
452
453   if (!BTMMKey)
454     BTMMKey = SCDynamicStoreKeyCreate(kCFAllocatorDefault,
455                                       CFSTR("Setup:/Network/BackToMyMac"));
456
457   if (!NetworkGlobalKeyIPv4)
458     NetworkGlobalKeyIPv4 =
459         SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault,
460                                                    kSCDynamicStoreDomainState,
461                                                    kSCEntNetIPv4);
462
463   if (!NetworkGlobalKeyIPv6)
464     NetworkGlobalKeyIPv6 =
465         SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault,
466                                                    kSCDynamicStoreDomainState,
467                                                    kSCEntNetIPv6);
468
469   if (!NetworkGlobalKeyDNS)
470     NetworkGlobalKeyDNS =
471         SCDynamicStoreKeyCreateNetworkGlobalEntity(kCFAllocatorDefault,
472                                                    kSCDynamicStoreDomainState,
473                                                    kSCEntNetDNS);
474
475   if (!HostNamesKey)
476     HostNamesKey = SCDynamicStoreKeyCreateHostNames(kCFAllocatorDefault);
477
478   if (!NetworkInterfaceKeyIPv4)
479     NetworkInterfaceKeyIPv4 =
480         SCDynamicStoreKeyCreateNetworkInterfaceEntity(kCFAllocatorDefault,
481                                                       kSCDynamicStoreDomainState,
482                                                       kSCCompAnyRegex,
483                                                       kSCEntNetIPv4);
484
485   if (!NetworkInterfaceKeyIPv6)
486     NetworkInterfaceKeyIPv6 =
487         SCDynamicStoreKeyCreateNetworkInterfaceEntity(kCFAllocatorDefault,
488                                                       kSCDynamicStoreDomainState,
489                                                       kSCCompAnyRegex,
490                                                       kSCEntNetIPv6);
491
492   if (store && ComputerNameKey && HostNamesKey &&
493       NetworkGlobalKeyIPv4 && NetworkGlobalKeyIPv6 && NetworkGlobalKeyDNS &&
494       NetworkInterfaceKeyIPv4 && NetworkInterfaceKeyIPv6)
495   {
496     key[0]     = ComputerNameKey;
497     key[1]     = BTMMKey;
498     key[2]     = NetworkGlobalKeyIPv4;
499     key[3]     = NetworkGlobalKeyIPv6;
500     key[4]     = NetworkGlobalKeyDNS;
501     key[5]     = HostNamesKey;
502
503     pattern[0] = NetworkInterfaceKeyIPv4;
504     pattern[1] = NetworkInterfaceKeyIPv6;
505
506     keys     = CFArrayCreate(kCFAllocatorDefault, (const void **)key,
507                              sizeof(key) / sizeof(key[0]),
508                              &kCFTypeArrayCallBacks);
509
510     patterns = CFArrayCreate(kCFAllocatorDefault, (const void **)pattern,
511                              sizeof(pattern) / sizeof(pattern[0]),
512                              &kCFTypeArrayCallBacks);
513
514     if (keys && patterns &&
515         SCDynamicStoreSetNotificationKeys(store, keys, patterns))
516     {
517       if ((storeRLS = SCDynamicStoreCreateRunLoopSource(kCFAllocatorDefault,
518                                                         store, 0)) != NULL)
519       {
520         CFRunLoopAddSource(CFRunLoopGetCurrent(), storeRLS,
521                            kCFRunLoopDefaultMode);
522       }
523     }
524   }
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 =
543       CFRunLoopTimerCreate(kCFAllocatorDefault,
544                            CFAbsoluteTimeGetCurrent() + (86400L * 365L * 10L),
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
606 static void
607 sysEventPowerNotifier(
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   {
624     case kIOMessageCanSystemPowerOff :
625     case kIOMessageCanSystemSleep :
626         threadData->sysevent.event |= SYSEVENT_CANSLEEP;
627         break;
628
629     case kIOMessageSystemWillRestart :
630     case kIOMessageSystemWillPowerOff :
631     case kIOMessageSystemWillSleep :
632         threadData->sysevent.event |= SYSEVENT_WILLSLEEP;
633         threadData->sysevent.event &= ~SYSEVENT_WOKE;
634         break;
635
636     case kIOMessageSystemHasPoweredOn :
637        /*
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
646     case kIOMessageSystemWillNotPowerOff :
647     case kIOMessageSystemWillNotSleep :
648 #  ifdef kIOMessageSystemWillPowerOn
649     case kIOMessageSystemWillPowerOn :
650 #  endif /* kIOMessageSystemWillPowerOn */
651     default:
652         sendit = 0;
653         break;
654   }
655
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
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     {
711      /*
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     {
721      /*
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 /*
733  * 'sysEventConfigurationNotifier()' - Network configuration change notification
734  *                                     callback.
735  */
736
737 static void
738 sysEventConfigurationNotifier(
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;
747
748   (void)store;                          /* anti-compiler-warning-code */
749
750   CFRange range = CFRangeMake(0, CFArrayGetCount(changedKeys));
751
752   if (CFArrayContainsValue(changedKeys, range, ComputerNameKey) ||
753       CFArrayContainsValue(changedKeys, range, BTMMKey))
754     threadData->sysevent.event |= SYSEVENT_NAMECHANGED;
755   else
756   {
757     threadData->sysevent.event |= SYSEVENT_NETCHANGED;
758
759    /*
760     * Indicate the network interface list needs updating...
761     */
762
763     NetIFUpdate = 1;
764   }
765
766  /*
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
769   * de-bounce these so we only end up generating one event for the main thread.
770   */
771
772   CFRunLoopTimerSetNextFireDate(threadData->timerRef,
773                                 CFAbsoluteTimeGetCurrent() + 5);
774 }
775
776
777 /*
778  * 'sysEventTimerNotifier()' - Handle delayed event notifications.
779  */
780
781 static void
782 sysEventTimerNotifier(
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
789   (void)timer;
790
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 }
804
805
806 /*
807  * 'sysUpdate()' - Update the current system state.
808  */
809
810 static void
811 sysUpdate(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
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...
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 ++)
841             if (!strcmp(p->reasons[i], "connecting-to-device") ||
842                 !strcmp(p->reasons[i], "cups-waiting-for-job-completed"))
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,
853                         "System sleep canceled because printer %s is active.",
854                         p->name);
855         IOCancelPowerChange(sysevent.powerKernelPort,
856                             sysevent.powerNotificationID);
857       }
858       else
859       {
860         cupsdLogMessage(CUPSD_LOG_DEBUG, "System wants to sleep.");
861         IOAllowPowerChange(sysevent.powerKernelPort,
862                            sysevent.powerNotificationID);
863       }
864     }
865
866     if (sysevent.event & SYSEVENT_WILLSLEEP)
867     {
868       cupsdLogMessage(CUPSD_LOG_DEBUG, "System going to sleep.");
869
870       Sleeping = 1;
871
872       cupsdCleanDirty();
873
874      /*
875       * If we have no printing jobs, allow the power change immediately.
876       * Otherwise set the SleepJobs time to 10 seconds in the future when
877       * we'll take more drastic measures...
878       */
879
880       if (cupsArrayCount(PrintingJobs) == 0)
881       {
882         cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep.");
883         IOAllowPowerChange(sysevent.powerKernelPort,
884                            sysevent.powerNotificationID);
885       }
886       else
887       {
888        /*
889         * If there are active printers that don't have the connecting-to-device
890         * or cups-waiting-for-job-completed printer-state-reasons then delay the
891         * sleep request, i.e., these reasons indicate a job is active...
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 ++)
901               if (!strcmp(p->reasons[i], "connecting-to-device") ||
902                   !strcmp(p->reasons[i], "cups-waiting-for-job-completed"))
903                 break;
904
905             if (!p->num_reasons || i >= p->num_reasons)
906               break;
907           }
908         }
909
910         if (p)
911         {
912           cupsdLogMessage(CUPSD_LOG_INFO,
913                           "System sleep delayed because printer %s is active.",
914                           p->name);
915
916           LastSysEvent = sysevent;
917           SleepJobs    = time(NULL) + 10;
918         }
919         else
920         {
921           cupsdLogMessage(CUPSD_LOG_DEBUG, "Allowing system sleep.");
922           IOAllowPowerChange(sysevent.powerKernelPort,
923                              sysevent.powerNotificationID);
924         }
925       }
926     }
927
928     if (sysevent.event & SYSEVENT_WOKE)
929     {
930       cupsdLogMessage(CUPSD_LOG_DEBUG, "System woke from sleep.");
931       IOAllowPowerChange(sysevent.powerKernelPort,
932                          sysevent.powerNotificationID);
933       Sleeping = 0;
934
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
962       if (NameChanged)
963         sysUpdateNames();
964
965       cupsdCheckJobs();
966     }
967
968     if (sysevent.event & SYSEVENT_NETCHANGED)
969     {
970       if (Sleeping)
971         cupsdLogMessage(CUPSD_LOG_DEBUG,
972                         "System network configuration changed - "
973                         "ignored while sleeping.");
974       else
975         cupsdLogMessage(CUPSD_LOG_DEBUG,
976                         "System network configuration changed.");
977     }
978
979     if (sysevent.event & SYSEVENT_NAMECHANGED)
980     {
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
990       {
991         cupsdLogMessage(CUPSD_LOG_DEBUG,
992                         "Computer name or BTMM domains changed.");
993
994         sysUpdateNames();
995       }
996     }
997   }
998 }
999
1000
1001 /*
1002  * 'sysUpdateNames()' - Update computer and/or BTMM domains.
1003  */
1004
1005 static void
1006 sysUpdateNames(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);
1021
1022 #  if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
1023  /*
1024   * Update the computer name and BTMM domain list...
1025   */
1026
1027   cupsdUpdateDNSSDName();
1028 #  endif /* HAVE_DNSSD || HAVE_AVAHI */
1029
1030  /*
1031   * Now re-register them...
1032   */
1033
1034   for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
1035        p;
1036        p = (cupsd_printer_t *)cupsArrayNext(Printers))
1037     cupsdRegisterPrinter(p);
1038 }
1039 #endif  /* __APPLE__ */