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