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 along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
30 #include <readline/readline.h>
31 #include <readline/history.h>
33 #include <lib/version.h>
38 #include "memory_vty.h"
41 #include "vtysh/vtysh.h"
42 #include "vtysh/vtysh_user.h"
44 /* VTY shell program name. */
47 /* Configuration file name and directory. */
48 static char vtysh_config_always
[MAXPATHLEN
] = SYSCONFDIR VTYSH_DEFAULT_CONFIG
;
49 static char quagga_config_default
[MAXPATHLEN
] = SYSCONFDIR FRR_DEFAULT_CONFIG
;
50 char *quagga_config
= quagga_config_default
;
51 char history_file
[MAXPATHLEN
];
53 /* Flag for indicate executing child command. */
56 /* VTY Socket prefix */
57 const char * vty_sock_path
= NULL
;
59 /* For sigsetjmp() & siglongjmp(). */
60 static sigjmp_buf jmpbuf
;
62 /* Flag for avoid recursive siglongjmp() call. */
63 static int jmpflag
= 0;
65 /* A static variable for holding the line. */
66 static char *line_read
;
68 /* Master of threads. */
69 struct thread_master
*master
;
74 /* SIGTSTP handler. This function care user's ^Z input. */
78 /* Execute "end" command. */
79 vtysh_execute ("end");
81 /* Initialize readline. */
85 /* Check jmpflag for duplicate siglongjmp(). */
91 /* Back to main command loop. */
92 siglongjmp (jmpbuf
, 1);
95 /* SIGINT handler. This function care user's ^Z input. */
99 /* Check this process is not child process. */
104 rl_forced_update_display ();
108 /* Signale wrapper for vtysh. We don't use sigevent because
109 * vtysh doesn't use threads. TODO */
111 vtysh_signal_set (int signo
, void (*func
)(int))
113 struct sigaction sig
;
114 struct sigaction osig
;
116 sig
.sa_handler
= func
;
117 sigemptyset (&sig
.sa_mask
);
120 sig
.sa_flags
|= SA_RESTART
;
121 #endif /* SA_RESTART */
123 sigaction (signo
, &sig
, &osig
);
126 /* Initialization of signal handles. */
128 vtysh_signal_init (void)
130 vtysh_signal_set (SIGINT
, sigint
);
131 vtysh_signal_set (SIGTSTP
, sigtstp
);
132 vtysh_signal_set (SIGPIPE
, SIG_IGN
);
135 /* Help information display. */
140 fprintf (stderr
, "Try `%s --help' for more information.\n", progname
);
142 printf ("Usage : %s [OPTION...]\n\n" \
143 "Integrated shell for FRR. \n\n" \
144 "-b, --boot Execute boot startup configuration\n" \
145 "-c, --command Execute argument as command\n" \
146 "-d, --daemon Connect only to the specified daemon\n" \
147 "-f, --inputfile Execute commands from specific file and exit\n" \
148 "-E, --echo Echo prompt and command in -c mode\n" \
149 "-C, --dryrun Check configuration for validity and exit\n" \
150 "-m, --markfile Mark input file with context end\n" \
151 " --vty_socket Override vty socket path\n" \
152 " --config_dir Override config directory path\n" \
153 "-w, --writeconfig Write integrated config (frr.conf) and exit\n" \
154 "-h, --help Display this help and exit\n\n" \
155 "Note that multiple commands may be executed from the command\n" \
156 "line by passing multiple -c args, or by embedding linefeed\n" \
157 "characters in one or more of the commands.\n\n" \
158 "Report bugs to %s\n", progname
, FRR_BUG_ADDRESS
);
163 /* VTY shell options, we use GNU getopt library. */
164 #define OPTION_VTYSOCK 1000
165 #define OPTION_CONFDIR 1001
166 struct option longopts
[] =
168 { "boot", no_argument
, NULL
, 'b'},
169 /* For compatibility with older zebra/quagga versions */
170 { "eval", required_argument
, NULL
, 'e'},
171 { "command", required_argument
, NULL
, 'c'},
172 { "daemon", required_argument
, NULL
, 'd'},
173 { "vty_socket", required_argument
, NULL
, OPTION_VTYSOCK
},
174 { "config_dir", required_argument
, NULL
, OPTION_CONFDIR
},
175 { "inputfile", required_argument
, NULL
, 'f'},
176 { "echo", no_argument
, NULL
, 'E'},
177 { "dryrun", no_argument
, NULL
, 'C'},
178 { "help", no_argument
, NULL
, 'h'},
179 { "noerror", no_argument
, NULL
, 'n'},
180 { "mark", no_argument
, NULL
, 'm'},
181 { "writeconfig", no_argument
, NULL
, 'w'},
185 /* Read a string, and return a pointer to it. Returns NULL on EOF. */
190 /* If the buffer has already been allocated, return the memory
191 * to the free pool. */
198 /* Get a line from the user. Change prompt according to node. XXX. */
199 line_read
= readline (vtysh_prompt ());
201 /* If the line has any text in it, save it on the history. But only if
202 * last command in history isn't the same one. */
203 if (line_read
&& *line_read
)
206 last
= previous_history();
207 if (!last
|| strcmp (last
->line
, line_read
) != 0) {
208 add_history (line_read
);
209 append_history(1,history_file
);
216 static void log_it(const char *line
)
218 time_t t
= time(NULL
);
219 struct tm
*tmp
= localtime(&t
);
220 const char *user
= getenv("USER");
226 strftime(tod
, sizeof tod
, "%Y%m%d-%H:%M.%S", tmp
);
228 fprintf(logfile
, "%s:%s %s\n", tod
, user
, line
);
234 vtysh_flock_config (const char *flock_file
)
238 flock_fd
= open (flock_file
, O_RDONLY
, 0644);
241 fprintf (stderr
, "Unable to create lock file: %s, %s\n",
242 flock_file
, safe_strerror (errno
));
246 while (count
< 400 && (flock (flock_fd
, LOCK_EX
| LOCK_NB
) < 0))
253 fprintf(stderr
, "Flock of %s failed, continuing this may cause issues\n",
258 vtysh_unflock_config (void)
260 flock (flock_fd
, LOCK_UN
);
264 /* VTY shell main routine. */
266 main (int argc
, char **argv
, char **env
)
272 const char *daemon_name
= NULL
;
273 const char *inputfile
= NULL
;
274 const char *vtysh_configfile_name
;
277 struct cmd_rec
*next
;
279 struct cmd_rec
*tail
= NULL
;
280 int echo_command
= 0;
285 char *homedir
= NULL
;
287 /* check for restricted functionality if vtysh is run setuid */
288 int restricted
= (getuid() != geteuid()) || (getgid() != getegid());
290 /* Preserve name of myself. */
291 progname
= ((p
= strrchr (argv
[0], '/')) ? ++p
: argv
[0]);
293 /* if logging open now */
294 if ((p
= getenv("VTYSH_LOG")) != NULL
)
295 logfile
= fopen(p
, "a");
297 /* Option handling. */
300 opt
= getopt_long (argc
, argv
, "be:c:d:nf:mEhCw", longopts
, 0);
316 cr
= XMALLOC(MTYPE_TMP
, sizeof(*cr
));
327 vty_sock_path
= optarg
;
331 * Skip option for Config Directory if setuid
335 fprintf (stderr
, "Overriding of Config Directory blocked for vtysh with setuid");
339 * Overwrite location for vtysh.conf
341 vtysh_configfile_name
= strrchr(VTYSH_DEFAULT_CONFIG
, '/');
342 if (vtysh_configfile_name
)
344 vtysh_configfile_name
++;
347 * VTYSH_DEFAULT_CONFIG configured with relative path
348 * during config? Should really never happen for
351 vtysh_configfile_name
= (char *) VTYSH_DEFAULT_CONFIG
;
352 strlcpy(vtysh_config_always
, optarg
, sizeof(vtysh_config_always
));
353 strlcat(vtysh_config_always
, "/", sizeof(vtysh_config_always
));
354 strlcat(vtysh_config_always
, vtysh_configfile_name
,
355 sizeof(vtysh_config_always
));
357 * Overwrite location for frr.conf
359 vtysh_configfile_name
= strrchr(FRR_DEFAULT_CONFIG
, '/');
360 if (vtysh_configfile_name
)
362 vtysh_configfile_name
++;
365 * FRR_DEFAULT_CONFIG configured with relative path
366 * during config? Should really never happen for
369 vtysh_configfile_name
= (char *) FRR_DEFAULT_CONFIG
;
370 strlcpy(quagga_config_default
, optarg
, sizeof(vtysh_config_always
));
371 strlcat(quagga_config_default
, "/", sizeof(vtysh_config_always
));
372 strlcat(quagga_config_default
, vtysh_configfile_name
,
373 sizeof(quagga_config_default
));
376 daemon_name
= optarg
;
406 vty_sock_path
= frr_vtydir
;
408 if (markfile
+ writeconfig
+ dryrun
+ boot_flag
> 1)
410 fprintf (stderr
, "Invalid combination of arguments. Please specify at "
411 "most one of:\n\t-b, -C, -m, -w\n");
414 if (inputfile
&& (writeconfig
|| boot_flag
))
416 fprintf (stderr
, "WARNING: Combinining the -f option with -b or -w is "
417 "NOT SUPPORTED since its\nresults are inconsistent!\n");
420 /* Initialize user input buffer. */
424 /* Signal and others. */
425 vtysh_signal_init ();
427 /* Make vty structure and register commands. */
431 vtysh_config_init ();
435 /* Read vtysh configuration file before connecting to daemons. */
436 vtysh_read_config(vtysh_config_always
);
442 fprintf(stderr
, "-f option MUST be specified with -m option\n");
445 return(vtysh_mark_file(inputfile
));
448 /* Start execution only if not in dry-run mode */
453 ret
= vtysh_read_config(inputfile
);
457 ret
= vtysh_read_config(quagga_config_default
);
465 vtysh_execute ("enable");
469 char *cmdnow
= cmd
->line
, *next
;
472 next
= strchr(cmdnow
, '\n');
477 printf("%s%s\n", vtysh_prompt(), cmdnow
);
479 ret
= vtysh_execute_no_pager(cmdnow
);
481 ! (ret
== CMD_SUCCESS
||
482 ret
== CMD_SUCCESS_DAEMON
||
486 while ((cmdnow
= next
) != NULL
);
490 XFREE(MTYPE_TMP
, cr
);
495 /* Ignore error messages */
498 if (freopen("/dev/null", "w", stdout
) == NULL
)
500 fprintf(stderr
, "Exiting: Failed to duplicate stdout with -n option");
505 /* Make sure we pass authentication before proceeding. */
508 /* Do not connect until we have passed authentication. */
509 if (vtysh_connect_all (daemon_name
) <= 0)
511 fprintf(stderr
, "Exiting: failed to connect to any daemons.\n");
520 vtysh_execute ("enable");
521 return vtysh_write_config_integrated ();
526 vtysh_flock_config (inputfile
);
527 ret
= vtysh_read_config(inputfile
);
528 vtysh_unflock_config ();
533 * Setup history file for use by both -c and regular input
534 * If we can't find the home directory, then don't store
535 * the history information
537 homedir
= vtysh_get_home ();
540 snprintf(history_file
, sizeof(history_file
), "%s/.history_quagga", homedir
);
541 if (read_history (history_file
) != 0)
545 fp
= open (history_file
, O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
);
549 read_history (history_file
);
556 /* Enter into enable node. */
557 vtysh_execute ("enable");
564 while ((eol
= strchr(cmd
->line
, '\n')) != NULL
)
568 add_history (cmd
->line
);
569 append_history (1, history_file
);
572 printf("%s%s\n", vtysh_prompt(), cmd
->line
);
577 ret
= vtysh_execute_no_pager(cmd
->line
);
579 ! (ret
== CMD_SUCCESS
||
580 ret
== CMD_SUCCESS_DAEMON
||
587 add_history (cmd
->line
);
588 append_history (1, history_file
);
591 printf("%s%s\n", vtysh_prompt(), cmd
->line
);
596 ret
= vtysh_execute_no_pager(cmd
->line
);
598 ! (ret
== CMD_SUCCESS
||
599 ret
== CMD_SUCCESS_DAEMON
||
607 XFREE(MTYPE_TMP
, cr
);
611 history_truncate_file(history_file
,1000);
615 /* Boot startup configuration file. */
618 vtysh_flock_config (quagga_config
);
619 int ret
= vtysh_read_config (quagga_config
);
620 vtysh_unflock_config ();
623 fprintf (stderr
, "Configuration file[%s] processing failure: %d\n",
636 vtysh_readline_init ();
640 /* Enter into enable node. */
641 vtysh_execute ("enable");
643 /* Preparation for longjmp() in sigtstp(). */
644 sigsetjmp (jmpbuf
, 1);
647 /* Main command loop. */
648 while (vtysh_rl_gets ())
649 vtysh_execute (line_read
);
651 history_truncate_file(history_file
,1000);