]> git.proxmox.com Git - mirror_frr.git/blame - vtysh/vtysh_main.c
*: add indent control files
[mirror_frr.git] / vtysh / vtysh_main.c
CommitLineData
718e3744 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 *
896014f4
DL
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
718e3744 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>
e43716f6
DS
27#include <sys/file.h>
28#include <unistd.h>
718e3744 29
30#include <readline/readline.h>
31#include <readline/history.h>
32
5e4fa164 33#include <lib/version.h>
718e3744 34#include "getopt.h"
35#include "command.h"
f366ad31 36#include "memory.h"
4201dd11 37#include "linklist.h"
fc7948fa 38#include "memory_vty.h"
eb05883f 39#include "libfrr.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
67e29abc 47/* Configuration file name and directory. */
ce2e9ec3 48static char vtysh_config_always[MAXPATHLEN] = SYSCONFDIR VTYSH_DEFAULT_CONFIG;
a0b4b67e 49static char quagga_config_default[MAXPATHLEN] = SYSCONFDIR FRR_DEFAULT_CONFIG;
cb947ba3 50char *quagga_config = quagga_config_default;
3f4ab7f9 51char history_file[MAXPATHLEN];
718e3744 52
718e3744 53/* Flag for indicate executing child command. */
54int execute_flag = 0;
55
87d79a9f 56/* VTY Socket prefix */
eb05883f 57const char * vty_sock_path = NULL;
87d79a9f 58
718e3744 59/* For sigsetjmp() & siglongjmp(). */
60static sigjmp_buf jmpbuf;
61
62/* Flag for avoid recursive siglongjmp() call. */
63static int jmpflag = 0;
64
65/* A static variable for holding the line. */
66static char *line_read;
67
68/* Master of threads. */
69struct thread_master *master;
b094d260 70
57fb9748
SH
71/* Command logging */
72FILE *logfile;
73
718e3744 74/* SIGTSTP handler. This function care user's ^Z input. */
c0e8c16f 75static void
718e3744 76sigtstp (int sig)
77{
78 /* Execute "end" command. */
79 vtysh_execute ("end");
80
81 /* Initialize readline. */
82 rl_initialize ();
83 printf ("\n");
84
85 /* Check jmpflag for duplicate siglongjmp(). */
86 if (! jmpflag)
87 return;
88
89 jmpflag = 0;
90
91 /* Back to main command loop. */
92 siglongjmp (jmpbuf, 1);
93}
94
95/* SIGINT handler. This function care user's ^Z input. */
c0e8c16f 96static void
718e3744 97sigint (int sig)
98{
99 /* Check this process is not child process. */
100 if (! execute_flag)
101 {
102 rl_initialize ();
103 printf ("\n");
104 rl_forced_update_display ();
105 }
106}
107
e42f5a37 108/* Signale wrapper for vtysh. We don't use sigevent because
109 * vtysh doesn't use threads. TODO */
7f720f54 110static void
e42f5a37 111vtysh_signal_set (int signo, void (*func)(int))
718e3744 112{
718e3744 113 struct sigaction sig;
114 struct sigaction osig;
115
116 sig.sa_handler = func;
117 sigemptyset (&sig.sa_mask);
118 sig.sa_flags = 0;
119#ifdef SA_RESTART
120 sig.sa_flags |= SA_RESTART;
121#endif /* SA_RESTART */
122
7f720f54 123 sigaction (signo, &sig, &osig);
718e3744 124}
125
126/* Initialization of signal handles. */
c0e8c16f
DS
127static void
128vtysh_signal_init (void)
718e3744 129{
e42f5a37 130 vtysh_signal_set (SIGINT, sigint);
131 vtysh_signal_set (SIGTSTP, sigtstp);
132 vtysh_signal_set (SIGPIPE, SIG_IGN);
718e3744 133}
b094d260 134
718e3744 135/* Help information display. */
136static void
137usage (int status)
138{
139 if (status != 0)
140 fprintf (stderr, "Try `%s --help' for more information.\n", progname);
141 else
95e735b5 142 printf ("Usage : %s [OPTION...]\n\n" \
0dca233e 143 "Integrated shell for FRR. \n\n" \
95e735b5 144 "-b, --boot Execute boot startup configuration\n" \
f366ad31
AS
145 "-c, --command Execute argument as command\n" \
146 "-d, --daemon Connect only to the specified daemon\n" \
0846286b 147 "-f, --inputfile Execute commands from specific file and exit\n" \
f366ad31 148 "-E, --echo Echo prompt and command in -c mode\n" \
876b8be0 149 "-C, --dryrun Check configuration for validity and exit\n" \
ce2e9ec3
MW
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" \
e20dc2ba 153 "-w, --writeconfig Write integrated config (frr.conf) and exit\n" \
95e735b5 154 "-h, --help Display this help and exit\n\n" \
f366ad31
AS
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" \
b2f36157 158 "Report bugs to %s\n", progname, FRR_BUG_ADDRESS);
95e735b5 159
718e3744 160 exit (status);
161}
162
163/* VTY shell options, we use GNU getopt library. */
87d79a9f 164#define OPTION_VTYSOCK 1000
ce2e9ec3 165#define OPTION_CONFDIR 1001
718e3744 166struct option longopts[] =
167{
b094d260 168 { "boot", no_argument, NULL, 'b'},
4991f6ca 169 /* For compatibility with older zebra/quagga versions */
718e3744 170 { "eval", required_argument, NULL, 'e'},
4991f6ca 171 { "command", required_argument, NULL, 'c'},
f366ad31 172 { "daemon", required_argument, NULL, 'd'},
87d79a9f 173 { "vty_socket", required_argument, NULL, OPTION_VTYSOCK},
ce2e9ec3 174 { "config_dir", required_argument, NULL, OPTION_CONFDIR},
0846286b 175 { "inputfile", required_argument, NULL, 'f'},
f366ad31 176 { "echo", no_argument, NULL, 'E'},
876b8be0 177 { "dryrun", no_argument, NULL, 'C'},
718e3744 178 { "help", no_argument, NULL, 'h'},
914131f8 179 { "noerror", no_argument, NULL, 'n'},
0846286b 180 { "mark", no_argument, NULL, 'm'},
a68f8616 181 { "writeconfig", no_argument, NULL, 'w'},
718e3744 182 { 0 }
183};
b094d260 184
718e3744 185/* Read a string, and return a pointer to it. Returns NULL on EOF. */
c0e8c16f
DS
186static char *
187vtysh_rl_gets (void)
718e3744 188{
4991f6ca 189 HIST_ENTRY *last;
718e3744 190 /* If the buffer has already been allocated, return the memory
95e735b5 191 * to the free pool. */
718e3744 192 if (line_read)
193 {
194 free (line_read);
195 line_read = NULL;
196 }
197
198 /* Get a line from the user. Change prompt according to node. XXX. */
199 line_read = readline (vtysh_prompt ());
200
4991f6ca 201 /* If the line has any text in it, save it on the history. But only if
95e735b5 202 * last command in history isn't the same one. */
718e3744 203 if (line_read && *line_read)
4991f6ca 204 {
205 using_history();
206 last = previous_history();
3f4ab7f9 207 if (!last || strcmp (last->line, line_read) != 0) {
4991f6ca 208 add_history (line_read);
3f4ab7f9
TP
209 append_history(1,history_file);
210 }
4991f6ca 211 }
718e3744 212
213 return (line_read);
214}
b094d260 215
57fb9748
SH
216static void log_it(const char *line)
217{
218 time_t t = time(NULL);
219 struct tm *tmp = localtime(&t);
f03db93b 220 const char *user = getenv("USER");
57fb9748
SH
221 char tod[64];
222
f03db93b
DL
223 if (!user)
224 user = "boot";
225
57fb9748
SH
226 strftime(tod, sizeof tod, "%Y%m%d-%H:%M.%S", tmp);
227
228 fprintf(logfile, "%s:%s %s\n", tod, user, line);
229}
230
e43716f6
DS
231static int flock_fd;
232
233static void
234vtysh_flock_config (const char *flock_file)
235{
236 int count = 0;
237
6ac014d0 238 flock_fd = open (flock_file, O_RDONLY, 0644);
e43716f6
DS
239 if (flock_fd < 0)
240 {
241 fprintf (stderr, "Unable to create lock file: %s, %s\n",
242 flock_file, safe_strerror (errno));
243 return;
244 }
245
246 while (count < 400 && (flock (flock_fd, LOCK_EX | LOCK_NB) < 0))
247 {
248 count++;
249 usleep (500000);
250 }
251
252 if (count >= 400)
253 fprintf(stderr, "Flock of %s failed, continuing this may cause issues\n",
254 flock_file);
255}
256
257static void
258vtysh_unflock_config (void)
259{
260 flock (flock_fd, LOCK_UN);
261 close (flock_fd);
262}
263
718e3744 264/* VTY shell main routine. */
265int
266main (int argc, char **argv, char **env)
267{
268 char *p;
269 int opt;
876b8be0 270 int dryrun = 0;
718e3744 271 int boot_flag = 0;
f366ad31 272 const char *daemon_name = NULL;
0846286b 273 const char *inputfile = NULL;
ff1c42fb 274 const char *vtysh_configfile_name;
f366ad31 275 struct cmd_rec {
cd272640 276 char *line;
f366ad31
AS
277 struct cmd_rec *next;
278 } *cmd = NULL;
279 struct cmd_rec *tail = NULL;
280 int echo_command = 0;
914131f8 281 int no_error = 0;
0846286b 282 int markfile = 0;
a68f8616 283 int writeconfig = 0;
3221dca8 284 int ret = 0;
fba55c8a 285 char *homedir = NULL;
718e3744 286
ce2e9ec3
MW
287 /* check for restricted functionality if vtysh is run setuid */
288 int restricted = (getuid() != geteuid()) || (getgid() != getegid());
289
718e3744 290 /* Preserve name of myself. */
291 progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
292
57fb9748
SH
293 /* if logging open now */
294 if ((p = getenv("VTYSH_LOG")) != NULL)
295 logfile = fopen(p, "a");
296
718e3744 297 /* Option handling. */
298 while (1)
299 {
a68f8616 300 opt = getopt_long (argc, argv, "be:c:d:nf:mEhCw", longopts, 0);
718e3744 301
302 if (opt == EOF)
303 break;
304
305 switch (opt)
306 {
307 case 0:
308 break;
309 case 'b':
310 boot_flag = 1;
311 break;
312 case 'e':
4991f6ca 313 case 'c':
f366ad31
AS
314 {
315 struct cmd_rec *cr;
c66f9c61 316 cr = XMALLOC(MTYPE_TMP, sizeof(*cr));
f366ad31
AS
317 cr->line = optarg;
318 cr->next = NULL;
319 if (tail)
320 tail->next = cr;
321 else
322 cmd = cr;
323 tail = cr;
324 }
325 break;
87d79a9f
MW
326 case OPTION_VTYSOCK:
327 vty_sock_path = optarg;
328 break;
ce2e9ec3
MW
329 case OPTION_CONFDIR:
330 /*
331 * Skip option for Config Directory if setuid
332 */
333 if (restricted)
334 {
335 fprintf (stderr, "Overriding of Config Directory blocked for vtysh with setuid");
336 return 1;
337 }
338 /*
339 * Overwrite location for vtysh.conf
340 */
341 vtysh_configfile_name = strrchr(VTYSH_DEFAULT_CONFIG, '/');
342 if (vtysh_configfile_name)
343 /* skip '/' */
344 vtysh_configfile_name++;
345 else
346 /*
347 * VTYSH_DEFAULT_CONFIG configured with relative path
348 * during config? Should really never happen for
349 * sensible config
350 */
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));
356 /*
e20dc2ba 357 * Overwrite location for frr.conf
ce2e9ec3 358 */
a0b4b67e 359 vtysh_configfile_name = strrchr(FRR_DEFAULT_CONFIG, '/');
ce2e9ec3
MW
360 if (vtysh_configfile_name)
361 /* skip '/' */
362 vtysh_configfile_name++;
363 else
364 /*
a0b4b67e 365 * FRR_DEFAULT_CONFIG configured with relative path
ce2e9ec3
MW
366 * during config? Should really never happen for
367 * sensible config
368 */
a0b4b67e 369 vtysh_configfile_name = (char *) FRR_DEFAULT_CONFIG;
ce2e9ec3
MW
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));
374 break;
f366ad31
AS
375 case 'd':
376 daemon_name = optarg;
377 break;
0846286b
DS
378 case 'f':
379 inputfile = optarg;
380 break;
381 case 'm':
382 markfile = 1;
383 break;
914131f8
SH
384 case 'n':
385 no_error = 1;
386 break;
f366ad31
AS
387 case 'E':
388 echo_command = 1;
718e3744 389 break;
876b8be0
PJ
390 case 'C':
391 dryrun = 1;
392 break;
a68f8616
DL
393 case 'w':
394 writeconfig = 1;
395 break;
718e3744 396 case 'h':
397 usage (0);
398 break;
718e3744 399 default:
400 usage (1);
401 break;
402 }
403 }
404
eb05883f
DL
405 if (!vty_sock_path)
406 vty_sock_path = frr_vtydir;
407
a68f8616
DL
408 if (markfile + writeconfig + dryrun + boot_flag > 1)
409 {
410 fprintf (stderr, "Invalid combination of arguments. Please specify at "
411 "most one of:\n\t-b, -C, -m, -w\n");
412 return 1;
413 }
414 if (inputfile && (writeconfig || boot_flag))
415 {
416 fprintf (stderr, "WARNING: Combinining the -f option with -b or -w is "
417 "NOT SUPPORTED since its\nresults are inconsistent!\n");
418 }
419
718e3744 420 /* Initialize user input buffer. */
421 line_read = NULL;
0fbd62a1 422 setlinebuf(stdout);
718e3744 423
424 /* Signal and others. */
e42f5a37 425 vtysh_signal_init ();
718e3744 426
427 /* Make vty structure and register commands. */
428 vtysh_init_vty ();
429 vtysh_init_cmd ();
430 vtysh_user_init ();
431 vtysh_config_init ();
432
433 vty_init_vtysh ();
434
f366ad31 435 /* Read vtysh configuration file before connecting to daemons. */
cb947ba3 436 vtysh_read_config(vtysh_config_always);
0846286b
DS
437
438 if (markfile)
439 {
440 if (!inputfile)
441 {
442 fprintf(stderr, "-f option MUST be specified with -m option\n");
443 return(1);
444 }
445 return(vtysh_mark_file(inputfile));
446 }
718e3744 447
876b8be0 448 /* Start execution only if not in dry-run mode */
cd272640 449 if (dryrun && !cmd)
0846286b
DS
450 {
451 if (inputfile)
452 {
3221dca8 453 ret = vtysh_read_config(inputfile);
0846286b 454 }
3221dca8
DS
455 else
456 {
457 ret = vtysh_read_config(quagga_config_default);
458 }
cd272640
DL
459
460 exit(ret);
461 }
462
463 if (dryrun && cmd)
464 {
465 vtysh_execute ("enable");
466 while (cmd)
467 {
468 struct cmd_rec *cr;
469 char *cmdnow = cmd->line, *next;
470 do
471 {
472 next = strchr(cmdnow, '\n');
473 if (next)
474 *next++ = '\0';
475
476 if (echo_command)
477 printf("%s%s\n", vtysh_prompt(), cmdnow);
478
479 ret = vtysh_execute_no_pager(cmdnow);
480 if (!no_error &&
481 ! (ret == CMD_SUCCESS ||
482 ret == CMD_SUCCESS_DAEMON ||
483 ret == CMD_WARNING))
484 exit(1);
485 }
486 while ((cmdnow = next) != NULL);
487
488 cr = cmd;
489 cmd = cmd->next;
490 XFREE(MTYPE_TMP, cr);
491 }
3221dca8 492 exit(ret);
0846286b
DS
493 }
494
914131f8
SH
495 /* Ignore error messages */
496 if (no_error)
7a49a5b5
DS
497 {
498 if (freopen("/dev/null", "w", stdout) == NULL)
499 {
500 fprintf(stderr, "Exiting: Failed to duplicate stdout with -n option");
501 exit(1);
502 }
503 }
914131f8 504
f366ad31
AS
505 /* Make sure we pass authentication before proceeding. */
506 vtysh_auth ();
507
508 /* Do not connect until we have passed authentication. */
509 if (vtysh_connect_all (daemon_name) <= 0)
510 {
511 fprintf(stderr, "Exiting: failed to connect to any daemons.\n");
8ca1689f
SK
512 if (no_error)
513 exit(0);
514 else
515 exit(1);
f366ad31
AS
516 }
517
a68f8616
DL
518 if (writeconfig)
519 {
9f1f8df3 520 vtysh_execute ("enable");
a68f8616
DL
521 return vtysh_write_config_integrated ();
522 }
523
0846286b
DS
524 if (inputfile)
525 {
8f6899f2 526 vtysh_flock_config (inputfile);
3221dca8 527 ret = vtysh_read_config(inputfile);
8f6899f2 528 vtysh_unflock_config ();
3221dca8 529 exit(ret);
0846286b
DS
530 }
531
fba55c8a
DS
532 /*
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
536 */
537 homedir = vtysh_get_home ();
538 if (homedir)
539 {
540 snprintf(history_file, sizeof(history_file), "%s/.history_quagga", homedir);
541 if (read_history (history_file) != 0)
542 {
543 int fp;
544
545 fp = open (history_file, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
546 if (fp)
547 close (fp);
548
549 read_history (history_file);
550 }
551 }
552
95e735b5 553 /* If eval mode. */
f366ad31 554 if (cmd)
718e3744 555 {
f366ad31
AS
556 /* Enter into enable node. */
557 vtysh_execute ("enable");
558
559 while (cmd != NULL)
560 {
57fb9748 561 int ret;
f366ad31
AS
562 char *eol;
563
564 while ((eol = strchr(cmd->line, '\n')) != NULL)
565 {
566 *eol = '\0';
57fb9748 567
fba55c8a
DS
568 add_history (cmd->line);
569 append_history (1, history_file);
570
f366ad31 571 if (echo_command)
57fb9748
SH
572 printf("%s%s\n", vtysh_prompt(), cmd->line);
573
574 if (logfile)
575 log_it(cmd->line);
576
577 ret = vtysh_execute_no_pager(cmd->line);
914131f8
SH
578 if (!no_error &&
579 ! (ret == CMD_SUCCESS ||
580 ret == CMD_SUCCESS_DAEMON ||
581 ret == CMD_WARNING))
57fb9748
SH
582 exit(1);
583
f366ad31
AS
584 cmd->line = eol+1;
585 }
57fb9748 586
fba55c8a
DS
587 add_history (cmd->line);
588 append_history (1, history_file);
589
f366ad31
AS
590 if (echo_command)
591 printf("%s%s\n", vtysh_prompt(), cmd->line);
57fb9748
SH
592
593 if (logfile)
594 log_it(cmd->line);
595
596 ret = vtysh_execute_no_pager(cmd->line);
914131f8
SH
597 if (!no_error &&
598 ! (ret == CMD_SUCCESS ||
599 ret == CMD_SUCCESS_DAEMON ||
600 ret == CMD_WARNING))
57fb9748 601 exit(1);
f366ad31
AS
602
603 {
604 struct cmd_rec *cr;
605 cr = cmd;
606 cmd = cmd->next;
c66f9c61 607 XFREE(MTYPE_TMP, cr);
f366ad31
AS
608 }
609 }
fba55c8a
DS
610
611 history_truncate_file(history_file,1000);
718e3744 612 exit (0);
613 }
cb947ba3 614
718e3744 615 /* Boot startup configuration file. */
616 if (boot_flag)
617 {
cb947ba3
DL
618 vtysh_flock_config (quagga_config);
619 int ret = vtysh_read_config (quagga_config);
e43716f6 620 vtysh_unflock_config ();
1db63918
DS
621 if (ret)
622 {
623 fprintf (stderr, "Configuration file[%s] processing failure: %d\n",
cb947ba3 624 quagga_config, ret);
6dbef6e7
DS
625 if (no_error)
626 exit (0);
627 else
1db63918 628 exit (ret);
e7168df4 629 }
630 else
631 exit (0);
718e3744 632 }
633
634 vtysh_pager_init ();
635
636 vtysh_readline_init ();
637
638 vty_hello (vty);
639
e7168df4 640 /* Enter into enable node. */
641 vtysh_execute ("enable");
642
718e3744 643 /* Preparation for longjmp() in sigtstp(). */
644 sigsetjmp (jmpbuf, 1);
645 jmpflag = 1;
646
647 /* Main command loop. */
648 while (vtysh_rl_gets ())
649 vtysh_execute (line_read);
650
3f4ab7f9 651 history_truncate_file(history_file,1000);
718e3744 652 printf ("\n");
653
654 /* Rest in peace. */
655 exit (0);
656}