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