/************************************************************************* * (C) 2010 AITNET ltd - Sofia/Bulgaria - * by Michael Pounov * * $Author: misho $ * $Id: aitcli.c,v 1.2.2.15 2010/06/08 08:43:47 misho Exp $ * *************************************************************************/ #include "global.h" #include "cli.h" #pragma GCC visibility push(hidden) // ------------------------------------------------ int cli_Errno; char cli_Error[STRSIZ]; #pragma GCC visibility pop // cli_GetErrno() Get error code of last operation inline int cli_GetErrno() { return cli_Errno; } // io_GetError() Get error text of last operation inline const char * cli_GetError() { return cli_Error; } // cli_SetErr() Set error to variables for internal use!!! inline void cli_SetErr(int eno, char *estr, ...) { va_list lst; cli_Errno = eno; memset(cli_Error, 0, STRSIZ); va_start(lst, estr); vsnprintf(cli_Error, STRSIZ, estr, lst); va_end(lst); } // ------------------------------------------------------------ static inline void clrscrEOL(linebuffer_t * __restrict buf) { register int i; if (buf) { write(buf->line_out, K_CR, 1); for (i = 0; i < buf->line_len; i++) write(buf->line_out, K_SPACE, 1); } } static inline void printfEOL(linebuffer_t * __restrict buf, int len, int prompt) { if (buf) { write(buf->line_out, K_CR, 1); if (prompt && buf->line_prompt) write(buf->line_out, buf->line_prompt, buf->line_bol); write(buf->line_out, buf->line_buf, len == -1 ? buf->line_eol - buf->line_bol: len); } } static inline void printfCR(linebuffer_t * __restrict buf, int prompt) { if (buf) { write(buf->line_out, K_CR, 1); if (prompt) if (prompt && buf->line_prompt) write(buf->line_out, buf->line_prompt, buf->line_bol); } } static inline void printfNL(linebuffer_t * __restrict buf, int prompt) { if (buf) { write(buf->line_out, K_ENTER, 1); if (prompt) if (prompt && buf->line_prompt) write(buf->line_out, buf->line_prompt, buf->line_bol); } } // ------------------------------------------------------------ static int bufCHAR(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; int pos; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; pos = buf->line_eol - buf->line_bol; if (buf->line_mode == LINEMODE_INS) memmove(buf->line_buf + pos + buf->line_keys[idx].key_len, buf->line_buf + pos, buf->line_len - buf->line_eol); if (buf->line_mode == LINEMODE_INS || buf->line_eol == buf->line_len - 1) buf->line_len += buf->line_keys[idx].key_len; buf->line_eol += buf->line_keys[idx].key_len; memcpy(buf->line_buf + pos, buf->line_keys[idx].key_ch, buf->line_keys[idx].key_len); buf->line_buf[buf->line_len - 1] = 0; write(buf->line_out, buf->line_keys[idx].key_ch, buf->line_keys[idx].key_len); if (buf->line_mode == LINEMODE_INS) { write(buf->line_out, (const u_char*) buf->line_buf + pos + buf->line_keys[idx].key_len, buf->line_len - buf->line_eol); printfEOL(buf, -1, 1); } return RETCODE_OK; } static int bufEOL(int idx, void * __restrict buffer) { if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; printfCR(buffer, 1); return RETCODE_EOL; } static int bufEOF(int idx, void * __restrict buffer) { /* if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; */ return RETCODE_EOF; } static int bufUP(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; int pos; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; if (!buf->line_h) buf->line_h = TAILQ_FIRST(&buf->line_history); else buf->line_h = TAILQ_NEXT(buf->line_h, hist_next); if (!buf->line_h) return RETCODE_OK; clrscrEOL(buf); cli_freeLine(buf); pos = buf->line_eol - buf->line_bol; buf->line_len += buf->line_h->hist_len; buf->line_eol += buf->line_h->hist_len; memcpy(buf->line_buf + pos, buf->line_h->hist_line, buf->line_h->hist_len); buf->line_buf[buf->line_len - 1] = 0; printfEOL(buf, -1, 1); return RETCODE_OK; } static int bufDOWN(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; int pos; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; if (!buf->line_h) buf->line_h = TAILQ_LAST(&buf->line_history, tqHistoryHead); else buf->line_h = TAILQ_PREV(buf->line_h, tqHistoryHead, hist_next); if (!buf->line_h) return RETCODE_OK; clrscrEOL(buf); cli_freeLine(buf); pos = buf->line_eol - buf->line_bol; buf->line_len += buf->line_h->hist_len; buf->line_eol += buf->line_h->hist_len; memcpy(buf->line_buf + pos, buf->line_h->hist_line, buf->line_h->hist_len); buf->line_buf[buf->line_len - 1] = 0; printfEOL(buf, -1, 1); return RETCODE_OK; } static int bufCLR(int idx, void * __restrict buffer) { if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; clrscrEOL(buffer); cli_freeLine(buffer); printfCR(buffer, 1); return RETCODE_OK; } static int bufBS(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; if (buf->line_bol < buf->line_eol) { clrscrEOL(buf); buf->line_eol--; buf->line_len--; memmove(buf->line_buf + buf->line_eol - buf->line_bol, buf->line_buf + buf->line_eol - buf->line_bol + 1, buf->line_len - buf->line_eol); buf->line_buf[buf->line_len - 1] = 0; printfEOL(buf, buf->line_len - 1, 1); printfEOL(buf, -1, 1); } return RETCODE_OK; } static int bufBTAB(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; if (buf->line_bol < buf->line_eol) { clrscrEOL(buf); buf->line_len = buf->line_eol - buf->line_bol + 1; buf->line_buf[buf->line_len - 1] = 0; printfEOL(buf, -1, 1); } return RETCODE_OK; } static int bufMODE(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; buf->line_mode = !buf->line_mode ? LINEMODE_OVER : LINEMODE_INS; return RETCODE_OK; } static int bufBEGIN(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; buf->line_eol = buf->line_bol; printfCR(buf, 1); return RETCODE_OK; } static int bufEND(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; buf->line_eol = buf->line_len - 1; printfEOL(buf, -1, 1); return RETCODE_OK; } static int bufLEFT(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; if (buf->line_bol < buf->line_eol) printfEOL(buf, --buf->line_eol - buf->line_bol, 1); return RETCODE_OK; } static int bufRIGHT(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; if (buf->line_eol < buf->line_len - 1) printfEOL(buf, ++buf->line_eol - buf->line_bol, 1); return RETCODE_OK; } static int bufDEL(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; clrscrEOL(buf); buf->line_len--; memmove(buf->line_buf + buf->line_eol - buf->line_bol, buf->line_buf + buf->line_eol - buf->line_bol + 1, buf->line_len - buf->line_eol); buf->line_buf[buf->line_len - 1] = 0; printfEOL(buf, buf->line_len - 1, 1); printfEOL(buf, -1, 1); return RETCODE_OK; } static int bufComp(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; char *str, *s, **app, *items[MAX_PROMPT_ITEMS], szLine[STRSIZ]; register int i, j; struct tagCommand *cmd, *c; int pos, ret = RETCODE_OK; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; str = strdup(buf->line_buf); if (!str) return RETCODE_ERR; else { s = str; io_TrimStr((u_char*) s); } i = j = 0; c = NULL; memset(szLine, 0, STRSIZ); if (*s) { memset(items, 0, sizeof(char*) * MAX_PROMPT_ITEMS); for (app = items, i = 0; app < items + MAX_PROMPT_ITEMS - 1 && (*app = strsep(&s, " \t")); *app ? i++ : i, *app ? app++ : app); if (i) { SLIST_FOREACH(cmd, &buf->line_cmds, cmd_next) { if (cmd->cmd_level == buf->line_level && !strncmp(cmd->cmd_name, items[0], strlen(items[0]))) { if (strncmp(cmd->cmd_name, CLI_CMD_SEP, strlen(CLI_CMD_SEP))) { j++; c = cmd; strlcat(szLine, " ", STRSIZ); strlcat(szLine, cmd->cmd_name, STRSIZ); } } } if (i > 1 && c) { /* we are on argument of command and has complition info */ j++; // always must be j > 1 ;) for arguments strlcpy(szLine, c->cmd_info, STRSIZ); } } else { /* we have valid char but i == 0, this case is illegal */ ret = RETCODE_ERR; goto endcomp; } } else { /* we on 0 position of prompt, show commands for this level */ SLIST_FOREACH(cmd, &buf->line_cmds, cmd_next) { if (cmd->cmd_level == buf->line_level) if (strncmp(cmd->cmd_name, CLI_CMD_SEP, strlen(CLI_CMD_SEP))) { j++; c = cmd; strlcat(szLine, " ", STRSIZ); strlcat(szLine, cmd->cmd_name, STRSIZ); } } } /* completion show actions ... */ if (j > 1 && c) { printfNL(buf, 0); write(buf->line_out, szLine, strlen(szLine)); printfNL(buf, 1); printfEOL(buf, buf->line_len - 1, 1); printfEOL(buf, -1, 1); } if (j == 1 && c) { clrscrEOL(buf); cli_freeLine(buf); pos = buf->line_eol - buf->line_bol; buf->line_len += c->cmd_len + 1; buf->line_eol += c->cmd_len + 1; memcpy(buf->line_buf + pos, c->cmd_name, c->cmd_len); buf->line_buf[pos + c->cmd_len] = (u_char) *K_SPACE; buf->line_buf[buf->line_len - 1] = 0; printfEOL(buf, -1, 1); } endcomp: free(str); return ret; } static int bufHelp(int idx, void * __restrict buffer) { linebuffer_t *buf = buffer; if (!buffer || idx < 0 || idx > MAX_BINDKEY) return RETCODE_ERR; cli_Cmd_Help(buf, -1, NULL); printfEOL(buf, buf->line_len - 1, 1); printfEOL(buf, -1, 1); return RETCODE_OK; } // --------------------------------------------------------------- /* * cli_Printf() Send message to CLI session * @buffer = CLI buffer * @fmt = printf format string * @... = arguments defined in fmt * return: none */ inline void cli_Printf(linebuffer_t * __restrict buffer, char *fmt, ...) { va_list lst; FILE *f; if (fmt) { f = fdopen(buffer->line_out, "a"); if (!f) { LOGERR; return; } va_start(lst, fmt); vfprintf(f, fmt, lst); va_end(lst); } else cli_SetErr(EINVAL, "Error:: invalid input parameters ..."); } /* * cli_PrintHelp() Print help screen * @buffer = CLI buffer * return: none */ inline void cli_PrintHelp(linebuffer_t * __restrict buffer) { if (buffer) bufHelp(0, buffer); else cli_SetErr(EINVAL, "Error:: invalid input parameters ..."); } /* * cli_BindKey() Bind function to key * @key = key structure * @buffer = CLI buffer * return: RETCODE_ERR error, RETCODE_OK ok, >0 bind at position */ int cli_BindKey(bindkey_t * __restrict key, linebuffer_t * __restrict buffer) { register int i; if (!key || !buffer) { cli_SetErr(EINVAL, "Error:: invalid input parameters ..."); return RETCODE_ERR; } for (i = 0; i < MAX_BINDKEY; i++) if (key->key_len == buffer->line_keys[i].key_len && !memcmp(key->key_ch, buffer->line_keys[i].key_ch, key->key_len)) { buffer->line_keys[i].key_func = key->key_func; return i; } return RETCODE_OK; } /* * cli_addCommand() Add command to CLI session * @buffer = CLI buffer * @csCmd = Command name * @cliLevel = Level in CLI, -1 unprivi(view from all), 0 main config, 1 sub config ... * @funcCmd = Callback function when user call command * @csInfo = Inline information for command * @csHelp = Help line when call help * return: RETCODE_ERR error, RETCODE_OK ok */ int cli_addCommand(linebuffer_t * __restrict buffer, const char *csCmd, int cliLevel, cmd_func_t funcCmd, const char *csInfo, const char *csHelp) { struct tagCommand *cmd; if (!buffer || !csCmd) { cli_SetErr(EINVAL, "Error:: invalid input parameters ..."); return RETCODE_ERR; } cmd = malloc(sizeof(struct tagCommand)); if (!cmd) { LOGERR; return RETCODE_ERR; } else memset(cmd, 0, sizeof(struct tagCommand)); cmd->cmd_level = cliLevel; cmd->cmd_func = funcCmd; cmd->cmd_len = strlcpy(cmd->cmd_name, csCmd, STRSIZ); if (csInfo) strlcpy(cmd->cmd_info, csInfo, STRSIZ); if (csHelp) strlcpy(cmd->cmd_help, csHelp, STRSIZ); SLIST_INSERT_HEAD(&buffer->line_cmds, cmd, cmd_next); return RETCODE_OK; } /* * cli_delCommand() Delete command from CLI session * @buffer = CLI buffer * @csCmd = Command name * @cliLevel = Level in CLI, -1 unprivi(view from all), 0 main config, 1 sub config ... * return: RETCODE_ERR error, RETCODE_OK ok */ int cli_delCommand(linebuffer_t * __restrict buffer, const char *csCmd, int cliLevel) { struct tagCommand *cmd; int ret = RETCODE_OK; if (!buffer || !csCmd) { cli_SetErr(EINVAL, "Error:: invalid input parameters ..."); return RETCODE_ERR; } SLIST_FOREACH(cmd, &buffer->line_cmds, cmd_next) if (cmd->cmd_level == cliLevel && !strcmp(cmd->cmd_name, csCmd)) { ret = 1; SLIST_REMOVE(&buffer->line_cmds, cmd, tagCommand, cmd_next); free(cmd); break; } return ret; } /* * cli_updCommand() Update command in CLI session * @buffer = CLI buffer * @csCmd = Command name * @cliLevel = Level in CLI, -1 unprivi(view from all), 0 main config, 1 sub config ... * @funcCmd = Callback function when user call command * @csInfo = Inline information for command * @csHelp = Help line when call help * return: RETCODE_ERR error, RETCODE_OK ok */ int cli_updCommand(linebuffer_t * __restrict buffer, const char *csCmd, int cliLevel, cmd_func_t funcCmd, const char *csInfo, const char *csHelp) { struct tagCommand *cmd; int ret = RETCODE_OK; if (!buffer || !csCmd) { cli_SetErr(EINVAL, "Error:: invalid input parameters ..."); return RETCODE_ERR; } SLIST_FOREACH(cmd, &buffer->line_cmds, cmd_next) if (cmd->cmd_level == cliLevel && !strcmp(cmd->cmd_name, csCmd)) { ret = 1; if (funcCmd) cmd->cmd_func = funcCmd; if (csInfo) strlcpy(cmd->cmd_info, csInfo, STRSIZ); if (csHelp) strlcpy(cmd->cmd_help, csHelp, STRSIZ); break; } return ret; } /* * cli_addHistory() Add line to history * @buffer = CLI buffer * @str = Add custom text or if NULL use readed line from CLI buffer * return: RETCODE_ERR error, RETCODE_OK ok */ int cli_addHistory(linebuffer_t * __restrict buffer, const char * __restrict str) { struct tagHistory *h; if (!buffer) { cli_SetErr(EINVAL, "Error:: invalid input parameters ..."); return RETCODE_ERR; } if (!(h = malloc(sizeof(struct tagHistory)))) { LOGERR; return RETCODE_ERR; } else memset(h, 0, sizeof(struct tagHistory)); if (str) { if (!*str) { free(h); return RETCODE_OK; } h->hist_len = strlcpy(h->hist_line, str, BUFSIZ); } else { if (!*buffer->line_buf || buffer->line_len < 2) { free(h); return RETCODE_OK; } memcpy(h->hist_line, buffer->line_buf, (h->hist_len = buffer->line_len)); io_TrimStr((u_char*) h->hist_line); h->hist_len = strlen(h->hist_line); } TAILQ_INSERT_HEAD(&buffer->line_history, h, hist_next); return h->hist_len; } /* * cli_saveHistory() Save history to file * @buffer = CLI buffer * @histfile = History filename, if NULL will be use default name * @lines = Maximum history lines to save * return: RETCODE_ERR error, RETCODE_OK ok */ int cli_saveHistory(linebuffer_t * __restrict buffer, const char *histfile, int lines) { FILE *f; mode_t mode; char szFName[MAXPATHLEN]; struct tagHistory *h; if (!buffer) { cli_SetErr(EINVAL, "Error:: invalid input parameters ..."); return RETCODE_ERR; } if (!histfile) strlcpy(szFName, HISTORY_FILE, MAXPATHLEN); else strlcpy(szFName, histfile, MAXPATHLEN); mode = umask(0177); f = fopen(szFName, "w"); if (!f) { LOGERR; return RETCODE_ERR; } TAILQ_FOREACH(h, &buffer->line_history, hist_next) { fprintf(f, "%s\n", h->hist_line); if (lines) lines--; else break; } fclose(f); umask(mode); return RETCODE_OK; } /* * cli_loadHistory() Load history from file * @buffer = CLI buffer * @histfile = History filename, if NULL will be use default name * return: RETCODE_ERR error, RETCODE_OK ok */ int cli_loadHistory(linebuffer_t * __restrict buffer, const char *histfile) { FILE *f; char szFName[MAXPATHLEN], buf[BUFSIZ]; struct tagHistory *h; if (!buffer) { cli_SetErr(EINVAL, "Error:: invalid input parameters ..."); return RETCODE_ERR; } if (!histfile) strlcpy(szFName, HISTORY_FILE, MAXPATHLEN); else strlcpy(szFName, histfile, MAXPATHLEN); f = fopen(szFName, "r"); if (!f) return RETCODE_OK; while (fgets(buf, BUFSIZ, f)) { if (!*buf || *buf == '#') continue; else io_TrimStr((u_char*) buf); if (!(h = malloc(sizeof(struct tagHistory)))) { LOGERR; fclose(f); return RETCODE_ERR; } else memset(h, 0, sizeof(struct tagHistory)); h->hist_len = strlcpy(h->hist_line, buf, BUFSIZ); TAILQ_INSERT_TAIL(&buffer->line_history, h, hist_next); } fclose(f); return RETCODE_OK; } /* * cli_resetHistory() Reset history search in CLI session * @buffer = CLI buffer * return: none */ inline void cli_resetHistory(linebuffer_t * __restrict buffer) { buffer->line_h = NULL; } /* * cli_freeLine() Clear entire line * @buffer = CLI buffer * return: RETCODE_ERR error, RETCODE_OK ok */ inline int cli_freeLine(linebuffer_t * __restrict buffer) { int code = RETCODE_ERR; if (buffer) { if (buffer->line_buf) free(buffer->line_buf); buffer->line_buf = malloc(BUFSIZ); if (buffer->line_buf) { memset(buffer->line_buf, 0, BUFSIZ); buffer->line_eol = buffer->line_bol; buffer->line_len = 1 + buffer->line_eol; code = RETCODE_OK; } else LOGERR; } else cli_SetErr(EINVAL, "Error:: invalid input parameters ..."); return code; } /* * cli_setPrompt() Set new prompt for CLI session * @buffer = CLI buffer * @prompt = new text for prompt or if NULL disable prompt * return: none */ inline void cli_setPrompt(linebuffer_t * __restrict buffer, const char *prompt) { if (buffer) { if (buffer->line_prompt) { free(buffer->line_prompt); buffer->line_prompt = NULL; buffer->line_bol = 0; } if (prompt) { buffer->line_prompt = strdup(prompt); if (buffer->line_prompt) { buffer->line_bol = strlen(buffer->line_prompt); buffer->line_eol = buffer->line_bol; buffer->line_len = 1 + buffer->line_eol; } else LOGERR; } } else cli_SetErr(EINVAL, "Error:: invalid input parameters ..."); } /* * cliEnd() Clear data, Free resources and close CLI session * @buffer = CLI buffer * return: RETCODE_ERR error, RETCODE_OK ok */ void cliEnd(linebuffer_t * __restrict buffer) { struct tagHistory *h; struct tagCommand *c; if (buffer) { while ((c = SLIST_FIRST(&buffer->line_cmds))) { SLIST_REMOVE_HEAD(&buffer->line_cmds, cmd_next); free(c); } while ((h = TAILQ_FIRST(&buffer->line_history))) { TAILQ_REMOVE(&buffer->line_history, h, hist_next); free(h); } if (buffer->line_prompt) free(buffer->line_prompt); if (buffer->line_keys) free(buffer->line_keys); if (buffer->line_buf) free(buffer->line_buf); free(buffer); buffer = NULL; } else cli_SetErr(EINVAL, "Error:: invalid input parameters ..."); } /* * cliInit() Start CLI session, allocate memory for resources and bind keys * @fin = Input device handle * @fout = Output device handle * @prompt = text for prompt, if NULL disable prompt * return: NULL if error or !=NULL CLI buffer */ linebuffer_t * cliInit(int fin, int fout, const char *prompt) { linebuffer_t *buffer; bindkey_t *keys; register int i; struct termios t; memset(&t, 0, sizeof t); /* init buffer */ buffer = malloc(sizeof (linebuffer_t)); if (!buffer) { LOGERR; return NULL; } else { memset(buffer, 0, sizeof(linebuffer_t)); buffer->line_in = fin; buffer->line_out = fout; TAILQ_INIT(&buffer->line_history); SLIST_INIT(&buffer->line_cmds); if (prompt) { buffer->line_prompt = strdup(prompt); if (!buffer->line_prompt) { LOGERR; free(buffer); return NULL; } else buffer->line_eol = buffer->line_bol = strlen(buffer->line_prompt); } } buffer->line_buf = malloc(BUFSIZ); if (!buffer->line_buf) { LOGERR; if (buffer->line_prompt) free(buffer->line_prompt); free(buffer); return NULL; } else { memset(buffer->line_buf, 0, BUFSIZ); buffer->line_len = 1 + buffer->line_eol; } keys = calloc(MAX_BINDKEY + 1, sizeof(bindkey_t)); if (!keys) { LOGERR; if (buffer->line_prompt) free(buffer->line_prompt); free(buffer->line_buf); free(buffer); return NULL; } else memset(keys, 0, sizeof(bindkey_t) * (MAX_BINDKEY + 1)); /* add helper functions */ cli_addCommand(buffer, "exit", 0, cli_Cmd_Exit, "exit ", "Exit from console"); cli_addCommand(buffer, "help", 0, cli_Cmd_Help, "help [command] ", "Help screen"); cli_addCommand(buffer, "-------", 0, NULL, "-------------------------", NULL); /* fill key bindings */ // ascii chars & ctrl+chars for (i = 0; i < 256; i++) { *keys[i].key_ch = (u_char) i; keys[i].key_len = 1; if (!i || i == *K_CTRL_D) keys[i].key_func = bufEOF; if (i == *K_CTRL_M || i == *K_CTRL_J) keys[i].key_func = bufEOL; if (i == *K_CTRL_H || i == *K_BACKSPACE) keys[i].key_func = bufBS; if (i == *K_CTRL_C) keys[i].key_func = bufCLR; if (i == *K_CTRL_A) keys[i].key_func = bufBEGIN; if (i == *K_CTRL_E) keys[i].key_func = bufEND; if (i == *K_TAB) keys[i].key_func = bufComp; if (i >= *K_SPACE && i < *K_BACKSPACE) keys[i].key_func = bufCHAR; if (i > *K_BACKSPACE && i < 0xff) keys[i].key_func = bufCHAR; if (i == '?') keys[i].key_func = bufHelp; } // alt+chars for (i = 256; i < 512; i++) { keys[i].key_ch[0] = 0x1b; keys[i].key_ch[1] = (u_char) i - 256; keys[i].key_len = 2; } // 3 bytes keys[i].key_len = sizeof K_F1 - 1; memcpy(keys[i].key_ch, K_F1, keys[i].key_len); i++; keys[i].key_len = sizeof K_F2 - 1; memcpy(keys[i].key_ch, K_F2, keys[i].key_len); i++; keys[i].key_len = sizeof K_F3 - 1; memcpy(keys[i].key_ch, K_F3, keys[i].key_len); i++; keys[i].key_len = sizeof K_F4 - 1; memcpy(keys[i].key_ch, K_F4, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_SH_F1 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F1, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_SH_F2 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F2, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_SH_F3 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F3, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_SH_F4 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F4, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_SH_F5 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F5, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_SH_F6 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F6, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_SH_F7 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F7, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_SH_F8 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F8, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_SH_F9 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F9, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_SH_F10 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F10, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_SH_F11 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F11, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_SH_F12 - 1; memcpy(keys[i].key_ch, K_CTRL_SH_F12, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_F1 - 1; memcpy(keys[i].key_ch, K_CTRL_F1, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_F2 - 1; memcpy(keys[i].key_ch, K_CTRL_F2, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_F3 - 1; memcpy(keys[i].key_ch, K_CTRL_F3, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_F4 - 1; memcpy(keys[i].key_ch, K_CTRL_F4, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_F5 - 1; memcpy(keys[i].key_ch, K_CTRL_F5, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_F6 - 1; memcpy(keys[i].key_ch, K_CTRL_F6, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_F7 - 1; memcpy(keys[i].key_ch, K_CTRL_F7, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_F8 - 1; memcpy(keys[i].key_ch, K_CTRL_F8, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_F9 - 1; memcpy(keys[i].key_ch, K_CTRL_F9, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_F10 - 1; memcpy(keys[i].key_ch, K_CTRL_F10, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_F11 - 1; memcpy(keys[i].key_ch, K_CTRL_F11, keys[i].key_len); i++; keys[i].key_len = sizeof K_CTRL_F12 - 1; memcpy(keys[i].key_ch, K_CTRL_F12, keys[i].key_len); i++; keys[i].key_len = sizeof K_HOME - 1; keys[i].key_func = bufBEGIN; memcpy(keys[i].key_ch, K_HOME, keys[i].key_len); i++; keys[i].key_len = sizeof K_END - 1; keys[i].key_func = bufEND; memcpy(keys[i].key_ch, K_END, keys[i].key_len); i++; keys[i].key_len = sizeof K_UP - 1; keys[i].key_func = bufUP; memcpy(keys[i].key_ch, K_UP, keys[i].key_len); i++; keys[i].key_len = sizeof K_DOWN - 1; keys[i].key_func = bufDOWN; memcpy(keys[i].key_ch, K_DOWN, keys[i].key_len); i++; keys[i].key_len = sizeof K_RIGHT - 1; keys[i].key_func = bufRIGHT; memcpy(keys[i].key_ch, K_RIGHT, keys[i].key_len); i++; keys[i].key_len = sizeof K_LEFT - 1; keys[i].key_func = bufLEFT; memcpy(keys[i].key_ch, K_LEFT, keys[i].key_len); i++; keys[i].key_len = sizeof K_BTAB - 1; keys[i].key_func = bufBTAB; memcpy(keys[i].key_ch, K_BTAB, keys[i].key_len); i++; // 4 bytes keys[i].key_len = sizeof K_INS - 1; keys[i].key_func = bufMODE; memcpy(keys[i].key_ch, K_INS, keys[i].key_len); i++; keys[i].key_len = sizeof K_DEL - 1; keys[i].key_func = bufDEL; memcpy(keys[i].key_ch, K_DEL, keys[i].key_len); i++; keys[i].key_len = sizeof K_PGUP - 1; memcpy(keys[i].key_ch, K_PGUP, keys[i].key_len); i++; keys[i].key_len = sizeof K_PGDN - 1; memcpy(keys[i].key_ch, K_PGDN, keys[i].key_len); i++; // 5 bytes keys[i].key_len = sizeof K_F5 - 1; memcpy(keys[i].key_ch, K_F5, keys[i].key_len); i++; keys[i].key_len = sizeof K_F6 - 1; memcpy(keys[i].key_ch, K_F6, keys[i].key_len); i++; keys[i].key_len = sizeof K_F7 - 1; memcpy(keys[i].key_ch, K_F7, keys[i].key_len); i++; keys[i].key_len = sizeof K_F8 - 1; memcpy(keys[i].key_ch, K_F8, keys[i].key_len); i++; keys[i].key_len = sizeof K_F9 - 1; memcpy(keys[i].key_ch, K_F9, keys[i].key_len); i++; keys[i].key_len = sizeof K_F10 - 1; memcpy(keys[i].key_ch, K_F10, keys[i].key_len); i++; keys[i].key_len = sizeof K_F11 - 1; memcpy(keys[i].key_ch, K_F11, keys[i].key_len); i++; keys[i].key_len = sizeof K_F12 - 1; memcpy(keys[i].key_ch, K_F12, keys[i].key_len); i++; tcgetattr(buffer->line_in, &t); t.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO | ECHOCTL | ECHOE | ECHOK | ECHOKE | ECHONL | ECHOPRT); t.c_iflag |= IGNBRK; t.c_cc[VMIN] = 1; t.c_cc[VTIME] = 0; tcsetattr(buffer->line_in, TCSANOW, &t); buffer->line_keys = keys; return buffer; } /* * cliReadLine() Read line from opened CLI session * @buffer = CLI buffer * return: NULL if error or !=NULL readed line, must be free after use! */ char * cliReadLine(linebuffer_t * __restrict buffer) { int code, readLen; register int i; struct pollfd fds; char buf[BUFSIZ], *str = NULL; if (!buffer) { cli_SetErr(EINVAL, "Error:: invalid input parameters ..."); return NULL; } memset(&fds, 0, sizeof fds); fds.fd = buffer->line_in; fds.events = POLLIN; printfCR(buffer, 1); while (42) { if (poll(&fds, 1, -1) < 1) { LOGERR; return str; } memset(buf, 0, sizeof buf); readLen = read(buffer->line_in, buf, BUFSIZ); if (readLen == -1) { LOGERR; return str; } if (!readLen) { if (buffer->line_buf) str = strdup(buffer->line_buf); else cli_SetErr(EPIPE, "Error:: unknown state ..."); return str; } recheck: for (code = RETCODE_OK, i = MAX_BINDKEY - 1; i > -1; i--) if (readLen >= buffer->line_keys[i].key_len && !memcmp(buffer->line_keys[i].key_ch, buf, buffer->line_keys[i].key_len)) { readLen -= buffer->line_keys[i].key_len; if (readLen) memmove(buf, buf + buffer->line_keys[i].key_len, readLen); else memset(buf, 0, buffer->line_keys[i].key_len); if (buffer->line_keys[i].key_func) if ((code = buffer->line_keys[i].key_func(i, buffer))) readLen = 0; if (readLen) goto recheck; else break; } if (code) break; } if (code != RETCODE_ERR && code != RETCODE_EOF && buffer->line_buf) str = strdup(buffer->line_buf); return str; } /* * cliNetLoop() CLI network main loop binded to socket * @buffer = CLI buffer * @csHistFile = History file name * @sock = client socket * @term = stdin termios * @win = window size of tty * return: RETCODE_ERR error, RETCODE_OK ok */ int cliNetLoop(linebuffer_t * __restrict buffer, const char *csHistFile, int sock, struct termios *term, struct winsize *win) { u_char buf[BUFSIZ]; int pty, r, s, alen, attrlen, flg, ret = 0; fd_set fds; struct timeval tv = { DEFAULT_SOCK_TIMEOUT, 0 }; struct telnetAttrs *a, Attr[10]; switch (forkpty(&pty, NULL, term, win)) { case -1: LOGERR; return -1; case 0: close(sock); if (buffer) ret = cliLoop(buffer, csHistFile) < 0 ? 1 : 0; else cli_SetErr(EINVAL, "Error:: invalid input parameters ..."); /* spawn Shell mode */ /* execl("/bin/tcsh", "tcsh", NULL); */ _exit(ret); default: /* spawn Shell mode */ telnet_SetCmd(Attr + 0, DO, TELOPT_TTYPE); telnet_SetCmd(Attr + 1, WILL, TELOPT_ECHO); telnet_Set_SubOpt(Attr + 2, TELOPT_LFLOW, LFLOW_OFF, NULL, 0); telnet_Set_SubOpt(Attr + 3, TELOPT_LFLOW, LFLOW_RESTART_XON, NULL, 0); telnet_SetCmd(Attr + 4, DO, TELOPT_LINEMODE); if ((ret = telnetSend(sock, Attr, 5, NULL, 0, 0)) == -1) { cli_Errno = telnet_GetErrno(); strlcpy(cli_Error, telnet_GetError(), STRSIZ); return -1; } else flg = 0; while (42) { FD_ZERO(&fds); FD_SET(sock, &fds); FD_SET(pty, &fds); if ((ret = select(FD_SETSIZE, &fds, NULL, NULL, &tv)) < 1) { if (!ret) cli_SetErr(ETIMEDOUT, "Client session timeout ..."); break; } r = FD_ISSET(sock, &fds) ? sock : pty; s = FD_ISSET(sock, &fds) ? pty : sock; if ((ret = telnetRecv(r, &a, &alen, buf, BUFSIZ)) < 0) { if (a) free(a); if (-2 == ret) continue; // EOF if (-3 == ret) shutdown(r, SHUT_RD); else { cli_Errno = telnet_GetErrno(); strlcpy(cli_Error, telnet_GetError(), STRSIZ); } break; } attrlen = 0; if (1 == flg && alen) { telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_SGA); telnet_SetCmd(&Attr[attrlen++], DO, TELOPT_ECHO); } if (2 == flg && alen) { telnet_SetCmd(&Attr[attrlen++], WILL, TELOPT_ECHO); telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW, LFLOW_OFF, NULL, 0); telnet_Set_SubOpt(&Attr[attrlen++], TELOPT_LFLOW, LFLOW_RESTART_XON, NULL, 0); telnet_SetCmd(&Attr[attrlen++], DONT, TELOPT_LINEMODE); } if (a) free(a); if ((ret = telnetSend(s, Attr, pty == s ? 0 : attrlen, buf, ret, 0)) == -1) { cli_Errno = telnet_GetErrno(); strlcpy(cli_Error, telnet_GetError(), STRSIZ); break; } else flg++; } close(pty); } return ret; } /* * cliLoop() CLI main loop * @buffer = CLI buffer * @csHistFile = History file name * return: RETCODE_ERR error, RETCODE_OK ok */ int cliLoop(linebuffer_t * __restrict buffer, const char *csHistFile) { char *line, *s, *t, **app, *items[MAX_PROMPT_ITEMS]; register int i; int ret = RETCODE_OK; struct tagCommand *cmd; /* --- main body of CLI --- */ if (cli_loadHistory(buffer, csHistFile) == RETCODE_ERR) return RETCODE_ERR; do { line = cliReadLine(buffer); if (!line) { printfNL(buffer, 0); break; } else cli_addHistory(buffer, NULL); // clear whitespaces for (s = line; isspace(*s); s++); if (*s) { for (t = s + strlen(s) - 1; t > s && isspace(*t); t--); *++t = 0; } if (*s) { memset(items, 0, sizeof(char*) * MAX_PROMPT_ITEMS); for (app = items; app < items + MAX_PROMPT_ITEMS - 1 && (*app = strsep(&s, " \t")); *app ? app++ : app); // exec_cmd ... i = 0; SLIST_FOREACH(cmd, &buffer->line_cmds, cmd_next) { if (*items[0] && !strncmp(cmd->cmd_name, items[0], strlen(items[0]))) break; else i++; } if (!cmd) { cli_Printf(buffer, "\nCommand '%s' not found!\n", items[0]); ret = -1; } else if (cmd->cmd_func) ret = cmd->cmd_func(buffer, i, items); else { clrscrEOL(buffer); printfCR(buffer, 1); } } cli_freeLine(buffer); cli_resetHistory(buffer); free(line); } while (ret < 1); cli_saveHistory(buffer, csHistFile, HISTORY_LINES); return ret; }