1 /*
2  * ipmish.c
3  *
4  * MontaVista IPMI basic UI to use the main UI code.
5  *
6  * Author: MontaVista Software, Inc.
7  *         Corey Minyard <minyard@mvista.com>
8  *         source@mvista.com
9  *
10  * Copyright 2002,2003 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 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <ctype.h>
40 #include <errno.h>
41 #include <termios.h>
42 #include <unistd.h>
43 #include <signal.h>
44 #include <OpenIPMI/selector.h>
45 #include <OpenIPMI/ipmiif.h>
46 #include <OpenIPMI/ipmi_conn.h>
47 #include <OpenIPMI/ipmi_err.h>
48 #include <OpenIPMI/ipmi_posix.h>
49 #include <OpenIPMI/ipmi_glib.h>
50 #include <OpenIPMI/ipmi_tcl.h>
51 #include <OpenIPMI/ipmi_cmdlang.h>
52 #include <OpenIPMI/ipmi_debug.h>
53 #include <readline/readline.h>
54 #include <readline/history.h>
55 
56 #ifdef HAVE_GLIB
57 #include <glib.h>
58 #endif
59 
60 /* Internal includes, do not use in your programs */
61 #include <OpenIPMI/internal/ipmi_malloc.h>
62 
63 #ifdef HAVE_UCDSNMP
64 # ifdef HAVE_NETSNMP
65 #  include <net-snmp/net-snmp-config.h>
66 #  include <net-snmp/net-snmp-includes.h>
67 # elif defined(HAVE_ALT_UCDSNMP_DIR)
68 #  include <ucd-snmp/asn1.h>
69 #  include <ucd-snmp/snmp_api.h>
70 #  include <ucd-snmp/snmp.h>
71 # else
72 #  include <asn1.h>
73 #  include <snmp_api.h>
74 #  include <snmp.h>
75 # endif
76 #endif
77 
78 extern os_handler_t ipmi_debug_os_handlers;
79 struct selector_s *debug_sel;
80 
81 os_hnd_fd_id_t *term_fd_id;
82 
83 static int done = 0;
84 static int evcount = 0;
85 static int handling_input = 0;
86 static int cmd_redisp = 1;
87 #ifdef HAVE_UCDSNMP
88 static int do_snmp = 0;
89 #endif
90 
91 static void user_input_ready(int fd, void *data, os_hnd_fd_id_t *id);
92 
93 static void
redraw_cmdline(int force)94 redraw_cmdline(int force)
95 {
96     int redisp = cmd_redisp;
97 
98     if (force)
99 	redisp = 1;
100     if (!done && handling_input && redisp) {
101 	rl_redisplay();
102 	fflush(stdout);
103     }
104 }
105 
106 static void
my_vlog(os_handler_t * handler,const char * format,enum ipmi_log_type_e log_type,va_list ap)107 my_vlog(os_handler_t         *handler,
108 	const char           *format,
109 	enum ipmi_log_type_e log_type,
110 	va_list              ap)
111 {
112     int do_nl = 1;
113     static int last_was_cont = 0;
114 
115     if (handling_input && !last_was_cont && !done && cmd_redisp)
116 	fputc('\n', stdout);
117 
118     last_was_cont = 0;
119     switch(log_type) {
120     case IPMI_LOG_INFO:
121 	printf("INFO: ");
122 	break;
123 
124     case IPMI_LOG_WARNING:
125 	printf("WARN: ");
126 	break;
127 
128     case IPMI_LOG_SEVERE:
129 	printf("SEVR: ");
130 	break;
131 
132     case IPMI_LOG_FATAL:
133 	printf("FATL: ");
134 	break;
135 
136     case IPMI_LOG_ERR_INFO:
137 	printf("EINF: ");
138 	break;
139 
140     case IPMI_LOG_DEBUG_START:
141 	do_nl = 0;
142 	last_was_cont = 1;
143 	/* FALLTHROUGH */
144     case IPMI_LOG_DEBUG:
145 	printf("DEBG: ");
146 	break;
147 
148     case IPMI_LOG_DEBUG_CONT:
149 	last_was_cont = 1;
150 	do_nl = 0;
151 	/* FALLTHROUGH */
152     case IPMI_LOG_DEBUG_END:
153 	break;
154     }
155 
156     vprintf(format, ap);
157     if (do_nl) {
158 	printf("\n");
159 	redraw_cmdline(0);
160     }
161 }
162 
163 #ifdef HAVE_GLIB
glib_handle_log(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)164 void glib_handle_log(const gchar *log_domain,
165 		     GLogLevelFlags log_level,
166 		     const gchar *message,
167 		     gpointer user_data)
168 {
169     char *pfx = "";
170 
171     if (log_level & G_LOG_LEVEL_ERROR)
172 	pfx = "FATL: ";
173     else if (log_level & G_LOG_LEVEL_CRITICAL)
174 	pfx = "SEVR: ";
175     else if (log_level & G_LOG_LEVEL_WARNING)
176 	pfx = "WARN: ";
177     else if (log_level & G_LOG_LEVEL_MESSAGE)
178 	pfx = "EINF: ";
179     else if (log_level & G_LOG_LEVEL_INFO)
180 	pfx = "INFO: ";
181     else if (log_level & G_LOG_LEVEL_DEBUG)
182 	pfx = "DEBG: ";
183     printf("%s%s\n", pfx, message);
184     redraw_cmdline(0);
185 }
186 #endif
187 
188 static void
enable_term_fd(ipmi_cmdlang_t * cmdlang)189 enable_term_fd(ipmi_cmdlang_t *cmdlang)
190 {
191     int rv;
192 
193     if (term_fd_id)
194 	return;
195 
196     rv = cmdlang->os_hnd->add_fd_to_wait_for(cmdlang->os_hnd, 0,
197 					     user_input_ready,
198 					     cmdlang,
199 					     NULL, &term_fd_id);
200     if (rv) {
201 	fprintf(stderr, "error enabling terminal handler, giving up\n");
202 	exit(1);
203     }
204 }
205 
206 static void
disable_term_fd(ipmi_cmdlang_t * cmdlang)207 disable_term_fd(ipmi_cmdlang_t *cmdlang)
208 {
209     int rv;
210 
211     if (!term_fd_id)
212 	return;
213 
214     rv = cmdlang->os_hnd->remove_fd_to_wait_for(cmdlang->os_hnd, term_fd_id);
215     if (rv) {
216 	fprintf(stderr, "error removing terminal handler, giving up\n");
217 	exit(1);
218     }
219     term_fd_id = NULL;
220 }
221 
222 #ifdef HAVE_UCDSNMP
223 #define IPMI_OID_SIZE 9
224 static oid ipmi_oid[IPMI_OID_SIZE] = {1,3,6,1,4,1,3183,1,1};
snmp_input(int op,struct snmp_session * session,int reqid,struct snmp_pdu * pdu,void * magic)225 int snmp_input(int op,
226 	       struct snmp_session *session,
227 	       int reqid,
228 	       struct snmp_pdu *pdu,
229 	       void *magic)
230 {
231     struct sockaddr_in   *src_ip;
232     uint32_t             specific;
233     struct variable_list *var;
234 
235 #ifdef HAVE_NETSNMP
236     if (op != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE)
237 	goto out;
238 #else
239     if (op != RECEIVED_MESSAGE)
240 	goto out;
241 #endif
242     if (pdu->command != SNMP_MSG_TRAP)
243 	goto out;
244     if (snmp_oid_compare(ipmi_oid, IPMI_OID_SIZE,
245 			 pdu->enterprise, pdu->enterprise_length)
246 	!= 0)
247     {
248 	goto out;
249     }
250     if (pdu->trap_type != SNMP_TRAP_ENTERPRISESPECIFIC)
251 	goto out;
252 
253     src_ip = (struct sockaddr_in *) &pdu->agent_addr;
254     specific = pdu->specific_type;
255 
256     var = pdu->variables;
257     if (var == NULL)
258 	goto out;
259     if (var->type != ASN_OCTET_STR)
260 	goto out;
261     if (snmp_oid_compare(ipmi_oid, IPMI_OID_SIZE, var->name, var->name_length)
262 	!= 0)
263     {
264 	goto out;
265     }
266     if (var->val_len < 46)
267 	goto out;
268 
269     ipmi_handle_snmp_trap_data(src_ip,
270 		    	       sizeof(*src_ip),
271 			       IPMI_EXTERN_ADDR_IP,
272 			       specific,
273 			       var->val.string,
274 			       var->val_len);
275 
276  out:
277     return 1;
278 }
279 
280 #ifdef HAVE_NETSNMP
281 static int
snmp_pre_parse(netsnmp_session * session,netsnmp_transport * transport,void * transport_data,int transport_data_length)282 snmp_pre_parse(netsnmp_session * session, netsnmp_transport *transport,
283 	       void *transport_data, int transport_data_length)
284 {
285     return 1;
286 }
287 #else
288 static int
snmp_pre_parse(struct snmp_session * session,snmp_ipaddr from)289 snmp_pre_parse(struct snmp_session *session, snmp_ipaddr from)
290 {
291     return 1;
292 }
293 #endif
294 
295 static struct snmp_session *snmp_session;
296 
297 struct snmp_fd_data {
298     int fd;
299     os_hnd_fd_id_t *id;
300     struct snmp_fd_data *next;
301 };
302 
303 static struct snmp_fd_data *snmpfd = NULL;
304 os_hnd_timer_id_t *snmp_timer = NULL;
305 
306 static void
snmp_check_read_fds(int fd,void * cb_data,os_hnd_fd_id_t * id)307 snmp_check_read_fds(int fd, void *cb_data, os_hnd_fd_id_t *id)
308 {
309     fd_set fdset;
310 
311     FD_ZERO(&fdset);
312     FD_SET(fd, &fdset);
313     snmp_read(&fdset);
314 }
315 
316 static void
snmp_check_timeout(void * cb_data,os_hnd_timer_id_t * id)317 snmp_check_timeout(void *cb_data, os_hnd_timer_id_t *id)
318 {
319     snmp_timeout();
320 }
321 
322 static void
snmp_setup_fds(os_handler_t * os_hnd)323 snmp_setup_fds(os_handler_t *os_hnd)
324 {
325     int nfds = 0, block = 0, i, rv;
326     fd_set fdset;
327     struct timeval tv;
328     struct snmp_fd_data *fdd, *nfdd, *prev = NULL;
329 
330     if (!do_snmp)
331 	return;
332 
333     FD_ZERO(&fdset);
334     tv.tv_sec = 0;
335     tv.tv_usec = 0;
336     snmp_select_info(&nfds, &fdset, &tv, &block);
337 
338     /* Run through the list.  Since the list is kept sorted, we only
339        need one pass. */
340     fdd = snmpfd;
341     for (i = 0; i < nfds; i++) {
342 	if (!FD_ISSET(i, &fdset))
343 	    continue;
344 
345 	if (fdd) {
346 	    if (fdd->fd == i) {
347 		/* Didn't change. */
348 		prev = fdd;
349 		fdd = fdd->next;
350 		continue;
351 	    }
352 	    if (fdd->fd < i) {
353 		/* Current one was deleted. */
354 		os_hnd->remove_fd_to_wait_for(os_hnd, fdd->id);
355 		if (prev)
356 		    prev->next = fdd->next;
357 		else
358 		    snmpfd = fdd->next;
359 		os_hnd->mem_free(fdd);
360 		continue;
361 	    }
362 	}
363 
364 	/* New one to add. */
365 	nfdd = os_hnd->mem_alloc(sizeof(*fdd));
366 	if (!nfdd) {
367 	    rv = ENOMEM;
368 	    goto err;
369 	}
370 	nfdd->fd = i;
371 	rv = os_hnd->add_fd_to_wait_for(os_hnd, i, snmp_check_read_fds,
372 					NULL, NULL, &nfdd->id);
373 	if (rv)
374 	    goto err;
375 
376 	/* Insert after */
377 	if (fdd) {
378 	    nfdd->next = fdd->next;
379 	    fdd->next = nfdd;
380 	} else {
381 	    nfdd->next = NULL;
382 	    snmpfd = fdd;
383 	}
384     }
385 
386     if (!block) {
387 	os_hnd->stop_timer(os_hnd, snmp_timer);
388     } else {
389 	os_hnd->stop_timer(os_hnd, snmp_timer);
390 	os_hnd->start_timer(os_hnd, snmp_timer, &tv, snmp_check_timeout, NULL);
391     }
392     return;
393 
394  err:
395     fprintf(stderr, "Error handling SNMP fd data: %s\n", strerror(rv));
396     exit(1);
397 }
398 
399 static int
snmp_init(os_handler_t * os_hnd)400 snmp_init(os_handler_t *os_hnd)
401 {
402     struct snmp_session session;
403 #ifdef HAVE_NETSNMP
404     netsnmp_transport *transport = NULL;
405     static char *snmp_default_port = "udp:162";
406     int rv;
407 
408     rv = os_hnd->alloc_timer(os_hnd, &snmp_timer);
409     if (rv) {
410 	fprintf(stderr, "Could not allocate SNMP timer\n");
411 	return -1;
412     }
413 
414     netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
415 			   NETSNMP_DS_LIB_MIB_ERRORS,
416 			   0);
417 
418     init_snmp("ipmish");
419 
420     transport = netsnmp_tdomain_transport(snmp_default_port, 1, "udp");
421     if (!transport) {
422         snmp_sess_perror("ipmish", &session);
423 	return -1;
424     }
425 #else
426     void *transport = NULL;
427 #endif
428     snmp_sess_init(&session);
429     session.peername = SNMP_DEFAULT_PEERNAME;
430     session.version = SNMP_DEFAULT_VERSION;
431     session.community_len = SNMP_DEFAULT_COMMUNITY_LEN;
432     session.retries = SNMP_DEFAULT_RETRIES;
433     session.timeout = SNMP_DEFAULT_TIMEOUT;
434     session.local_port = SNMP_TRAP_PORT;
435     session.callback = snmp_input;
436     session.callback_magic = transport;
437     session.authenticator = NULL;
438     session.isAuthoritative = SNMP_SESS_UNKNOWNAUTH;
439 
440 #ifdef HAVE_NETSNMP
441     snmp_session = snmp_add(&session, transport, snmp_pre_parse, NULL);
442 #else
443     snmp_session = snmp_open_ex(&session, snmp_pre_parse,
444 				NULL, NULL, NULL, NULL);
445 #endif
446     if (snmp_session == NULL) {
447         snmp_sess_perror("ipmish", &session);
448 	return -1;
449     }
450 
451     return 0;
452 }
453 #else
snmp_setup_fds(os_handler_t * os_hnd)454 static void snmp_setup_fds(os_handler_t *os_hnd) { }
455 #endif /* HAVE_UCDSNMP */
456 
457 typedef struct out_data_s
458 {
459     FILE *stream;
460     int  indent;
461 } out_data_t;
462 
463 static int columns = 80;
464 
465 static void
out_help(FILE * s,int indent,const char * name,const char * v)466 out_help(FILE *s, int indent, const char *name, const char *v)
467 {
468     int pos, endpos;
469     const char *endword;
470     const char *endspace;
471 
472     pos = fprintf(s, "%*s%s ", indent, "", name);
473     while (*v) {
474 	endword = v;
475 	while (isspace(*endword)) {
476 	    if (*endword == '\n') {
477 		v = endword + 1;
478 		fprintf(s, "\n%*s", indent+2, "");
479 		pos = indent + 2;
480 	    }
481 	    endword++;
482 	}
483 	endspace = endword;
484 	while (*endword && !isspace(*endword))
485 	    endword++;
486 	endpos = pos + endword - v;
487 	if (endpos > columns) {
488 	    v = endspace;
489 	    fprintf(s, "\n%*s", indent+2, "");
490 	    pos = indent + 2;
491 	}
492 	fwrite(v, 1, endword-v, s);
493 	pos += endword - v;
494 	v = endword;
495     }
496     fputc('\n', s);
497 }
498 
499 static void
out_value(ipmi_cmdlang_t * info,const char * name,const char * value)500 out_value(ipmi_cmdlang_t *info, const char *name, const char *value)
501 {
502     out_data_t *out_data = info->user_data;
503 
504     if (value) {
505 	if (info->help) {
506 	    out_help(out_data->stream, out_data->indent*2, name, value);
507 	} else {
508 	    fprintf(out_data->stream, "%*s%s: %s\n", out_data->indent*2, "",
509 		    name, value);
510 	}
511     } else
512 	fprintf(out_data->stream, "%*s%s\n", out_data->indent*2, "", name);
513     fflush(out_data->stream);
514 }
515 
516 static void
out_binary(ipmi_cmdlang_t * info,const char * name,const char * value,unsigned int len)517 out_binary(ipmi_cmdlang_t *info, const char *name, const char *value,
518 	   unsigned int len)
519 {
520     out_data_t *out_data = info->user_data;
521     unsigned char *data = (unsigned char *) value;
522     int indent2 = (out_data->indent * 2) + strlen(name) + 1;
523     unsigned int i;
524     char *sep = ":";
525 
526     if (info->help)
527       sep = "";
528 
529     fprintf(out_data->stream, "%*s%s%s", out_data->indent*2, "", name, sep);
530     for (i=0; i<len; i++) {
531 	if ((i != 0) && ((i % 8) == 0))
532 	    fprintf(out_data->stream, "\n%*s", indent2, "");
533 	fprintf(out_data->stream, " 0x%2.2x", (data[i] & 0xff));
534     }
535     fprintf(out_data->stream, "\n");
536 
537     fflush(out_data->stream);
538 }
539 
540 static void
out_unicode(ipmi_cmdlang_t * info,const char * name,const char * value,unsigned int len)541 out_unicode(ipmi_cmdlang_t *info, const char *name, const char *value,
542 	    unsigned int len)
543 {
544     out_binary(info, name, value, len);
545 }
546 
547 static void
down_level(ipmi_cmdlang_t * info)548 down_level(ipmi_cmdlang_t *info)
549 {
550     out_data_t *out_data = info->user_data;
551 
552     out_data->indent++;
553 }
554 
555 static void
up_level(ipmi_cmdlang_t * info)556 up_level(ipmi_cmdlang_t *info)
557 {
558     out_data_t *out_data = info->user_data;
559 
560     out_data->indent--;
561 }
562 
563 static void cmd_done(ipmi_cmdlang_t *info);
564 
565 static out_data_t lout_data =
566 {
567     .stream = NULL,
568     .indent = 0,
569 };
570 static char cmdlang_objstr[IPMI_MAX_NAME_LEN];
571 static ipmi_cmdlang_t cmdlang =
572 {
573     .out = out_value,
574     .out_binary = out_binary,
575     .out_unicode = out_unicode,
576     .down = down_level,
577     .up = up_level,
578     .done = cmd_done,
579 
580     .os_hnd = NULL,
581 
582     .user_data = &lout_data,
583 
584     .objstr = cmdlang_objstr,
585     .objstr_len = sizeof(cmdlang_objstr),
586 };
587 
588 int *done_ptr = NULL;
589 
590 static void
cmd_done(ipmi_cmdlang_t * info)591 cmd_done(ipmi_cmdlang_t *info)
592 {
593     out_data_t *out_data = info->user_data;
594 
595     if (info->err) {
596 	char errval[128];
597 	if (!info->location)
598 	    info->location = "";
599 	if (strlen(info->objstr) == 0) {
600 	    fprintf(out_data->stream, "error: %s: %s (0x%x, %s)\n",
601 		    info->location, info->errstr,
602 		    info->err,
603 		    ipmi_get_error_string(info->err, errval, sizeof(errval)));
604 	} else {
605 	    fprintf(out_data->stream, "error: %s %s: %s (0x%x, %s)\n",
606 		    info->location, info->objstr, info->errstr,
607 		    info->err,
608 		    ipmi_get_error_string(info->err, errval, sizeof(errval)));
609 	}
610 	if (info->errstr_dynalloc)
611 	    ipmi_mem_free(info->errstr);
612 	info->errstr_dynalloc = 0;
613 	info->errstr = NULL;
614 	info->location = NULL;
615 	info->objstr[0] = '\0';
616 	info->err = 0;
617     }
618 
619     if (done_ptr) {
620 	*done_ptr = 1;
621     } else {
622 	handling_input = 1;
623 	redraw_cmdline(1);
624 	enable_term_fd(info);
625 	fflush(out_data->stream);
626     }
627 }
628 
629 void
ipmi_cmdlang_global_err(char * objstr,char * location,char * errstr,int errval)630 ipmi_cmdlang_global_err(char *objstr,
631 			char *location,
632 			char *errstr,
633 			int  errval)
634 {
635     if (handling_input && !done && cmd_redisp)
636 	fputc('\n', stdout);
637     if (objstr)
638 	fprintf(stderr, "global error: %s %s: %s (0x%x)", location, objstr,
639 		errstr, errval);
640     else
641 	fprintf(stderr, "global error: %s: %s (0x%x)", location,
642 		errstr, errval);
643     evcount = 0;
644     redraw_cmdline(0);
645 }
646 
647 void
ipmi_cmdlang_report_event(ipmi_cmdlang_event_t * event)648 ipmi_cmdlang_report_event(ipmi_cmdlang_event_t *event)
649 {
650     unsigned int                level, len;
651     enum ipmi_cmdlang_out_types type;
652     char                        *name, *value;
653     int                         indent2;
654     unsigned int                i;
655 
656     if (handling_input && !done && cmd_redisp)
657 	fputc('\n', stdout);
658     ipmi_cmdlang_event_restart(event);
659     printf("Event\n");
660     while (ipmi_cmdlang_event_next_field(event, &level, &type, &name, &len,
661 					 &value))
662     {
663 	switch (type) {
664 	case IPMI_CMDLANG_STRING:
665 	    if (value)
666 		printf("  %*s%s: %s\n", level*2, "", name, value);
667 	    else
668 		printf("  %*s%s\n", level*2, "", name);
669 	    break;
670 
671 	case IPMI_CMDLANG_BINARY:
672 	case IPMI_CMDLANG_UNICODE:
673 	    indent2 = (level * 2) + strlen(name) + 1;
674 	    printf("  %*s%s:", level*2, "", name);
675 	    for (i=0; i<len; i++) {
676 		if ((i != 0) && ((i % 8) == 0))
677 		    printf("\n  %*s", indent2, "");
678 		printf(" 0x%2.2x", value[i] & 0xff);
679 	    }
680 	    printf("\n");
681 
682 	    fflush(stdout);
683 	    break;
684 	}
685     }
686     evcount = 0;
687     redraw_cmdline(0);
688 }
689 
690 static void
user_input_ready(int fd,void * data,os_hnd_fd_id_t * id)691 user_input_ready(int fd, void *data, os_hnd_fd_id_t *id)
692 {
693     rl_callback_read_char();
694 }
695 
696 static void
rl_ipmish_cb_handler(char * cmdline)697 rl_ipmish_cb_handler(char *cmdline)
698 {
699     char *expansion = NULL;
700     int result;
701 
702     if (cmdline == NULL) {
703 	done = 1;
704 	evcount = 1; /* Force a newline */
705 	return;
706     }
707     result = history_expand(cmdline, &expansion);
708     if (result < 0 || result == 2) {
709 	fprintf(stderr, "%s\n", expansion);
710     } else if (expansion && strlen(expansion)){
711 	cmdlang.err = 0;
712 	cmdlang.errstr = NULL;
713 	cmdlang.errstr_dynalloc = 0;
714 	cmdlang.location = NULL;
715 	handling_input = 0;
716 	add_history(expansion);
717 	ipmi_cmdlang_handle(&cmdlang, expansion);
718     }
719     if (expansion)
720 	free(expansion);
721 }
722 
723 static void
cleanup_term(void)724 cleanup_term(void)
725 {
726     rl_callback_handler_remove();
727     disable_term_fd(&cmdlang);
728 }
729 
730 static void cleanup_sig(int sig);
731 
732 static void
setup_term(os_handler_t * os_hnd)733 setup_term(os_handler_t *os_hnd)
734 {
735     signal(SIGINT, cleanup_sig);
736     signal(SIGPIPE, cleanup_sig);
737     signal(SIGUSR1, cleanup_sig);
738     signal(SIGUSR2, cleanup_sig);
739 #ifdef SIGPWR
740     signal(SIGPWR, cleanup_sig);
741 #endif
742 
743     stifle_history(500);
744     rl_callback_handler_install("> ", rl_ipmish_cb_handler);
745     lout_data.stream = stdout;
746 
747     cmdlang.os_hnd = os_hnd;
748 }
749 
750 static void
redisp_cmd(ipmi_cmd_info_t * cmd_info)751 redisp_cmd(ipmi_cmd_info_t *cmd_info)
752 {
753     ipmi_cmdlang_t  *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
754     int             curr_arg = ipmi_cmdlang_get_curr_arg(cmd_info);
755     int             argc = ipmi_cmdlang_get_argc(cmd_info);
756     char            **argv = ipmi_cmdlang_get_argv(cmd_info);
757     int             redisp;
758 
759     if ((argc - curr_arg) < 1) {
760 	/* Not enough parameters */
761 	cmdlang->errstr = "Not enough parameters";
762 	cmdlang->err = EINVAL;
763 	goto out_err;
764     }
765 
766     ipmi_cmdlang_get_bool(argv[curr_arg], &redisp, cmd_info);
767     if (cmdlang->err) {
768 	cmdlang->errstr = "redisp setting invalid";
769 	goto out_err;
770     }
771     curr_arg++;
772 
773     cmd_redisp = redisp;
774 
775     ipmi_cmdlang_out(cmd_info, "redisp set", NULL);
776 
777  out_err:
778     cmdlang->location = "ipmish.c(redisp_cmd)";
779 }
780 
781 static void
exit_cmd(ipmi_cmd_info_t * cmd_info)782 exit_cmd(ipmi_cmd_info_t *cmd_info)
783 {
784     done = 1;
785     evcount = 0;
786     ipmi_cmdlang_out(cmd_info, "Exiting ipmish", NULL);
787 }
788 
789 static int read_nest = 0;
790 static void
read_cmd(ipmi_cmd_info_t * cmd_info)791 read_cmd(ipmi_cmd_info_t *cmd_info)
792 {
793     ipmi_cmdlang_t *cmdlang = ipmi_cmdinfo_get_cmdlang(cmd_info);
794     int            cdone;
795     char           cmdline[256];
796     FILE           *s;
797     out_data_t     my_out_data;
798     ipmi_cmdlang_t my_cmdlang = *cmdlang;
799     int            curr_arg = ipmi_cmdlang_get_curr_arg(cmd_info);
800     int            argc = ipmi_cmdlang_get_argc(cmd_info);
801     char           **argv = ipmi_cmdlang_get_argv(cmd_info);
802     int            *saved_done_ptr;
803     char           *fname;
804 
805     if ((argc - curr_arg) < 1) {
806 	cmdlang->errstr = "No filename entered";
807 	cmdlang->err = EINVAL;
808 	goto out_err;
809     }
810 
811     fname = argv[curr_arg];
812     curr_arg++;
813     s = fopen(fname, "r");
814     if (!s) {
815 	cmdlang->errstr = "Unable to openfile";
816 	cmdlang->err = errno;
817 	goto out_err;
818     }
819 
820     if (!read_nest) {
821 	handling_input = 0;
822 	disable_term_fd(cmdlang);
823     }
824     read_nest++;
825     saved_done_ptr = done_ptr;
826 
827     /* not record the file's commands into history */
828     while (fgets(cmdline, sizeof(cmdline), s)) {
829 	my_out_data.stream = stdout;
830 	my_out_data.indent = 0;
831 	my_cmdlang.user_data = &my_out_data;
832 	cdone = 0;
833 	done_ptr = &cdone;
834 	printf("> %s", cmdline);
835 	fflush(stdout);
836 	ipmi_cmdlang_handle(&my_cmdlang, cmdline);
837 	while (!cdone) {
838 	    snmp_setup_fds(cmdlang->os_hnd);
839 	    cmdlang->os_hnd->perform_one_op(cmdlang->os_hnd, NULL);
840 	}
841 	done_ptr = NULL;
842     }
843     fclose(s);
844 
845     done_ptr = saved_done_ptr;
846     read_nest--;
847     if (!read_nest) {
848 	handling_input = 1;
849 	enable_term_fd(cmdlang);
850     }
851 
852     ipmi_cmdlang_out(cmd_info, "File read", fname);
853 
854     return;
855 
856  out_err:
857     cmdlang->location = "ipmish.c(read_cmd)";
858 }
859 
860 static void
setup_cmds(void)861 setup_cmds(void)
862 {
863     int rv;
864 
865     rv = ipmi_cmdlang_reg_cmd(NULL,
866 			      "redisp_cmd",
867 			      "on|off - If an asynchronous event comes in,"
868 			      " redisplay the current working command.  This"
869 			      " is on by default.",
870 			      redisp_cmd, NULL, NULL, NULL);
871     if (rv) {
872 	fprintf(stderr, "Error adding exit command: 0x%x\n", rv);
873 	exit(1);
874     }
875 
876     rv = ipmi_cmdlang_reg_cmd(NULL,
877 			      "exit",
878 			      "- leave the program",
879 			      exit_cmd, NULL, NULL, NULL);
880     if (rv) {
881 	fprintf(stderr, "Error adding exit command: 0x%x\n", rv);
882 	exit(1);
883     }
884 
885     rv = ipmi_cmdlang_reg_cmd(NULL,
886 			      "read",
887 			      "<file> - Read commands from the file and"
888 			      " execute them",
889 			      read_cmd, NULL, NULL, NULL);
890     if (rv) {
891 	fprintf(stderr, "Error adding read command: 0x%x\n", rv);
892 	exit(1);
893     }
894 }
895 
896 static void
domain_down(void * cb_data)897 domain_down(void *cb_data)
898 {
899     int *count = cb_data;
900     (*count)--;
901 }
902 
903 static void
shutdown_domain_handler(ipmi_domain_t * domain,void * cb_data)904 shutdown_domain_handler(ipmi_domain_t *domain, void *cb_data)
905 {
906     int *count = cb_data;
907     int rv;
908 
909     rv = ipmi_domain_close(domain, domain_down, cb_data);
910     if (!rv)
911 	(*count)++;
912 }
913 
914 static void
cleanup_sig(int sig)915 cleanup_sig(int sig)
916 {
917     fprintf(stderr, "Exiting due to signal %d\n", sig);
918     done = 0;
919     ipmi_domain_iterate_domains(shutdown_domain_handler, &done);
920     while (done) {
921 	snmp_setup_fds(cmdlang.os_hnd);
922 	cmdlang.os_hnd->perform_one_op(cmdlang.os_hnd, NULL);
923     }
924     cleanup_term();
925     exit(1);
926 }
927 
928 typedef struct exec_list_s
929 {
930     char *str;
931     struct exec_list_s *next;
932 } exec_list_t;
933 static exec_list_t *execs, *execs_tail;
934 
935 static void
add_exec_str(char * str)936 add_exec_str(char *str)
937 {
938     exec_list_t *e;
939 
940     e = malloc(sizeof(*e));
941     if (!e) {
942 	fprintf(stderr, "Out of memory");
943 	exit(1);
944     }
945     e->str = str;
946     e->next = NULL;
947     if (execs)
948 	execs_tail->next = e;
949     else
950 	execs = e;
951     execs_tail = e;
952 }
953 
954 static char *usage_str =
955 "%s is a program that gives access to the OpenIPMI library from a command\n"
956 "line.  It is designed to be script driven.  Format is:\n"
957 "  %s [options]\n"
958 "Options are:\n"
959 "  --execute <string> - execute the given string at startup.  This may be\n"
960 "    entered multiple times for multiple commands.\n"
961 "  -x <string> - same as --execute\n"
962 "  --dlock - turn on lock debugging.\n"
963 "  --dmem - turn on memory debugging.\n"
964 "  --drawmsg - turn on raw message tracing.\n"
965 "  --dmsg - turn on message tracing debugging.\n"
966 "  --dmsgerr - turn on printing out low-level message errors.\n"
967 #ifdef HAVE_GLIB
968 "  --glib - use glib for the OS handler.\n"
969 #endif
970 #ifdef HAVE_TCL
971 "  --tcl - use tcl for the OS handler.\n"
972 #endif
973 #ifdef HAVE_UCDSNMP
974 "  --snmp - turn on SNMP trap handling.\n"
975 #endif
976 "  --help - This output.\n"
977 ;
usage(char * name)978 static void usage(char *name)
979 {
980     fprintf(stderr, usage_str, name, name);
981 }
982 
983 int
main(int argc,char * argv[])984 main(int argc, char *argv[])
985 {
986     int              rv;
987     int              curr_arg = 1;
988     const char       *arg;
989     os_handler_t     *os_hnd;
990     int              use_debug_os = 0;
991     char             *colstr;
992 #ifdef HAVE_GLIB
993     int              use_glib = 0;
994 #endif
995 #ifdef HAVE_TCL
996     int              use_tcl = 0;
997 #endif
998 
999     colstr = getenv("COLUMNS");
1000     if (colstr) {
1001 	int tmp = strtoul(colstr, NULL, 0);
1002 	if (tmp)
1003 	    columns = tmp;
1004     }
1005 
1006     while ((curr_arg < argc) && (argv[curr_arg][0] == '-')) {
1007 	arg = argv[curr_arg];
1008 	curr_arg++;
1009 	if (strcmp(arg, "--") == 0) {
1010 	    break;
1011 	} else if ((strcmp(arg, "-x") == 0) || (strcmp(arg, "--execute") == 0))
1012 	{
1013 	    if (curr_arg >= argc) {
1014 		fprintf(stderr, "No option given for %s", arg);
1015 		usage(argv[0]);
1016 		return 1;
1017 	    }
1018 	    add_exec_str(argv[curr_arg]);
1019 	    curr_arg++;
1020 	} else if (strcmp(arg, "--dlock") == 0) {
1021 	    DEBUG_LOCKS_ENABLE();
1022 	    use_debug_os = 1;
1023 	} else if (strcmp(arg, "--dmem") == 0) {
1024 	    DEBUG_MALLOC_ENABLE();
1025 	} else if (strcmp(arg, "--drawmsg") == 0) {
1026 	    DEBUG_RAWMSG_ENABLE();
1027 	} else if (strcmp(arg, "--dmsg") == 0) {
1028 	    DEBUG_MSG_ENABLE();
1029 	} else if (strcmp(arg, "--dmsgerr") == 0) {
1030 	    DEBUG_MSG_ERR_ENABLE();
1031 #ifdef HAVE_UCDSNMP
1032 	} else if (strcmp(arg, "--snmp") == 0) {
1033 	    do_snmp = 1;
1034 #endif
1035 #ifdef HAVE_GLIB
1036 	} else if (strcmp(arg, "--glib") == 0) {
1037 	    use_glib = 1;
1038 #endif
1039 #ifdef HAVE_TCL
1040 	} else if (strcmp(arg, "--tcl") == 0) {
1041 	    use_tcl = 1;
1042 #endif
1043 	} else if (strcmp(arg, "--help") == 0) {
1044 	    usage(argv[0]);
1045 	    return 0;
1046 	} else {
1047 	    fprintf(stderr, "Unknown option: %s\n", arg);
1048 	    usage(argv[0]);
1049 	    return 1;
1050 	}
1051     }
1052 
1053     rl_initialize();
1054 
1055     if (use_debug_os) {
1056 	os_hnd = &ipmi_debug_os_handlers;
1057 	rv = sel_alloc_selector_nothread(&debug_sel);
1058 	if (rv) {
1059 	    fprintf(stderr, "Could not allocate selector\n");
1060 	    return 1;
1061 	}
1062 #ifdef HAVE_GLIB
1063     } else if (use_glib) {
1064 	g_thread_init(NULL);
1065 	os_hnd = ipmi_glib_get_os_handler(0);
1066 	if (!os_hnd) {
1067 	    fprintf(stderr,
1068 		    "ipmi_smi_setup_con: Unable to allocate os handler\n");
1069 	    return 1;
1070 	}
1071 	g_log_set_handler("OpenIPMI",
1072 			  G_LOG_LEVEL_ERROR
1073 			  | G_LOG_LEVEL_CRITICAL
1074 			  | G_LOG_LEVEL_WARNING
1075 			  | G_LOG_LEVEL_MESSAGE
1076 			  | G_LOG_LEVEL_INFO
1077 			  | G_LOG_LEVEL_DEBUG
1078 			  | G_LOG_FLAG_FATAL,
1079 			  glib_handle_log,
1080                           NULL);
1081 #endif
1082 #ifdef HAVE_TCL
1083     } else if (use_tcl) {
1084 	os_hnd = ipmi_tcl_get_os_handler(0);
1085 	if (!os_hnd) {
1086 	    fprintf(stderr,
1087 		    "ipmi_smi_setup_con: Unable to allocate os handler\n");
1088 	    return 1;
1089 	}
1090 #endif
1091     } else {
1092 	os_hnd = ipmi_posix_setup_os_handler();
1093 	if (!os_hnd) {
1094 	    fprintf(stderr,
1095 		    "ipmi_smi_setup_con: Unable to allocate os handler\n");
1096 	    return 1;
1097 	}
1098     }
1099 
1100     os_hnd->set_log_handler(os_hnd, my_vlog);
1101 
1102     /* Initialize the OpenIPMI library. */
1103     ipmi_init(os_hnd);
1104 
1105 #ifdef HAVE_UCDSNMP
1106     if (do_snmp) {
1107 	if (snmp_init(os_hnd) < 0)
1108 	    return 1;
1109     }
1110 #endif
1111 
1112     rv = ipmi_cmdlang_init(os_hnd);
1113     if (rv) {
1114 	fprintf(stderr, "Unable to initialize command processor: 0x%x\n", rv);
1115 	return 1;
1116     }
1117 
1118     setup_cmds();
1119 
1120     setup_term(os_hnd);
1121 
1122     while (execs) {
1123 	exec_list_t *e = execs;
1124 	int         cdone = 0;
1125 	read_nest = 1;
1126 	execs = e->next;
1127 	printf("> %s\n", e->str);
1128 	fflush(stdout);
1129 	done_ptr = &cdone;
1130 	rl_ipmish_cb_handler(e->str);
1131 	while (!cdone) {
1132 	    snmp_setup_fds(os_hnd);
1133 	    os_hnd->perform_one_op(os_hnd, NULL);
1134 	}
1135 	done_ptr = NULL;
1136 	free(e);
1137 	read_nest = 0;
1138     }
1139 
1140     fflush(stdout);
1141 
1142     handling_input = 1;
1143     enable_term_fd(&cmdlang);
1144 
1145     while (!done) {
1146 	snmp_setup_fds(os_hnd);
1147 	os_hnd->perform_one_op(os_hnd, NULL);
1148     }
1149 
1150     cleanup_term();
1151 
1152     /* Shut down all existing domains. */
1153 
1154     done = 0;
1155     ipmi_domain_iterate_domains(shutdown_domain_handler, &done);
1156     while (done) {
1157 	snmp_setup_fds(os_hnd);
1158 	os_hnd->perform_one_op(os_hnd, NULL);
1159     }
1160 
1161     ipmi_cmdlang_cleanup();
1162     ipmi_shutdown();
1163 
1164     ipmi_debug_malloc_cleanup();
1165 
1166     os_hnd->free_os_handler(os_hnd);
1167 
1168     /* remove the prompt which editline printed */
1169     printf("\b\b  \b\b");
1170     if (evcount)
1171 	printf("\n");
1172     fflush(stdout);
1173 
1174     if (rv)
1175 	return 1;
1176     return 0;
1177 }
1178