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