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

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

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