/* Virtual terminal interface shell. * Copyright (C) 2000 Kunihiro Ishiguro * * 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 #include #include #include #include #include #include #include "getopt.h" #include "command.h" #include "memory.h" #include "vtysh/vtysh.h" #include "vtysh/vtysh_user.h" /* VTY shell program name. */ char *progname; /* Configuration file name and directory. */ char config_default[] = SYSCONFDIR VTYSH_DEFAULT_CONFIG; char history_file[MAXPATHLEN]; /* Flag for indicate executing child command. */ int execute_flag = 0; /* For sigsetjmp() & siglongjmp(). */ static sigjmp_buf jmpbuf; /* Flag for avoid recursive siglongjmp() call. */ static int jmpflag = 0; /* A static variable for holding the line. */ static char *line_read; /* Master of threads. */ struct thread_master *master; /* Command logging */ FILE *logfile; /* SIGTSTP handler. This function care user's ^Z input. */ void sigtstp (int sig) { /* Execute "end" command. */ vtysh_execute ("end"); /* Initialize readline. */ rl_initialize (); printf ("\n"); /* Check jmpflag for duplicate siglongjmp(). */ if (! jmpflag) return; jmpflag = 0; /* Back to main command loop. */ siglongjmp (jmpbuf, 1); } /* SIGINT handler. This function care user's ^Z input. */ void sigint (int sig) { /* Check this process is not child process. */ if (! execute_flag) { rl_initialize (); printf ("\n"); rl_forced_update_display (); } } /* Signale wrapper for vtysh. We don't use sigevent because * vtysh doesn't use threads. TODO */ RETSIGTYPE * vtysh_signal_set (int signo, void (*func)(int)) { int ret; struct sigaction sig; struct sigaction osig; sig.sa_handler = func; sigemptyset (&sig.sa_mask); sig.sa_flags = 0; #ifdef SA_RESTART sig.sa_flags |= SA_RESTART; #endif /* SA_RESTART */ ret = sigaction (signo, &sig, &osig); if (ret < 0) return (SIG_ERR); else return (osig.sa_handler); } /* Initialization of signal handles. */ void vtysh_signal_init () { vtysh_signal_set (SIGINT, sigint); vtysh_signal_set (SIGTSTP, sigtstp); vtysh_signal_set (SIGPIPE, SIG_IGN); } /* Help information display. */ static void usage (int status) { if (status != 0) fprintf (stderr, "Try `%s --help' for more information.\n", progname); else printf ("Usage : %s [OPTION...]\n\n" \ "Integrated shell for Quagga routing software suite. \n\n" \ "-b, --boot Execute boot startup configuration\n" \ "-c, --command Execute argument as command\n" \ "-d, --daemon Connect only to the specified daemon\n" \ "-E, --echo Echo prompt and command in -c mode\n" \ "-C, --dryrun Check configuration for validity and exit\n" \ "-h, --help Display this help and exit\n\n" \ "Note that multiple commands may be executed from the command\n" \ "line by passing multiple -c args, or by embedding linefeed\n" \ "characters in one or more of the commands.\n\n" \ "Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); exit (status); } /* VTY shell options, we use GNU getopt library. */ struct option longopts[] = { { "boot", no_argument, NULL, 'b'}, /* For compatibility with older zebra/quagga versions */ { "eval", required_argument, NULL, 'e'}, { "command", required_argument, NULL, 'c'}, { "daemon", required_argument, NULL, 'd'}, { "echo", no_argument, NULL, 'E'}, { "dryrun", no_argument, NULL, 'C'}, { "help", no_argument, NULL, 'h'}, { "noerror", no_argument, NULL, 'n'}, { 0 } }; /* Read a string, and return a pointer to it. Returns NULL on EOF. */ char * vtysh_rl_gets () { HIST_ENTRY *last; /* If the buffer has already been allocated, return the memory * to the free pool. */ if (line_read) { free (line_read); line_read = NULL; } /* Get a line from the user. Change prompt according to node. XXX. */ line_read = readline (vtysh_prompt ()); /* If the line has any text in it, save it on the history. But only if * last command in history isn't the same one. */ if (line_read && *line_read) { using_history(); last = previous_history(); if (!last || strcmp (last->line, line_read) != 0) { add_history (line_read); append_history(1,history_file); } } return (line_read); } static void log_it(const char *line) { time_t t = time(NULL); struct tm *tmp = localtime(&t); char *user = getenv("USER") ? : "boot"; char tod[64]; strftime(tod, sizeof tod, "%Y%m%d-%H:%M.%S", tmp); fprintf(logfile, "%s:%s %s\n", tod, user, line); } /* VTY shell main routine. */ int main (int argc, char **argv, char **env) { char *p; int opt; int dryrun = 0; int boot_flag = 0; const char *daemon_name = NULL; struct cmd_rec { const char *line; struct cmd_rec *next; } *cmd = NULL; struct cmd_rec *tail = NULL; int echo_command = 0; int no_error = 0; /* Preserve name of myself. */ progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]); /* if logging open now */ if ((p = getenv("VTYSH_LOG")) != NULL) logfile = fopen(p, "a"); /* Option handling. */ while (1) { opt = getopt_long (argc, argv, "be:c:d:nEhC", longopts, 0); if (opt == EOF) break; switch (opt) { case 0: break; case 'b': boot_flag = 1; break; case 'e': case 'c': { struct cmd_rec *cr; cr = XMALLOC(0, sizeof(*cr)); cr->line = optarg; cr->next = NULL; if (tail) tail->next = cr; else cmd = cr; tail = cr; } break; case 'd': daemon_name = optarg; break; case 'n': no_error = 1; break; case 'E': echo_command = 1; break; case 'C': dryrun = 1; break; case 'h': usage (0); break; default: usage (1); break; } } /* Initialize user input buffer. */ line_read = NULL; setlinebuf(stdout); /* Signal and others. */ vtysh_signal_init (); /* Make vty structure and register commands. */ vtysh_init_vty (); vtysh_init_cmd (); vtysh_user_init (); vtysh_config_init (); vty_init_vtysh (); sort_node (); /* Read vtysh configuration file before connecting to daemons. */ vtysh_read_config (config_default); /* Start execution only if not in dry-run mode */ if(dryrun) return(0); /* Ignore error messages */ if (no_error) freopen("/dev/null", "w", stdout); /* Make sure we pass authentication before proceeding. */ vtysh_auth (); /* Do not connect until we have passed authentication. */ if (vtysh_connect_all (daemon_name) <= 0) { fprintf(stderr, "Exiting: failed to connect to any daemons.\n"); exit(1); } /* If eval mode. */ if (cmd) { /* Enter into enable node. */ vtysh_execute ("enable"); while (cmd != NULL) { int ret; char *eol; while ((eol = strchr(cmd->line, '\n')) != NULL) { *eol = '\0'; if (echo_command) printf("%s%s\n", vtysh_prompt(), cmd->line); if (logfile) log_it(cmd->line); ret = vtysh_execute_no_pager(cmd->line); if (!no_error && ! (ret == CMD_SUCCESS || ret == CMD_SUCCESS_DAEMON || ret == CMD_WARNING)) exit(1); cmd->line = eol+1; } if (echo_command) printf("%s%s\n", vtysh_prompt(), cmd->line); if (logfile) log_it(cmd->line); ret = vtysh_execute_no_pager(cmd->line); if (!no_error && ! (ret == CMD_SUCCESS || ret == CMD_SUCCESS_DAEMON || ret == CMD_WARNING)) exit(1); { struct cmd_rec *cr; cr = cmd; cmd = cmd->next; XFREE(0, cr); } } exit (0); } /* Boot startup configuration file. */ if (boot_flag) { if (vtysh_read_config (integrate_default)) { fprintf (stderr, "Can't open configuration file [%s]\n", integrate_default); exit (1); } else exit (0); } vtysh_pager_init (); vtysh_readline_init (); vty_hello (vty); /* Enter into enable node. */ vtysh_execute ("enable"); /* Preparation for longjmp() in sigtstp(). */ sigsetjmp (jmpbuf, 1); jmpflag = 1; snprintf(history_file, sizeof(history_file), "%s/.history_quagga", getenv("HOME")); read_history(history_file); /* Main command loop. */ while (vtysh_rl_gets ()) vtysh_execute (line_read); history_truncate_file(history_file,1000); printf ("\n"); /* Rest in peace. */ exit (0); }