Annotation of libaitcli/src/aitcli.c, revision 1.2.2.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.2.2 ! misho       6: * $Id: aitcli.c,v 1.2.2.1 2010/06/04 11:46:40 misho Exp $
1.1       misho       7: *
                      8: *************************************************************************/
                      9: #include "global.h"
                     10: 
                     11: 
                     12: #pragma GCC visibility push(hidden)
                     13: 
1.2.2.2 ! misho      14: /*
        !            15: commands_t cli_stdCmds[] = {
1.1       misho      16:        { "test", cli_Cmd_Unsupported, "Test - Don`t use default command structure!", "test <cr>", cli_Comp_Filename }, 
                     17:        { "-------", NULL, "---------------------", NULL, NULL }, 
                     18:        { "help", cli_Cmd_Help, "Help screen", "help [command] <cr>", NULL }, 
                     19:        { "exit", cli_Cmd_Exit, "Exit from console", "exit <cr>", NULL }, 
                     20:        { NULL, NULL, NULL, NULL }
                     21: };
1.2.2.2 ! misho      22: */
1.1       misho      23: 
                     24: // ------------------------------------------------
                     25: 
                     26: int cli_Errno;
                     27: char cli_Error[STRSIZ];
                     28: 
                     29: #pragma GCC visibility pop
                     30: 
                     31: // cli_GetErrno() Get error code of last operation
1.2.2.2 ! misho      32: inline int
        !            33: cli_GetErrno()
1.1       misho      34: {
                     35:        return cli_Errno;
                     36: }
                     37: 
                     38: // io_GetError() Get error text of last operation
1.2.2.2 ! misho      39: inline const char *
        !            40: cli_GetError()
1.1       misho      41: {
                     42:        return cli_Error;
                     43: }
                     44: 
                     45: // cli_SetErr() Set error to variables for internal use!!!
1.2.2.2 ! misho      46: inline void
        !            47: cli_SetErr(int eno, char *estr, ...)
1.1       misho      48: {
                     49:        va_list lst;
                     50: 
                     51:        cli_Errno = eno;
                     52:        memset(cli_Error, 0, STRSIZ);
                     53:        va_start(lst, estr);
                     54:        vsnprintf(cli_Error, STRSIZ, estr, lst);
                     55:        va_end(lst);
                     56: }
                     57: 
                     58: // ------------------------------------------------------------
                     59: 
1.2.2.2 ! misho      60: static inline void
        !            61: clrscrEOL(linebuffer_t * __restrict buf)
1.1       misho      62: {
1.2.2.2 ! misho      63:        register int i;
1.1       misho      64: 
1.2.2.2 ! misho      65:        if (buf) {
        !            66:                write(buf->line_out, K_CR, 1);
1.1       misho      67: 
1.2.2.2 ! misho      68:                for (i = 0; i < buf->line_len; i++)
        !            69:                        write(buf->line_out, K_SPACE, 1);
        !            70:        }
1.1       misho      71: }
                     72: 
1.2.2.2 ! misho      73: static inline void
        !            74: printfEOL(linebuffer_t * __restrict buf, int len, int prompt)
1.2       misho      75: {
1.2.2.2 ! misho      76:        if (buf) {
        !            77:                write(buf->line_out, K_CR, 1);
1.2       misho      78: 
1.2.2.2 ! misho      79:                if (prompt && buf->line_prompt)
        !            80:                        write(buf->line_out, buf->line_prompt, buf->line_bol);
1.2       misho      81: 
1.2.2.2 ! misho      82:                write(buf->line_out, buf->line_buf, len == -1 ? buf->line_eol - buf->line_bol: len);
        !            83:        }
1.2       misho      84: }
                     85: 
1.2.2.2 ! misho      86: static inline void
        !            87: printfCR(linebuffer_t * __restrict buf, int prompt)
1.2       misho      88: {
1.2.2.2 ! misho      89:        if (buf) {
        !            90:                write(buf->line_out, K_CR, 1);
        !            91: 
        !            92:                if (prompt)
        !            93:                        if (prompt && buf->line_prompt)
        !            94:                                write(buf->line_out, buf->line_prompt, buf->line_bol);
        !            95:        }
1.2       misho      96: }
                     97: 
