]> git.proxmox.com Git - mirror_frr.git/blame - vtysh/vtysh_main.c
Merge pull request #12816 from gpnaveen/stc_rte_err_msg
[mirror_frr.git] / vtysh / vtysh_main.c
CommitLineData
acddc0ed 1// SPDX-License-Identifier: GPL-2.0-or-later
718e3744 2/* Virtual terminal interface shell.
3 * Copyright (C) 2000 Kunihiro Ishiguro
718e3744 4 */
5
6#include <zebra.h>
7
8#include <sys/un.h>
9#include <setjmp.h>
10#include <sys/wait.h>
11#include <pwd.h>
e43716f6
DS
12#include <sys/file.h>
13#include <unistd.h>
718e3744 14
9c149084
DL
15/* readline carries some ancient definitions around */
16#pragma GCC diagnostic push
17#pragma GCC diagnostic ignored "-Wstrict-prototypes"
718e3744 18#include <readline/readline.h>
19#include <readline/history.h>
9c149084 20#pragma GCC diagnostic pop
718e3744 21
8860ffdc
DS
22/*
23 * The append_history function only appears in newer versions
24 * of the readline library it appears like. Since we don't
25 * need this just silently ignore the code on these
26 * ancient platforms.
27 */
28#if !defined HAVE_APPEND_HISTORY
29#define append_history(A, B)
30#endif
31
5e4fa164 32#include <lib/version.h>
718e3744 33#include "getopt.h"
34#include "command.h"
f366ad31 35#include "memory.h"
4201dd11 36#include "linklist.h"
eb05883f 37#include "libfrr.h"
1f9128d6
QY
38#include "ferr.h"
39#include "lib_errors.h"
718e3744 40
41#include "vtysh/vtysh.h"
42#include "vtysh/vtysh_user.h"
b094d260 43
718e3744 44/* VTY shell program name. */
45char *progname;
46
32f3268f
DL
47/* SUID mode */
48static uid_t elevuid, realuid;
49static gid_t elevgid, realgid;
50
9b8a8249
DL
51#define VTYSH_CONFIG_NAME "vtysh.conf"
52#define FRR_CONFIG_NAME "frr.conf"
53
67e29abc 54/* Configuration file name and directory. */
ff44f570
DS
55static char vtysh_config[MAXPATHLEN * 3];
56char frr_config[MAXPATHLEN * 3];
9b8a8249
DL
57char vtydir[MAXPATHLEN];
58static char history_file[MAXPATHLEN];
718e3744 59
718e3744 60/* Flag for indicate executing child command. */
61int execute_flag = 0;
62
86b28610 63/* Flag to indicate if in user/unprivileged mode. */
186f6af2 64int user_mode;
86b28610 65
718e3744 66/* Master of threads. */
67struct thread_master *master;
b094d260 68
57fb9748
SH
69/* Command logging */
70FILE *logfile;
71
149a3fff
DA
72static void vtysh_rl_callback(char *line_read)
73{
74 HIST_ENTRY *last;
75
76 rl_callback_handler_remove();
77
78 if (!line_read) {
79 vtysh_loop_exited = true;
80 return;
81 }
82
83 /* If the line has any text in it, save it on the history. But only if
84 * last command in history isn't the same one.
85 */
86 if (*line_read) {
87 using_history();
88 last = previous_history();
89 if (!last || strcmp(last->line, line_read) != 0) {
90 add_history(line_read);
91 append_history(1, history_file);
92 }
93 }
94
95 vtysh_execute(line_read);
96
97 if (!vtysh_loop_exited)
98 rl_callback_handler_install(vtysh_prompt(), vtysh_rl_callback);
5098d577
DS
99
100 free(line_read);
149a3fff
DA
101}
102
718e3744 103/* SIGTSTP handler. This function care user's ^Z input. */
d62a17ae 104static void sigtstp(int sig)
718e3744 105{
149a3fff
DA
106 rl_callback_handler_remove();
107
d62a17ae 108 /* Execute "end" command. */
109 vtysh_execute("end");
718e3744 110
149a3fff
DA
111 if (!vtysh_loop_exited)
112 rl_callback_handler_install(vtysh_prompt(), vtysh_rl_callback);
113
d62a17ae 114 /* Initialize readline. */
115 rl_initialize();
116 printf("\n");
149a3fff 117 rl_forced_update_display();
718e3744 118}
119
120/* SIGINT handler. This function care user's ^Z input. */
d62a17ae 121static void sigint(int sig)
718e3744 122{
d62a17ae 123 /* Check this process is not child process. */
124 if (!execute_flag) {
125 rl_initialize();
126 printf("\n");
127 rl_forced_update_display();
128 }
718e3744 129}
130
e42f5a37 131/* Signale wrapper for vtysh. We don't use sigevent because
132 * vtysh doesn't use threads. TODO */
d62a17ae 133static void vtysh_signal_set(int signo, void (*func)(int))
718e3744 134{
d62a17ae 135 struct sigaction sig;
136 struct sigaction osig;
718e3744 137
d62a17ae 138 sig.sa_handler = func;
139 sigemptyset(&sig.sa_mask);
140 sig.sa_flags = 0;
718e3744 141#ifdef SA_RESTART
d62a17ae 142 sig.sa_flags |= SA_RESTART;
718e3744 143#endif /* SA_RESTART */
144
d62a17ae 145 sigaction(signo, &sig, &osig);
718e3744 146}
147
148/* Initialization of signal handles. */
d62a17ae 149static void vtysh_signal_init(void)
718e3744 150{
d62a17ae 151 vtysh_signal_set(SIGINT, sigint);
152 vtysh_signal_set(SIGTSTP, sigtstp);
153 vtysh_signal_set(SIGPIPE, SIG_IGN);
718e3744 154}
b094d260 155
718e3744 156/* Help information display. */
d62a17ae 157static void usage(int status)
718e3744 158{
d62a17ae 159 if (status != 0)
160 fprintf(stderr, "Try `%s --help' for more information.\n",
161 progname);
162 else
163 printf("Usage : %s [OPTION...]\n\n"
69d4cc70
DA
164 "Integrated shell for FRR (version " FRR_VERSION
165 "). \n"
166 "Configured with:\n " FRR_CONFIG_ARGS
167 "\n\n"
d62a17ae 168 "-b, --boot Execute boot startup configuration\n"
169 "-c, --command Execute argument as command\n"
170 "-d, --daemon Connect only to the specified daemon\n"
171 "-f, --inputfile Execute commands from specific file and exit\n"
172 "-E, --echo Echo prompt and command in -c mode\n"
173 "-C, --dryrun Check configuration for validity and exit\n"
174 "-m, --markfile Mark input file with context end\n"
175 " --vty_socket Override vty socket path\n"
176 " --config_dir Override config directory path\n"
8bd33a03 177 "-N --pathspace Insert prefix into config & socket paths\n"
86b28610 178 "-u --user Run as an unprivileged user\n"
d62a17ae 179 "-w, --writeconfig Write integrated config (frr.conf) and exit\n"
69d4cc70 180 "-H, --histfile Override history file\n"
d62a17ae 181 "-h, --help Display this help and exit\n\n"
182 "Note that multiple commands may be executed from the command\n"
183 "line by passing multiple -c args, or by embedding linefeed\n"
184 "characters in one or more of the commands.\n\n"
185 "Report bugs to %s\n",
186 progname, FRR_BUG_ADDRESS);
187
188 exit(status);
718e3744 189}
190
191/* VTY shell options, we use GNU getopt library. */
87d79a9f 192#define OPTION_VTYSOCK 1000
ce2e9ec3 193#define OPTION_CONFDIR 1001
d62a17ae 194struct option longopts[] = {
195 {"boot", no_argument, NULL, 'b'},
196 /* For compatibility with older zebra/quagga versions */
197 {"eval", required_argument, NULL, 'e'},
198 {"command", required_argument, NULL, 'c'},
199 {"daemon", required_argument, NULL, 'd'},
200 {"vty_socket", required_argument, NULL, OPTION_VTYSOCK},
201 {"config_dir", required_argument, NULL, OPTION_CONFDIR},
202 {"inputfile", required_argument, NULL, 'f'},
69d4cc70 203 {"histfile", required_argument, NULL, 'H'},
d62a17ae 204 {"echo", no_argument, NULL, 'E'},
205 {"dryrun", no_argument, NULL, 'C'},
206 {"help", no_argument, NULL, 'h'},
207 {"noerror", no_argument, NULL, 'n'},
208 {"mark", no_argument, NULL, 'm'},
209 {"writeconfig", no_argument, NULL, 'w'},
bd29d463 210 {"pathspace", required_argument, NULL, 'N'},
86b28610 211 {"user", no_argument, NULL, 'u'},
744bc17d 212 {"timestamp", no_argument, NULL, 't'},
d62a17ae 213 {0}};
b094d260 214
4c92dd90
DL
215bool vtysh_loop_exited;
216
4c92dd90
DL
217static struct thread *vtysh_rl_read_thread;
218
219static void vtysh_rl_read(struct thread *thread)
220{
221 thread_add_read(master, vtysh_rl_read, NULL, STDIN_FILENO,
222 &vtysh_rl_read_thread);
223 rl_callback_read_char();
224}
225
226/* Read a string, and return a pointer to it. Returns NULL on EOF. */
227static void vtysh_rl_run(void)
228{
229 struct thread thread;
230
231 master = thread_master_create(NULL);
232
233 rl_callback_handler_install(vtysh_prompt(), vtysh_rl_callback);
234 thread_add_read(master, vtysh_rl_read, NULL, STDIN_FILENO,
235 &vtysh_rl_read_thread);
236
237 while (!vtysh_loop_exited && thread_fetch(master, &thread))
238 thread_call(&thread);
239
240 if (!vtysh_loop_exited)
241 rl_callback_handler_remove();
242
243 thread_master_free(master);
718e3744 244}
b094d260 245
57fb9748
SH
246static void log_it(const char *line)
247{
d62a17ae 248 time_t t = time(NULL);
a2700b50 249 struct tm tmp;
d62a17ae 250 const char *user = getenv("USER");
251 char tod[64];
57fb9748 252
a2700b50 253 localtime_r(&t, &tmp);
d62a17ae 254 if (!user)
255 user = "boot";
f03db93b 256
0d6f7fd6 257 strftime(tod, sizeof(tod), "%Y%m%d-%H:%M.%S", &tmp);
d62a17ae 258
259 fprintf(logfile, "%s:%s %s\n", tod, user, line);
57fb9748
SH
260}
261
e43716f6
DS
262static int flock_fd;
263
d62a17ae 264static void vtysh_flock_config(const char *flock_file)
e43716f6 265{
d62a17ae 266 int count = 0;
267
268 flock_fd = open(flock_file, O_RDONLY, 0644);
269 if (flock_fd < 0) {
270 fprintf(stderr, "Unable to create lock file: %s, %s\n",
271 flock_file, safe_strerror(errno));
272 return;
273 }
274
275 while (count < 400 && (flock(flock_fd, LOCK_EX | LOCK_NB) < 0)) {
276 count++;
277 usleep(500000);
278 }
279
280 if (count >= 400)
281 fprintf(stderr,
282 "Flock of %s failed, continuing this may cause issues\n",
283 flock_file);
e43716f6
DS
284}
285
d62a17ae 286static void vtysh_unflock_config(void)
e43716f6 287{
d62a17ae 288 flock(flock_fd, LOCK_UN);
289 close(flock_fd);
e43716f6
DS
290}
291
32f3268f
DL
292void suid_on(void)
293{
294 if (elevuid != realuid && seteuid(elevuid)) {
295 perror("seteuid(on)");
296 exit(1);
297 }
298 if (elevgid != realgid && setegid(elevgid)) {
299 perror("setegid(on)");
300 exit(1);
301 }
302}
303
304void suid_off(void)
305{
306 if (elevuid != realuid && seteuid(realuid)) {
307 perror("seteuid(off)");
308 exit(1);
309 }
310 if (elevgid != realgid && setegid(realgid)) {
311 perror("setegid(off)");
312 exit(1);
313 }
314}
315
718e3744 316/* VTY shell main routine. */
d62a17ae 317int main(int argc, char **argv, char **env)
718e3744 318{
d62a17ae 319 char *p;
320 int opt;
321 int dryrun = 0;
322 int boot_flag = 0;
744bc17d 323 bool ts_flag = false;
d62a17ae 324 const char *daemon_name = NULL;
325 const char *inputfile = NULL;
d62a17ae 326 struct cmd_rec {
327 char *line;
328 struct cmd_rec *next;
329 } *cmd = NULL;
330 struct cmd_rec *tail = NULL;
331 int echo_command = 0;
332 int no_error = 0;
333 int markfile = 0;
334 int writeconfig = 0;
335 int ret = 0;
336 char *homedir = NULL;
32f3268f 337 int ditch_suid = 0;
9b8a8249 338 char sysconfdir[MAXPATHLEN];
1dede1f8 339 const char *pathspace_arg = NULL;
8bd33a03 340 char pathspace[MAXPATHLEN] = "";
69d4cc70 341 const char *histfile = NULL;
99a9f25c 342 const char *histfile_env = getenv("VTYSH_HISTFILE");
d62a17ae 343
32f3268f
DL
344 /* SUID: drop down to calling user & go back up when needed */
345 elevuid = geteuid();
346 elevgid = getegid();
347 realuid = getuid();
348 realgid = getgid();
349 suid_off();
d62a17ae 350
186f6af2
LB
351 user_mode = 0; /* may be set in options processing */
352
d62a17ae 353 /* Preserve name of myself. */
354 progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]);
355
9b8a8249 356 strlcpy(sysconfdir, frr_sysconfdir, sizeof(sysconfdir));
43e587c1
DS
357
358 frr_init_vtydir();
9b8a8249
DL
359 strlcpy(vtydir, frr_vtydir, sizeof(vtydir));
360
d62a17ae 361 /* Option handling. */
362 while (1) {
744bc17d 363 opt = getopt_long(argc, argv, "be:c:d:nf:H:mEhCwN:ut", longopts,
69d4cc70 364 0);
d62a17ae 365
366 if (opt == EOF)
367 break;
368
369 switch (opt) {
370 case 0:
371 break;
372 case 'b':
373 boot_flag = 1;
374 break;
375 case 'e':
376 case 'c': {
377 struct cmd_rec *cr;
378 cr = XMALLOC(MTYPE_TMP, sizeof(*cr));
379 cr->line = optarg;
380 cr->next = NULL;
381 if (tail)
382 tail->next = cr;
383 else
384 cmd = cr;
385 tail = cr;
386 } break;
387 case OPTION_VTYSOCK:
32f3268f 388 ditch_suid = 1; /* option disables SUID */
9b8a8249 389 strlcpy(vtydir, optarg, sizeof(vtydir));
d62a17ae 390 break;
391 case OPTION_CONFDIR:
32f3268f 392 ditch_suid = 1; /* option disables SUID */
45d00587 393 snprintf(sysconfdir, sizeof(sysconfdir), "%s/", optarg);
d62a17ae 394 break;
8bd33a03
DL
395 case 'N':
396 if (strchr(optarg, '/') || strchr(optarg, '.')) {
397 fprintf(stderr,
398 "slashes or dots are not permitted in the --pathspace option.\n");
399 exit(1);
400 }
1dede1f8
CF
401 pathspace_arg = optarg;
402 snprintf(pathspace, sizeof(pathspace), "%s/", optarg);
8bd33a03 403 break;
d62a17ae 404 case 'd':
405 daemon_name = optarg;
406 break;
407 case 'f':
408 inputfile = optarg;
409 break;
410 case 'm':
411 markfile = 1;
412 break;
413 case 'n':
414 no_error = 1;
415 break;
416 case 'E':
417 echo_command = 1;
418 break;
419 case 'C':
420 dryrun = 1;
421 break;
86b28610
LB
422 case 'u':
423 user_mode = 1;
424 break;
744bc17d
CH
425 case 't':
426 ts_flag = true;
427 break;
d62a17ae 428 case 'w':
429 writeconfig = 1;
430 break;
431 case 'h':
432 usage(0);
433 break;
69d4cc70
DA
434 case 'H':
435 histfile = optarg;
436 break;
d62a17ae 437 default:
438 usage(1);
439 break;
440 }
441 }
442
32f3268f
DL
443 if (ditch_suid) {
444 elevuid = realuid;
445 elevgid = realgid;
446 }
447
d62a17ae 448 if (markfile + writeconfig + dryrun + boot_flag > 1) {
449 fprintf(stderr,
3efd0893 450 "Invalid combination of arguments. Please specify at most one of:\n\t-b, -C, -m, -w\n");
d62a17ae 451 return 1;
452 }
453 if (inputfile && (writeconfig || boot_flag)) {
454 fprintf(stderr,
3efd0893 455 "WARNING: Combinining the -f option with -b or -w is NOT SUPPORTED since its\nresults are inconsistent!\n");
d62a17ae 456 }
457
e82314b1 458 snprintf(vtysh_config, sizeof(vtysh_config), "%s%s%s", sysconfdir,
60466a63 459 pathspace, VTYSH_CONFIG_NAME);
e82314b1 460 snprintf(frr_config, sizeof(frr_config), "%s%s%s", sysconfdir,
60466a63 461 pathspace, FRR_CONFIG_NAME);
1dede1f8
CF
462
463 if (pathspace_arg) {
464 strlcat(vtydir, "/", sizeof(vtydir));
465 strlcat(vtydir, pathspace_arg, sizeof(vtydir));
466 }
9b8a8249 467
d62a17ae 468 /* Initialize user input buffer. */
d62a17ae 469 setlinebuf(stdout);
470
471 /* Signal and others. */
472 vtysh_signal_init();
473
474 /* Make vty structure and register commands. */
475 vtysh_init_vty();
476 vtysh_init_cmd();
477 vtysh_user_init();
478 vtysh_config_init();
479
480 vty_init_vtysh();
481
86b28610
LB
482 if (!user_mode) {
483 /* Read vtysh configuration file before connecting to daemons.
484 * (file may not be readable to calling user in SUID mode) */
485 suid_on();
77f7b4b0 486 vtysh_read_config(vtysh_config, dryrun);
86b28610
LB
487 suid_off();
488 }
1f9128d6
QY
489 /* Error code library system */
490 log_ref_init();
491 lib_error_init();
d62a17ae 492
493 if (markfile) {
494 if (!inputfile) {
495 fprintf(stderr,
496 "-f option MUST be specified with -m option\n");
95f7965d 497 return 1;
d62a17ae 498 }
499 return (vtysh_mark_file(inputfile));
500 }
501
502 /* Start execution only if not in dry-run mode */
503 if (dryrun && !cmd) {
504 if (inputfile) {
77f7b4b0 505 ret = vtysh_read_config(inputfile, dryrun);
d62a17ae 506 } else {
77f7b4b0 507 ret = vtysh_read_config(frr_config, dryrun);
d62a17ae 508 }
509
510 exit(ret);
718e3744 511 }
d62a17ae 512
47402c0f 513 if (dryrun && cmd && cmd->line) {
f205fcdb
LB
514 if (!user_mode)
515 vtysh_execute("enable");
d62a17ae 516 while (cmd) {
517 struct cmd_rec *cr;
518 char *cmdnow = cmd->line, *next;
519 do {
520 next = strchr(cmdnow, '\n');
521 if (next)
522 *next++ = '\0';
523
524 if (echo_command)
525 printf("%s%s\n", vtysh_prompt(),
526 cmdnow);
527
528 ret = vtysh_execute_no_pager(cmdnow);
529 if (!no_error
530 && !(ret == CMD_SUCCESS
531 || ret == CMD_SUCCESS_DAEMON
532 || ret == CMD_WARNING))
533 exit(1);
534 } while ((cmdnow = next) != NULL);
535
536 cr = cmd;
537 cmd = cmd->next;
538 XFREE(MTYPE_TMP, cr);
539 }
540 exit(ret);
0846286b 541 }
d62a17ae 542
543 /* Ignore error messages */
544 if (no_error) {
545 if (freopen("/dev/null", "w", stdout) == NULL) {
546 fprintf(stderr,
547 "Exiting: Failed to duplicate stdout with -n option");
548 exit(1);
549 }
0846286b 550 }
d62a17ae 551
32f3268f
DL
552 /* SUID: go back up elevated privs */
553 suid_on();
554
d62a17ae 555 /* Make sure we pass authentication before proceeding. */
556 vtysh_auth();
557
558 /* Do not connect until we have passed authentication. */
559 if (vtysh_connect_all(daemon_name) <= 0) {
560 fprintf(stderr, "Exiting: failed to connect to any daemons.\n");
14275475
QY
561 if (geteuid() != 0)
562 fprintf(stderr,
563 "Hint: if this seems wrong, try running me as a privileged user!\n");
d62a17ae 564 if (no_error)
565 exit(0);
566 else
567 exit(1);
3221dca8 568 }
cd272640 569
32f3268f
DL
570 /* SUID: back down, don't need privs further on */
571 suid_off();
572
d62a17ae 573 if (writeconfig) {
f205fcdb
LB
574 if (user_mode) {
575 fprintf(stderr,
576 "writeconfig cannot be used when running as an unprivileged user.\n");
577 if (no_error)
578 exit(0);
579 else
580 exit(1);
581 }
d62a17ae 582 vtysh_execute("enable");
583 return vtysh_write_config_integrated();
7a49a5b5 584 }
d62a17ae 585
586 if (inputfile) {
587 vtysh_flock_config(inputfile);
77f7b4b0 588 ret = vtysh_read_config(inputfile, dryrun);
d62a17ae 589 vtysh_unflock_config();
590 exit(ret);
fba55c8a 591 }
d62a17ae 592
593 /*
594 * Setup history file for use by both -c and regular input
595 * If we can't find the home directory, then don't store
69d4cc70 596 * the history information.
11743d10 597 * VTYSH_HISTFILE is preferred over command line
69d4cc70 598 * argument (-H/--histfile).
d62a17ae 599 */
99a9f25c
DS
600 if (histfile_env) {
601 strlcpy(history_file, histfile_env, sizeof(history_file));
69d4cc70
DA
602 } else if (histfile) {
603 strlcpy(history_file, histfile, sizeof(history_file));
604 } else {
605 homedir = vtysh_get_home();
606 if (homedir)
607 snprintf(history_file, sizeof(history_file),
608 "%s/.history_frr", homedir);
609 }
610
611 if (strlen(history_file) > 0) {
d62a17ae 612 if (read_history(history_file) != 0) {
613 int fp;
614
615 fp = open(history_file, O_CREAT | O_EXCL,
616 S_IRUSR | S_IWUSR);
cbb65f5e 617 if (fp != -1)
d62a17ae 618 close(fp);
619
620 read_history(history_file);
621 }
622 }
623
32f3268f
DL
624 if (getenv("VTYSH_LOG")) {
625 const char *logpath = getenv("VTYSH_LOG");
626
627 logfile = fopen(logpath, "a");
628 if (!logfile) {
629 fprintf(stderr, "Failed to open logfile (%s): %s\n",
630 logpath, strerror(errno));
631 exit(1);
632 }
633 }
634
d62a17ae 635 /* If eval mode. */
47402c0f 636 if (cmd && cmd->line) {
d62a17ae 637 /* Enter into enable node. */
f205fcdb
LB
638 if (!user_mode)
639 vtysh_execute("enable");
d62a17ae 640
744bc17d
CH
641 vtysh_add_timestamp = ts_flag;
642
d62a17ae 643 while (cmd != NULL) {
d62a17ae 644 char *eol;
645
646 while ((eol = strchr(cmd->line, '\n')) != NULL) {
647 *eol = '\0';
648
649 add_history(cmd->line);
650 append_history(1, history_file);
651
652 if (echo_command)
653 printf("%s%s\n", vtysh_prompt(),
654 cmd->line);
655
656 if (logfile)
657 log_it(cmd->line);
658
659 ret = vtysh_execute_no_pager(cmd->line);
660 if (!no_error
661 && !(ret == CMD_SUCCESS
662 || ret == CMD_SUCCESS_DAEMON
663 || ret == CMD_WARNING))
664 exit(1);
665
666 cmd->line = eol + 1;
667 }
668
669 add_history(cmd->line);
670 append_history(1, history_file);
671
672 if (echo_command)
673 printf("%s%s\n", vtysh_prompt(), cmd->line);
674
675 if (logfile)
676 log_it(cmd->line);
677
a4364a44 678 /*
76015847
QY
679 * Parsing logic for regular commands will be different
680 * than for those commands requiring further
681 * processing, such as cli instructions terminating
682 * with question-mark character.
a4364a44
RM
683 */
684 if (!vtysh_execute_command_questionmark(cmd->line))
685 ret = CMD_SUCCESS;
686 else
687 ret = vtysh_execute_no_pager(cmd->line);
688
d62a17ae 689 if (!no_error
690 && !(ret == CMD_SUCCESS || ret == CMD_SUCCESS_DAEMON
691 || ret == CMD_WARNING))
692 exit(1);
693
694 {
695 struct cmd_rec *cr;
696 cr = cmd;
697 cmd = cmd->next;
698 XFREE(MTYPE_TMP, cr);
699 }
700 }
701
702 history_truncate_file(history_file, 1000);
703 exit(0);
704 }
705
706 /* Boot startup configuration file. */
707 if (boot_flag) {
9b8a8249 708 vtysh_flock_config(frr_config);
77f7b4b0 709 ret = vtysh_read_config(frr_config, dryrun);
d62a17ae 710 vtysh_unflock_config();
711 if (ret) {
712 fprintf(stderr,
713 "Configuration file[%s] processing failure: %d\n",
9b8a8249 714 frr_config, ret);
d62a17ae 715 if (no_error)
716 exit(0);
717 else
718 exit(ret);
719 } else
720 exit(0);
e7168df4 721 }
718e3744 722
d62a17ae 723 vtysh_readline_init();
718e3744 724
d62a17ae 725 vty_hello(vty);
718e3744 726
d62a17ae 727 /* Enter into enable node. */
f205fcdb
LB
728 if (!user_mode)
729 vtysh_execute("enable");
e7168df4 730
744bc17d
CH
731 vtysh_add_timestamp = ts_flag;
732
d62a17ae 733 /* Main command loop. */
4c92dd90 734 vtysh_rl_run();
718e3744 735
193a5a95
QY
736 vtysh_uninit();
737
d62a17ae 738 history_truncate_file(history_file, 1000);
739 printf("\n");
718e3744 740
d62a17ae 741 /* Rest in peace. */
742 exit(0);
718e3744 743}