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
31 #include <readline/readline.h>
32 #include <readline/history.h>
34 #include <lib/version.h>
40 #include "memory_vty.h"
42 #include "vtysh/vtysh.h"
43 #include "vtysh/vtysh_user.h"
45 /* VTY shell program name. */
48 static zebra_capabilities_t _caps_p
[] =
55 struct zebra_privs_t vtysh_privs
=
57 #if defined(QUAGGA_USER) && defined(QUAGGA_GROUP)
59 .group
= QUAGGA_GROUP
,
62 .vty_group
= VTY_GROUP
,
65 .cap_num_p
= array_size(_caps_p
),
69 /* Configuration file name and directory. */
70 char config_default
[] = SYSCONFDIR VTYSH_DEFAULT_CONFIG
;
71 char quagga_config_default
[] = SYSCONFDIR QUAGGA_DEFAULT_CONFIG
;
72 char history_file
[MAXPATHLEN
];
74 /* Flag for indicate executing child command. */
77 /* For sigsetjmp() & siglongjmp(). */
78 static sigjmp_buf jmpbuf
;
80 /* Flag for avoid recursive siglongjmp() call. */
81 static int jmpflag
= 0;
83 /* A static variable for holding the line. */
84 static char *line_read
;
86 /* Master of threads. */
87 struct thread_master
*master
;
92 /* SIGTSTP handler. This function care user's ^Z input. */
96 /* Execute "end" command. */
97 vtysh_execute ("end");
99 /* Initialize readline. */
103 /* Check jmpflag for duplicate siglongjmp(). */
109 /* Back to main command loop. */
110 siglongjmp (jmpbuf
, 1);
113 /* SIGINT handler. This function care user's ^Z input. */
117 /* Check this process is not child process. */
122 rl_forced_update_display ();
126 /* Signale wrapper for vtysh. We don't use sigevent because
127 * vtysh doesn't use threads. TODO */
129 vtysh_signal_set (int signo
, void (*func
)(int))
131 struct sigaction sig
;
132 struct sigaction osig
;
134 sig
.sa_handler
= func
;
135 sigemptyset (&sig
.sa_mask
);
138 sig
.sa_flags
|= SA_RESTART
;
139 #endif /* SA_RESTART */
141 sigaction (signo
, &sig
, &osig
);
144 /* Initialization of signal handles. */
146 vtysh_signal_init (void)
148 vtysh_signal_set (SIGINT
, sigint
);
149 vtysh_signal_set (SIGTSTP
, sigtstp
);
150 vtysh_signal_set (SIGPIPE
, SIG_IGN
);
153 /* Help information display. */
158 fprintf (stderr
, "Try `%s --help' for more information.\n", progname
);
160 printf ("Usage : %s [OPTION...]\n\n" \
161 "Integrated shell for Quagga routing software suite. \n\n" \
162 "-b, --boot Execute boot startup configuration\n" \
163 "-c, --command Execute argument as command\n" \
164 "-d, --daemon Connect only to the specified daemon\n" \
165 "-f, --inputfile Execute commands from specific file and exit\n" \
166 "-E, --echo Echo prompt and command in -c mode\n" \
167 "-C, --dryrun Check configuration for validity and exit\n" \
168 "-m, --markfile Mark input file with context end\n"
169 "-h, --help Display this help and exit\n\n" \
170 "Note that multiple commands may be executed from the command\n" \
171 "line by passing multiple -c args, or by embedding linefeed\n" \
172 "characters in one or more of the commands.\n\n" \
173 "Report bugs to %s\n", progname
, ZEBRA_BUG_ADDRESS
);
178 /* VTY shell options, we use GNU getopt library. */
179 struct option longopts
[] =
181 { "boot", no_argument
, NULL
, 'b'},
182 /* For compatibility with older zebra/quagga versions */
183 { "eval", required_argument
, NULL
, 'e'},
184 { "command", required_argument
, NULL
, 'c'},
185 { "daemon", required_argument
, NULL
, 'd'},
186 { "inputfile", required_argument
, NULL
, 'f'},
187 { "echo", no_argument
, NULL
, 'E'},
188 { "dryrun", no_argument
, NULL
, 'C'},
189 { "help", no_argument
, NULL
, 'h'},
190 { "noerror", no_argument
, NULL
, 'n'},
191 { "mark", no_argument
, NULL
, 'm'},
195 /* Read a string, and return a pointer to it. Returns NULL on EOF. */
200 /* If the buffer has already been allocated, return the memory
201 * to the free pool. */
208 /* Get a line from the user. Change prompt according to node. XXX. */
209 line_read
= readline (vtysh_prompt ());
211 /* If the line has any text in it, save it on the history. But only if
212 * last command in history isn't the same one. */
213 if (line_read
&& *line_read
)
216 last
= previous_history();
217 if (!last
|| strcmp (last
->line
, line_read
) != 0) {
218 add_history (line_read
);
219 append_history(1,history_file
);
226 static void log_it(const char *line
)
228 time_t t
= time(NULL
);
229 struct tm
*tmp
= localtime(&t
);
230 const char *user
= getenv("USER");
236 strftime(tod
, sizeof tod
, "%Y%m%d-%H:%M.%S", tmp
);
238 fprintf(logfile
, "%s:%s %s\n", tod
, user
, line
);
244 vtysh_flock_config (const char *flock_file
)
248 flock_fd
= open (flock_file
, O_RDONLY
, 0644);
251 fprintf (stderr
, "Unable to create lock file: %s, %s\n",
252 flock_file
, safe_strerror (errno
));
256 while (count
< 400 && (flock (flock_fd
, LOCK_EX
| LOCK_NB
) < 0))
263 fprintf(stderr
, "Flock of %s failed, continuing this may cause issues\n",
268 vtysh_unflock_config (void)
270 flock (flock_fd
, LOCK_UN
);
274 /* VTY shell main routine. */
276 main (int argc
, char **argv
, char **env
)
282 const char *daemon_name
= NULL
;
283 const char *inputfile
= NULL
;
286 struct cmd_rec
*next
;
288 struct cmd_rec
*tail
= NULL
;
289 int echo_command
= 0;
293 char *homedir
= NULL
;
295 /* Preserve name of myself. */
296 progname
= ((p
= strrchr (argv
[0], '/')) ? ++p
: argv
[0]);
298 /* if logging open now */
299 if ((p
= getenv("VTYSH_LOG")) != NULL
)
300 logfile
= fopen(p
, "a");
302 /* Option handling. */
305 opt
= getopt_long (argc
, argv
, "be:c:d:nf:mEhC", longopts
, 0);
321 cr
= XMALLOC(MTYPE_TMP
, sizeof(*cr
));
332 daemon_name
= optarg
;
358 /* Initialize user input buffer. */
362 zprivs_init (&vtysh_privs
);
364 /* Signal and others. */
365 vtysh_signal_init ();
367 /* Make vty structure and register commands. */
371 vtysh_config_init ();
375 /* Read vtysh configuration file before connecting to daemons. */
376 vtysh_read_config(config_default
);
382 fprintf(stderr
, "-f option MUST be specified with -m option\n");
385 return(vtysh_mark_file(inputfile
));
388 /* Start execution only if not in dry-run mode */
393 ret
= vtysh_read_config(inputfile
);
397 ret
= vtysh_read_config(quagga_config_default
);
402 /* Ignore error messages */
405 if (freopen("/dev/null", "w", stdout
) == NULL
)
407 fprintf(stderr
, "Exiting: Failed to duplicate stdout with -n option");
412 /* Make sure we pass authentication before proceeding. */
415 /* Do not connect until we have passed authentication. */
416 if (vtysh_connect_all (daemon_name
) <= 0)
418 fprintf(stderr
, "Exiting: failed to connect to any daemons.\n");
427 vtysh_flock_config (inputfile
);
428 ret
= vtysh_read_config(inputfile
);
429 vtysh_unflock_config ();
434 * Setup history file for use by both -c and regular input
435 * If we can't find the home directory, then don't store
436 * the history information
438 homedir
= vtysh_get_home ();
441 snprintf(history_file
, sizeof(history_file
), "%s/.history_quagga", homedir
);
442 if (read_history (history_file
) != 0)
446 fp
= open (history_file
, O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
);
450 read_history (history_file
);
457 /* Enter into enable node. */
458 vtysh_execute ("enable");
465 while ((eol
= strchr(cmd
->line
, '\n')) != NULL
)
469 add_history (cmd
->line
);
470 append_history (1, history_file
);
473 printf("%s%s\n", vtysh_prompt(), cmd
->line
);
478 ret
= vtysh_execute_no_pager(cmd
->line
);
480 ! (ret
== CMD_SUCCESS
||
481 ret
== CMD_SUCCESS_DAEMON
||
488 add_history (cmd
->line
);
489 append_history (1, history_file
);
492 printf("%s%s\n", vtysh_prompt(), cmd
->line
);
497 ret
= vtysh_execute_no_pager(cmd
->line
);
499 ! (ret
== CMD_SUCCESS
||
500 ret
== CMD_SUCCESS_DAEMON
||
508 XFREE(MTYPE_TMP
, cr
);
512 history_truncate_file(history_file
,1000);
516 /* Boot startup configuration file. */
519 vtysh_flock_config (integrate_default
);
520 int ret
= vtysh_read_config (integrate_default
);
521 vtysh_unflock_config ();
524 fprintf (stderr
, "Configuration file[%s] processing failure: %d\n",
525 integrate_default
, ret
);
537 vtysh_readline_init ();
541 /* Enter into enable node. */
542 vtysh_execute ("enable");
544 /* Preparation for longjmp() in sigtstp(). */
545 sigsetjmp (jmpbuf
, 1);
548 /* Main command loop. */
549 while (vtysh_rl_gets ())
550 vtysh_execute (line_read
);
552 history_truncate_file(history_file
,1000);