Annotation of libaitcli/src/aitcli.c, revision 1.1.1.1.2.12

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.12! misho       6: * $Id: aitcli.c,v 1.1.1.1.2.11 2010/04/28 13:54:45 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.12! misho     210:        struct telnetAttrs *a, Attr[10];
1.1.1.1.2.10  misho     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.12! misho     220: #ifdef NULL_PREP_TERM
        !           221:                        rl_prep_term_function = cli_Null_Prep_Term;
        !           222: #endif
1.1.1.1.2.10  misho     223: 
                    224:                        cliNetInit(getprogname(), STDIN_FILENO, term);
1.1.1.1.2.7  misho     225:                        cliTTY(NULL, NULL, NULL, win);
1.1.1.1.2.10  misho     226: //                     ret = cliExec(cmdList, csPrompt) < 0 ? 1 : 0;
                    227:                        execl("/bin/tcsh", "tcsh", NULL);
1.1.1.1.2.7  misho     228:                        _exit(ret);
                    229:                default:
                    230:                        telnet_SetCmd(Attr, DO, TELOPT_TTYPE);
1.1.1.1.2.12! misho     231:                        telnet_SetCmd(Attr + 1, DO, TELOPT_LFLOW);
        !           232:                        telnet_Set_SubOpt(Attr + 2, TELOPT_LFLOW, LFLOW_OFF, NULL, 0);
        !           233:                        telnet_SetCmd(Attr + 3, DO, TELOPT_BINARY);
        !           234:                        telnet_SetCmd(Attr + 4, DO, TELOPT_NAWS);
        !           235:                        telnet_SetCmd(Attr + 5, DO, TELOPT_STATUS);
        !           236:                        telnet_SetCmd(Attr + 6, DONT, TELOPT_ECHO);
        !           237: #ifdef TELNET_LINEMODE_ENABLE
        !           238:                        telnet_SetCmd(Attr + 7, DO, TELOPT_LINEMODE);
        !           239:                        telnet_Set_SubOpt(Attr + 8, TELOPT_LINEMODE, LM_MODE, "\x0", 1);
        !           240:                        if ((ret = telnetSend(sock, Attr, 9, NULL, 0, 0)) == -1) {
        !           241: #else
        !           242:                        if ((ret = telnetSend(sock, Attr, 7, NULL, 0, 0)) == -1) {
        !           243: #endif
1.1.1.1.2.7  misho     244:                                cli_Errno = telnet_GetErrno();
                    245:                                strlcpy(cli_Error, telnet_GetError(), STRSIZ);
                    246:                                return -1;
                    247:                        }
                    248: 
                    249:                        while (42) {
                    250:                                FD_ZERO(&fds);
                    251:                                FD_SET(sock, &fds);
                    252:                                FD_SET(pty, &fds);
1.1.1.1.2.10  misho     253:                                if ((ret = select(FD_SETSIZE, &fds, NULL, NULL, &tv)) < 1) {
                    254:                                        if (!ret)
                    255:                                                cli_SetErr(ETIMEDOUT, "Client session timeout ...");
                    256: 
1.1.1.1.2.7  misho     257:                                        break;
1.1.1.1.2.10  misho     258:                                }
1.1.1.1.2.7  misho     259: 
                    260:                                r = FD_ISSET(sock, &fds) ? sock : pty;
                    261:                                s = FD_ISSET(sock, &fds) ? pty : sock;
                    262: 
                    263:                                if ((ret = telnetRecv(r, &a, &alen, buf, BUFSIZ)) < 0) {
1.1.1.1.2.10  misho     264:                                        if (a)
                    265:                                                free(a);
                    266: 
1.1.1.1.2.7  misho     267:                                        if (-2 == ret)
                    268:                                                continue;
                    269:                                        // EOF
                    270:                                        if (-3 == ret)
                    271:                                                shutdown(r, SHUT_RD);
                    272:                                        else {
                    273:                                                cli_Errno = telnet_GetErrno();
                    274:                                                strlcpy(cli_Error, telnet_GetError(), STRSIZ);
                    275:                                        }
                    276:                                        break;
                    277:                                }
1.1.1.1.2.10  misho     278:                                for (attrlen = i = 0; i < alen; i++) {
                    279:                                        if (TELOPT_TTYPE == a[i].ta_opt && WILL == a[i].ta_cmd)
                    280:                                                telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_TTYPE, TELQUAL_SEND, NULL, 0);
1.1.1.1.2.12! misho     281: #ifdef TELNET_LINEMODE_ENABLE
1.1.1.1.2.10  misho     282:                                        if (TELOPT_LINEMODE == a[i].ta_cmd && SB == a[i].ta_cmd) {
                    283:                                                telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LINEMODE, LM_SLC, 
                    284:                                                                "\x1\x0\x0\x3\xe2\x3\x4\x0\x0\x5\x0\x0\x7\xe2\x1c"
                    285:                                                                "\x8\x82\x4\x9\x0\x0\xa\x82\x7f\xb\x82\x15\xc\x82\x17"
                    286:                                                                "\xd\x82\x12\xe\x82\x16\xf\x82\x11\x10\x82\x13", 42);
                    287:                                        }
1.1.1.1.2.12! misho     288: #endif
1.1.1.1.2.10  misho     289:                                }
                    290:                                if (a)
                    291:                                        free(a);
