/* VTY Command Line Pipe Handling * Copyright (C) 1997, 98 Kunihiro Ishiguro * * Revisions: Copyright (C) 2010 Chris Hall (GMCH), Highwayman * * This file is part of GNU Zebra. * * GNU Zebra is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include #include "vty.h" #include "vty_cli.h" #include "vty_command.h" #include "vty_io.h" #include "vio_lines.h" #include "command_execute.h" #include "command_queue.h" #include "memory.h" /*============================================================================== * VTY Command Line Input Pipe * * Here are the mechanics which support the: * * < file_name * * <| shell_command * * and the: * * > file_name * >> file_name * | shell_command * *============================================================================== * The file_name handling * * Two directories are supported: * * "home" -- being the root for configuration files * * "cd" -- being the root for relative filenames, in the usual way * * "here" -- being the directory for the enclosing "< filename" * see below for more detailed semantics * * There are the global values: * * config home -- defaults to directory for configuration file. * may be set by command. * * config cd -- defaults to cwd at start up * * There are the local values, within a given CLI instance (ie VTY): * * cli home -- set to config home when CLI instance starts, or when * config home is set within the CLI. * * cli cd -- similarly * * cli here -- outside < is same as cli home, unless explicitly set * - set by cli here * - pushed each time executes <, and set to directory for * the < filename. * - pushed each time executes <| and left unchanged * - popped each time exits < or <| * - set to parent by "no cli here" inside <, or to * default state outside < * * And then the filename syntax: * * /path -- absolute path -- in the usual way * * path -- path wrt "cli cd" -- in the usual way * * ~/path -- path in "cli home" -- so "config" stuff * * ~./path -- path in "here" -- so wrt to enclosing < file * *============================================================================== * The Input Pipe Commands * * These are "universal commands". * * They are: * * < filename -- filename as above * * treat contents of given file as command lines. * (That may include further < commands.) * * See notes on cli here for pushing/popping the here * directory. * * TODO: Filename and quotes ?? * * <| shell_command -- the shell command is executed "as is" by system(). * * the stdout and stderr are collected. * * treats stdout as command lines. * (That may include further < commands.) * * anything from stderr is sent to the VTY output. * * As far as the top level CLI is concerned, these are discrete commands. * That is to say: * * -- except where blocked while reading the "pipe", all commands are * executed one after another, in one Routing Engine operation. * * -- in any event, all output is gathered in the VTY buffering, and will * be sent to the console (or where ever) only when the outermost command * completes. * * There are three options associated with the output from a < operation: * * -- suppress command line echo * * whether to suppress echo of commands to the VTY before they are * dispatched. * * The default is not to suppress command line echo. * * -- suppress command results * * whether to suppress any output generated by each command. * * The default is not to suppress command results. * * -- suppress "more" * * whether to do "--more--", if currently applies, when finally * outputting all the command results. * * The default is not to suppress "more". * * This option can only be set for the outermost < operation. * * Remembering that all output occurs in one go when the outermost < operation * completes. * * The default is to show everything and implement "--more--", pretty much as * if the commands had been typed in. Except that "--more--" applies to * everything (including the command lines) together, rather than to the output * of each command individually. * * These options may be changed by flags attached to the <, as follows: * * ! suppress command line echo * * ? suppress "--more--" * * * suppress command output * * TODO: cli xxx commands for echo and output suppression and notes .... * * TODO: Error handling.... * * TODO: Closing the CLI.... * * TODO: Time out and the CLI.... * * * * * * * * * * */ #if 0 /*============================================================================== * */ #include "misc.h" #include "command_local.h" #include "vty_io_pipe.h" #include "vty_io_file.h" #include "vty_io_basic.h" /*============================================================================== * VTY File Output * * This is for input and output of configuration files and piped stuff. * * When reading the configuration (and piped stuff in the configuration) I/O * is blocking... nothing else can run while this is going on. Otherwise, * all I/O is non-blocking. */ /*============================================================================== * Prototypes. */ /*------------------------------------------------------------------------------ * * */ extern cmd_return_code_t uty_file_read_open(vty_io vio, qstring name, bool reflect) { const char* name_str ; int fd ; vio_vf vf ; vfd_io_type_t iot ; name_str = qs_make_string(name) ; iot = vfd_io_read | vfd_io_blocking ; /* TODO blocking */ /* Do the basic file open. */ fd = uty_fd_file_open(name_str, iot) ; if (fd < 0) { uty_out(vio, "%% Could not open input file %s\n", name_str) ; return CMD_WARNING ; } /* OK -- now push the new input onto the vin_stack. */ vf = uty_vf_new(vio, name_str, fd, vfd_file, iot) ; uty_vin_push(vio, vf, VIN_FILE, NULL, NULL, 16 * 1024) ; vf->parse_type = cmd_parse_strict ; vf->reflect_enabled = reflect ; return CMD_SUCCESS ; } ; /*------------------------------------------------------------------------------ * * */ extern cmd_return_code_t uty_file_write_open(vty_io vio, qstring name, bool append) { const char* name_str ; int fd ; vio_vf vf ; vfd_io_type_t iot ; iot = vfd_io_write | vfd_io_blocking ; /* TODO blocking */ if (append) iot |= vfd_io_append ; name_str = qs_make_string(name) ; /* Do the basic file open. */ fd = uty_fd_file_open(name_str, iot) ; if (fd < 0) { uty_out(vio, "%% Could not open output file %s\n", name_str) ; return CMD_WARNING ; } /* OK -- now push the new input onto the vin_stack. */ vf = uty_vf_new(vio, name_str, fd, vfd_file, iot) ; uty_vout_push(vio, vf, VOUT_FILE, NULL, NULL, 16 * 1024) ; return CMD_SUCCESS ; } ; /*============================================================================== * Command line fetch from a file or pipe. * * Returns: CMD_SUCCESS -- have another command line ready to go * CMD_WAITING -- do not have a command line at the moment * CMD_EOF -- ran into EOF * CMD_IO_ERROR -- ran into an I/O error */ extern cmd_return_code_t uty_file_fetch_command_line(vio_vf vf, qstring* line) { assert(vf->vin_state == vf_open) ; if (vf->line_complete) { vio_fifo_set_hold_mark(vf->ibuf) ; /* advance hold */ vf->line_complete = false ; vf->line_number += vf->line_step ; qs_set_len_nn(vf->cl, 0) ; vf->line_step = 0 ; } ; while (1) { char* s, * p, * q, * e ; size_t have ; ulen len ; s = vio_fifo_get(vf->ibuf, &have) ; /* If nothing in hand, try and get some more. * * Either exits or loops back to set s & have. */ if (have == 0) { int get ; get = vio_fifo_read_nb(vf->ibuf, vio_vfd_fd(vf->vfd), 100) ; if (get > 0) continue ; /* loop back */ if (get == 0) return CMD_WAITING ; /* need to set read ready ! */ if (get == -1) ; /* register error */ if (get == -2) return (qs_len_nn(vf->cl) > 0) ? CMD_SUCCESS : CMD_EOF ; } ; /* Try to find a '\n' -- converting all other control chars to ' ' * * When we find '\n' step back across any trailing ' ' (which includes * any control chars before the '\n'). * * This means that we cope with "\r\n" line terminators. But not anything * more exotic. */ p = s ; e = s + have ; /* have != 0 */ q = NULL ; while (p < e) { if (*p++ < 0x20) { if (*(p-1) != '\n') { *(p-1) = ' ' ; /* everything other than '\n' */ continue ; } ; ++vf->line_step ; /* got a '\n' */ q = p ; /* point just past '\n' */ do --q ; while ((q > s) && (*(q-1) == ' ')) ; /* discard trailing "spaces" */ break ; } ; } ; /* Step past what have just consumed -- we have a hold_mark, so * stuff is still in the fifo. */ vio_fifo_step(vf->ibuf, p - s) ; /* If not found '\n', then we have a line fragment that needs to be * appended to any previous line fragments. * * Loops back to try to get some more form the fifo. */ if (q == NULL) { qs_append_str_n(vf->cl, s, p - s) ; continue ; } ; /* If we have nothing so far, set alias to point at what we have in * the fifo. Otherwise, append to what we have. * * End up with: s = start of entire line, so far. * p = end of entire line so far. */ len = q - s ; /* length to add */ if (qs_len_nn(vf->cl) == 0) { qs_set_alias_n(vf->cl, s, len) ; p = q ; } else { if (len != 0) qs_append_str_n(vf->cl, s, len) ; s = qs_char_nn(vf->cl) ; p = s + qs_len_nn(vf->cl) ; if ((len == 0) && (p > s) && (*(p-1) == ' ')) { /* Have an empty end of line section, and the last character * of what we have so far is ' ', so need now to trim trailing * spaces off the stored stuff. */ do --p ; while ((p > s) && (*(p-1) == ' ')) ; qs_set_len_nn(vf->cl, p - s) ; } ; } ; /* Now worry about we have a trailing '\'. */ if ((p == s) || (*(p-1) != '\\')) break ; /* no \ => no continuation => success */ /* Have a trailing '\'. * * If there are an odd number of '\', strip the last one and loop * round to collect the continuation. * * If there are an even number of '\', then this is not a continuation. * * Note that this rule deals with the case of the continuation line * being empty... e.g. ....\\\ n n -- where n is '\n' */ q = p ; do --q ; while ((q > s) && (*(q-1) == '\\')) ; if (((p - q) & 1) == 0) break ; /* even => no continuation => success */ qs_set_len_nn(vf->cl, p - s - 1) ; /* strip odd '\' */ continue ; /* loop back to fetch more */ } ; /* Success have a line in hand */ vf->line_complete = true ; *line = vf->cl ; return CMD_SUCCESS ; } ; /*------------------------------------------------------------------------------ * Command output push to a file or pipe. * * Returns: CMD_SUCCESS -- done * CMD_IO_ERROR -- ran into an I/O error */ extern cmd_return_code_t uty_file_out_push(vio_vf vf) { assert(vf->vout_state == vf_open) ; if (vio_fifo_write_nb(vf->obuf, vio_vfd_fd(vf->vfd), false) >= 0) return CMD_SUCCESS ; else return CMD_IO_ERROR ; } ; /*------------------------------------------------------------------------------ * Tidy up after input file has been closed */ extern void uty_file_read_close(vio_vf vf) { return ; } ; /*------------------------------------------------------------------------------ * Flush output buffer and close. * * Returns: true <=> buffer (now) empty */ extern bool uty_file_write_close(vio_vf vf, bool final) { return vio_fifo_write_nb(vf->obuf, vio_vfd_fd(vf->vfd), true) == 0 ; } ; #endif