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