1.1.1.1.2.11  misho     292: 
                    293:                                /*
                    294:                                if (s == sock && ret > 2 && 0x1b == buf[0] && 0x5b == buf[1]) {
                    295:                                        memmove(buf, buf + 3, ret);
                    296:                                        ret -= 3;
                    297:                                }
                    298:                                */
1.1.1.1.2.10  misho     299: #include "syslog.h"
                    300:                                int j;
                    301:                                for (j = 0; j < ret; j++)
                    302:                                        syslog(LOG_CRIT, "prepare to send %d = %X", j, buf[j]);
1.1.1.1.2.11  misho     303:                                syslog(LOG_CRIT, "send packet %d === %s ========", ret, r == pty ? "pty -> sock" : "sock -> pty");
1.1.1.1.2.10  misho     304:                                if ((ret = telnetSend(s, Attr, attrlen, buf, ret, 0)) == -1) {
1.1.1.1.2.7  misho     305:                                        cli_Errno = telnet_GetErrno();
                    306:                                        strlcpy(cli_Error, telnet_GetError(), STRSIZ);
                    307:                                        break;
                    308:                                }
                    309: 
                    310:                                /*
                    311:                                if ((ret = read(r, &ch, 1)) < 1) {
                    312:                                        if (!ret)
                    313:                                                shutdown(r, SHUT_RD);
                    314:                                        break;
                    315:                                }
                    316:                                if (write(s, &ch, 1) == -1) 
                    317:                                        break;
                    318:                                */
                    319:                        }
                    320: 
                    321:                        close(pty);
                    322:        }
                    323: 
                    324:        return ret;
                    325: }
                    326: 
                    327: /*
1.1       misho     328:  * cliExec() Execute CLI main loop
                    329:  * @cmdList = Commands list
                    330:  * @csPrompt = Prompt text
                    331:  * return: -1 error, 0 = exit w/^+D, 1 done.
                    332: */
