]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/sysman.c
Load cups into easysw/current.
[thirdparty/cups.git] / scheduler / sysman.c
1 /*
2 * "$Id: sysman.c 6291 2007-02-19 21:54: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 cupsdAddSelect(SysEventPipes[0], (cupsd_selfunc_t)cupsdUpdateSystemMonitor,
164 NULL, NULL);
165
166 /*
167 * Set non-blocking mode on the descriptor we will be receiving notification
168 * events on.
169 */
170
171 flags = fcntl(SysEventPipes[0], F_GETFL, 0);
172 fcntl(SysEventPipes[0], F_SETFL, flags | O_NONBLOCK);
173
174 /*
175 * Start the thread that runs the runloop...
176 */
177
178 pthread_mutex_init(&SysEventThreadMutex, NULL);
179 pthread_cond_init(&SysEventThreadCond, NULL);
180 pthread_create(&SysEventThread, NULL, (void *(*)())sysEventThreadEntry, NULL);
181 }
182
183
184 /*
185 * 'cupsdStopSystemMonitor()' - Stop monitoring for system change.
186 */
187
188 void
189 cupsdStopSystemMonitor(void)
190 {
191 CFRunLoopRef rl; /* The event handler runloop */
192
193
194 if (SysEventThread)
195 {
196 /*
197 * Make sure the thread has completed it's initialization and
198 * stored it's runloop reference in the shared global.
199 */
200
201 pthread_mutex_lock(&SysEventThreadMutex);
202
203 if (!SysEventRunloop)
204 pthread_cond_wait(&SysEventThreadCond, &SysEventThreadMutex);
205
206 rl = SysEventRunloop;
207 SysEventRunloop = NULL;
208
209 pthread_mutex_unlock(&SysEventThreadMutex);
210
211 if (rl)
212 CFRunLoopStop(rl);
213
214 pthread_join(SysEventThread, NULL);
215 pthread_mutex_destroy(&SysEventThreadMutex);
216 pthread_cond_destroy(&SysEventThreadCond);
217 }
218
219 if (SysEventPipes[0] >= 0)
220 {
221 cupsdRemoveSelect(SysEventPipes[0]);
222 cupsdClosePipe(SysEventPipes);
223 }
224 }
225
226
227 /*
228 * 'cupsdUpdateSystemMonitor()' - Update the current system state.
229 */
230
231 void
232 cupsdUpdateSystemMonitor(void)
233 {
234 int i; /* Looping var */
235 cupsd_sysevent_t sysevent; /* The system event */
236 cupsd_printer_t *p; /* Printer information */
237
238
239 /*
240 * Drain the event pipe...
241 */
242
243 while (read((int)SysEventPipes[0], &sysevent, sizeof(sysevent))
244 == sizeof(sysevent))
245 {
246 if (sysevent.event & SYSEVENT_CANSLEEP)
247 {
248 /*
249 * If there are active printers that don't have the connecting-to-device
250 * printer-state-reason then cancel the sleep request (i.e. this reason
251 * indicates a job that is not yet connected to the printer)...
252 */
253
254 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
255 p;
256 p = (cupsd_printer_t *)cupsArrayNext(Printers))
257 {
258 if (p->job)
259 {
260 for (i = 0; i < p->num_reasons; i ++)
261 if (!strcmp(p->reasons[i], "connecting-to-device"))
262 break;
263
264 if (!p->num_reasons || i >= p->num_reasons)
265 break;
266 }
267 }
268
269 if (p)
270 {
271 cupsdLogMessage(CUPSD_LOG_INFO,
272 "System sleep canceled because printer %s is active",
273 p->name);
274 IOCancelPowerChange(sysevent.powerKernelPort,
275 sysevent.powerNotificationID);
276 }
277 else
278 {
279 cupsdLogMessage(CUPSD_LOG_DEBUG, "System wants to sleep");
280 IOAllowPowerChange(sysevent.powerKernelPort,
281 sysevent.powerNotificationID);
282 }
283 }
284
285 if (sysevent.event & SYSEVENT_WILLSLEEP)
286 {
287 cupsdLogMessage(CUPSD_LOG_DEBUG, "System going to sleep");
288
289 Sleeping = 1;
290
291 cupsdStopAllJobs(0);
292 cupsdSaveAllJobs();
293
294 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
295 p;
296 p = (cupsd_printer_t *)cupsArrayNext(Printers))
297 {
298 if (p->type & CUPS_PRINTER_REMOTE)
299 {
300 cupsdLogMessage(CUPSD_LOG_DEBUG,
301 "Deleting remote destination \"%s\"", p->name);
302 cupsArraySave(Printers);
303 cupsdDeletePrinter(p, 0);
304 cupsArrayRestore(Printers);
305 }
306 else
307 {
308 cupsdLogMessage(CUPSD_LOG_DEBUG,
309 "Deregistering local printer \"%s\"", p->name);
310 cupsdDeregisterPrinter(p, 0);
311 }
312 }
313
314 IOAllowPowerChange(sysevent.powerKernelPort,
315 sysevent.powerNotificationID);
316 }
317
318 if (sysevent.event & SYSEVENT_WOKE)
319 {
320 cupsdLogMessage(CUPSD_LOG_DEBUG, "System woke from sleep");
321 IOAllowPowerChange(sysevent.powerKernelPort,
322 sysevent.powerNotificationID);
323 Sleeping = 0;
324 cupsdCheckJobs();
325 }
326
327 if (sysevent.event & SYSEVENT_NETCHANGED)
328 {
329 if (!Sleeping)
330 {
331 cupsdLogMessage(CUPSD_LOG_DEBUG,
332 "System network configuration changed");
333
334 /*
335 * Resetting browse_time before calling cupsdSendBrowseList causes
336 * browse packets to be sent for local shared printers.
337 */
338
339 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
340 p;
341 p = (cupsd_printer_t *)cupsArrayNext(Printers))
342 p->browse_time = 0;
343
344 cupsdSendBrowseList();
345 cupsdRestartPolling();
346 }
347 else
348 cupsdLogMessage(CUPSD_LOG_DEBUG,
349 "System network configuration changed; "
350 "ignored while sleeping");
351 }
352
353 if (sysevent.event & SYSEVENT_NAMECHANGED)
354 {
355 if (!Sleeping)
356 {
357 cupsdLogMessage(CUPSD_LOG_DEBUG, "Computer name changed");
358
359 /*
360 * De-register the individual printers...
361 */
362
363 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
364 p;
365 p = (cupsd_printer_t *)cupsArrayNext(Printers))
366 cupsdDeregisterPrinter(p, 1);
367
368 /*
369 * Now re-register them...
370 */
371
372 for (p = (cupsd_printer_t *)cupsArrayFirst(Printers);
373 p;
374 p = (cupsd_printer_t *)cupsArrayNext(Printers))
375 {
376 p->browse_time = 0;
377 cupsdRegisterPrinter(p);
378 }
379 }
380 else
381 cupsdLogMessage(CUPSD_LOG_DEBUG,
382 "Computer name changed; ignored while sleeping");
383 }
384 }
385 }
386
387
388 /*
389 * 'sysEventThreadEntry()' - A thread to receive power and computer name
390 * change notifications.
391 */
392
393 static void * /* O - Return status/value */
394 sysEventThreadEntry(void)
395 {
396 io_object_t powerNotifierObj;
397 /* Power notifier object */
398 IONotificationPortRef powerNotifierPort;
399 /* Power notifier port */
400 SCDynamicStoreRef store = NULL;/* System Config dynamic store */
401 CFRunLoopSourceRef powerRLS = NULL,/* Power runloop source */
402 storeRLS = NULL;/* System Config runloop source */
403 CFStringRef key[5], /* System Config keys */
404 pattern[2]; /* System Config patterns */
405 CFArrayRef keys = NULL, /* System Config key array*/
406 patterns = NULL;/* System Config pattern array */
407 SCDynamicStoreContext storeContext; /* Dynamic store context */
408 CFRunLoopTimerContext timerContext; /* Timer context */
409 cupsd_thread_data_t threadData; /* Thread context data for the *
410 * runloop notifiers */
411
412
413 /*
414 * Register for power state change notifications
415 */
416
417 bzero(&threadData, sizeof(threadData));
418
419 threadData.sysevent.powerKernelPort =
420 IORegisterForSystemPower(&threadData, &powerNotifierPort,
421 sysEventPowerNotifier, &powerNotifierObj);
422
423 if (threadData.sysevent.powerKernelPort)
424 {
425 powerRLS = IONotificationPortGetRunLoopSource(powerNotifierPort);
426 CFRunLoopAddSource(CFRunLoopGetCurrent(), powerRLS, kCFRunLoopDefaultMode);
427 }
428 else
429 DEBUG_puts("sysEventThreadEntry: error registering for system power "
430 "notifications");
431
432 /*
433 * Register for system configuration change notifications
434 */
435
436 bzero(&storeContext, sizeof(storeContext));
437 storeContext.info = &threadData;
438
439 store = SCDynamicStoreCreate(NULL, CFSTR("cupsd"),
440 sysEventConfigurationNotifier, &storeContext);
441
442 if (!ComputerNameKey)
443 ComputerNameKey = SCDynamicStoreKeyCreateComputerName(NULL);
444
445 if (!NetworkGlobalKeyIPv4)
446 NetworkGlobalKeyIPv4 =
447 SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
448 kSCDynamicStoreDomainState,
449 kSCEntNetIPv4);
450
451 if (!NetworkGlobalKeyIPv6)
452 NetworkGlobalKeyIPv6 =
453 SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
454 kSCDynamicStoreDomainState,
455 kSCEntNetIPv6);
456
457 if (!NetworkGlobalKeyDNS)
458 NetworkGlobalKeyDNS =
459 SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL,
460 kSCDynamicStoreDomainState,
461 kSCEntNetDNS);
462
463 if (!HostNamesKey)
464 HostNamesKey = SCDynamicStoreKeyCreateHostNames(NULL);
465
466 if (!NetworkInterfaceKeyIPv4)
467 NetworkInterfaceKeyIPv4 =
468 SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
469 kSCDynamicStoreDomainState,
470 kSCCompAnyRegex,
471 kSCEntNetIPv4);
472
473 if (!NetworkInterfaceKeyIPv6)
474 NetworkInterfaceKeyIPv6 =
475 SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
476 kSCDynamicStoreDomainState,
477 kSCCompAnyRegex,
478 kSCEntNetIPv6);
479
480 if (store && ComputerNameKey && HostNamesKey &&
481 NetworkGlobalKeyIPv4 && NetworkGlobalKeyIPv6 && NetworkGlobalKeyDNS &&
482 NetworkInterfaceKeyIPv4 && NetworkInterfaceKeyIPv6)
483 {
484 key[0] = ComputerNameKey;
485 key[1] = NetworkGlobalKeyIPv4;
486 key[2] = NetworkGlobalKeyIPv6;
487 key[3] = NetworkGlobalKeyDNS;
488 key[4] = HostNamesKey;
489
490 pattern[0] = NetworkInterfaceKeyIPv4;
491 pattern[1] = NetworkInterfaceKeyIPv6;
492
493 keys = CFArrayCreate(NULL, (const void **)key,
494 sizeof(key) / sizeof(key[0]),
495 &kCFTypeArrayCallBacks);
496
497 patterns = CFArrayCreate(NULL, (const void **)pattern,
498 sizeof(pattern) / sizeof(pattern[0]),
499 &kCFTypeArrayCallBacks);
500
501 if (keys && patterns &&
502 SCDynamicStoreSetNotificationKeys(store, keys, patterns))
503 {
504 if ((storeRLS = SCDynamicStoreCreateRunLoopSource(NULL, store, 0))
505 != NULL)
506 {
507 CFRunLoopAddSource(CFRunLoopGetCurrent(), storeRLS,
508 kCFRunLoopDefaultMode);
509 }
510 else
511 DEBUG_printf(("sysEventThreadEntry: SCDynamicStoreCreateRunLoopSource "
512 "failed: %s\n", SCErrorString(SCError())));
513 }
514 else
515 DEBUG_printf(("sysEventThreadEntry: SCDynamicStoreSetNotificationKeys "
516 "failed: %s\n", SCErrorString(SCError())));
517 }
518 else
519 DEBUG_printf(("sysEventThreadEntry: SCDynamicStoreCreate failed: %s\n",
520 SCErrorString(SCError())));
521
522 if (keys)
523 CFRelease(keys);
524
525 if (patterns)
526 CFRelease(patterns);
527
528 /*
529 * Set up a timer to delay the wake change notifications.
530 *
531 * The initial time is set a decade or so into the future, we'll adjust
532 * this later.
533 */
534
535 bzero(&timerContext, sizeof(timerContext));
536 timerContext.info = &threadData;
537
538 threadData.timerRef =
539 CFRunLoopTimerCreate(NULL,
540 CFAbsoluteTimeGetCurrent() + (86400L * 365L * 10L),
541 86400L * 365L * 10L, 0, 0, sysEventTimerNotifier,
542 &timerContext);
543 CFRunLoopAddTimer(CFRunLoopGetCurrent(), threadData.timerRef,
544 kCFRunLoopDefaultMode);
545
546 /*
547 * Store our runloop in a global so the main thread can use it to stop us.
548 */
549
550 pthread_mutex_lock(&SysEventThreadMutex);
551
552 SysEventRunloop = CFRunLoopGetCurrent();
553
554 pthread_cond_signal(&SysEventThreadCond);
555 pthread_mutex_unlock(&SysEventThreadMutex);
556
557 /*
558 * Disappear into the runloop until it's stopped by the main thread.
559 */
560
561 CFRunLoopRun();
562
563 /*
564 * Clean up before exiting.
565 */
566
567 if (threadData.timerRef)
568 {
569 CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), threadData.timerRef,
570 kCFRunLoopDefaultMode);
571 CFRelease(threadData.timerRef);
572 }
573
574 if (threadData.sysevent.powerKernelPort)
575 {
576 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), powerRLS,
577 kCFRunLoopDefaultMode);
578 IODeregisterForSystemPower(&powerNotifierObj);
579 IOServiceClose(threadData.sysevent.powerKernelPort);
580 IONotificationPortDestroy(powerNotifierPort);
581 }
582
583 if (storeRLS)
584 {
585 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), storeRLS,
586 kCFRunLoopDefaultMode);
587 CFRunLoopSourceInvalidate(storeRLS);
588 CFRelease(storeRLS);
589 }
590
591 if (store)
592 CFRelease(store);
593
594 pthread_exit(NULL);
595 }
596
597
598 /*
599 * 'sysEventPowerNotifier()' - Handle power notification events.
600 */
601
602 static void
603 sysEventPowerNotifier(
604 void *context, /* I - Thread context data */
605 io_service_t service, /* I - Unused service info */
606 natural_t messageType, /* I - Type of message */
607 void *messageArgument) /* I - Message data */
608 {
609 int sendit = 1; /* Send event to main thread? *
610 * (0 = no, 1 = yes, 2 = delayed */
611 cupsd_thread_data_t *threadData; /* Thread context data */
612
613
614 threadData = (cupsd_thread_data_t *)context;
615
616 (void)service; /* anti-compiler-warning-code */
617
618 switch (messageType)
619 {
620 case kIOMessageCanSystemPowerOff:
621 case kIOMessageCanSystemSleep:
622 threadData->sysevent.event |= SYSEVENT_CANSLEEP;
623 break;
624
625 case kIOMessageSystemWillRestart:
626 case kIOMessageSystemWillPowerOff:
627 case kIOMessageSystemWillSleep:
628 threadData->sysevent.event |= SYSEVENT_WILLSLEEP;
629 break;
630
631 case kIOMessageSystemHasPoweredOn:
632 /*
633 * Because powered on is followed by a net-changed event, delay
634 * before sending it.
635 */
636
637 sendit = 2;
638 threadData->sysevent.event |= SYSEVENT_WOKE;
639 break;
640
641 case kIOMessageSystemWillNotPowerOff:
642 case kIOMessageSystemWillNotSleep:
643 #ifdef kIOMessageSystemWillPowerOn
644 case kIOMessageSystemWillPowerOn:
645 #endif /* kIOMessageSystemWillPowerOn */
646 default:
647 sendit = 0;
648 break;
649 }
650
651 if (sendit == 0)
652 IOAllowPowerChange(threadData->sysevent.powerKernelPort,
653 (long)messageArgument);
654 else
655 {
656 threadData->sysevent.powerNotificationID = (long)messageArgument;
657
658 if (sendit == 1)
659 {
660 /*
661 * Send the event to the main thread now.
662 */
663
664 write(SysEventPipes[1], &threadData->sysevent,
665 sizeof(threadData->sysevent));
666 threadData->sysevent.event = 0;
667 }
668 else
669 {
670 /*
671 * Send the event to the main thread after 1 to 2 seconds.
672 */
673
674 CFRunLoopTimerSetNextFireDate(threadData->timerRef,
675 CFAbsoluteTimeGetCurrent() + 2);
676 }
677 }
678 }
679
680
681 /*
682 * 'sysEventConfigurationNotifier()' - Computer name changed notification
683 * callback.
684 */
685
686 static void
687 sysEventConfigurationNotifier(
688 SCDynamicStoreRef store, /* I - System data (unused) */
689 CFArrayRef changedKeys, /* I - Changed data */
690 void *context) /* I - Thread context data */
691 {
692 cupsd_thread_data_t *threadData; /* Thread context data */
693
694
695 threadData = (cupsd_thread_data_t *)context;
696
697 (void)store; /* anti-compiler-warning-code */
698
699 CFRange range = CFRangeMake(0, CFArrayGetCount(changedKeys));
700
701 if (CFArrayContainsValue(changedKeys, range, ComputerNameKey))
702 threadData->sysevent.event |= SYSEVENT_NAMECHANGED;
703 else
704 {
705 threadData->sysevent.event |= SYSEVENT_NETCHANGED;
706
707 /*
708 * Indicate the network interface list needs updating...
709 */
710
711 NetIFUpdate = 1;
712 }
713
714 /*
715 * Because we registered for several different kinds of change notifications
716 * this callback usually gets called several times in a row. We use a timer to
717 * de-bounce these so we only end up generating one event for the main thread.
718 */
719
720 CFRunLoopTimerSetNextFireDate(threadData->timerRef,
721 CFAbsoluteTimeGetCurrent() + 5);
722 }
723
724
725 /*
726 * 'sysEventTimerNotifier()' - Handle delayed event notifications.
727 */
728
729 static void
730 sysEventTimerNotifier(
731 CFRunLoopTimerRef timer, /* I - Timer information */
732 void *context) /* I - Thread context data */
733 {
734 cupsd_thread_data_t *threadData; /* Thread context data */
735
736
737 threadData = (cupsd_thread_data_t *)context;
738
739 /*
740 * If an event is still pending send it to the main thread.
741 */
742
743 if (threadData->sysevent.event)
744 {
745 write(SysEventPipes[1], &threadData->sysevent,
746 sizeof(threadData->sysevent));
747 threadData->sysevent.event = 0;
748 }
749 }
750 #endif /* __APPLE__ */
751
752
753 /*
754 * End of "$Id: sysman.c 6291 2007-02-19 21:54:27Z mike $".
755 */