1.2.2.2 ! misho      98: static inline void
        !            99: printfCLI(linebuffer_t * __restrict buf, const unsigned char *text, int textlen, int prompt)
1.2       misho     100: {
1.2.2.2 ! misho     101:        if (buf && text && textlen) {
        !           102:                if (prompt && buf->line_prompt)
        !           103:                        write(buf->line_out, buf->line_prompt, buf->line_bol);
1.2       misho     104: 
1.2.2.2 ! misho     105:                write(buf->line_out, text, textlen);
        !           106:        }
1.2       misho     107: }
                    108: 
1.2.2.2 ! misho     109: // ------------------------------------------------------------
        !           110: 
1.2       misho     111: /*
                    112:  * cliNetInit() Initialize Readline if CLI bind to socket
                    113:  * @csProg = program name
                    114:  * @pty = Master pty
                    115:  * @term = stdin termios
                    116:  * return: none
                    117: */
1.2.2.2 ! misho     118: /*
1.2       misho     119: void cliNetInit(const char *csProg, int pty, struct termios *term)
                    120: {
                    121:        struct termios t;
                    122:        int on = 1;
                    123: 
                    124:        memset(&t, 0, sizeof t);
                    125:        if (term)
                    126:                t = *term;
                    127:        else {
                    128:                t.c_lflag = TTYDEF_LFLAG;
                    129:                t.c_iflag = TTYDEF_IFLAG;
                    130:                t.c_oflag = TTYDEF_OFLAG;
                    131:                t.c_cflag = TTYDEF_CFLAG;
                    132:                cfsetspeed(&t, B9600);
                    133:        }
                    134: 
                    135:        t.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO | ECHOCTL | ECHOE | ECHOK | ECHOKE | ECHONL | ECHOPRT);
                    136: //     t.c_iflag &= ~(ICRNL | BRKINT | INPCK | ISTRIP | IXON);
                    137:        t.c_iflag &= ~ICRNL;
                    138:        t.c_iflag |= IGNBRK;
                    139:        t.c_cc[VMIN] = 1;
                    140:        t.c_cc[VTIME] = 0;
                    141:        tcsetattr(pty, TCSANOW, &t);
                    142: 
                    143:        ioctl(pty, TIOCPKT, &on);
                    144: 
                    145:        rl_readline_name = csProg;
                    146:        rl_variable_bind("editing-mode", "emacs");
                    147: 
                    148:        rl_instream = fdopen(pty, "r");
                    149:        rl_outstream = NULL;
                    150: }
1.2.2.2 ! misho     151: */
1.2       misho     152: /*
                    153:  * cliNetExec() Execute net CLI main loop
                    154:  * @cmdList = Commands list
                    155:  * @csPrompt = Prompt text
                    156:  * @sock = client socket
                    157:  * @term = stdin termios
                    158:  * @win = window size of tty
                    159:  * return: -1 error, 0 = exit w/^+D, 1 done.
                    160: */
