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