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