1.2.2.1   misho     161: int
1.2.2.2 ! misho     162: cliNetExec(commands_t *cmdList, const char *csPrompt, int sock, struct termios *term, struct winsize *win)
1.2       misho     163: {
                    164:        int pty, ret = 0, r, s, alen, attrlen, flg;
                    165:        fd_set fds;
                    166:        struct timeval tv = { DEFAULT_SOCK_TIMEOUT, 0 };
                    167:        u_char buf[BUFSIZ];
                    168:        struct telnetAttrs *a, Attr[10];
                    169: 
                    170:        switch (forkpty(&pty, NULL, term, win)) {
                    171:                case -1:
                    172:                        LOGERR;
                    173:                        return -1;
                    174:                case 0:
                    175:                        close(sock);
                    176: 
                    177:                        ret = cliExec(cmdList, csPrompt) < 0 ? 1 : 0;
                    178:                        /* spawn Shell mode */
                    179:                        /*
                    180:                        execl("/bin/tcsh", "tcsh", NULL);
                    181:                        */
                    182:                        _exit(ret);
                    183:                default:
                    184:                        /* spawn Shell mode */
                    185:                        telnet_SetCmd(Attr + 0, DO, TELOPT_TTYPE);
                    186:                        telnet_SetCmd(Attr + 1, WILL, TELOPT_ECHO);
                    187:                        telnet_Set_SubOpt(Attr + 2, TELOPT_LFLOW, LFLOW_OFF, NULL, 0);
                    188:                        telnet_Set_SubOpt(Attr + 3, TELOPT_LFLOW, LFLOW_RESTART_XON, NULL, 0);
                    189:                        telnet_SetCmd(Attr + 4, DO, TELOPT_LINEMODE);
                    190:                        if ((ret = telnetSend(sock, Attr, 5, NULL, 0, 0)) == -1) {
                    191:                                cli_Errno = telnet_GetErrno();
                    192:                                strlcpy(cli_Error, telnet_GetError(), STRSIZ);
                    193:                                return -1;
                    194:                        } else
                    195:                                flg = 0;
                    196: 
                    197:                        while (42) {
                    198:                                FD_ZERO(&fds);
                    199:                                FD_SET(sock, &fds);
                    200:                                FD_SET(pty, &fds);
                    201:                                if ((ret = select(FD_SETSIZE, &fds, NULL, NULL, &tv)) < 1) {
                    202:                                        if (!ret)
                    203:                                                cli_SetErr(ETIMEDOUT, "Client session timeout ...");
                    204: 
                    205:                                        break;
                    206:                                }
                    207: 
                    208:                                r = FD_ISSET(sock, &fds) ? sock : pty;
                    209:                                s = FD_ISSET(sock, &fds) ? pty : sock;
                    210: 
                    211:                                if ((ret = telnetRecv(r, &a, &alen, buf, BUFSIZ)) < 0) {
                    212:                                        if (a)
                    213:                                                free(a);
                    214: 
                    215:                                        if (-2 == ret)
                    216:                                                continue;
                    217:                                        // EOF
                    218:                                        if (-3 == ret)
                    219:                                                shutdown(r, SHUT_RD);
                    220:                                        else {
                    221:                                                cli_Errno = telnet_GetErrno();
                    222:                                                strlcpy(cli_Error, telnet_GetError(), STRSIZ);
                    223:                                        }
                    224:                                        break;
                    225:                                }
                    226:                                attrlen = 0;
                    227:                                if (1 == flg && alen) {
                    228:                                        telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_SGA);
                    229:                                        telnet_SetCmd(&Attr[attrlen++], DO, TELOPT_ECHO);
                    230:                                }
                    231:                                if (2 == flg && alen) {
                    232:                                        telnet_SetCmd(&Attr[attrlen++], WILL, TELOPT_ECHO);
                    233:                                        telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW, 
                    234:                                                        LFLOW_OFF, NULL, 0);
                    235:                                        telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW, 
                    236:                                                        LFLOW_RESTART_XON, NULL, 0);
                    237:                                        telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_LINEMODE);
                    238:                                }
                    239:                                if (a)
                    240:                                        free(a);
                    241: 
                    242:                                if ((ret = telnetSend(s, Attr, pty == s ? 0 : attrlen, buf, ret, 0)) == -1) {
                    243:                                        cli_Errno = telnet_GetErrno();
                    244:                                        strlcpy(cli_Error, telnet_GetError(), STRSIZ);
                    245:                                        break;
                    246:                                } else
                    247:                                        flg++;
                    248:                        }
                    249: 
                    250:                        close(pty);
                    251:        }
                    252: 
                    253:        return ret;
                    254: }
                    255: 
                    256: /*
1.1       misho     257:  * cliExec() Execute CLI main loop
                    258:  * @cmdList = Commands list
                    259:  * @csPrompt = Prompt text
                    260:  * return: -1 error, 0 = exit w/^+D, 1 done.
                    261: */
