]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/sysman.c
61f342dcb92750a9ed0fda1a587f44d6d0e0b9bb
[thirdparty/cups.git] / scheduler / sysman.c
1 /*
2 * "$Id: sysman.c 6090 2006-11-14 16:35:27Z mike $"
3 *
4 * System management definitions for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2006 by Easy Software Products.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
19 *
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * Contents:
25 *
26 * cupsdStartSystemMonitor() - Start monitoring for system change.
27 * cupsdStopSystemMonitor() - Stop monitoring for system change.
28 * cupsdUpdateSystemMonitor() - Update the current system state.
29 * sysEventThreadEntry() - A thread to receive power and computer
30 * name change notifications.
31 * sysEventPowerNotifier() - Handle power notification events.
32 * sysEventConfigurationNotifier() - Computer name changed notification
33 * callback.
34 * sysEventTimerNotifier() - Handle delayed event notifications.
35 */
36
37
38 /*
39 * Include necessary headers...
40 */
41
42 #include "cupsd.h"
43
44
45 /*
46 * Power management is a new addition to CUPS. Right now it is only
47 * implemented on MacOS X, but essentially we use these three functions
48 * to let the OS know when it is OK to put the system to sleep, typically
49 * when we are not in the middle of printing a job.
50 *
51 * Once put to sleep, we invalidate all remote printers since it is
52 * common to wake up in a new location.
53 */
54
55 #ifdef __APPLE__
56 /*
57 * This is the Apple-specific system event code. It works by creating
58 * a worker thread that waits for events from the OS and relays them
59 * to the main thread via a traditional pipe.
60 */
61
62 /*
63 * Include MacOS-specific headers...
64 */
65
66 # include <IOKit/IOKitLib.h>
67 # include <IOKit/IOMessage.h>
68 # include <IOKit/pwr_mgt/IOPMLib.h>
69 # include <SystemConfiguration/SystemConfiguration.h>
70 # include <pthread.h>
71
72
73 /*
74 * Constants...
75 */
76
77 # define SYSEVENT_CANSLEEP 0x1 /* Decide whether to allow sleep or not */
78 # define SYSEVENT_WILLSLEEP 0x2 /* Computer will go to sleep */
79 # define SYSEVENT_WOKE 0x4 /* Computer woke from sleep */
80 # define SYSEVENT_NETCHANGED 0x8 /* Network changed */
81 # define SYSEVENT_NAMECHANGED 0x10 /* Computer name changed */
82
83
84 /*
85 * Structures...
86 */
87
88 typedef struct cupsd_sysevent_s /*** System event data ****/
89 {
90 unsigned char event; /* Event bit field */
91 io_connect_t powerKernelPort; /* Power context data */
92 long powerNotificationID; /* Power event data */
93 } cupsd_sysevent_t;
94
95
96 typedef struct cupsd_thread_data_s /*** Thread context data ****/
97 {
98 cupsd_sysevent_t sysevent; /* System event */
99 CFRunLoopTimerRef timerRef; /* Timer to delay some change *
100 * notifications */
101 } cupsd_thread_data_t;
102
103
104 /*
105 * Local globals...
106 */
107
108 static pthread_t SysEventThread = NULL;
109 /* Thread to host a runloop */
110 static pthread_mutex_t SysEventThreadMutex = { 0 };
111 /* Coordinates access to shared gloabals */
112 static pthread_cond_t SysEventThreadCond = { 0 };
113 /* Thread initialization complete condition */
114 static CFRunLoopRef SysEventRunloop = NULL;
115 /* The runloop. Access must be protected! */
116 static CFStringRef ComputerNameKey = NULL,
117 /* Computer name key */
118 NetworkGlobalKeyIPv4 = NULL,
119 /* Network global IPv4 key */
120 NetworkGlobalKeyIPv6 = NULL,
121 /* Network global IPv6 key */
122 NetworkGlobalKeyDNS = NULL,
123 /* Network global DNS key */
124 HostNamesKey = NULL,
125 /* Host name key */
126 NetworkInterfaceKeyIPv4 = NULL,
127 /* Netowrk interface key */
128 NetworkInterfaceKeyIPv6 = NULL;
129 /* Netowrk interface key */
130
131
132 /*
133 * Local functions...
134 */
135
136 static void *sysEventThreadEntry(void);
137 static void sysEventPowerNotifier(void *context, io_service_t service,
138 natural_t messageType,
139 void *messageArgument);
140 static void sysEventConfigurationNotifier(SCDynamicStoreRef store,
141 CFArrayRef changedKeys,
142 void *context);
143 static void sysEventTimerNotifier(CFRunLoopTimerRef timer, void *context);
144
145
146 /*
147 * 'cupsdStartSystemMonitor()' - Start monitoring for system change.
148 */
149
150 void
151 cupsdStartSystemMonitor(void)
152 {
153 int flags; /* fcntl flags on pipe */
154
155
156 if (cupsdOpenPipe(SysEventPipes))
157 {
158 cupsdLogMessage(CUPSD_LOG_ERROR, "System event monitor pipe() failed - %s!",
159 strerror(errno));
160 return;
161 }
162
163 cupsdLogMessage(CUPSD_LOG_DEBUG2,
164 "cupsdStartSystemMonitor: Adding fd %d to InputSet...",
165 SysEventPipes[0]);
166 FD_SET(SysEventPipes[0], InputSet);
167
168 /*
169 * Set non-blocking mode on the descriptor we will be receiving notification
170 * events on.
171 */
172
173 flags = fcntl(SysEventPipes[0], F_GETFL, 0);
174 fcntl(SysEventPipes[0], F_SETFL, flags | O_NONBLOCK);
175
176 /*
177 * Start the thread that runs the runloop...
178 */
179
180 pthread_mutex_init(&SysEventThreadMutex, NULL);
181 pthread_cond_init(&SysEventThreadCond, NULL);
182 pthread_create(&SysEventThread, NULL, (void *(*)())sysEventThreadEntry, NULL);
183 }
184
185
186 /*
187 * 'cupsdStopSystemMonitor()' - Stop monitoring for system change.
188 */
189
190 void
191 cupsdStopSystemMonitor(void)
192 {
193 CFRunLoopRef rl; /* The event handler runloop */
194
195
196 if (SysEventThread)
197 {
198 /*
199 * Make sure the thread has completed it's initialization and
200 * stored it's runloop reference in the shared global.
201 */
202
203 pthread_mutex_lock(&SysEventThreadMutex);
204
205 if (!SysEventRunloop)
206 pthread_cond_wait(&SysEventThreadCond, &SysEventThreadMutex);
207
208 rl = SysEventRunloop;
209 SysEventRunloop = NULL;
210
211 pthread_mutex_unlock(&SysEventThreadMutex);
212
213 if (rl)
214 CFRunLoopStop(rl);
215
216 pthread_join(SysEventThread, NULL);
217 pthread_mutex_destroy(&SysEventThreadMutex);
218 pthread_cond_destroy(&SysEventThreadCond);
219 }
220
221 if (SysEventPipes[0] >= 0)
222 {
223 cupsdLogMessage(CUPSD_LOG_DEBUG2,
224 "cupsdStopSystemMonitor: Removing fd %d from InputSet...",
225 SysEventPipes[0]);
226
227 FD_CLR(SysEventPipes[0], InputSet);
228
229 cupsdClosePipe(SysEventPipes);
230 }
231 }
232
233
234 /*
235 * 'cupsdUpdateSystemMonitor()' - Update the current system state.
236 */
237
238 void
239 cupsdUpdateSystemMonitor(void)
240 {
241 int i; /* Looping var */
242 cupsd_sysevent_t sysevent; /* The system event */
243 cupsd_printer_t *p; /* Printer information */
244
245
246 /*
247 * Drain the event pipe...
248 */
249
250 while (read((int)SysEventPipes[0], &sysevent, sizeof(sysevent))
251 == sizeof(sysevent))
252 {
253 if (sysevent.event & SYSEVENT_CANSLEEP)
254 {
255 /*
256 * If there are active printers that don't have the connecting-to-device
257 * printer-state-reason then cancel the sleep request (i.e. this reason
258 * indicates a job that is not yet connected to the printer)...
259 */
260
261 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
262 p;
263 p = (cupsd_printer_t *)cupsArrayNext(Printers))
264 {
265 if (p->job)
266 {
267 for (i = 0; i < p->num_reasons; i ++)
268 if (!strcmp(p->reasons[i], "connecting-to-device"))
269 break;
270
271 if (!p->num_reasons || i >= p->num_reasons)
272 break;
273 }
274 }
275
276 if (p)
277 {
278 cupsdLogMessage(CUPSD_LOG_INFO,
279 "System sleep canceled because printer %s is active",
280 p->name);
281 IOCancelPowerChange(sysevent.powerKernelPort,
282 sysevent.powerNotificationID);
283 }
284 else
285 {
286 cupsdLogMessage(CUPSD_LOG_DEBUG, "System wants to sleep");
287 IOAllowPowerChange(sysevent.powerKernelPort,
288 sysevent.powerNotificationID);
289 }
290 }
291
292 if (sysevent.event & SYSEVENT_WILLSLEEP)
293 {
294 cupsdLogMessage(CUPSD_LOG_DEBUG, "System going to sleep");
295
296 Sleeping = 1;
297
298 cupsdStopAllJobs(0);
299 cupsdSaveAllJobs();
300
301 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
302 p;
303 p = (cupsd_printer_t *)cupsArrayNext(Printers))
304 {
305 if (p->type & CUPS_PRINTER_REMOTE)
306 {
307 cupsdLogMessage(CUPSD_LOG_DEBUG,
308 "Deleting remote destination \"%s\"", p->name);
309 cupsArraySave(Printers);
310 cupsdDeletePrinter(p, 0);
311 cupsArrayRestore(Printers);
312 }
313 else
314 {
315 cupsdLogMessage(CUPSD_LOG_DEBUG,
316 "Deregistering local printer \"%s\"", p->name);
317 cupsdSendBrowseDelete(p);
318 }
319 }
320
321 IOAllowPowerChange(sysevent.powerKernelPort,
322 sysevent.powerNotificationID);
323 }
324
325 if (sysevent.event & SYSEVENT_WOKE)
326 {
327 cupsdLogMessage(CUPSD_LOG_DEBUG, "System woke from sleep");
328 IOAllowPowerChange(sysevent.powerKernelPort,
329 sysevent.powerNotificationID);
330 Sleeping = 0;
331 cupsdCheckJobs();
332 }
333
334 if (sysevent.event & SYSEVENT_NETCHANGED)
335 {
336 if (!Sleeping)
337 {
338 cupsdLogMessage(CUPSD_LOG_DEBUG,
339 "System network configuration changed");
340
341 /*
342 * Resetting browse_time before calling cupsdSendBrowseList causes
343 * browse packets to be sent for local shared printers.
344 */
345
346 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
347 p;
348 p = (cupsd_printer_t *)cupsArrayNext(Printers))
349 p->browse_time = 0;
350
351 cupsdSendBrowseList();
352 cupsdRestartPolling();
353 }
354 else
355 cupsdLogMessage(CUPSD_LOG_DEBUG,
356 "System network configuration changed; "
357 "ignored while sleeping");
358 }
359
360 if (sysevent.event & SYSEVENT_NAMECHANGED)
361 {
362 if (!Sleeping)
363 {
364 cupsdLogMessage(CUPSD_LOG_DEBUG, "Computer name changed");
365
366 /*
367 * De-register the individual printers...
368 */
369
370 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
371 p;
372 p = (cupsd_printer_t *)cupsArrayNext(Printers))
373 cupsdSendBrowseDelete(p);
374
375 /*
376 * Now re-register them...
377 *
378 * TODO: This might need updating for MDNS.
379 */
380
381 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
382 p;
383 p = (cupsd_printer_t *)cupsArrayNext(Printers))
384 p->browse_time = 0;
385 }
386 else
387 cupsdLogMessage(CUPSD_LOG_DEBUG,
388 "Computer name changed; ignored while sleeping");
389 }
390 }
391 }
392
393
394 /*
395 * 'sysEventThreadEntry()' - A thread to receive power and computer name
396 * change notifications.
397 */
398
399 static void * /* O - Return status/value */
400 sysEventThreadEntry(void)
401 {
402 io_object_t powerNotifierObj;
403 /* Power notifier object */
404 IONotificationPortRef powerNotifierPort;
405 /* Power notifier port */
406 SCDynamicStoreRef store = NULL;/* System Config dynamic store */
407 CFRunLoopSourceRef powerRLS = NULL,/* Power runloop source */
408 storeRLS = NULL;/* System Config runloop source */
409 CFStringRef key[5], /* System Config keys */
410 pattern[2]; /* System Config patterns */
411 CFArrayRef keys = NULL, /* System Config key array*/
412 patterns = NULL;/* System Config pattern array */
413 SCDynamicStoreContext storeContext; /* Dynamic store context */
414 CFRunLoopTimerContext timerContext; /* Timer context */
415 cupsd_thread_data_t threadData; /* Thread context data for the *
416 * runloop notifiers */
417
418
419 /*
420 * Register for power state change notifications
421 */
422
423 bzero(&threadData, sizeof(threadData));
424
425 threadData.sysevent.powerKernelPort =
426 IORegisterForSystemPower(&threadData, &powerNotifierPort,
427 sysEventPowerNotifier, &powerNotifierObj);
428
429 if (threadData.sysevent.powerKernelPort)
430 {
431 powerRLS = IONotificationPortGetRunLoopSource(powerNotifierPort);
432 CFRunLoopAddSource(CFRunLoopGetCurrent(), powerRLS, kCFRunLoopDefaultMode);
433 }
434 else
435 DEBUG_puts("sysEventThreadEntry: error registering for system power "
436 "notifications");
437
438 /*
439 * Register for system configuration change notifications
440 */
441
442 bzero(&storeContext, sizeof(storeContext));
443 storeContext.info = &threadData;
444
445 store = SCDynamicStoreCreate(NULL, CFSTR("cupsd"),
446 sysEventConfigurationNotifier, &storeContext);
447
448 if (!ComputerNameKey)
449 ComputerNameKey = SCDynamicStoreKeyCreateComputerName(NULL);
450
451 if (!NetworkGlobalKeyIPv4)
452 NetworkGlobalKeyIPv4 =
453 SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
454 kSCDynamicStoreDomainState,
455 kSCEntNetIPv4);
456
457 if (!NetworkGlobalKeyIPv6)
458 NetworkGlobalKeyIPv6 =
459 SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
460 kSCDynamicStoreDomainState,
461 kSCEntNetIPv6);
462
463 if (!NetworkGlobalKeyDNS)
464 NetworkGlobalKeyDNS =
465 SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
466 kSCDynamicStoreDomainState,
467 kSCEntNetDNS);
468
469 if (!HostNamesKey)
470 HostNamesKey = SCDynamicStoreKeyCreateHostNames(NULL);
471
472 if (!NetworkInterfaceKeyIPv4)
473 NetworkInterfaceKeyIPv4 =
474 SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
475 kSCDynamicStoreDomainState,
476 kSCCompAnyRegex,
477 kSCEntNetIPv4);
478
479 if (!NetworkInterfaceKeyIPv6)
480 NetworkInterfaceKeyIPv6 =
481 SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
482 kSCDynamicStoreDomainState,
483 kSCCompAnyRegex,
484 kSCEntNetIPv6);
485
486 if (store && ComputerNameKey && HostNamesKey &&
487 NetworkGlobalKeyIPv4 && NetworkGlobalKeyIPv6 && NetworkGlobalKeyDNS &&
488 NetworkInterfaceKeyIPv4 && NetworkInterfaceKeyIPv6)
489 {
490 key[0] = ComputerNameKey;
491 key[1] = NetworkGlobalKeyIPv4;
492 key[2] = NetworkGlobalKeyIPv6;
493 key[3] = NetworkGlobalKeyDNS;
494 key[4] = HostNamesKey;
495
496 pattern[0] = NetworkInterfaceKeyIPv4;
497 pattern[1] = NetworkInterfaceKeyIPv6;
498
499 keys = CFArrayCreate(NULL, (const void **)key,
500 sizeof(key) / sizeof(key[0]),
501 &kCFTypeArrayCallBacks);
502
503 patterns = CFArrayCreate(NULL, (const void **)pattern,
504 sizeof(pattern) / sizeof(pattern[0]),
505 &kCFTypeArrayCallBacks);
506
507 if (keys && patterns &&
508 SCDynamicStoreSetNotificationKeys(store, keys, patterns))
509 {
510 if ((storeRLS = SCDynamicStoreCreateRunLoopSource(NULL, store, 0))
511 != NULL)
512 {
513 CFRunLoopAddSource(CFRunLoopGetCurrent(), storeRLS,
514 kCFRunLoopDefaultMode);
515 }
516 else
517 DEBUG_printf(("sysEventThreadEntry: SCDynamicStoreCreateRunLoopSource "
518 "failed: %s\n", SCErrorString(SCError())));
519 }
520 else
521 DEBUG_printf(("sysEventThreadEntry: SCDynamicStoreSetNotificationKeys "
522 "failed: %s\n", SCErrorString(SCError())));
523 }
524 else
525 DEBUG_printf(("sysEventThreadEntry: SCDynamicStoreCreate failed: %s\n",
526 SCErrorString(SCError())));
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 bzero(&timerContext, sizeof(timerContext));
542 timerContext.info = &threadData;
543
544 threadData.timerRef =
545 CFRunLoopTimerCreate(NULL,
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
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 break;
636
637 case kIOMessageSystemHasPoweredOn:
638 /*
639 * Because powered on is followed by a net-changed event, delay
640 * before sending it.
641 */
642
643 sendit = 2;
644 threadData->sysevent.event |= SYSEVENT_WOKE;
645 break;
646
647 case kIOMessageSystemWillNotPowerOff:
648 case kIOMessageSystemWillNotSleep:
649 #ifdef kIOMessageSystemWillPowerOn
650 case kIOMessageSystemWillPowerOn:
651 #endif /* kIOMessageSystemWillPowerOn */
652 default:
653 sendit = 0;
654 break;
655 }
656
657 if (sendit == 0)
658 IOAllowPowerChange(threadData->sysevent.powerKernelPort,
659 (long)messageArgument);
660 else
661 {
662 threadData->sysevent.powerNotificationID = (long)messageArgument;
663
664 if (sendit == 1)
665 {
666 /*
667 * Send the event to the main thread now.
668 */
669
670 write(SysEventPipes[1], &threadData->sysevent,
671 sizeof(threadData->sysevent));
672 threadData->sysevent.event = 0;
673 }
674 else
675 {
676 /*
677 * Send the event to the main thread after 1 to 2 seconds.
678 */
679
680 CFRunLoopTimerSetNextFireDate(threadData->timerRef,
681 CFAbsoluteTimeGetCurrent() + 2);
682 }
683 }
684 }
685
686
687 /*
688 * 'sysEventConfigurationNotifier()' - Computer name changed notification
689 * callback.
690 */
691
692 static void
693 sysEventConfigurationNotifier(
694 SCDynamicStoreRef store, /* I - System data (unused) */
695 CFArrayRef changedKeys, /* I - Changed data */
696 void *context) /* I - Thread context data */
697 {
698 cupsd_thread_data_t *threadData; /* Thread context data */
699
700
701 threadData = (cupsd_thread_data_t *)context;
702
703 (void)store; /* anti-compiler-warning-code */
704
705 CFRange range = CFRangeMake(0, CFArrayGetCount(changedKeys));
706
707 if (CFArrayContainsValue(changedKeys, range, ComputerNameKey))
708 threadData->sysevent.event |= SYSEVENT_NAMECHANGED;
709 else
710 {
711 threadData->sysevent.event |= SYSEVENT_NETCHANGED;
712
713 /*
714 * Indicate the network interface list needs updating...
715 */
716
717 NetIFUpdate = 1;
718 }
719
720 /*
721 * Because we registered for several different kinds of change notifications
722 * this callback usually gets called several times in a row. We use a timer to
723 * de-bounce these so we only end up generating one event for the main thread.
724 */
725
726 CFRunLoopTimerSetNextFireDate(threadData->timerRef,
727 CFAbsoluteTimeGetCurrent() + 5);
728 }
729
730
731 /*
732 * 'sysEventTimerNotifier()' - Handle delayed event notifications.
733 */
734
735 static void
736 sysEventTimerNotifier(
737 CFRunLoopTimerRef timer, /* I - Timer information */
738 void *context) /* I - Thread context data */
739 {
740 cupsd_thread_data_t *threadData; /* Thread context data */
741
742
743 threadData = (cupsd_thread_data_t *)context;
744
745 /*
746 * If an event is still pending send it to the main thread.
747 */
748
749 if (threadData->sysevent.event)
750 {
751 write(SysEventPipes[1], &threadData->sysevent,
752 sizeof(threadData->sysevent));
753 threadData->sysevent.event = 0;
754 }
755 }
756 #endif /* __APPLE__ */
757
758
759 /*
760 * End of "$Id: sysman.c 6090 2006-11-14 16:35:27Z mike $".
761 */