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
cupsdCleanDirty(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
cupsdMarkDirty(int what)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
cupsdSetBusyState(int working)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
cupsdAllowSleep(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
cupsdStartSystemMonitor(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
cupsdStopSystemMonitor(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 */
sysEventThreadEntry(void)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
sysEventPowerNotifier(void * context,io_service_t service,natural_t messageType,void * messageArgument)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
sysEventConfigurationNotifier(SCDynamicStoreRef store,CFArrayRef changedKeys,void * context)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
sysEventTimerNotifier(CFRunLoopTimerRef timer,void * context)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
sysUpdate(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
sysUpdateNames(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__ */
1040