]>
git.proxmox.com Git - mirror_frr.git/blob - vtysh/vtysh_main.c
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. */
48 static uid_t elevuid
, realuid
;
49 static gid_t elevgid
, realgid
;
51 #define VTYSH_CONFIG_NAME "vtysh.conf"
52 #define FRR_CONFIG_NAME "frr.conf"
54 /* Configuration file name and directory. */
55 static char vtysh_config
[MAXPATHLEN
* 3];
56 char frr_config
[MAXPATHLEN
* 3];
57 char vtydir
[MAXPATHLEN
];
58 static char history_file
[MAXPATHLEN
];
60 /* Flag for indicate executing child command. */
63 /* Flag to indicate if in user/unprivileged mode. */
66 /* For sigsetjmp() & siglongjmp(). */
67 static sigjmp_buf jmpbuf
;
69 /* Flag for avoid recursive siglongjmp() call. */
70 static int jmpflag
= 0;
72 /* A static variable for holding the line. */
73 static char *line_read
;
75 /* Master of threads. */
76 struct thread_master
*master
;
81 /* SIGTSTP handler. This function care user's ^Z input. */
82 static void sigtstp(int sig
)
84 /* Execute "end" command. */
87 /* Initialize readline. */
91 /* Check jmpflag for duplicate siglongjmp(). */
97 /* Back to main command loop. */
98 siglongjmp(jmpbuf
, 1);
101 /* SIGINT handler. This function care user's ^Z input. */
102 static void sigint(int sig
)
104 /* Check this process is not child process. */
108 rl_forced_update_display();
112 /* Signale wrapper for vtysh. We don't use sigevent because
113 * vtysh doesn't use threads. TODO */
114 static void vtysh_signal_set(int signo
, void (*func
)(int))
116 struct sigaction sig
;
117 struct sigaction osig
;
119 sig
.sa_handler
= func
;
120 sigemptyset(&sig
.sa_mask
);
123 sig
.sa_flags
|= SA_RESTART
;
124 #endif /* SA_RESTART */
126 sigaction(signo
, &sig
, &osig
);
129 /* Initialization of signal handles. */
130 static void vtysh_signal_init(void)
132 vtysh_signal_set(SIGINT
, sigint
);
133 vtysh_signal_set(SIGTSTP
, sigtstp
);
134 vtysh_signal_set(SIGPIPE
, SIG_IGN
);
137 /* Help information display. */
138 static void usage(int status
)
141 fprintf(stderr
, "Try `%s --help' for more information.\n",
144 printf("Usage : %s [OPTION...]\n\n"
145 "Integrated shell for FRR. \n\n"
146 "-b, --boot Execute boot startup configuration\n"
147 "-c, --command Execute argument as command\n"
148 "-d, --daemon Connect only to the specified daemon\n"
149 "-f, --inputfile Execute commands from specific file and exit\n"
150 "-E, --echo Echo prompt and command in -c mode\n"
151 "-C, --dryrun Check configuration for validity and exit\n"
152 "-m, --markfile Mark input file with context end\n"
153 " --vty_socket Override vty socket path\n"
154 " --config_dir Override config directory path\n"
155 "-N --pathspace Insert prefix into config & socket paths\n"
156 "-u --user Run as an unprivileged user\n"
157 "-w, --writeconfig Write integrated config (frr.conf) and exit\n"
158 "-h, --help Display this help and exit\n\n"
159 "Note that multiple commands may be executed from the command\n"
160 "line by passing multiple -c args, or by embedding linefeed\n"
161 "characters in one or more of the commands.\n\n"
162 "Report bugs to %s\n",
163 progname
, FRR_BUG_ADDRESS
);
168 /* VTY shell options, we use GNU getopt library. */
169 #define OPTION_VTYSOCK 1000
170 #define OPTION_CONFDIR 1001
171 struct option longopts
[] = {
172 {"boot", no_argument
, NULL
, 'b'},
173 /* For compatibility with older zebra/quagga versions */
174 {"eval", required_argument
, NULL
, 'e'},
175 {"command", required_argument
, NULL
, 'c'},
176 {"daemon", required_argument
, NULL
, 'd'},
177 {"vty_socket", required_argument
, NULL
, OPTION_VTYSOCK
},
178 {"config_dir", required_argument
, NULL
, OPTION_CONFDIR
},
179 {"inputfile", required_argument
, NULL
, 'f'},
180 {"echo", no_argument
, NULL
, 'E'},
181 {"dryrun", no_argument
, NULL
, 'C'},
182 {"help", no_argument
, NULL
, 'h'},
183 {"noerror", no_argument
, NULL
, 'n'},
184 {"mark", no_argument
, NULL
, 'm'},
185 {"writeconfig", no_argument
, NULL
, 'w'},
186 {"pathspace", required_argument
, NULL
, 'N'},
187 {"user", no_argument
, NULL
, 'u'},
190 /* Read a string, and return a pointer to it. Returns NULL on EOF. */
191 static char *vtysh_rl_gets(void)
194 /* If the buffer has already been allocated, return the memory
195 * to the free pool. */
201 /* Get a line from the user. Change prompt according to node. XXX. */
202 line_read
= readline(vtysh_prompt());
204 /* If the line has any text in it, save it on the history. But only if
205 * last command in history isn't the same one. */
206 if (line_read
&& *line_read
) {
208 last
= previous_history();
209 if (!last
|| strcmp(last
->line
, line_read
) != 0) {
210 add_history(line_read
);
211 append_history(1, history_file
);
218 static void log_it(const char *line
)
220 time_t t
= time(NULL
);
221 struct tm
*tmp
= localtime(&t
);
222 const char *user
= getenv("USER");
228 strftime(tod
, sizeof tod
, "%Y%m%d-%H:%M.%S", tmp
);
230 fprintf(logfile
, "%s:%s %s\n", tod
, user
, line
);
235 static void vtysh_flock_config(const char *flock_file
)
239 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 "Flock of %s failed, continuing this may cause issues\n",
257 static void vtysh_unflock_config(void)
259 flock(flock_fd
, LOCK_UN
);
265 if (elevuid
!= realuid
&& seteuid(elevuid
)) {
266 perror("seteuid(on)");
269 if (elevgid
!= realgid
&& setegid(elevgid
)) {
270 perror("setegid(on)");
277 if (elevuid
!= realuid
&& seteuid(realuid
)) {
278 perror("seteuid(off)");
281 if (elevgid
!= realgid
&& setegid(realgid
)) {
282 perror("setegid(off)");
287 /* VTY shell main routine. */
288 int main(int argc
, char **argv
, char **env
)
294 const char *daemon_name
= NULL
;
295 const char *inputfile
= NULL
;
298 struct cmd_rec
*next
;
300 struct cmd_rec
*tail
= NULL
;
301 int echo_command
= 0;
306 char *homedir
= NULL
;
308 char sysconfdir
[MAXPATHLEN
];
309 const char *pathspace_arg
= NULL
;
310 char pathspace
[MAXPATHLEN
] = "";
312 /* SUID: drop down to calling user & go back up when needed */
319 user_mode
= 0; /* may be set in options processing */
321 /* Preserve name of myself. */
322 progname
= ((p
= strrchr(argv
[0], '/')) ? ++p
: argv
[0]);
324 strlcpy(sysconfdir
, frr_sysconfdir
, sizeof(sysconfdir
));
325 strlcpy(vtydir
, frr_vtydir
, sizeof(vtydir
));
327 /* Option handling. */
329 opt
= getopt_long(argc
, argv
, "be:c:d:nf:mEhCwN:u",
344 cr
= XMALLOC(MTYPE_TMP
, sizeof(*cr
));
354 ditch_suid
= 1; /* option disables SUID */
355 strlcpy(vtydir
, optarg
, sizeof(vtydir
));
358 ditch_suid
= 1; /* option disables SUID */
359 snprintf(sysconfdir
, sizeof(sysconfdir
), "%s/", optarg
);
362 if (strchr(optarg
, '/') || strchr(optarg
, '.')) {
364 "slashes or dots are not permitted in the --pathspace option.\n");
367 pathspace_arg
= optarg
;
368 snprintf(pathspace
, sizeof(pathspace
), "%s/", optarg
);
371 daemon_name
= optarg
;
408 if (markfile
+ writeconfig
+ dryrun
+ boot_flag
> 1) {
410 "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 "WARNING: Combinining the -f option with -b or -w is "
417 "NOT SUPPORTED since its\nresults are inconsistent!\n");
420 snprintf(vtysh_config
, sizeof(vtysh_config
), "%s%s%s", sysconfdir
,
421 pathspace
, VTYSH_CONFIG_NAME
);
422 snprintf(frr_config
, sizeof(frr_config
), "%s%s%s", sysconfdir
,
423 pathspace
, FRR_CONFIG_NAME
);
426 strlcat(vtydir
, "/", sizeof(vtydir
));
427 strlcat(vtydir
, pathspace_arg
, sizeof(vtydir
));
430 /* Initialize user input buffer. */
434 /* Signal and others. */
437 /* Make vty structure and register commands. */
446 /* Read vtysh configuration file before connecting to daemons.
447 * (file may not be readable to calling user in SUID mode) */
449 vtysh_read_config(vtysh_config
);
456 "-f option MUST be specified with -m option\n");
459 return (vtysh_mark_file(inputfile
));
462 /* Start execution only if not in dry-run mode */
463 if (dryrun
&& !cmd
) {
465 ret
= vtysh_read_config(inputfile
);
467 ret
= vtysh_read_config(frr_config
);
473 if (dryrun
&& cmd
&& cmd
->line
) {
475 vtysh_execute("enable");
478 char *cmdnow
= cmd
->line
, *next
;
480 next
= strchr(cmdnow
, '\n');
485 printf("%s%s\n", vtysh_prompt(),
488 ret
= vtysh_execute_no_pager(cmdnow
);
490 && !(ret
== CMD_SUCCESS
491 || ret
== CMD_SUCCESS_DAEMON
492 || ret
== CMD_WARNING
))
494 } while ((cmdnow
= next
) != NULL
);
498 XFREE(MTYPE_TMP
, cr
);
503 /* Ignore error messages */
505 if (freopen("/dev/null", "w", stdout
) == NULL
) {
507 "Exiting: Failed to duplicate stdout with -n option");
512 /* SUID: go back up elevated privs */
515 /* Make sure we pass authentication before proceeding. */
518 /* Do not connect until we have passed authentication. */
519 if (vtysh_connect_all(daemon_name
) <= 0) {
520 fprintf(stderr
, "Exiting: failed to connect to any daemons.\n");
527 /* SUID: back down, don't need privs further on */
533 "writeconfig cannot be used when running as an unprivileged user.\n");
539 vtysh_execute("enable");
540 return vtysh_write_config_integrated();
544 vtysh_flock_config(inputfile
);
545 ret
= vtysh_read_config(inputfile
);
546 vtysh_unflock_config();
551 * Setup history file for use by both -c and regular input
552 * If we can't find the home directory, then don't store
553 * the history information
555 homedir
= vtysh_get_home();
557 snprintf(history_file
, sizeof(history_file
), "%s/.history_frr",
559 if (read_history(history_file
) != 0) {
562 fp
= open(history_file
, O_CREAT
| O_EXCL
,
567 read_history(history_file
);
571 if (getenv("VTYSH_LOG")) {
572 const char *logpath
= getenv("VTYSH_LOG");
574 logfile
= fopen(logpath
, "a");
576 fprintf(stderr
, "Failed to open logfile (%s): %s\n",
577 logpath
, strerror(errno
));
583 if (cmd
&& cmd
->line
) {
584 /* Enter into enable node. */
586 vtysh_execute("enable");
588 while (cmd
!= NULL
) {
592 while ((eol
= strchr(cmd
->line
, '\n')) != NULL
) {
595 add_history(cmd
->line
);
596 append_history(1, history_file
);
599 printf("%s%s\n", vtysh_prompt(),
605 ret
= vtysh_execute_no_pager(cmd
->line
);
607 && !(ret
== CMD_SUCCESS
608 || ret
== CMD_SUCCESS_DAEMON
609 || ret
== CMD_WARNING
))
615 add_history(cmd
->line
);
616 append_history(1, history_file
);
619 printf("%s%s\n", vtysh_prompt(), cmd
->line
);
625 * Parsing logic for regular commands will be different
626 * than for those commands requiring further
627 * processing, such as cli instructions terminating
628 * with question-mark character.
630 if (!vtysh_execute_command_questionmark(cmd
->line
))
633 ret
= vtysh_execute_no_pager(cmd
->line
);
636 && !(ret
== CMD_SUCCESS
|| ret
== CMD_SUCCESS_DAEMON
637 || ret
== CMD_WARNING
))
644 XFREE(MTYPE_TMP
, cr
);
648 history_truncate_file(history_file
, 1000);
652 /* Boot startup configuration file. */
654 vtysh_flock_config(frr_config
);
655 int ret
= vtysh_read_config(frr_config
);
656 vtysh_unflock_config();
659 "Configuration file[%s] processing failure: %d\n",
671 vtysh_readline_init();
675 /* Enter into enable node. */
677 vtysh_execute("enable");
679 /* Preparation for longjmp() in sigtstp(). */
680 sigsetjmp(jmpbuf
, 1);
683 /* Main command loop. */
684 while (vtysh_rl_gets())
685 vtysh_execute(line_read
);
689 history_truncate_file(history_file
, 1000);