]> git.proxmox.com Git - mirror_frr.git/blame - vtysh/vtysh_main.c
build: rename (1 of ?): configure.ac + preproc
[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 *
16 * You should have received a copy of the GNU General Public License
17 * along with GNU Zebra; see the file COPYING. If not, write to the Free
18 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 * 02111-1307, USA.
20 */
21
22#include <zebra.h>
23
24#include <sys/un.h>
25#include <setjmp.h>
26#include <sys/wait.h>
27#include <pwd.h>
e43716f6
DS
28#include <sys/file.h>
29#include <unistd.h>
718e3744 30
31#include <readline/readline.h>
32#include <readline/history.h>
33
5e4fa164 34#include <lib/version.h>
718e3744 35#include "getopt.h"
36#include "command.h"
f366ad31 37#include "memory.h"
4201dd11 38#include "linklist.h"
fc7948fa 39#include "memory_vty.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. */
cb947ba3
DL
48static char vtysh_config_always[] = SYSCONFDIR VTYSH_DEFAULT_CONFIG;
49static char quagga_config_default[] = SYSCONFDIR QUAGGA_DEFAULT_CONFIG;
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
56/* For sigsetjmp() & siglongjmp(). */
57static sigjmp_buf jmpbuf;
58
59/* Flag for avoid recursive siglongjmp() call. */
60static int jmpflag = 0;
61
62/* A static variable for holding the line. */
63static char *line_read;
64
65/* Master of threads. */
66struct thread_master *master;
b094d260 67
57fb9748
SH
68/* Command logging */
69FILE *logfile;
70
718e3744 71/* SIGTSTP handler. This function care user's ^Z input. */
c0e8c16f 72static void
718e3744 73sigtstp (int sig)
74{
75 /* Execute "end" command. */
76 vtysh_execute ("end");
77
78 /* Initialize readline. */
79 rl_initialize ();
80 printf ("\n");
81
82 /* Check jmpflag for duplicate siglongjmp(). */
83 if (! jmpflag)
84 return;
85
86 jmpflag = 0;
87
88 /* Back to main command loop. */
89 siglongjmp (jmpbuf, 1);
90}
91
92/* SIGINT handler. This function care user's ^Z input. */
c0e8c16f 93static void
718e3744 94sigint (int sig)
95{
96 /* Check this process is not child process. */
97 if (! execute_flag)
98 {
99 rl_initialize ();
100 printf ("\n");
101 rl_forced_update_display ();
102 }
103}
104
e42f5a37 105/* Signale wrapper for vtysh. We don't use sigevent because
106 * vtysh doesn't use threads. TODO */
7f720f54 107static void
e42f5a37 108vtysh_signal_set (int signo, void (*func)(int))
718e3744 109{
718e3744 110 struct sigaction sig;
111 struct sigaction osig;
112
113 sig.sa_handler = func;
114 sigemptyset (&sig.sa_mask);
115 sig.sa_flags = 0;
116#ifdef SA_RESTART
117 sig.sa_flags |= SA_RESTART;
118#endif /* SA_RESTART */
119
7f720f54 120 sigaction (signo, &sig, &osig);
718e3744 121}
122
123/* Initialization of signal handles. */
c0e8c16f
DS
124static void
125vtysh_signal_init (void)
718e3744 126{
e42f5a37 127 vtysh_signal_set (SIGINT, sigint);
128 vtysh_signal_set (SIGTSTP, sigtstp);
129 vtysh_signal_set (SIGPIPE, SIG_IGN);
718e3744 130}
b094d260 131
718e3744 132/* Help information display. */
133static void
134usage (int status)
135{
136 if (status != 0)
137 fprintf (stderr, "Try `%s --help' for more information.\n", progname);
138 else
95e735b5 139 printf ("Usage : %s [OPTION...]\n\n" \
f366ad31 140 "Integrated shell for Quagga routing software suite. \n\n" \
95e735b5 141 "-b, --boot Execute boot startup configuration\n" \
f366ad31
AS
142 "-c, --command Execute argument as command\n" \
143 "-d, --daemon Connect only to the specified daemon\n" \
0846286b 144 "-f, --inputfile Execute commands from specific file and exit\n" \
f366ad31 145 "-E, --echo Echo prompt and command in -c mode\n" \
876b8be0 146 "-C, --dryrun Check configuration for validity and exit\n" \
0846286b 147 "-m, --markfile Mark input file with context end\n"
a68f8616 148 "-w, --writeconfig Write integrated config (Quagga.conf) and exit\n"
95e735b5 149 "-h, --help Display this help and exit\n\n" \
f366ad31
AS
150 "Note that multiple commands may be executed from the command\n" \
151 "line by passing multiple -c args, or by embedding linefeed\n" \
152 "characters in one or more of the commands.\n\n" \
b2f36157 153 "Report bugs to %s\n", progname, FRR_BUG_ADDRESS);
95e735b5 154
718e3744 155 exit (status);
156}
157
158/* VTY shell options, we use GNU getopt library. */
159struct option longopts[] =
160{
b094d260 161 { "boot", no_argument, NULL, 'b'},
4991f6ca 162 /* For compatibility with older zebra/quagga versions */
718e3744 163 { "eval", required_argument, NULL, 'e'},
4991f6ca 164 { "command", required_argument, NULL, 'c'},
f366ad31 165 { "daemon", required_argument, NULL, 'd'},
0846286b 166 { "inputfile", required_argument, NULL, 'f'},
f366ad31 167 { "echo", no_argument, NULL, 'E'},
876b8be0 168 { "dryrun", no_argument, NULL, 'C'},
718e3744 169 { "help", no_argument, NULL, 'h'},
914131f8 170 { "noerror", no_argument, NULL, 'n'},
0846286b 171 { "mark", no_argument, NULL, 'm'},
a68f8616 172 { "writeconfig", no_argument, NULL, 'w'},
718e3744 173 { 0 }
174};
b094d260 175
718e3744 176/* Read a string, and return a pointer to it. Returns NULL on EOF. */
c0e8c16f
DS
177static char *
178vtysh_rl_gets (void)
718e3744 179{
4991f6ca 180 HIST_ENTRY *last;
718e3744 181 /* If the buffer has already been allocated, return the memory
95e735b5 182 * to the free pool. */
718e3744 183 if (line_read)
184 {
185 free (line_read);
186 line_read = NULL;
187 }
188
189 /* Get a line from the user. Change prompt according to node. XXX. */
190 line_read = readline (vtysh_prompt ());
191
4991f6ca 192 /* If the line has any text in it, save it on the history. But only if
95e735b5 193 * last command in history isn't the same one. */
718e3744 194 if (line_read && *line_read)
4991f6ca 195 {
196 using_history();
197 last = previous_history();
3f4ab7f9 198 if (!last || strcmp (last->line, line_read) != 0) {
4991f6ca 199 add_history (line_read);
3f4ab7f9
TP
200 append_history(1,history_file);
201 }
4991f6ca 202 }
718e3744 203
204 return (line_read);
205}
b094d260 206
57fb9748
SH
207static void log_it(const char *line)
208{
209 time_t t = time(NULL);
210 struct tm *tmp = localtime(&t);
f03db93b 211 const char *user = getenv("USER");
57fb9748
SH
212 char tod[64];
213
f03db93b
DL
214 if (!user)
215 user = "boot";
216
57fb9748
SH
217 strftime(tod, sizeof tod, "%Y%m%d-%H:%M.%S", tmp);
218
219 fprintf(logfile, "%s:%s %s\n", tod, user, line);
220}
221
e43716f6
DS
222static int flock_fd;
223
224static void
225vtysh_flock_config (const char *flock_file)
226{
227 int count = 0;
228
6ac014d0 229 flock_fd = open (flock_file, O_RDONLY, 0644);
e43716f6
DS
230 if (flock_fd < 0)
231 {
232 fprintf (stderr, "Unable to create lock file: %s, %s\n",
233 flock_file, safe_strerror (errno));
234 return;
235 }
236
237 while (count < 400 && (flock (flock_fd, LOCK_EX | LOCK_NB) < 0))
238 {
239 count++;
240 usleep (500000);
241 }
242
243 if (count >= 400)
244 fprintf(stderr, "Flock of %s failed, continuing this may cause issues\n",
245 flock_file);
246}
247
248static void
249vtysh_unflock_config (void)
250{
251 flock (flock_fd, LOCK_UN);
252 close (flock_fd);
253}
254
718e3744 255/* VTY shell main routine. */
256int
257main (int argc, char **argv, char **env)
258{
259 char *p;
260 int opt;
876b8be0 261 int dryrun = 0;
718e3744 262 int boot_flag = 0;
f366ad31 263 const char *daemon_name = NULL;
0846286b 264 const char *inputfile = NULL;
f366ad31
AS
265 struct cmd_rec {
266 const char *line;
267 struct cmd_rec *next;
268 } *cmd = NULL;
269 struct cmd_rec *tail = NULL;
270 int echo_command = 0;
914131f8 271 int no_error = 0;
0846286b 272 int markfile = 0;
a68f8616 273 int writeconfig = 0;
3221dca8 274 int ret = 0;
fba55c8a 275 char *homedir = NULL;
718e3744 276
277 /* Preserve name of myself. */
278 progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]);
279
57fb9748
SH
280 /* if logging open now */
281 if ((p = getenv("VTYSH_LOG")) != NULL)
282 logfile = fopen(p, "a");
283
718e3744 284 /* Option handling. */
285 while (1)
286 {
a68f8616 287 opt = getopt_long (argc, argv, "be:c:d:nf:mEhCw", longopts, 0);
718e3744 288
289 if (opt == EOF)
290 break;
291
292 switch (opt)
293 {
294 case 0:
295 break;
296 case 'b':
297 boot_flag = 1;
298 break;
299 case 'e':
4991f6ca 300 case 'c':
f366ad31
AS
301 {
302 struct cmd_rec *cr;
c66f9c61 303 cr = XMALLOC(MTYPE_TMP, sizeof(*cr));
f366ad31
AS
304 cr->line = optarg;
305 cr->next = NULL;
306 if (tail)
307 tail->next = cr;
308 else
309 cmd = cr;
310 tail = cr;
311 }
312 break;
313 case 'd':
314 daemon_name = optarg;
315 break;
0846286b
DS
316 case 'f':
317 inputfile = optarg;
318 break;
319 case 'm':
320 markfile = 1;
321 break;
914131f8
SH
322 case 'n':
323 no_error = 1;
324 break;
f366ad31
AS
325 case 'E':
326 echo_command = 1;
718e3744 327 break;
876b8be0
PJ
328 case 'C':
329 dryrun = 1;
330 break;
a68f8616
DL
331 case 'w':
332 writeconfig = 1;
333 break;
718e3744 334 case 'h':
335 usage (0);
336 break;
718e3744 337 default:
338 usage (1);
339 break;
340 }
341 }
342
a68f8616
DL
343 if (markfile + writeconfig + dryrun + boot_flag > 1)
344 {
345 fprintf (stderr, "Invalid combination of arguments. Please specify at "
346 "most one of:\n\t-b, -C, -m, -w\n");
347 return 1;
348 }
349 if (inputfile && (writeconfig || boot_flag))
350 {
351 fprintf (stderr, "WARNING: Combinining the -f option with -b or -w is "
352 "NOT SUPPORTED since its\nresults are inconsistent!\n");
353 }
354
718e3744 355 /* Initialize user input buffer. */
356 line_read = NULL;
0fbd62a1 357 setlinebuf(stdout);
718e3744 358
359 /* Signal and others. */
e42f5a37 360 vtysh_signal_init ();
718e3744 361
362 /* Make vty structure and register commands. */
363 vtysh_init_vty ();
364 vtysh_init_cmd ();
365 vtysh_user_init ();
366 vtysh_config_init ();
367
368 vty_init_vtysh ();
369
f366ad31 370 /* Read vtysh configuration file before connecting to daemons. */
cb947ba3 371 vtysh_read_config(vtysh_config_always);
0846286b
DS
372
373 if (markfile)
374 {
375 if (!inputfile)
376 {
377 fprintf(stderr, "-f option MUST be specified with -m option\n");
378 return(1);
379 }
380 return(vtysh_mark_file(inputfile));
381 }
718e3744 382
876b8be0
PJ
383 /* Start execution only if not in dry-run mode */
384 if(dryrun)
0846286b
DS
385 {
386 if (inputfile)
387 {
3221dca8 388 ret = vtysh_read_config(inputfile);
0846286b 389 }
3221dca8
DS
390 else
391 {
392 ret = vtysh_read_config(quagga_config_default);
393 }
394 exit(ret);
0846286b
DS
395 }
396
914131f8
SH
397 /* Ignore error messages */
398 if (no_error)
7a49a5b5
DS
399 {
400 if (freopen("/dev/null", "w", stdout) == NULL)
401 {
402 fprintf(stderr, "Exiting: Failed to duplicate stdout with -n option");
403 exit(1);
404 }
405 }
914131f8 406
f366ad31
AS
407 /* Make sure we pass authentication before proceeding. */
408 vtysh_auth ();
409
410 /* Do not connect until we have passed authentication. */
411 if (vtysh_connect_all (daemon_name) <= 0)
412 {
413 fprintf(stderr, "Exiting: failed to connect to any daemons.\n");
8ca1689f
SK
414 if (no_error)
415 exit(0);
416 else
417 exit(1);
f366ad31
AS
418 }
419
a68f8616
DL
420 if (writeconfig)
421 {
9f1f8df3 422 vtysh_execute ("enable");
a68f8616
DL
423 return vtysh_write_config_integrated ();
424 }
425
0846286b
DS
426 if (inputfile)
427 {
8f6899f2 428 vtysh_flock_config (inputfile);
3221dca8 429 ret = vtysh_read_config(inputfile);
8f6899f2 430 vtysh_unflock_config ();
3221dca8 431 exit(ret);
0846286b
DS
432 }
433
fba55c8a
DS
434 /*
435 * Setup history file for use by both -c and regular input
436 * If we can't find the home directory, then don't store
437 * the history information
438 */
439 homedir = vtysh_get_home ();
440 if (homedir)
441 {
442 snprintf(history_file, sizeof(history_file), "%s/.history_quagga", homedir);
443 if (read_history (history_file) != 0)
444 {
445 int fp;
446
447 fp = open (history_file, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
448 if (fp)
449 close (fp);
450
451 read_history (history_file);
452 }
453 }
454
95e735b5 455 /* If eval mode. */
f366ad31 456 if (cmd)
718e3744 457 {
f366ad31
AS
458 /* Enter into enable node. */
459 vtysh_execute ("enable");
460
461 while (cmd != NULL)
462 {
57fb9748 463 int ret;
f366ad31
AS
464 char *eol;
465
466 while ((eol = strchr(cmd->line, '\n')) != NULL)
467 {
468 *eol = '\0';
57fb9748 469
fba55c8a
DS
470 add_history (cmd->line);
471 append_history (1, history_file);
472
f366ad31 473 if (echo_command)
57fb9748
SH
474 printf("%s%s\n", vtysh_prompt(), cmd->line);
475
476 if (logfile)
477 log_it(cmd->line);
478
479 ret = vtysh_execute_no_pager(cmd->line);
914131f8
SH
480 if (!no_error &&
481 ! (ret == CMD_SUCCESS ||
482 ret == CMD_SUCCESS_DAEMON ||
483 ret == CMD_WARNING))
57fb9748
SH
484 exit(1);
485
f366ad31
AS
486 cmd->line = eol+1;
487 }
57fb9748 488
fba55c8a
DS
489 add_history (cmd->line);
490 append_history (1, history_file);
491
f366ad31
AS
492 if (echo_command)
493 printf("%s%s\n", vtysh_prompt(), cmd->line);
57fb9748
SH
494
495 if (logfile)
496 log_it(cmd->line);
497
498 ret = vtysh_execute_no_pager(cmd->line);
914131f8
SH
499 if (!no_error &&
500 ! (ret == CMD_SUCCESS ||
501 ret == CMD_SUCCESS_DAEMON ||
502 ret == CMD_WARNING))
57fb9748 503 exit(1);
f366ad31
AS
504
505 {
506 struct cmd_rec *cr;
507 cr = cmd;
508 cmd = cmd->next;
c66f9c61 509 XFREE(MTYPE_TMP, cr);
f366ad31
AS
510 }
511 }
fba55c8a
DS
512
513 history_truncate_file(history_file,1000);
718e3744 514 exit (0);
515 }
cb947ba3 516
718e3744 517 /* Boot startup configuration file. */
518 if (boot_flag)
519 {
cb947ba3
DL
520 vtysh_flock_config (quagga_config);
521 int ret = vtysh_read_config (quagga_config);
e43716f6 522 vtysh_unflock_config ();
1db63918
DS
523 if (ret)
524 {
525 fprintf (stderr, "Configuration file[%s] processing failure: %d\n",
cb947ba3 526 quagga_config, ret);
6dbef6e7
DS
527 if (no_error)
528 exit (0);
529 else
1db63918 530 exit (ret);
e7168df4 531 }
532 else
533 exit (0);
718e3744 534 }
535
536 vtysh_pager_init ();
537
538 vtysh_readline_init ();
539
540 vty_hello (vty);
541
e7168df4 542 /* Enter into enable node. */
543 vtysh_execute ("enable");
544
718e3744 545 /* Preparation for longjmp() in sigtstp(). */
546 sigsetjmp (jmpbuf, 1);
547 jmpflag = 1;
548
549 /* Main command loop. */
550 while (vtysh_rl_gets ())
551 vtysh_execute (line_read);
552
3f4ab7f9 553 history_truncate_file(history_file,1000);
718e3744 554 printf ("\n");
555
556 /* Rest in peace. */
557 exit (0);
558}