Annotation of libaitcli/src/aitcli.c, revision 1.2.2.1
1.1 misho 1: /*************************************************************************
2: * (C) 2010 AITNET ltd - Sofia/Bulgaria - <misho@aitbg.com>
3: * by Michael Pounov <misho@openbsd-bg.org>
4: *
5: * $Author: misho $
1.2.2.1 ! misho 6: * $Id: aitcli.c,v 1.2 2010/06/04 11:32:47 misho Exp $
1.1 misho 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:
1.2 misho 27: char cli_pending_special_char;
28:
1.1 misho 29: #pragma GCC visibility pop
30:
31:
1.2 misho 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:
1.1 misho 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
1.2 misho 141: * return: -1 error, != -1 printed chars
1.1 misho 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: /*
1.2 misho 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: */
1.2.2.1 ! misho 276: int
! 277: cliNetExec(cliCommands_t *cmdList, const char *csPrompt, int sock, struct termios *term, struct winsize *win)
1.2 misho 278: {
279: int pty, ret = 0, r, s, alen, attrlen, flg;
280: fd_set fds;
281: struct timeval tv = { DEFAULT_SOCK_TIMEOUT, 0 };
282: u_char buf[BUFSIZ];
283: struct telnetAttrs *a, Attr[10];
284:
285: switch (forkpty(&pty, NULL, term, win)) {
286: case -1:
287: LOGERR;
288: return -1;
289: case 0:
290: close(sock);
291:
292: ret = cliExec(cmdList, csPrompt) < 0 ? 1 : 0;
293: /* spawn Shell mode */
294: /*
295: execl("/bin/tcsh", "tcsh", NULL);
296: */
297: _exit(ret);
298: default:
299: /* spawn Shell mode */
300: telnet_SetCmd(Attr + 0, DO, TELOPT_TTYPE);
301: telnet_SetCmd(Attr + 1, WILL, TELOPT_ECHO);
302: telnet_Set_SubOpt(Attr + 2, TELOPT_LFLOW, LFLOW_OFF, NULL, 0);
303: telnet_Set_SubOpt(Attr + 3, TELOPT_LFLOW, LFLOW_RESTART_XON, NULL, 0);
304: telnet_SetCmd(Attr + 4, DO, TELOPT_LINEMODE);
305: if ((ret = telnetSend(sock, Attr, 5, NULL, 0, 0)) == -1) {
306: cli_Errno = telnet_GetErrno();
307: strlcpy(cli_Error, telnet_GetError(), STRSIZ);
308: return -1;
309: } else
310: flg = 0;
311:
312: while (42) {
313: FD_ZERO(&fds);
314: FD_SET(sock, &fds);
315: FD_SET(pty, &fds);
316: if ((ret = select(FD_SETSIZE, &fds, NULL, NULL, &tv)) < 1) {
317: if (!ret)
318: cli_SetErr(ETIMEDOUT, "Client session timeout ...");
319:
320: break;
321: }
322:
323: r = FD_ISSET(sock, &fds) ? sock : pty;
324: s = FD_ISSET(sock, &fds) ? pty : sock;
325:
326: if ((ret = telnetRecv(r, &a, &alen, buf, BUFSIZ)) < 0) {
327: if (a)
328: free(a);
329:
330: if (-2 == ret)
331: continue;
332: // EOF
333: if (-3 == ret)
334: shutdown(r, SHUT_RD);
335: else {
336: cli_Errno = telnet_GetErrno();
337: strlcpy(cli_Error, telnet_GetError(), STRSIZ);
338: }
339: break;
340: }
341: attrlen = 0;
342: if (1 == flg && alen) {
343: telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_SGA);
344: telnet_SetCmd(&Attr[attrlen++], DO, TELOPT_ECHO);
345: }
346: if (2 == flg && alen) {
347: telnet_SetCmd(&Attr[attrlen++], WILL, TELOPT_ECHO);
348: telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW,
349: LFLOW_OFF, NULL, 0);
350: telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW,
351: LFLOW_RESTART_XON, NULL, 0);
352: telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_LINEMODE);
353: }
354: if (a)
355: free(a);
356:
357: if ((ret = telnetSend(s, Attr, pty == s ? 0 : attrlen, buf, ret, 0)) == -1) {
358: cli_Errno = telnet_GetErrno();
359: strlcpy(cli_Error, telnet_GetError(), STRSIZ);
360: break;
361: } else
362: flg++;
363: }
364:
365: close(pty);
366: }
367:
368: return ret;
369: }
370:
371: /*
1.1 misho 372: * cliExec() Execute CLI main loop
373: * @cmdList = Commands list
374: * @csPrompt = Prompt text
375: * return: -1 error, 0 = exit w/^+D, 1 done.
376: */
1.2 misho 377: int cliExec(cliCommands_t *cmdList, const char *csPrompt)
1.1 misho 378: {
379: char *line, *s, *t, **app, *items[MAX_PROMPT_ITEMS];
380: int ret = 0;
381: register int i;
382: cliCommands_t *cmd = NULL;
1.2 misho 383: FILE *out;
1.1 misho 384:
385: inline int inline_help()
386: {
387: cli_Cmd_Help(cmdList ? cmdList : cli_stdCmds, -1, out, NULL);
388: rl_on_new_line();
389: return 0;
390: }
391:
392: char **cli_stdCompletion(const char *text, int start, int end)
393: {
394: register int i;
395: char **matches = NULL;
396:
397: char *cmdCompGet(const char *text, int state)
398: {
399: int len = strlen(text);
400:
401: for (i = state; cmdList[i].cmd_name; i++) {
402: if (strncmp(cmdList[i].cmd_name, "---", 3) &&
403: !strncmp(cmdList[i].cmd_name, text, len))
404: return strdup(cmdList[i].cmd_name);
405: }
406:
407: return NULL;
408: }
409:
410: if (!start)
411: matches = rl_completion_matches(text, cmdCompGet);
412: else
413: for (i = 0; cmdList[i].cmd_name; i++) {
414: if (!cmdList[i].cmd_comp)
415: continue;
416: if (!strncmp(rl_line_buffer, cmdList[i].cmd_name, strlen(cmdList[i].cmd_name)))
417: matches = rl_completion_matches(text, cmdList[i].cmd_comp);
418: }
419:
420: return matches;
421: }
422: char *cli_stdCompEntry(const char *ignore, int invoking_key)
423: {
424: return NULL;
425: }
426:
427: /* --- main body of CLI --- */
428:
1.2 misho 429: out = rl_outstream;
430: if (!out)
431: out = stdout;
432:
1.1 misho 433: rl_bind_key('?', inline_help);
434: if (!rl_attempted_completion_function)
435: cliComp(cli_stdCompletion, cli_stdCompEntry);
436:
437: do {
438: line = readline(csPrompt);
439: if (!line) { // ^+d
440: cli_Printf(out, "\n");
441: break;
442: }
443: // clear whitespaces
444: for (s = line; isspace(*s); s++);
445: if (*s) {
446: for (t = s + strlen(s) - 1; t > s && isspace(*t); t--);
447: *++t = 0;
448: }
449:
450: if (*s) {
451: add_history(s);
452:
453: memset(items, 0, sizeof(char*) * MAX_PROMPT_ITEMS);
454: for (app = items; app < items + MAX_PROMPT_ITEMS - 1 && (*app = strsep(&s, " \t"));
455: *app ? app++ : app);
456:
457: /*
458: for (i = 0; i < MAX_PROMPT_ITEMS; i++)
459: cli_Printf(out, "i=%d %s\n", i, items[i]);
460: */
461:
462: // exec_cmd ...
463: for (cmd = NULL, i = 0; cmdList[i].cmd_name; i++)
464: if (*items[0] && !strncmp(cmdList[i].cmd_name, items[0], strlen(items[0]))) {
465: cmd = &cmdList[i];
466: break;
467: }
468: if (!cmd) {
469: cli_Printf(out, "Command '%s' not found!\n", items[0]);
470: ret = -1;
471: } else
472: ret = cmd->cmd_func(cmdList, i, out, items);
473: }
474:
475: free(line);
476: } while (ret < 1);
477:
478: return ret;
479: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>