File:  [ELWIX - Embedded LightWeight unIX -] / libaitcli / src / aitcli.c
Revision 1.1.1.1.2.11: download - view: text, annotated - select for diffs - revision graph
Wed Apr 28 13:54:45 2010 UTC (14 years, 2 months ago) by misho
Branches: cli1_0
Diff to: branchpoint 1.1.1.1: preferred, unified
removed silly func

    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.11 2010/04/28 13:54: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: #pragma GCC visibility pop
   28: 
   29: 
   30: #ifdef NULL_PREP_TERM
   31: 
   32: static void cli_Null_Prep_Term(int meta)
   33: {
   34: }
   35: 
   36: #endif
   37: 
   38: 
   39: // cli_GetErrno() Get error code of last operation
   40: inline int cli_GetErrno()
   41: {
   42: 	return cli_Errno;
   43: }
   44: 
   45: // io_GetError() Get error text of last operation
   46: inline const char *cli_GetError()
   47: {
   48: 	return cli_Error;
   49: }
   50: 
   51: // cli_SetErr() Set error to variables for internal use!!!
   52: inline void cli_SetErr(int eno, char *estr, ...)
   53: {
   54: 	va_list lst;
   55: 
   56: 	cli_Errno = eno;
   57: 	memset(cli_Error, 0, STRSIZ);
   58: 	va_start(lst, estr);
   59: 	vsnprintf(cli_Error, STRSIZ, estr, lst);
   60: 	va_end(lst);
   61: }
   62: 
   63: // ------------------------------------------------------------
   64: 
   65: /*
   66:  * cli_Printf() Printf CLI features
   67:  * @out = Output stream
   68:  * @csFormat = Printf format string
   69:  * return: -1 error, != -1 printed chars
   70: */
   71: inline int cli_Printf(FILE *out, const char *csFormat, ...)
   72: {
   73: 	va_list lst;
   74: 	int ret;
   75: 
   76: 	va_start(lst, csFormat);
   77: 
   78: 	ret = vfprintf(out, csFormat, lst);
   79: 	if (-1 == ret)
   80: 		LOGERR;
   81: 
   82: 	va_end(lst);
   83: 	return ret;
   84: }
   85: 
   86: 
   87: /*
   88:  * cliComp() Initialize completion CLI features
   89:  * @cmdComplete = Completion function
   90:  * @cmdEntry = Compentry function
   91:  * return: none
   92: */
   93: inline void cliComp(cli_Completion_t *cmdComplete, cli_CompEntry_t *cmdEntry)
   94: {
   95: 	// command completon
   96: 	rl_attempted_completion_function = cmdComplete;
   97: 	rl_completion_entry_function = cmdEntry;
   98: }
   99: 
  100: /*
  101:  * cliTTY() Initialize I/O TTY CLI features
  102:  * @term = terminal name
  103:  * @inp = input handle
  104:  * @out = output handle
  105:  * @win = window size
  106:  * return: -1 error, != -1 ok
  107: */
  108: inline int cliTTY(const char *term, FILE *inp, FILE *out, struct winsize *win)
  109: {
  110: 	if (term)
  111: 		rl_terminal_name = term;
  112: 
  113: 	if (inp)
  114: 		rl_instream = inp;
  115: 	if (out)
  116: 		rl_outstream = out;
  117: 
  118: 	if (win)
  119: 	       if (ioctl(!rl_outstream ? STDOUT_FILENO : fileno(rl_outstream), TIOCSWINSZ, win) == -1) {
  120: 		       LOGERR;
  121: 		       return -1;
  122: 	       }
  123: 
  124: 	return 0;
  125: }
  126: 
  127: /*
  128:  * cli_ReadHistory() Read CLI History from file
  129:  * @csFile = history file name, if NULL default history name is ".aitcli.history"
  130:  * return: -1 error; != -1 readed ok
  131: */
  132: inline int cli_ReadHistory(const char *csFile)
  133: {
  134: 	return read_history(!csFile ? ".aitcli.history" : csFile);
  135: }
  136: 
  137: /*
  138:  * cli_WriteHistory() Write CLI History to file
  139:  * @csFile = history file name, if NULL default history name is ".aitcli.history"
  140:  * @lineNum = save number of history entry lines, if -1 all lines saved without limit
  141:  * return: -1 error; != -1 readed ok
  142: */
  143: inline int cli_WriteHistory(const char *csFile, int lineNum)
  144: {
  145: 	int ret;
  146: 	const char *psFile = !csFile ? ".aitcli.history" : csFile;
  147: 
  148: 	ret = write_history(psFile);
  149: 	if (-1 != ret && -1 != lineNum)
  150: 		history_truncate_file(psFile, lineNum);
  151: 
  152: 	return ret;
  153: }
  154: 
  155: /*
  156:  * cliNetInit() Initialize Readline if CLI bind to socket
  157:  * @csProg = program name
  158:  * @pty = Master pty
  159:  * @term = stdin termios
  160:  * return: none
  161: */
  162: void cliNetInit(const char *csProg, int pty, struct termios *term)
  163: {
  164: 	struct termios t;
  165: 	int on = 1;
  166: 
  167: 	memset(&t, 0, sizeof t);
  168: 	if (term)
  169: 		t = *term;
  170: 	else {
  171: 		t.c_lflag = TTYDEF_LFLAG;
  172: 		t.c_iflag = TTYDEF_IFLAG;
  173: 		t.c_oflag = TTYDEF_OFLAG;
  174: 		cfsetspeed(&t, B9600);
  175: 	}
  176: 
  177: 	t.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO | ECHOCTL | ECHOE | ECHOK | ECHOKE | ECHONL | ECHOPRT);
  178: 	t.c_iflag &= ~(ICRNL | BRKINT | INPCK | ISTRIP | IXON);
  179: 	t.c_oflag &= ~OPOST;
  180: 	t.c_cflag &= ~PARENB;
  181: 	t.c_cc[VMIN] = 1;
  182: 	t.c_cc[VTIME] = 0;
  183: 	tcsetattr(pty, TCSANOW, &t);
  184: 
  185: 	ioctl(pty, TIOCPKT, &on);
  186: 
  187: 	rl_readline_name = csProg;
  188: 	rl_variable_bind("editing-mode", "emacs");
  189: 
  190: #ifdef NULL_PREP_TERM
  191: 	rl_instream = fdopen(pty, "r");
  192: #endif
  193: }
  194: 
  195: /*
  196:  * cliNetExec() Execute net CLI main loop
  197:  * @cmdList = Commands list
  198:  * @csPrompt = Prompt text
  199:  * @sock = client socket
  200:  * @term = stdin termios
  201:  * @win = window size of tty
  202:  * return: -1 error, 0 = exit w/^+D, 1 done.
  203: */
  204: int cliNetExec(cliCommands_t *cmdList, const char *csPrompt, int sock, struct termios *term, struct winsize *win)
  205: {
  206: 	int pty, ret = 0, r, s, alen, attrlen;
  207: 	fd_set fds;
  208: 	struct timeval tv = { DEFAULT_SOCK_TIMEOUT, 0 };
  209: 	u_char buf[BUFSIZ];
  210: 	struct telnetAttrs *a, Attr[5];
  211: 	register int i;
  212: 
  213: 	switch (forkpty(&pty, NULL, term, win)) {
  214: 		case -1:
  215: 			LOGERR;
  216: 			return -1;
  217: 		case 0:
  218: 			close(sock);
  219: 
  220: //			rl_prep_term_function = cli_Null_Prep_Term;
  221: 
  222: 			cliNetInit(getprogname(), STDIN_FILENO, term);
  223: 			cliTTY(NULL, NULL, NULL, win);
  224: //			ret = cliExec(cmdList, csPrompt) < 0 ? 1 : 0;
  225: 			execl("/bin/tcsh", "tcsh", NULL);
  226: 			_exit(ret);
  227: 		default:
  228: 			telnet_SetCmd(Attr, DO, TELOPT_TTYPE);
  229: 			telnet_SetCmd(Attr + 1, WILL, TELOPT_SGA);
  230: 			telnet_SetCmd(Attr + 2, DO, TELOPT_LFLOW);
  231: 			telnet_SetCmd(Attr + 3, DO, TELOPT_LINEMODE);
  232: 			telnet_Set_SubOpt(Attr + 4, TELOPT_LINEMODE, LM_MODE, "\x0", 1);
  233: 			if ((ret = telnetSend(sock, Attr, 5, NULL, 0, 0)) == -1) {
  234: 				cli_Errno = telnet_GetErrno();
  235: 				strlcpy(cli_Error, telnet_GetError(), STRSIZ);
  236: 				return -1;
  237: 			}
  238: 
  239: 			while (42) {
  240: 				FD_ZERO(&fds);
  241: 				FD_SET(sock, &fds);
  242: 				FD_SET(pty, &fds);
  243: 				if ((ret = select(FD_SETSIZE, &fds, NULL, NULL, &tv)) < 1) {
  244: 					if (!ret)
  245: 						cli_SetErr(ETIMEDOUT, "Client session timeout ...");
  246: 
  247: 					break;
  248: 				}
  249: 
  250: 				r = FD_ISSET(sock, &fds) ? sock : pty;
  251: 				s = FD_ISSET(sock, &fds) ? pty : sock;
  252: 
  253: 				if ((ret = telnetRecv(r, &a, &alen, buf, BUFSIZ)) < 0) {
  254: 					if (a)
  255: 						free(a);
  256: 
  257: 					if (-2 == ret)
  258: 						continue;
  259: 					// EOF
  260: 					if (-3 == ret)
  261: 						shutdown(r, SHUT_RD);
  262: 					else {
  263: 						cli_Errno = telnet_GetErrno();
  264: 						strlcpy(cli_Error, telnet_GetError(), STRSIZ);
  265: 					}
  266: 					break;
  267: 				}
  268: 				for (attrlen = i = 0; i < alen; i++) {
  269: 					if (TELOPT_TTYPE == a[i].ta_opt && WILL == a[i].ta_cmd)
  270: 						telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_TTYPE, TELQUAL_SEND, NULL, 0);
  271: 		//			if (TELOPT_LINEMODE == a[i].ta_opt && WILL == a[i].ta_cmd)
  272: 					if (TELOPT_LINEMODE == a[i].ta_cmd && SB == a[i].ta_cmd) {
  273: 						telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LINEMODE, LM_SLC, 
  274: 								"\x1\x0\x0\x3\xe2\x3\x4\x0\x0\x5\x0\x0\x7\xe2\x1c"
  275: 								"\x8\x82\x4\x9\x0\x0\xa\x82\x7f\xb\x82\x15\xc\x82\x17"
  276: 								"\xd\x82\x12\xe\x82\x16\xf\x82\x11\x10\x82\x13", 42);
  277: 					}
  278: 				}
  279: 				if (a)
  280: 					free(a);
  281: 
  282: 				/*
  283: 				if (s == sock && ret > 2 && 0x1b == buf[0] && 0x5b == buf[1]) {
  284: 					memmove(buf, buf + 3, ret);
  285: 					ret -= 3;
  286: 				}
  287: 				*/
  288: #include "syslog.h"
  289: 				int j;
  290: 				for (j = 0; j < ret; j++)
  291: 					syslog(LOG_CRIT, "prepare to send %d = %X", j, buf[j]);
  292: 				syslog(LOG_CRIT, "send packet %d === %s ========", ret, r == pty ? "pty -> sock" : "sock -> pty");
  293: 				if ((ret = telnetSend(s, Attr, attrlen, buf, ret, 0)) == -1) {
  294: 					cli_Errno = telnet_GetErrno();
  295: 					strlcpy(cli_Error, telnet_GetError(), STRSIZ);
  296: 					break;
  297: 				}
  298: 
  299: 				/*
  300: 				if ((ret = read(r, &ch, 1)) < 1) {
  301: 					if (!ret)
  302: 						shutdown(r, SHUT_RD);
  303: 					break;
  304: 				}
  305: 				if (write(s, &ch, 1) == -1) 
  306: 					break;
  307: 				*/
  308: 			}
  309: 
  310: 			close(pty);
  311: 	}
  312: 
  313: 	return ret;
  314: }
  315: 
  316: /*
  317:  * cliExec() Execute CLI main loop
  318:  * @cmdList = Commands list
  319:  * @csPrompt = Prompt text
  320:  * return: -1 error, 0 = exit w/^+D, 1 done.
  321: */
  322: int cliExec(cliCommands_t *cmdList, const char *csPrompt)
  323: {
  324: 	char *line, *s, *t, **app, *items[MAX_PROMPT_ITEMS];
  325: 	int ret = 0;
  326: 	register int i;
  327: 	cliCommands_t *cmd = NULL;
  328: 	FILE *out;
  329: 
  330: 	inline int inline_help()
  331: 	{
  332: 		cli_Cmd_Help(cmdList ? cmdList : cli_stdCmds, -1, out, NULL);
  333: 		rl_on_new_line();
  334: 		return 0;
  335: 	}
  336: 
  337: 	char **cli_stdCompletion(const char *text, int start, int end)
  338: 	{
  339: 		register int i;
  340: 		char **matches = NULL;
  341: 
  342: 		char *cmdCompGet(const char *text, int state)
  343: 		{
  344: 			int len = strlen(text);
  345: 
  346: 			for (i = state; cmdList[i].cmd_name; i++) {
  347: 				if (strncmp(cmdList[i].cmd_name, "---", 3) && 
  348: 						!strncmp(cmdList[i].cmd_name, text, len))
  349: 					return strdup(cmdList[i].cmd_name);
  350: 			}
  351: 
  352: 			return NULL;
  353: 		}
  354: 
  355: 		if (!start)
  356: 			matches = rl_completion_matches(text, cmdCompGet);
  357: 		else
  358: 			for (i = 0; cmdList[i].cmd_name; i++) {
  359: 				if (!cmdList[i].cmd_comp)
  360: 					continue;
  361: 				if (!strncmp(rl_line_buffer, cmdList[i].cmd_name, strlen(cmdList[i].cmd_name)))
  362: 					matches = rl_completion_matches(text, cmdList[i].cmd_comp);
  363: 			}
  364: 
  365: 		return matches;
  366: 	}
  367: 	char *cli_stdCompEntry(const char *ignore, int invoking_key)
  368: 	{
  369: 		return NULL;
  370: 	}
  371: 
  372: 	/* --- main body of CLI --- */
  373: 
  374: 	out = rl_outstream;
  375: 	if (!out)
  376: 		out = stdout;
  377: 
  378: 	rl_bind_key('?', inline_help);
  379: 	if (!rl_attempted_completion_function) 
  380: 		cliComp(cli_stdCompletion, cli_stdCompEntry);
  381: 
  382: 	do {
  383: 		line = readline(csPrompt);
  384: 		if (!line) {	// ^+d
  385: 			cli_Printf(out, "\n");
  386: 			break;
  387: 		}
  388: 		// clear whitespaces
  389: 		for (s = line; isspace(*s); s++);
  390: 		if (*s) {
  391: 			for (t = s + strlen(s) - 1; t > s && isspace(*t); t--);
  392: 			*++t = 0;
  393: 		}
  394: 
  395: 		if (*s) {
  396: 			add_history(s);
  397: 
  398: 			memset(items, 0, sizeof(char*) * MAX_PROMPT_ITEMS);
  399: 			for (app = items; app < items + MAX_PROMPT_ITEMS - 1 && (*app = strsep(&s, " \t")); 
  400: 					*app ? app++ : app);
  401: 
  402: 			/*
  403: 			for (i = 0; i < MAX_PROMPT_ITEMS; i++)
  404: 				cli_Printf(out, "i=%d %s\n", i, items[i]);
  405: 				*/
  406: 
  407: 			// exec_cmd ...
  408: 			for (cmd = NULL, i = 0; cmdList[i].cmd_name; i++)
  409: 				if (*items[0] && !strncmp(cmdList[i].cmd_name, items[0], strlen(items[0]))) {
  410: 					cmd = &cmdList[i];
  411: 					break;
  412: 				}
  413: 			if (!cmd) {
  414: 				cli_Printf(out, "Command '%s' not found!\n", items[0]);
  415: 				ret = -1;
  416: 			} else
  417: 				ret = cmd->cmd_func(cmdList, i, out, items);
  418: 		}
  419: 
  420: 		free(line);
  421: 	} while (ret < 1);
  422: 
  423: 	return ret;
  424: }

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