1.2.2.2 ! misho     262: /*
1.2       misho     263: int cliExec(cliCommands_t *cmdList, const char *csPrompt)
1.1       misho     264: {
                    265:        char *line, *s, *t, **app, *items[MAX_PROMPT_ITEMS];
                    266:        int ret = 0;
                    267:        register int i;
                    268:        cliCommands_t *cmd = NULL;
1.2       misho     269:        FILE *out;
1.1       misho     270: 
                    271:        inline int inline_help()
                    272:        {
                    273:                cli_Cmd_Help(cmdList ? cmdList : cli_stdCmds, -1, out, NULL);
                    274:                rl_on_new_line();
                    275:                return 0;
                    276:        }
                    277: 
                    278:        char **cli_stdCompletion(const char *text, int start, int end)
                    279:        {
                    280:                register int i;
                    281:                char **matches = NULL;
                    282: 
                    283:                char *cmdCompGet(const char *text, int state)
                    284:                {
                    285:                        int len = strlen(text);
                    286: 
                    287:                        for (i = state; cmdList[i].cmd_name; i++) {
                    288:                                if (strncmp(cmdList[i].cmd_name, "---", 3) && 
                    289:                                                !strncmp(cmdList[i].cmd_name, text, len))
                    290:                                        return strdup(cmdList[i].cmd_name);
                    291:                        }
                    292: 
                    293:                        return NULL;
                    294:                }
                    295: 
                    296:                if (!start)
                    297:                        matches = rl_completion_matches(text, cmdCompGet);
                    298:                else
                    299:                        for (i = 0; cmdList[i].cmd_name; i++) {
                    300:                                if (!cmdList[i].cmd_comp)
                    301:                                        continue;
                    302:                                if (!strncmp(rl_line_buffer, cmdList[i].cmd_name, strlen(cmdList[i].cmd_name)))
                    303:                                        matches = rl_completion_matches(text, cmdList[i].cmd_comp);
                    304:                        }
                    305: 
                    306:                return matches;
                    307:        }
                    308:        char *cli_stdCompEntry(const char *ignore, int invoking_key)
                    309:        {
                    310:                return NULL;
                    311:        }
1.2.2.2 ! misho     312: */
1.1       misho     313:        /* --- main body of CLI --- */
1.2.2.2 ! misho     314: /*
1.2       misho     315:        out = rl_outstream;
                    316:        if (!out)
                    317:                out = stdout;
                    318: 
1.1       misho     319:        rl_bind_key('?', inline_help);
                    320:        if (!rl_attempted_completion_function) 
                    321:                cliComp(cli_stdCompletion, cli_stdCompEntry);
                    322: 
                    323:        do {
                    324:                line = readline(csPrompt);
                    325:                if (!line) {    // ^+d
                    326:                        cli_Printf(out, "\n");
                    327:                        break;
                    328:                }
                    329:                // clear whitespaces
                    330:                for (s = line; isspace(*s); s++);
                    331:                if (*s) {
                    332:                        for (t = s + strlen(s) - 1; t > s && isspace(*t); t--);
                    333:                        *++t = 0;
                    334:                }
                    335: 
                    336:                if (*s) {
                    337:                        add_history(s);
                    338: 
                    339:                        memset(items, 0, sizeof(char*) * MAX_PROMPT_ITEMS);
                    340:                        for (app = items; app < items + MAX_PROMPT_ITEMS - 1 && (*app = strsep(&s, " \t")); 
                    341:                                        *app ? app++ : app);
                    342: 
                    343:                        // exec_cmd ...
                    344:                        for (cmd = NULL, i = 0; cmdList[i].cmd_name; i++)
                    345:                                if (*items[0] && !strncmp(cmdList[i].cmd_name, items[0], strlen(items[0]))) {
                    346:                                        cmd = &cmdList[i];
                    347:                                        break;
                    348:                                }
                    349:                        if (!cmd) {
                    350:                                cli_Printf(out, "Command '%s' not found!\n", items[0]);
                    351:                                ret = -1;
                    352:                        } else
                    353:                                ret = cmd->cmd_func(cmdList, i, out, items);
                    354:                }
                    355: 
                    356:                free(line);
                    357:        } while (ret < 1);
                    358: 
                    359:        return ret;
                    360: }
1.2.2.2 ! misho     361: */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>