File:  [ELWIX - Embedded LightWeight unIX -] / libaitcli / src / aitcli.c
Revision 1.1.1.1.2.16: download - view: text, annotated - select for diffs - revision graph
Fri Jun 4 11:25:45 2010 UTC (14 years, 1 month ago) by misho
Branches: cli1_0
Diff to: branchpoint 1.1.1.1: preferred, unified
final for version with readline

    1: /*************************************************************************
    2: * (C) 2010 AITNET ltd - Sofia/Bulgaria - <misho@aitbg.com>
    3: *  by Michael Pounov <misho@openbsd-bg.org>
    4: *
    5: * $Author: misho $
    6: * $Id: aitcli.c,v 1.1.1.1.2.16 2010/06/04 11:25:45 misho Exp $
    7: *
    8: *************************************************************************/
    9: #include "global.h"
   10: 
   11: 
   12: #pragma GCC visibility push(hidden)
   13: 
   14: cliCommands_t cli_stdCmds[] = {
   15: 	{ "test", cli_Cmd_Unsupported, "Test - Don`t use default command structure!", "test <cr>", cli_Comp_Filename }, 
   16: 	{ "-------", NULL, "---------------------", NULL, NULL }, 
   17: 	{ "help", cli_Cmd_Help, "Help screen", "help [command] <cr>", NULL }, 
   18: 	{ "exit", cli_Cmd_Exit, "Exit from console", "exit <cr>", NULL }, 
   19: 	{ NULL, NULL, NULL, NULL }
   20: };
   21: 
   22: // ------------------------------------------------
   23: 
   24: int cli_Errno;
   25: char cli_Error[STRSIZ];
   26: 
   27: char cli_pending_special_char;
   28: 
   29: #pragma GCC visibility pop
   30: 
   31: 
   32: static void cli_Null_Prep_Term(int meta)
   33: {
   34: }
   35: 
   36: #include <syslog.h>
   37: static int cli_Net_rl_GetCh(FILE *s)
   38: {
   39: 	int ch = rl_getc(s);
   40: 
   41: 	if (!cli_pending_special_char && 0x1b == ch) {
   42: 		cli_pending_special_char = ch;
   43: 		return ch;
   44: 	}
   45: 	if (cli_pending_special_char && 0x5b == ch) {
   46: 		cli_pending_special_char = ch;
   47: 		return ch;
   48: 	}
   49: 	if (0x5b == cli_pending_special_char) {
   50: 		cli_pending_special_char = 0;
   51: 		return ch;
   52: 	}
   53: 
   54: 	syslog(LOG_CRIT, "+++++ getc=%0x\n", ch);
   55: 	cli_pending_special_char = 0;
   56: 	fputc(ch, rl_outstream);
   57: 	fflush(rl_outstream);
   58: 	return ch;
   59: }
   60: 
   61: #if 0
   62: static void cli_Line_Handler(char *line)
   63: {
   64: 	int len;
   65: 	static char cli_Buffer[BUFSIZ];
   66: 	static int cli_BufferLen, cli_BufferPos;
   67: 
   68: 	if (!line) {	// EOF
   69: 		fwrite("\x4", 1, 1, rl_outstream);	// ctrl+D
   70: 		goto end;
   71: 	} else
   72: 		len = strlen(line);
   73: 	if (BUFSIZ - 2 < len)
   74: 		cli_BufferPos = cli_BufferLen = 0;
   75: 	else {
   76: 		if (BUFSIZ - 2 < len + cli_BufferLen) {
   77: 			if (BUFSIZ - 2 >= cli_BufferLen - cli_BufferPos + len) {
   78: 				cli_BufferLen -= cli_BufferPos;
   79: 				memmove(cli_Buffer, cli_Buffer + cli_BufferPos, cli_BufferLen);
   80: 			} else
   81: 				cli_BufferLen = 0;
   82: 
   83: 			cli_BufferPos = 0;
   84: 		}
   85: 
   86: 		memcpy(cli_Buffer + cli_BufferLen, line, len);
   87: 		cli_BufferLen += len;
   88: 		cli_Buffer[cli_BufferLen++] = '\r';
   89: 		cli_Buffer[cli_BufferLen++] = '\n';
   90: 	}
   91: 
   92: 	fwrite(cli_Buffer, 1, cli_BufferLen, rl_outstream);
   93: 	if (!cli_pending_special_char) {
   94: 		fwrite("\r", 1, 1, rl_outstream);
   95: 		if (*line)
   96: 			add_history(line);
   97: 	}
   98: 
   99: 	free(line);
  100: end:
  101: 	rl_callback_handler_remove();
  102: 	if (cli_pending_special_char) {
  103: 		fwrite(&cli_pending_special_char, 1, 1, rl_outstream);
  104: 		cli_pending_special_char = 0;
  105: 	}
  106: 	fflush(rl_outstream);
  107: }
  108: #endif
  109: 
  110: 
  111: // cli_GetErrno() Get error code of last operation
  112: inline int cli_GetErrno()
  113: {
  114: 	return cli_Errno;
  115: }
  116: 
  117: // io_GetError() Get error text of last operation
  118: inline const char *cli_GetError()
  119: {
  120: 	return cli_Error;
  121: }
  122: 
  123: // cli_SetErr() Set error to variables for internal use!!!
  124: inline void cli_SetErr(int eno, char *estr, ...)
  125: {
  126: 	va_list lst;
  127: 
  128: 	cli_Errno = eno;
  129: 	memset(cli_Error, 0, STRSIZ);
  130: 	va_start(lst, estr);
  131: 	vsnprintf(cli_Error, STRSIZ, estr, lst);
  132: 	va_end(lst);
  133: }
  134: 
  135: // ------------------------------------------------------------
  136: 
  137: /*
  138:  * cli_Printf() Printf CLI features
  139:  * @out = Output stream
  140:  * @csFormat = Printf format string
  141:  * return: -1 error, != -1 printed chars
  142: */
  143: inline int cli_Printf(FILE *out, const char *csFormat, ...)
  144: {
  145: 	va_list lst;
  146: 	int ret;
  147: 
  148: 	va_start(lst, csFormat);
  149: 
  150: 	ret = vfprintf(out, csFormat, lst);
  151: 	if (-1 == ret)
  152: 		LOGERR;
  153: 
  154: 	va_end(lst);
  155: 	return ret;
  156: }
  157: 
  158: 
  159: /*
  160:  * cliComp() Initialize completion CLI features
  161:  * @cmdComplete = Completion function
  162:  * @cmdEntry = Compentry function
  163:  * return: none
  164: */
  165: inline void cliComp(cli_Completion_t *cmdComplete, cli_CompEntry_t *cmdEntry)
  166: {
  167: 	// command completon
  168: 	rl_attempted_completion_function = cmdComplete;
  169: 	rl_completion_entry_function = cmdEntry;
  170: }
  171: 
  172: /*
  173:  * cliTTY() Initialize I/O TTY CLI features
  174:  * @term = terminal name
  175:  * @inp = input handle
  176:  * @out = output handle
  177:  * @win = window size
  178:  * return: -1 error, != -1 ok
  179: */
  180: inline int cliTTY(const char *term, FILE *inp, FILE *out, struct winsize *win)
  181: {
  182: 	if (term)
  183: 		rl_terminal_name = term;
  184: 
  185: 	if (inp)
  186: 		rl_instream = inp;
  187: 	if (out)
  188: 		rl_outstream = out;
  189: 
  190: 	if (win)
  191: 	       if (ioctl(!rl_outstream ? STDOUT_FILENO : fileno(rl_outstream), TIOCSWINSZ, win) == -1) {
  192: 		       LOGERR;
  193: 		       return -1;
  194: 	       }
  195: 
  196: 	return 0;
  197: }
  198: 
  199: /*
  200:  * cli_ReadHistory() Read CLI History from file
  201:  * @csFile = history file name, if NULL default history name is ".aitcli.history"
  202:  * return: -1 error; != -1 readed ok
  203: */
  204: inline int cli_ReadHistory(const char *csFile)
  205: {
  206: 	return read_history(!csFile ? ".aitcli.history" : csFile);
  207: }
  208: 
  209: /*
  210:  * cli_WriteHistory() Write CLI History to file
  211:  * @csFile = history file name, if NULL default history name is ".aitcli.history"
  212:  * @lineNum = save number of history entry lines, if -1 all lines saved without limit
  213:  * return: -1 error; != -1 readed ok
  214: */
  215: inline int cli_WriteHistory(const char *csFile, int lineNum)
  216: {
  217: 	int ret;
  218: 	const char *psFile = !csFile ? ".aitcli.history" : csFile;
  219: 
  220: 	ret = write_history(psFile);
  221: 	if (-1 != ret && -1 != lineNum)
  222: 		history_truncate_file(psFile, lineNum);
  223: 
  224: 	return ret;
  225: }
  226: 
  227: /*
  228:  * cliNetInit() Initialize Readline if CLI bind to socket
  229:  * @csProg = program name
  230:  * @pty = Master pty
  231:  * @term = stdin termios
  232:  * return: none
  233: */
  234: void cliNetInit(const char *csProg, int pty, struct termios *term)
  235: {
  236: 	struct termios t;
  237: 	int on = 1;
  238: 
  239: 	memset(&t, 0, sizeof t);
  240: 	if (term)
  241: 		t = *term;
  242: 	else {
  243: 		t.c_lflag = TTYDEF_LFLAG;
  244: 		t.c_iflag = TTYDEF_IFLAG;
  245: 		t.c_oflag = TTYDEF_OFLAG;
  246: 		t.c_cflag = TTYDEF_CFLAG;
  247: 		cfsetspeed(&t, B9600);
  248: 	}
  249: 
  250: 	t.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO | ECHOCTL | ECHOE | ECHOK | ECHOKE | ECHONL | ECHOPRT);
  251: //	t.c_iflag &= ~(ICRNL | BRKINT | INPCK | ISTRIP | IXON);
  252: 	t.c_iflag &= ~ICRNL;
  253: 	t.c_iflag |= IGNBRK;
  254: 	t.c_cc[VMIN] = 1;
  255: 	t.c_cc[VTIME] = 0;
  256: 	tcsetattr(pty, TCSANOW, &t);
  257: 
  258: 	ioctl(pty, TIOCPKT, &on);
  259: 
  260: 	rl_readline_name = csProg;
  261: 	rl_variable_bind("editing-mode", "emacs");
  262: 
  263: 	rl_instream = fdopen(pty, "r");
  264: 	rl_outstream = NULL;
  265: }
  266: 
  267: /*
  268:  * cliNetExec() Execute net CLI main loop
  269:  * @cmdList = Commands list
  270:  * @csPrompt = Prompt text
  271:  * @sock = client socket
  272:  * @term = stdin termios
  273:  * @win = window size of tty
  274:  * return: -1 error, 0 = exit w/^+D, 1 done.
  275: */
  276: int cliNetExec(cliCommands_t *cmdList, const char *csPrompt, int sock, struct termios *term, struct winsize *win)
  277: {
  278: 	int pty, ret = 0, alen, attrlen, flg;
  279: 	fd_set fds;
  280: 	struct timeval tv = { DEFAULT_SOCK_TIMEOUT, 0 };
  281: 	u_char buf[BUFSIZ];
  282: 	struct telnetAttrs *a, Attr[10];
  283: 
  284: 	switch (forkpty(&pty, NULL, term, win)) {
  285: 		case -1:
  286: 			LOGERR;
  287: 			return -1;
  288: 		case 0:
  289: 			close(sock);
  290: 
  291: 			rl_prep_term_function = cli_Null_Prep_Term;
  292: 			rl_getc_function = cli_Net_rl_GetCh;
  293: 
  294: 			cliNetInit(getprogname(), STDIN_FILENO, term);
  295: 			ret = cliExec(cmdList, csPrompt) < 0 ? 1 : 0;
  296: 			/* spawn Shell mode */
  297: 			/*
  298: 			execl("/bin/tcsh", "tcsh", NULL);
  299: 			*/
  300: 			_exit(ret);
  301: 		default:
  302: 			cliNetInit(getprogname(), pty, term);
  303: 
  304: 			/* spawn Shell mode */
  305: 			/*
  306: 			telnet_SetCmd(Attr + 0, DO, TELOPT_TTYPE);
  307: 			telnet_SetCmd(Attr + 1, WILL, TELOPT_ECHO);
  308: 			telnet_Set_SubOpt(Attr + 2, TELOPT_LFLOW, LFLOW_OFF, NULL, 0);
  309: 			telnet_Set_SubOpt(Attr + 3, TELOPT_LFLOW, LFLOW_RESTART_XON, NULL, 0);
  310: 			telnet_SetCmd(Attr + 4, DO, TELOPT_LINEMODE);
  311: 			*/
  312: 			telnet_SetCmd(Attr + 0, DO, TELOPT_TTYPE);
  313: 			telnet_SetCmd(Attr + 1, WILL, TELOPT_ECHO);
  314: 			telnet_Set_SubOpt(Attr + 2, TELOPT_LFLOW, LFLOW_OFF, NULL, 0);
  315: 			telnet_Set_SubOpt(Attr + 3, TELOPT_LFLOW, LFLOW_RESTART_XON, NULL, 0);
  316: 			telnet_SetCmd(Attr + 4, DO, TELOPT_LINEMODE);
  317: 			if ((ret = telnetSend(sock, Attr, 5, NULL, 0, 0)) == -1) {
  318: 				cli_Errno = telnet_GetErrno();
  319: 				strlcpy(cli_Error, telnet_GetError(), STRSIZ);
  320: 				return -1;
  321: 			} else
  322: 				flg = 0;
  323: 
  324: 			while (42) {
  325: 				FD_ZERO(&fds);
  326: 				FD_SET(sock, &fds);
  327: 				FD_SET(pty, &fds);
  328: 				if ((ret = select(FD_SETSIZE, &fds, NULL, NULL, &tv)) < 1) {
  329: 					if (!ret)
  330: 						cli_SetErr(ETIMEDOUT, "Client session timeout ...");
  331: 
  332: 					break;
  333: 				}
  334: 
  335: 				if (FD_ISSET(sock, &fds)) {
  336: 					memset(buf, 0, BUFSIZ);
  337: 					if ((ret = telnetRecv(sock, &a, &alen, buf, BUFSIZ)) < 0) {
  338: 						if (a)
  339: 							free(a);
  340: 
  341: 						if (-2 == ret)
  342: 							continue;
  343: 						// EOF
  344: 						if (-3 == ret)
  345: 							shutdown(sock, SHUT_RD);
  346: 						else {
  347: 							cli_Errno = telnet_GetErrno();
  348: 							strlcpy(cli_Error, telnet_GetError(), STRSIZ);
  349: 						}
  350: 						break;
  351: 					}
  352: 					if (a)
  353: 						free(a);
  354: 					if (alen) {
  355: 						attrlen = 0;
  356: 						if (1 == flg && alen) {
  357: 							telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_SGA);
  358: 							telnet_SetCmd(&Attr[attrlen++], DO, TELOPT_ECHO);
  359: 						}
  360: 						if (2 == flg && alen) {
  361: 							telnet_SetCmd(&Attr[attrlen++], WILL, TELOPT_ECHO);
  362: 							telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW, 
  363: 									LFLOW_OFF, NULL, 0);
  364: 							telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW, 
  365: 									LFLOW_RESTART_XON, NULL, 0);
  366: 							telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_LINEMODE);
  367: 						}
  368: 						if ((ret = telnetSend(sock, Attr, attrlen, buf, ret, 0)) == -1) {
  369: 							cli_Errno = telnet_GetErrno();
  370: 							strlcpy(cli_Error, telnet_GetError(), STRSIZ);
  371: 							break;
  372: 						}
  373: 					}
  374: syslog(LOG_CRIT, "alen=%d ret=%d b0=%x b1=%x b2=%x\n", alen, ret, buf[0], buf[1], buf[2]);
  375: 
  376: 					if ((ret = write(pty, buf, ret)) == -1) {
  377: 						LOGERR;
  378: 						break;
  379: 					}
  380: 				}
  381: 
  382: 				if (FD_ISSET(pty, &fds)) {
  383: 					memset(buf, 0, BUFSIZ);
  384: 					if ((ret = read(pty, buf, BUFSIZ)) < 1) {
  385: 						if (!ret)
  386: 							shutdown(sock, SHUT_WR);
  387: 						else
  388: 							LOGERR;
  389: 						break;
  390: 					}
  391: syslog(LOG_CRIT, "ret=%d b0=%x b1=%x b2=%x\n", ret, buf[0], buf[1], buf[2]);
  392: 					attrlen = 0;
  393: 					if (1 == flg && alen) {
  394: 						telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_SGA);
  395: 						telnet_SetCmd(&Attr[attrlen++], DO, TELOPT_ECHO);
  396: 					}
  397: 					if (2 == flg && alen) {
  398: 						telnet_SetCmd(&Attr[attrlen++], WILL, TELOPT_ECHO);
  399: 						telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW, 
  400: 								LFLOW_OFF, NULL, 0);
  401: 						telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW, 
  402: 								LFLOW_RESTART_XON, NULL, 0);
  403: 						telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_LINEMODE);
  404: 					}
  405: 					if ((ret = telnetSend(sock, Attr, attrlen, buf, ret, 0)) == -1) {
  406: 						cli_Errno = telnet_GetErrno();
  407: 						strlcpy(cli_Error, telnet_GetError(), STRSIZ);
  408: 						break;
  409: 					}
  410: 				}
  411: 			}
  412: 
  413: 			close(pty);
  414: 	}
  415: 
  416: 	return ret;
  417: }
  418: 
  419: /*
  420:  * cliExec() Execute CLI main loop
  421:  * @cmdList = Commands list
  422:  * @csPrompt = Prompt text
  423:  * return: -1 error, 0 = exit w/^+D, 1 done.
  424: */
  425: int cliExec(cliCommands_t *cmdList, const char *csPrompt)
  426: {
  427: 	char *line, *s, *t, **app, *items[MAX_PROMPT_ITEMS];
  428: 	int ret = 0;
  429: 	register int i;
  430: 	cliCommands_t *cmd = NULL;
  431: 	FILE *out;
  432: 
  433: 	inline int inline_help()
  434: 	{
  435: 		cli_Cmd_Help(cmdList ? cmdList : cli_stdCmds, -1, out, NULL);
  436: 		rl_on_new_line();
  437: 		return 0;
  438: 	}
  439: 
  440: 	char **cli_stdCompletion(const char *text, int start, int end)
  441: 	{
  442: 		register int i;
  443: 		char **matches = NULL;
  444: 
  445: 		char *cmdCompGet(const char *text, int state)
  446: 		{
  447: 			int len = strlen(text);
  448: 
  449: 			for (i = state; cmdList[i].cmd_name; i++) {
  450: 				if (strncmp(cmdList[i].cmd_name, "---", 3) && 
  451: 						!strncmp(cmdList[i].cmd_name, text, len))
  452: 					return strdup(cmdList[i].cmd_name);
  453: 			}
  454: 
  455: 			return NULL;
  456: 		}
  457: 
  458: 		if (!start)
  459: 			matches = rl_completion_matches(text, cmdCompGet);
  460: 		else
  461: 			for (i = 0; cmdList[i].cmd_name; i++) {
  462: 				if (!cmdList[i].cmd_comp)
  463: 					continue;
  464: 				if (!strncmp(rl_line_buffer, cmdList[i].cmd_name, strlen(cmdList[i].cmd_name)))
  465: 					matches = rl_completion_matches(text, cmdList[i].cmd_comp);
  466: 			}
  467: 
  468: 		return matches;
  469: 	}
  470: 	char *cli_stdCompEntry(const char *ignore, int invoking_key)
  471: 	{
  472: 		return NULL;
  473: 	}
  474: 
  475: 	/* --- main body of CLI --- */
  476: 
  477: 	out = rl_outstream;
  478: 	if (!out)
  479: 		out = stdout;
  480: 
  481: 	rl_bind_key('?', inline_help);
  482: 	if (!rl_attempted_completion_function) 
  483: 		cliComp(cli_stdCompletion, cli_stdCompEntry);
  484: 
  485: 	do {
  486: 		line = readline(csPrompt);
  487: 		if (!line) {	// ^+d
  488: 			cli_Printf(out, "\n");
  489: 			break;
  490: 		}
  491: 		// clear whitespaces
  492: 		for (s = line; isspace(*s); s++);
  493: 		if (*s) {
  494: 			for (t = s + strlen(s) - 1; t > s && isspace(*t); t--);
  495: 			*++t = 0;
  496: 		}
  497: 
  498: 		if (*s) {
  499: 			add_history(s);
  500: 
  501: 			memset(items, 0, sizeof(char*) * MAX_PROMPT_ITEMS);
  502: 			for (app = items; app < items + MAX_PROMPT_ITEMS - 1 && (*app = strsep(&s, " \t")); 
  503: 					*app ? app++ : app);
  504: 
  505: 			/*
  506: 			for (i = 0; i < MAX_PROMPT_ITEMS; i++)
  507: 				cli_Printf(out, "i=%d %s\n", i, items[i]);
  508: 				*/
  509: 
  510: 			// exec_cmd ...
  511: 			for (cmd = NULL, i = 0; cmdList[i].cmd_name; i++)
  512: 				if (*items[0] && !strncmp(cmdList[i].cmd_name, items[0], strlen(items[0]))) {
  513: 					cmd = &cmdList[i];
  514: 					break;
  515: 				}
  516: 			if (!cmd) {
  517: 				cli_Printf(out, "Command '%s' not found!\n", items[0]);
  518: 				ret = -1;
  519: 			} else
  520: 				ret = cmd->cmd_func(cmdList, i, out, items);
  521: 		}
  522: 
  523: 		free(line);
  524: 	} while (ret < 1);
  525: 
  526: 	return ret;
  527: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>