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