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>