1 /*
2  * tcl_os_hnd.c
3  *
4  * TCL OS-handlers for OpenIPMI
5  *
6  * Author: MontaVista Software, Inc.
7  *         Corey Minyard <minyard@mvista.com>
8  *         source@mvista.com
9  *
10  * Copyright 2006 MontaVista Software Inc.
11  *
12  *  This program is free software; you can redistribute it and/or
13  *  modify it under the terms of the GNU Lesser General Public License
14  *  as published by the Free Software Foundation; either version 2 of
15  *  the License, or (at your option) any later version.
16  *
17  *
18  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
19  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
24  *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
26  *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
27  *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  *  You should have received a copy of the GNU Lesser General Public
30  *  License along with this program; if not, write to the Free
31  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
32  */
33 
34 #include <config.h>
35 
36 /* Get the rwlocks for GNU. */
37 #define _GNU_SOURCE
38 
39 #include <stdlib.h>
40 #include <errno.h>
41 #include <stdio.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <pthread.h>
47 #include <string.h>
48 #include <signal.h>
49 
50 #ifdef HAVE_GDBM
51 #include <gdbm.h>
52 #endif
53 
54 #include <OpenIPMI/os_handler.h>
55 #include <OpenIPMI/ipmi_tcl.h>
56 
57 #include <tcl.h>
58 
59 typedef struct t_os_hnd_data_s
60 {
61     int       priority;
62     os_vlog_t log_handler;
63 
64 #ifdef HAVE_GDBM
65     char      *gdbm_filename;
66     GDBM_FILE gdbmf;
67     Tcl_Mutex gdbm_lock;
68 #endif
69 } t_os_hnd_data_t;
70 
71 
72 struct os_hnd_fd_id_s
73 {
74     int                fd;
75     void               *cb_data;
76     os_data_ready_t    data_ready;
77     os_handler_t       *handler;
78     os_fd_data_freed_t freed;
79 };
80 
81 static void
fd_handler(ClientData data,int mask)82 fd_handler(ClientData data, int mask)
83 {
84     os_hnd_fd_id_t  *fd_data = (os_hnd_fd_id_t *) data;
85     void            *cb_data;
86     os_data_ready_t handler;
87     int             fd;
88 
89     handler = fd_data->data_ready;
90     cb_data = fd_data->cb_data;
91     fd = fd_data->fd;
92     handler(fd, cb_data, fd_data);
93 }
94 
95 static int
add_fd(os_handler_t * handler,int fd,os_data_ready_t data_ready,void * cb_data,os_fd_data_freed_t freed,os_hnd_fd_id_t ** id)96 add_fd(os_handler_t       *handler,
97        int                fd,
98        os_data_ready_t    data_ready,
99        void               *cb_data,
100        os_fd_data_freed_t freed,
101        os_hnd_fd_id_t     **id)
102 {
103     os_hnd_fd_id_t  *fd_data;
104 
105     fd_data = malloc(sizeof(*fd_data));
106     if (!fd_data)
107 	return ENOMEM;
108     memset(fd_data, 0, sizeof(*fd_data));
109 
110     fd_data->fd = fd;
111     fd_data->cb_data = cb_data;
112     fd_data->data_ready = data_ready;
113     fd_data->handler = handler;
114     fd_data->freed = freed;
115 
116     Tcl_CreateFileHandler(fd, TCL_READABLE, fd_handler, fd_data);
117 
118     *id = fd_data;
119     return 0;
120 }
121 
122 static int
remove_fd(os_handler_t * handler,os_hnd_fd_id_t * fd_data)123 remove_fd(os_handler_t *handler, os_hnd_fd_id_t *fd_data)
124 {
125     Tcl_DeleteFileHandler(fd_data->fd);
126     free(fd_data);
127     return 0;
128 }
129 
130 struct os_hnd_timer_id_s
131 {
132     void           *cb_data;
133     os_timed_out_t timed_out;
134     int            running;
135     os_handler_t   *handler;
136     Tcl_TimerToken token;
137 };
138 
139 static void
timer_handler(ClientData data)140 timer_handler(ClientData data)
141 {
142     os_hnd_timer_id_t *timer_data = (os_hnd_timer_id_t *) data;
143     /* Make a copy of this, because the handler may delete the timer
144        data. */
145     void              *cb_data;
146     os_timed_out_t    timed_out;
147 
148     timed_out = timer_data->timed_out;
149     cb_data = timer_data->cb_data;
150     timer_data->running = 0;
151     timed_out(cb_data, timer_data);
152 }
153 
154 static int
start_timer(os_handler_t * handler,os_hnd_timer_id_t * id,struct timeval * timeout,os_timed_out_t timed_out,void * cb_data)155 start_timer(os_handler_t      *handler,
156 	    os_hnd_timer_id_t *id,
157 	    struct timeval    *timeout,
158 	    os_timed_out_t    timed_out,
159 	    void              *cb_data)
160 {
161     int interval;
162 
163     if (id->running)
164 	return EBUSY;
165 
166     id->running = 1;
167     id->cb_data = cb_data;
168     id->timed_out = timed_out;
169 
170     interval = (timeout->tv_sec * 1000) | ((timeout->tv_usec + 999) / 1000);
171     id->token = Tcl_CreateTimerHandler(interval, timer_handler, id);
172     return 0;
173 }
174 
175 static int
stop_timer(os_handler_t * handler,os_hnd_timer_id_t * id)176 stop_timer(os_handler_t *handler, os_hnd_timer_id_t *id)
177 {
178     if (!id->running)
179 	return EINVAL;
180 
181     id->running = 0;
182     Tcl_DeleteTimerHandler(id->token);
183     return 0;
184 }
185 
186 static int
alloc_timer(os_handler_t * handler,os_hnd_timer_id_t ** id)187 alloc_timer(os_handler_t      *handler,
188 	    os_hnd_timer_id_t **id)
189 {
190     os_hnd_timer_id_t *timer_data;
191 
192     timer_data = malloc(sizeof(*timer_data));
193     if (!timer_data)
194 	return ENOMEM;
195 
196     timer_data->running = 0;
197     timer_data->timed_out = NULL;
198     timer_data->handler = handler;
199 
200     *id = timer_data;
201     return 0;
202 }
203 
204 static int
free_timer(os_handler_t * handler,os_hnd_timer_id_t * id)205 free_timer(os_handler_t *handler, os_hnd_timer_id_t *id)
206 {
207     if (id->running)
208 	return EBUSY;
209 
210     free(id);
211     return 0;
212 }
213 
214 
215 static int
get_random(os_handler_t * handler,void * data,unsigned int len)216 get_random(os_handler_t *handler, void *data, unsigned int len)
217 {
218     int fd = open("/dev/urandom", O_RDONLY);
219     int rv = 0;
220 
221     if (fd == -1)
222 	return errno;
223 
224     while (len > 0) {
225 	rv = read(fd, data, len);
226 	if (rv < 0) {
227 	    rv = errno;
228 	    goto out;
229 	}
230 	len -= rv;
231     }
232 
233     rv = 0;
234 
235  out:
236     close(fd);
237     return rv;
238 }
239 
240 static void
default_vlog(const char * format,enum ipmi_log_type_e log_type,va_list ap)241 default_vlog(const char           *format,
242 	     enum ipmi_log_type_e log_type,
243 	     va_list              ap)
244 {
245     int do_nl = 1;
246 
247     switch(log_type)
248     {
249 	case IPMI_LOG_INFO:
250 	    fprintf(stderr, "INFO: ");
251 	    break;
252 
253 	case IPMI_LOG_WARNING:
254 	    fprintf(stderr, "WARN: ");
255 	    break;
256 
257 	case IPMI_LOG_SEVERE:
258 	    fprintf(stderr, "SEVR: ");
259 	    break;
260 
261 	case IPMI_LOG_FATAL:
262 	    fprintf(stderr, "FATL: ");
263 	    break;
264 
265 	case IPMI_LOG_ERR_INFO:
266 	    fprintf(stderr, "EINF: ");
267 	    break;
268 
269 	case IPMI_LOG_DEBUG_START:
270 	    do_nl = 0;
271 	    /* FALLTHROUGH */
272 	case IPMI_LOG_DEBUG:
273 	    fprintf(stderr, "DEBG: ");
274 	    break;
275 
276 	case IPMI_LOG_DEBUG_CONT:
277 	    do_nl = 0;
278 	    /* FALLTHROUGH */
279 	case IPMI_LOG_DEBUG_END:
280 	    break;
281     }
282 
283     vfprintf(stderr, format, ap);
284 
285     if (do_nl)
286 	fprintf(stderr, "\n");
287 }
288 
289 static void
tcl_vlog(os_handler_t * handler,enum ipmi_log_type_e log_type,const char * format,va_list ap)290 tcl_vlog(os_handler_t         *handler,
291 	 enum ipmi_log_type_e log_type,
292 	 const char           *format,
293 	 va_list              ap)
294 {
295     t_os_hnd_data_t *info = handler->internal_data;
296     os_vlog_t       log_handler = info->log_handler;
297 
298     if (log_handler)
299 	log_handler(handler, format, log_type, ap);
300     else
301 	default_vlog(format, log_type, ap);
302 }
303 
304 static void
tcl_log(os_handler_t * handler,enum ipmi_log_type_e log_type,const char * format,...)305 tcl_log(os_handler_t         *handler,
306 	enum ipmi_log_type_e log_type,
307 	const char           *format,
308 	...)
309 {
310     va_list ap;
311 
312     va_start(ap, format);
313     tcl_vlog(handler, log_type, format, ap);
314     va_end(ap);
315 }
316 
317 struct os_hnd_lock_s
318 {
319     Tcl_Mutex mutex;
320 };
321 
322 static int
create_lock(os_handler_t * handler,os_hnd_lock_t ** id)323 create_lock(os_handler_t  *handler,
324 	    os_hnd_lock_t **id)
325 {
326     os_hnd_lock_t *lock;
327 
328     lock = malloc(sizeof(*lock));
329     if (!lock)
330 	return ENOMEM;
331     memset(lock, 0, sizeof(*lock));
332 
333     *id = lock;
334     return 0;
335 }
336 
337 static int
destroy_lock(os_handler_t * handler,os_hnd_lock_t * id)338 destroy_lock(os_handler_t  *handler,
339 	     os_hnd_lock_t *id)
340 {
341     Tcl_MutexFinalize(&id->mutex);
342     free(id);
343     return 0;
344 }
345 
346 static int
lock(os_handler_t * handler,os_hnd_lock_t * id)347 lock(os_handler_t  *handler,
348      os_hnd_lock_t *id)
349 {
350     Tcl_MutexLock(&id->mutex);
351     return 0;
352 }
353 
354 static int
unlock(os_handler_t * handler,os_hnd_lock_t * id)355 unlock(os_handler_t  *handler,
356        os_hnd_lock_t *id)
357 {
358     Tcl_MutexUnlock(&id->mutex);
359     return 0;
360 }
361 
362 /*
363  * Tcl conditions provide neither an indication of timeout for a timed
364  * condition wait nor a broadcast function.  Those have to be simulated
365  * and that requires some complexity.
366  */
367 struct os_hnd_cond_s
368 {
369     unsigned int  waiters;
370     unsigned int  woken;
371     Tcl_Condition cond;
372 };
373 
374 static int
create_cond(os_handler_t * handler,os_hnd_cond_t ** new_cond)375 create_cond(os_handler_t  *handler,
376 	    os_hnd_cond_t **new_cond)
377 {
378     os_hnd_cond_t *cond;
379 
380     cond = malloc(sizeof(*cond));
381     if (!cond)
382 	return ENOMEM;
383     memset(cond, 0, sizeof(*cond));
384 
385     *new_cond = cond;
386     return 0;
387 }
388 
389 static int
destroy_cond(os_handler_t * handler,os_hnd_cond_t * cond)390 destroy_cond(os_handler_t  *handler,
391 	     os_hnd_cond_t *cond)
392 {
393     if (cond->waiters)
394 	return EBUSY;
395     Tcl_ConditionFinalize(cond->cond);
396     free(cond);
397     return 0;
398 }
399 
400 static int
cond_wait(os_handler_t * handler,os_hnd_cond_t * cond,os_hnd_lock_t * lock)401 cond_wait(os_handler_t  *handler,
402 	  os_hnd_cond_t *cond,
403 	  os_hnd_lock_t *lock)
404 {
405     while (cond->waiters >= cond->woken) {
406 	cond->waiters++;
407 	Tcl_ConditionWait(&cond->cond, &lock->mutex, NULL);
408 	cond->waiters--;
409     }
410     cond->woken--;
411     return 0;
412 }
413 
414 static int
cond_timedwait(os_handler_t * handler,os_hnd_cond_t * cond,os_hnd_lock_t * lock,struct timeval * rtimeout)415 cond_timedwait(os_handler_t   *handler,
416 	       os_hnd_cond_t  *cond,
417 	       os_hnd_lock_t  *lock,
418 	       struct timeval *rtimeout)
419 {
420     Tcl_Time timeout;
421     Tcl_Time then;
422     Tcl_Time now;
423 
424     /* Calculate when the timeout should occur. */
425     Tcl_GetTime(&then);
426     then.sec += rtimeout->tv_sec;
427     then.usec += rtimeout->tv_usec;
428     while (then.usec >= 1000000) {
429 	then.usec -= 1000000;
430 	then.sec += 1;
431     }
432     while (then.usec < 0) {
433 	then.usec += 1000000;
434 	then.sec -= 1;
435     }
436 
437     timeout.sec = rtimeout->tv_sec;
438     timeout.usec = rtimeout->tv_usec;
439     while (cond->waiters >= cond->woken) {
440 	cond->waiters++;
441 	Tcl_ConditionWait(&cond->cond, &lock->mutex, &timeout);
442 	cond->waiters--;
443 	/* If we are woken, just return. */
444 	if (cond->waiters < cond->woken)
445 	    break;
446 
447 	/* Otherwise, calculate the time left.  If it is <0, then we
448 	   have timed out. */
449 	Tcl_GetTime(&now);
450 	timeout.sec = then.sec - now.sec;
451 	timeout.usec = then.usec - now.usec;
452 	while (then.usec < 0) {
453 	    timeout.usec += 1000000;
454 	    timeout.sec -= 1;
455 	}
456 	if (timeout.sec < 0)
457 	    return ETIMEDOUT;
458     }
459     cond->woken--;
460     return 0;
461 }
462 
463 static int
cond_wake(os_handler_t * handler,os_hnd_cond_t * cond)464 cond_wake(os_handler_t  *handler,
465 	  os_hnd_cond_t *cond)
466 {
467     cond->woken++;
468     Tcl_ConditionNotify(&cond->cond);
469     return 0;
470 }
471 
472 static int
cond_broadcast(os_handler_t * handler,os_hnd_cond_t * cond)473 cond_broadcast(os_handler_t  *handler,
474 	       os_hnd_cond_t *cond)
475 {
476     while (cond->waiters > cond->woken) {
477 	cond->woken++;
478 	Tcl_ConditionNotify(&cond->cond);
479     }
480     return 0;
481 }
482 
483 #if 0
484 static int
485 create_thread(os_handler_t       *handler,
486 	      int                priority,
487 	      void               (*startup)(void *data),
488 	      void               *data)
489 {
490     int rv;
491 
492     rv = Tcl_CreateThread(NULL, startup, data, TCL_THREAD_STACK_DEFAULT,
493 			  TCL_THREAD_NOFLAGS);
494 
495     return 0;
496 }
497 
498 static int
499 thread_exit(os_handler_t *handler)
500 {
501     Tcl_ExitThread(0);
502     return 0;
503 }
504 #endif
505 
506 static void
timeout_callback(ClientData data)507 timeout_callback(ClientData data)
508 {
509     /* Nothing to do */
510 }
511 
512 static int
perform_one_op(os_handler_t * os_hnd,struct timeval * timeout)513 perform_one_op(os_handler_t   *os_hnd,
514 	       struct timeval *timeout)
515 {
516     /* Note that this is not technically 100% correct in a
517        multi-threaded environment, since another thread may run
518        it, but it is pretty close, I guess. */
519     int   time_ms;
520     Tcl_TimerToken token = NULL;
521 
522     if (timeout) {
523 	time_ms= (timeout->tv_sec * 1000) + ((timeout->tv_usec+500) / 1000);
524 	token = Tcl_CreateTimerHandler(time_ms, timeout_callback, NULL);
525     }
526     Tcl_DoOneEvent(TCL_ALL_EVENTS);
527     if (token)
528 	Tcl_DeleteTimerHandler(token);
529     return 0;
530 }
531 
532 static void
operation_loop(os_handler_t * os_hnd)533 operation_loop(os_handler_t *os_hnd)
534 {
535     for (;;)
536 	Tcl_DoOneEvent(TCL_ALL_EVENTS);
537 }
538 
539 static void
free_os_handler(os_handler_t * os_hnd)540 free_os_handler(os_handler_t *os_hnd)
541 {
542     t_os_hnd_data_t *info = os_hnd->internal_data;
543 
544 #ifdef HAVE_GDBM
545     Tcl_MutexFinalize(&info->gdbm_lock);
546     if (info->gdbm_filename)
547 	free(info->gdbm_filename);
548     if (info->gdbmf)
549 	gdbm_close(info->gdbmf);
550 #endif
551     free(info);
552     free(os_hnd);
553 }
554 
555 static void *
tcl_malloc(int size)556 tcl_malloc(int size)
557 {
558     return malloc(size);
559 }
560 
561 static void
tcl_free(void * data)562 tcl_free(void *data)
563 {
564     free(data);
565 }
566 
567 #ifdef HAVE_GDBM
568 #define GDBM_FILE ".OpenIPMI_db"
569 
570 static void
init_gdbm(t_os_hnd_data_t * info)571 init_gdbm(t_os_hnd_data_t *info)
572 {
573     if (!info->gdbm_filename) {
574 	char *home = getenv("HOME");
575 	if (!home)
576 	    return;
577 	info->gdbm_filename = malloc(strlen(home)+strlen(GDBM_FILE)+2);
578 	if (!info->gdbm_filename)
579 	    return;
580 	strcpy(info->gdbm_filename, home);
581 	strcat(info->gdbm_filename, "/");
582 	strcat(info->gdbm_filename, GDBM_FILE);
583     }
584 
585     info->gdbmf = gdbm_open(info->gdbm_filename, 512, GDBM_WRCREAT, 0600,
586 			    NULL);
587     /* gdbmf will be NULL on error, which is what reports an error. */
588 }
589 
590 static int
database_store(os_handler_t * handler,char * key,unsigned char * data,unsigned int data_len)591 database_store(os_handler_t  *handler,
592 	       char          *key,
593 	       unsigned char *data,
594 	       unsigned int  data_len)
595 {
596     t_os_hnd_data_t *info = handler->internal_data;
597     datum           gkey, gdata;
598     int             rv;
599 
600     Tcl_MutexLock(&info->gdbm_lock);
601     if (!info->gdbmf) {
602 	init_gdbm(info);
603 	if (!info->gdbmf) {
604 	    Tcl_MutexUnlock(&info->gdbm_lock);
605 	    return EINVAL;
606 	}
607     }
608 
609     gkey.dptr = key;
610     gkey.dsize = strlen(key);
611     gdata.dptr = (char *) data;
612     gdata.dsize = data_len;
613 
614     rv = gdbm_store(info->gdbmf, gkey, gdata, GDBM_REPLACE);
615     Tcl_MutexUnlock(&info->gdbm_lock);
616     if (rv)
617 	return EINVAL;
618     return 0;
619 }
620 
621 static int
database_find(os_handler_t * handler,char * key,unsigned int * fetch_completed,unsigned char ** data,unsigned int * data_len,void (* got_data)(void * cb_data,int err,unsigned char * data,unsigned int data_len),void * cb_data)622 database_find(os_handler_t  *handler,
623 	      char          *key,
624 	      unsigned int  *fetch_completed,
625 	      unsigned char **data,
626 	      unsigned int  *data_len,
627 	      void (*got_data)(void          *cb_data,
628 			       int           err,
629 			       unsigned char *data,
630 			       unsigned int  data_len),
631 	      void *cb_data)
632 {
633     t_os_hnd_data_t *info = handler->internal_data;
634     datum           gkey, gdata;
635 
636     Tcl_MutexLock(&info->gdbm_lock);
637     if (!info->gdbmf) {
638 	init_gdbm(info);
639 	if (!info->gdbmf) {
640 	    Tcl_MutexUnlock(&info->gdbm_lock);
641 	    return EINVAL;
642 	}
643     }
644 
645     gkey.dptr = key;
646     gkey.dsize = strlen(key);
647     gdata = gdbm_fetch(info->gdbmf, gkey);
648     Tcl_MutexUnlock(&info->gdbm_lock);
649     if (!gdata.dptr)
650 	return EINVAL;
651     *data = (unsigned char *) gdata.dptr;
652     *data_len = gdata.dsize;
653     *fetch_completed = 1;
654     return 0;
655 }
656 
657 static void
database_free(os_handler_t * handler,unsigned char * data)658 database_free(os_handler_t  *handler,
659 	      unsigned char *data)
660 {
661     free(data);
662 }
663 
664 static int
set_gdbm_filename(os_handler_t * os_hnd,char * name)665 set_gdbm_filename(os_handler_t *os_hnd, char *name)
666 {
667     t_os_hnd_data_t *info = os_hnd->internal_data;
668     char            *nname;
669 
670     nname = strdup(name);
671     if (!nname)
672 	return ENOMEM;
673     if (info->gdbm_filename)
674 	free(info->gdbm_filename);
675     info->gdbm_filename = nname;
676     return 0;
677 }
678 #endif
679 
sset_log_handler(os_handler_t * handler,os_vlog_t log_handler)680 void sset_log_handler(os_handler_t *handler,
681 		      os_vlog_t    log_handler)
682 {
683     t_os_hnd_data_t *info = handler->internal_data;
684 
685     info->log_handler = log_handler;
686 }
687 
get_tcl_time(os_handler_t * handler,struct timeval * tv)688 static int get_tcl_time(os_handler_t *handler,
689 			struct timeval *tv)
690 {
691     Tcl_Time now;
692     Tcl_GetTime(&now);
693     tv->tv_sec = now.sec;
694     tv->tv_usec = now.usec;
695     return 0;
696 }
697 
698 
699 static os_handler_t ipmi_tcl_os_handler =
700 {
701     .mem_alloc = tcl_malloc,
702     .mem_free = tcl_free,
703 
704     .add_fd_to_wait_for = add_fd,
705     .remove_fd_to_wait_for = remove_fd,
706 
707     .start_timer = start_timer,
708     .stop_timer = stop_timer,
709     .alloc_timer = alloc_timer,
710     .free_timer = free_timer,
711 
712     .get_random = get_random,
713     .log = tcl_log,
714     .vlog = tcl_vlog,
715 
716     .create_lock = create_lock,
717     .destroy_lock = destroy_lock,
718     .lock = lock,
719     .unlock = unlock,
720 
721     .create_cond = create_cond,
722     .destroy_cond = destroy_cond,
723     .cond_wait = cond_wait,
724     .cond_timedwait = cond_timedwait,
725     .cond_wake = cond_wake,
726     .cond_broadcast = cond_broadcast,
727 
728 #if 0
729     .create_thread = create_thread,
730     .thread_exit = thread_exit,
731 #endif
732 
733     .free_os_handler = free_os_handler,
734 
735     .perform_one_op = perform_one_op,
736     .operation_loop = operation_loop,
737 
738 #ifdef HAVE_GDBM
739     .database_store = database_store,
740     .database_find = database_find,
741     .database_free = database_free,
742     .database_set_filename = set_gdbm_filename,
743 #endif
744     .set_log_handler = sset_log_handler,
745     .get_monotonic_time = get_tcl_time,
746     .get_real_time = get_tcl_time
747 };
748 
749 
750 os_handler_t *
ipmi_tcl_get_os_handler(int priority)751 ipmi_tcl_get_os_handler(int priority)
752 {
753     os_handler_t    *rv;
754     t_os_hnd_data_t *info;
755 
756     rv = malloc(sizeof(*rv));
757     if (!rv)
758 	return NULL;
759 
760     memcpy(rv, &ipmi_tcl_os_handler, sizeof(*rv));
761 
762     info = malloc(sizeof(*info));
763     if (! info) {
764 	free(rv);
765 	return NULL;
766     }
767     memset(info, 0, sizeof(*info));
768 
769     info->priority = priority;
770     rv->internal_data = info;
771 
772     return rv;
773 }
774