Annotation of libaitcli/src/aitcli.c, revision 1.1.1.1.2.11
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.1.1.1.2.11! misho 6: * $Id: aitcli.c,v 1.1.1.1.2.10 2010/04/28 11:23:56 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:
27: #pragma GCC visibility pop
28:
29:
1.1.1.1.2.11! misho 30: #ifdef NULL_PREP_TERM
! 31:
1.1.1.1.2.4 misho 32: static void cli_Null_Prep_Term(int meta)
33: {
34: }
35:
1.1.1.1.2.11! misho 36: #endif
! 37:
1.1.1.1.2.4 misho 38:
1.1 misho 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
1.1.1.1.2.4 misho 69: * return: -1 error, != -1 printed chars
1.1 misho 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: /*
1.1.1.1.2.1 misho 101: * cliTTY() Initialize I/O TTY CLI features
1.1.1.1.2.3 misho 102: * @term = terminal name
1.1.1.1.2.1 misho 103: * @inp = input handle
104: * @out = output handle
1.1.1.1.2.4 misho 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: /*
1.1.1.1.2.5 misho 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"
1.1.1.1.2.6 misho 140: * @lineNum = save number of history entry lines, if -1 all lines saved without limit
1.1.1.1.2.5 misho 141: * return: -1 error; != -1 readed ok
142: */
1.1.1.1.2.6 misho 143: inline int cli_WriteHistory(const char *csFile, int lineNum)
1.1.1.1.2.5 misho 144: {
1.1.1.1.2.6 misho 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;
1.1.1.1.2.5 misho 153: }
154:
155: /*
1.1.1.1.2.4 misho 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;
1.1.1.1.2.10 misho 165: int on = 1;
1.1.1.1.2.4 misho 166:
1.1.1.1.2.10 misho 167: memset(&t, 0, sizeof t);
168: if (term)
1.1.1.1.2.4 misho 169: t = *term;
1.1.1.1.2.10 misho 170: else {
171: t.c_lflag = TTYDEF_LFLAG;
172: t.c_iflag = TTYDEF_IFLAG;
173: t.c_oflag = TTYDEF_OFLAG;
174: cfsetspeed(&t, B9600);
1.1.1.1.2.4 misho 175: }
176:
1.1.1.1.2.10 misho 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);
1.1.1.1.2.4 misho 184:
1.1.1.1.2.10 misho 185: ioctl(pty, TIOCPKT, &on);
1.1.1.1.2.4 misho 186:
1.1.1.1.2.11! misho 187: rl_readline_name = csProg;
! 188: rl_variable_bind("editing-mode", "emacs");
! 189:
! 190: #ifdef NULL_PREP_TERM
1.1.1.1.2.10 misho 191: rl_instream = fdopen(pty, "r");
1.1.1.1.2.11! misho 192: #endif
1.1.1.1.2.1 misho 193: }
194:
195: /*
1.1.1.1.2.7 misho 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: {
1.1.1.1.2.10 misho 206: int pty, ret = 0, r, s, alen, attrlen;
1.1.1.1.2.7 misho 207: fd_set fds;
208: struct timeval tv = { DEFAULT_SOCK_TIMEOUT, 0 };
209: u_char buf[BUFSIZ];
1.1.1.1.2.10 misho 210: struct telnetAttrs *a, Attr[5];
211: register int i;
1.1.1.1.2.7 misho 212:
213: switch (forkpty(&pty, NULL, term, win)) {
214: case -1:
215: LOGERR;
216: return -1;
217: case 0:
218: close(sock);
219:
1.1.1.1.2.11! misho 220: // rl_prep_term_function = cli_Null_Prep_Term;
1.1.1.1.2.10 misho 221:
222: cliNetInit(getprogname(), STDIN_FILENO, term);
1.1.1.1.2.7 misho 223: cliTTY(NULL, NULL, NULL, win);
1.1.1.1.2.10 misho 224: // ret = cliExec(cmdList, csPrompt) < 0 ? 1 : 0;
225: execl("/bin/tcsh", "tcsh", NULL);
1.1.1.1.2.7 misho 226: _exit(ret);
227: default:
228: telnet_SetCmd(Attr, DO, TELOPT_TTYPE);
1.1.1.1.2.10 misho 229: telnet_SetCmd(Attr + 1, WILL, TELOPT_SGA);
1.1.1.1.2.11! misho 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) {
1.1.1.1.2.7 misho 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);
1.1.1.1.2.10 misho 243: if ((ret = select(FD_SETSIZE, &fds, NULL, NULL, &tv)) < 1) {
244: if (!ret)
245: cli_SetErr(ETIMEDOUT, "Client session timeout ...");
246:
1.1.1.1.2.7 misho 247: break;
1.1.1.1.2.10 misho 248: }
1.1.1.1.2.7 misho 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) {
1.1.1.1.2.10 misho 254: if (a)
255: free(a);
256:
1.1.1.1.2.7 misho 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: }
1.1.1.1.2.10 misho 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);
1.1.1.1.2.11! misho 271: // if (TELOPT_LINEMODE == a[i].ta_opt && WILL == a[i].ta_cmd)
1.1.1.1.2.10 misho 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);
1.1.1.1.2.11! misho 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: */
1.1.1.1.2.10 misho 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]);
1.1.1.1.2.11! misho 292: syslog(LOG_CRIT, "send packet %d === %s ========", ret, r == pty ? "pty -> sock" : "sock -> pty");
1.1.1.1.2.10 misho 293: if ((ret = telnetSend(s, Attr, attrlen, buf, ret, 0)) == -1) {
1.1.1.1.2.7 misho 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: /*
1.1 misho 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: */
1.1.1.1.2.2 misho 322: int cliExec(cliCommands_t *cmdList, const char *csPrompt)
1.1 misho 323: {
324: char *line, *s, *t, **app, *items[MAX_PROMPT_ITEMS];
325: int ret = 0;
326: register int i;
327: cliCommands_t *cmd = NULL;
1.1.1.1.2.2 misho 328: FILE *out;
1.1 misho 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:
1.1.1.1.2.2 misho 374: out = rl_outstream;
375: if (!out)
376: out = stdout;
377:
1.1 misho 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>