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