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