1.1.1.1.2.2  misho     333: int cliExec(cliCommands_t *cmdList, const char *csPrompt)
1.1       misho     334: {
                    335:        char *line, *s, *t, **app, *items[MAX_PROMPT_ITEMS];
                    336:        int ret = 0;
                    337:        register int i;
                    338:        cliCommands_t *cmd = NULL;
1.1.1.1.2.2  misho     339:        FILE *out;
1.1       misho     340: 
                    341:        inline int inline_help()
                    342:        {
                    343:                cli_Cmd_Help(cmdList ? cmdList : cli_stdCmds, -1, out, NULL);
                    344:                rl_on_new_line();
                    345:                return 0;
                    346:        }
                    347: 
                    348:        char **cli_stdCompletion(const char *text, int start, int end)
                    349:        {
                    350:                register int i;
                    351:                char **matches = NULL;
                    352: 
                    353:                char *cmdCompGet(const char *text, int state)
                    354:                {
                    355:                        int len = strlen(text);
                    356: 
                    357:                        for (i = state; cmdList[i].cmd_name; i++) {
                    358:                                if (strncmp(cmdList[i].cmd_name, "---", 3) && 
                    359:                                                !strncmp(cmdList[i].cmd_name, text, len))
                    360:                                        return strdup(cmdList[i].cmd_name);
                    361:                        }
                    362: 
                    363:                        return NULL;
                    364:                }
                    365: 
                    366:                if (!start)
                    367:                        matches = rl_completion_matches(text, cmdCompGet);
                    368:                else
                    369:                        for (i = 0; cmdList[i].cmd_name; i++) {
                    370:                                if (!cmdList[i].cmd_comp)
                    371:                                        continue;
                    372:                                if (!strncmp(rl_line_buffer, cmdList[i].cmd_name, strlen(cmdList[i].cmd_name)))
                    373:                                        matches = rl_completion_matches(text, cmdList[i].cmd_comp);
                    374:                        }
                    375: 
                    376:                return matches;
                    377:        }
                    378:        char *cli_stdCompEntry(const char *ignore, int invoking_key)
                    379:        {
                    380:                return NULL;
                    381:        }
                    382: 
                    383:        /* --- main body of CLI --- */
                    384: 
1.1.1.1.2.2  misho     385:        out = rl_outstream;
                    386:        if (!out)
                    387:                out = stdout;
                    388: 
1.1       misho     389:        rl_bind_key('?', inline_help);
                    390:        if (!rl_attempted_completion_function) 
                    391:                cliComp(cli_stdCompletion, cli_stdCompEntry);
                    392: 
                    393:        do {
                    394:                line = readline(csPrompt);
                    395:                if (!line) {    // ^+d
                    396:                        cli_Printf(out, "\n");
                    397:                        break;
                    398:                }
                    399:                // clear whitespaces
                    400:                for (s = line; isspace(*s); s++);
                    401:                if (*s) {
                    402:                        for (t = s + strlen(s) - 1; t > s && isspace(*t); t--);
                    403:                        *++t = 0;
                    404:                }
                    405: 
                    406:                if (*s) {
                    407:                        add_history(s);
                    408: 
                    409:                        memset(items, 0, sizeof(char*) * MAX_PROMPT_ITEMS);
                    410:                        for (app = items; app < items + MAX_PROMPT_ITEMS - 1 && (*app = strsep(&s, " \t")); 
                    411:                                        *app ? app++ : app);
                    412: 
                    413:                        /*
                    414:                        for (i = 0; i < MAX_PROMPT_ITEMS; i++)
                    415:                                cli_Printf(out, "i=%d %s\n", i, items[i]);
                    416:                                */
                    417: 
                    418:                        // exec_cmd ...
                    419:                        for (cmd = NULL, i = 0; cmdList[i].cmd_name; i++)
                    420:                                if (*items[0] && !strncmp(cmdList[i].cmd_name, items[0], strlen(items[0]))) {
                    421:                                        cmd = &cmdList[i];
                    422:                                        break;
                    423:                                }
                    424:                        if (!cmd) {
                    425:                                cli_Printf(out, "Command '%s' not found!\n", items[0]);
                    426:                                ret = -1;
                    427:                        } else
                    428:                                ret = cmd->cmd_func(cmdList, i, out, items);
                    429:                }
                    430: 
                    431:                free(line);
                    432:        } while (ret < 1);
                    433: 
                    434:        return ret;
                    435: }

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