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