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