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