1 /* Virtual terminal interface shell.
2 * Copyright (C) 2000 Kunihiro Ishiguro
4 * This file is part of GNU Zebra.
6 * GNU Zebra is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
11 * GNU Zebra is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GNU Zebra; see the file COPYING. If not, write to the Free
18 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
29 #include <readline/readline.h>
30 #include <readline/history.h>
32 #include <lib/version.h>
37 #include "vtysh/vtysh.h"
38 #include "vtysh/vtysh_user.h"
40 /* VTY shell program name. */
43 /* Configuration file name and directory. */
44 char config_default[] = SYSCONFDIR VTYSH_DEFAULT_CONFIG;
45 char history_file[MAXPATHLEN];
47 /* Flag for indicate executing child command. */
50 /* For sigsetjmp() & siglongjmp(). */
51 static sigjmp_buf jmpbuf;
53 /* Flag for avoid recursive siglongjmp() call. */
54 static int jmpflag = 0;
56 /* A static variable for holding the line. */
57 static char *line_read;
59 /* Master of threads. */
60 struct thread_master *master;
65 /* SIGTSTP handler. This function care user's ^Z input. */
69 /* Execute "end" command. */
70 vtysh_execute ("end");
72 /* Initialize readline. */
76 /* Check jmpflag for duplicate siglongjmp(). */
82 /* Back to main command loop. */
83 siglongjmp (jmpbuf, 1);
86 /* SIGINT handler. This function care user's ^Z input. */
90 /* Check this process is not child process. */
95 rl_forced_update_display ();
99 /* Signale wrapper for vtysh. We don't use sigevent because
100 * vtysh doesn't use threads. TODO */
102 vtysh_signal_set (int signo, void (*func)(int))
104 struct sigaction sig;
105 struct sigaction osig;
107 sig.sa_handler = func;
108 sigemptyset (&sig.sa_mask);
111 sig.sa_flags |= SA_RESTART;
112 #endif /* SA_RESTART */
114 sigaction (signo, &sig, &osig);
117 /* Initialization of signal handles. */
121 vtysh_signal_set (SIGINT, sigint);
122 vtysh_signal_set (SIGTSTP, sigtstp);
123 vtysh_signal_set (SIGPIPE, SIG_IGN);
126 /* Help information display. */
131 fprintf (stderr, "Try `%s --help' for more information.\n", progname);
133 printf ("Usage : %s [OPTION...]\n\n" \
134 "Integrated shell for Quagga routing software suite. \n\n" \
135 "-b, --boot Execute boot startup configuration\n" \
136 "-c, --command Execute argument as command\n" \
137 "-d, --daemon Connect only to the specified daemon\n" \
138 "-E, --echo Echo prompt and command in -c mode\n" \
139 "-C, --dryrun Check configuration for validity and exit\n" \
140 "-h, --help Display this help and exit\n\n" \
141 "Note that multiple commands may be executed from the command\n" \
142 "line by passing multiple -c args, or by embedding linefeed\n" \
143 "characters in one or more of the commands.\n\n" \
144 "Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS);
149 /* VTY shell options, we use GNU getopt library. */
150 struct option longopts[] =
152 { "boot", no_argument, NULL, 'b'},
153 /* For compatibility with older zebra/quagga versions */
154 { "eval", required_argument, NULL, 'e'},
155 { "command", required_argument, NULL, 'c'},
156 { "daemon", required_argument, NULL, 'd'},
157 { "echo", no_argument, NULL, 'E'},
158 { "dryrun", no_argument, NULL, 'C'},
159 { "help", no_argument, NULL, 'h'},
160 { "noerror", no_argument, NULL, 'n'},
164 /* Read a string, and return a pointer to it. Returns NULL on EOF. */
169 /* If the buffer has already been allocated, return the memory
170 * to the free pool. */
177 /* Get a line from the user. Change prompt according to node. XXX. */
178 line_read = readline (vtysh_prompt ());
180 /* If the line has any text in it, save it on the history. But only if
181 * last command in history isn't the same one. */
182 if (line_read && *line_read)
185 last = previous_history();
186 if (!last || strcmp (last->line, line_read) != 0) {
187 add_history (line_read);
188 append_history(1,history_file);
195 static void log_it(const char *line)
197 time_t t = time(NULL);
198 struct tm *tmp = localtime(&t);
199 const char *user = getenv("USER");
205 strftime(tod, sizeof tod, "%Y%m%d-%H:%M.%S", tmp);
207 fprintf(logfile, "%s:%s %s\n", tod, user, line);
210 /* VTY shell main routine. */
212 main (int argc, char **argv, char **env)
218 const char *daemon_name = NULL;
221 struct cmd_rec *next;
223 struct cmd_rec *tail = NULL;
224 int echo_command = 0;
226 char *homedir = NULL;
228 /* Preserve name of myself. */
229 progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
231 /* if logging open now */
232 if ((p = getenv("VTYSH_LOG")) != NULL)
233 logfile = fopen(p, "a");
235 /* Option handling. */
238 opt = getopt_long (argc, argv, "be:c:d:nEhC", longopts, 0);
254 cr = XMALLOC(MTYPE_TMP, sizeof(*cr));
265 daemon_name = optarg;
285 /* Initialize user input buffer. */
289 /* Signal and others. */
290 vtysh_signal_init ();
292 /* Make vty structure and register commands. */
296 vtysh_config_init ();
300 /* Read vtysh configuration file before connecting to daemons. */
301 vtysh_read_config (config_default);
303 /* Start execution only if not in dry-run mode */
307 /* Ignore error messages */
309 freopen("/dev/null", "w", stdout);
311 /* Make sure we pass authentication before proceeding. */
314 /* Do not connect until we have passed authentication. */
315 if (vtysh_connect_all (daemon_name) <= 0)
317 fprintf(stderr, "Exiting: failed to connect to any daemons.\n");
322 * Setup history file for use by both -c and regular input
323 * If we can't find the home directory, then don't store
324 * the history information
326 homedir = vtysh_get_home ();
329 snprintf(history_file, sizeof(history_file), "%s/.history_quagga", homedir);
330 if (read_history (history_file) != 0)
334 fp = open (history_file, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
338 read_history (history_file);
345 /* Enter into enable node. */
346 vtysh_execute ("enable");
353 while ((eol = strchr(cmd->line, '\n')) != NULL)
357 add_history (cmd->line);
358 append_history (1, history_file);
361 printf("%s%s\n", vtysh_prompt(), cmd->line);
366 ret = vtysh_execute_no_pager(cmd->line);
368 ! (ret == CMD_SUCCESS ||
369 ret == CMD_SUCCESS_DAEMON ||
376 add_history (cmd->line);
377 append_history (1, history_file);
380 printf("%s%s\n", vtysh_prompt(), cmd->line);
385 ret = vtysh_execute_no_pager(cmd->line);
387 ! (ret == CMD_SUCCESS ||
388 ret == CMD_SUCCESS_DAEMON ||
400 history_truncate_file(history_file,1000);
404 /* Boot startup configuration file. */
407 if (vtysh_read_config (integrate_default))
409 fprintf (stderr, "Can't open configuration file [%s]\n",
419 vtysh_readline_init ();
423 /* Enter into enable node. */
424 vtysh_execute ("enable");
426 /* Preparation for longjmp() in sigtstp(). */
427 sigsetjmp (jmpbuf, 1);
430 /* Main command loop. */
431 while (vtysh_rl_gets ())
432 vtysh_execute (line_read);
434 history_truncate_file(history_file,1000);