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