Inital commit
This commit is contained in:
parent
edc5e091bb
commit
d6e393ea58
145 changed files with 58117 additions and 0 deletions
407
apps/cli/cli_main.c
Normal file
407
apps/cli/cli_main.c
Normal file
|
|
@ -0,0 +1,407 @@
|
|||
/*
|
||||
*
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLICON.
|
||||
|
||||
CLICON is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
CLICON is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CLICON; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clicon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#define __USE_GNU /* strverscmp */
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <dlfcn.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/param.h>
|
||||
#include <netinet/in.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* clicon */
|
||||
#include <clicon/clicon.h>
|
||||
|
||||
#include "clicon_cli_api.h"
|
||||
|
||||
#include "cli_plugin.h"
|
||||
#include "cli_generate.h"
|
||||
#include "cli_common.h"
|
||||
#include "cli_handle.h"
|
||||
|
||||
/* Command line options to be passed to getopt(3) */
|
||||
#define CLI_OPTS "hD:f:F:1u:d:m:cP:qpGLl:"
|
||||
|
||||
static int
|
||||
cli_terminate(clicon_handle h)
|
||||
{
|
||||
yang_spec *yspec;
|
||||
|
||||
if ((yspec = clicon_dbspec_yang(h)) != NULL)
|
||||
yspec_free(yspec);
|
||||
cli_plugin_finish(h);
|
||||
exit_candidate_db(h);
|
||||
cli_handle_exit(h);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* cli_sig_term
|
||||
* Unlink pidfile and quit
|
||||
*/
|
||||
static void
|
||||
cli_sig_term(int arg)
|
||||
{
|
||||
clicon_log(LOG_NOTICE, "%s: %u Terminated (killed by sig %d)",
|
||||
__PROGRAM__, getpid(), arg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup signal handlers
|
||||
*/
|
||||
static void
|
||||
cli_signal_init (clicon_handle h)
|
||||
{
|
||||
cli_signal_block(h);
|
||||
set_signal(SIGTERM, cli_sig_term, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
cli_interactive(clicon_handle h)
|
||||
{
|
||||
int res;
|
||||
char *cmd;
|
||||
char *new_mode;
|
||||
int result;
|
||||
|
||||
/* Loop through all commands */
|
||||
while(!cli_exiting(h)) {
|
||||
// save_mode =
|
||||
new_mode = cli_syntax_mode(h);
|
||||
if ((cmd = clicon_cliread(h)) == NULL) {
|
||||
cli_set_exiting(h, 1); /* EOF */
|
||||
break;
|
||||
}
|
||||
if ((res = clicon_parse(h, cmd, &new_mode, &result)) < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
usage(char *argv0, clicon_handle h)
|
||||
{
|
||||
char *confsock = clicon_sock(h);
|
||||
char *plgdir = clicon_cli_dir(h);
|
||||
|
||||
fprintf(stderr, "usage:%s [options] [commands]\n"
|
||||
"where commands is a CLI command or options passed to the main plugin\n"
|
||||
"where options are\n"
|
||||
"\t-h \t\tHelp\n"
|
||||
"\t-D <level> \tDebug\n"
|
||||
"\t-f <file> \tConfig-file (mandatory)\n"
|
||||
"\t-F <file> \tRead commands from file (default stdin)\n"
|
||||
"\t-1\t\tDo not enter interactive mode\n"
|
||||
"\t-u <sockpath>\tconfig UNIX domain path (default: %s)\n"
|
||||
"\t-d <dir>\tSpecify plugin directory (default: %s)\n"
|
||||
"\t-m <mode>\tSpecify plugin syntax mode\n"
|
||||
"\t-c \t\tWrite to candidate db directly, not via config backend\n"
|
||||
"\t-P <dbname> \tWrite to private database\n"
|
||||
"\t-q \t\tQuiet mode, dont print greetings or prompt, terminate on ctrl-C\n"
|
||||
"\t-p \t\tPrint database yang specification\n"
|
||||
"\t-G \t\tPrint CLI syntax generated from dbspec (if CLICON_CLI_GENMODEL enabled)\n"
|
||||
"\t-L \t\tDebug print dynamic CLI syntax including completions and expansions\n"
|
||||
"\t-l <s|e|o> \tLog on (s)yslog, std(e)rr or std(o)ut (stderr is default)\n",
|
||||
argv0,
|
||||
confsock ? confsock : "none",
|
||||
plgdir ? plgdir : "none"
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
char c;
|
||||
enum candidate_db_type dbtype;
|
||||
char private_db[MAXPATHLEN];
|
||||
int once;
|
||||
char *tmp;
|
||||
char *argv0 = argv[0];
|
||||
clicon_handle h;
|
||||
int printspec = 0;
|
||||
int printgen = 0;
|
||||
int logclisyntax = 0;
|
||||
int help = 0;
|
||||
char *treename;
|
||||
char *running_db;
|
||||
int logdst = CLICON_LOG_STDERR;
|
||||
|
||||
/* Defaults */
|
||||
|
||||
/* In the startup, logs to stderr & debug flag set later */
|
||||
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
|
||||
/* Initiate CLICON handle */
|
||||
if ((h = cli_handle_init()) == NULL)
|
||||
goto done;
|
||||
if (cli_plugin_init(h) != 0)
|
||||
goto done;
|
||||
dbtype = CANDIDATE_DB_SHARED;
|
||||
once = 0;
|
||||
private_db[0] = '\0';
|
||||
cli_set_send2backend(h, 1); /* send changes to config daemon */
|
||||
cli_set_comment(h, '#'); /* Default to handle #! clicon_cli scripts */
|
||||
|
||||
/*
|
||||
* First-step command-line options for help, debug, config-file and log,
|
||||
*/
|
||||
optind = 1;
|
||||
opterr = 0;
|
||||
while ((c = getopt(argc, argv, CLI_OPTS)) != -1)
|
||||
switch (c) {
|
||||
case '?':
|
||||
case 'h':
|
||||
/* Defer the call to usage() to later. Reason is that for helpful
|
||||
text messages, default dirs, etc, are not set until later.
|
||||
But this means that we need to check if 'help' is set before
|
||||
exiting, and then call usage() before exit.
|
||||
*/
|
||||
help = 1;
|
||||
break;
|
||||
case 'D' : /* debug */
|
||||
if (sscanf(optarg, "%d", &debug) != 1)
|
||||
usage(argv[0], h);
|
||||
break;
|
||||
case 'f': /* config file */
|
||||
if (!strlen(optarg))
|
||||
usage(argv[0], h);
|
||||
clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg);
|
||||
break;
|
||||
case 'l': /* Log destination: s|e|o */
|
||||
switch (optarg[0]){
|
||||
case 's':
|
||||
logdst = CLICON_LOG_SYSLOG;
|
||||
break;
|
||||
case 'e':
|
||||
logdst = CLICON_LOG_STDERR;
|
||||
break;
|
||||
case 'o':
|
||||
logdst = CLICON_LOG_STDOUT;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0], h);
|
||||
}
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Logs, error and debug to stderr or syslog, set debug level
|
||||
*/
|
||||
clicon_log_init(__PROGRAM__, debug?LOG_DEBUG:LOG_INFO, logdst);
|
||||
|
||||
clicon_debug_init(debug, NULL);
|
||||
|
||||
/* Find and read configfile */
|
||||
if (clicon_options_main(h) < 0){
|
||||
if (help)
|
||||
usage(argv[0], h);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Now rest of options */
|
||||
opterr = 0;
|
||||
optind = 1;
|
||||
while ((c = getopt(argc, argv, CLI_OPTS)) != -1){
|
||||
switch (c) {
|
||||
case 'D' : /* debug */
|
||||
case 'f': /* config file */
|
||||
case 'l': /* Log destination */
|
||||
break; /* see above */
|
||||
case 'F': /* read commands from file */
|
||||
if (freopen(optarg, "r", stdin) == NULL){
|
||||
cli_output(stderr, "freopen: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case '1' : /* Quit after reading database once - dont wait for events */
|
||||
once = 1;
|
||||
break;
|
||||
case 'u': /* config unix domain path/ ip host */
|
||||
if (!strlen(optarg))
|
||||
usage(argv[0], h);
|
||||
clicon_option_str_set(h, "CLICON_SOCK", optarg);
|
||||
break;
|
||||
case 'd': /* Plugin directory: overrides configfile */
|
||||
if (!strlen(optarg))
|
||||
usage(argv[0], h);
|
||||
clicon_option_str_set(h, "CLICON_CLI_DIR", optarg);
|
||||
break;
|
||||
case 'm': /* CLI syntax mode */
|
||||
if (!strlen(optarg))
|
||||
usage(argv[0], h);
|
||||
clicon_option_str_set(h, "CLICON_CLI_MODE", optarg);
|
||||
break;
|
||||
case 'c' : /* No config daemon (used in bootstrapping and file load) */
|
||||
cli_set_send2backend(h, 0);
|
||||
break;
|
||||
case 'P' : /* load to private database with given name */
|
||||
dbtype = CANDIDATE_DB_PRIVATE;
|
||||
clicon_option_str_set(h, "CLICON_CANDIDATE_DB", optarg); /* override default */
|
||||
break;
|
||||
case 'q' : /* Quiet mode */
|
||||
clicon_option_str_set(h, "CLICON_QUIET", "on");
|
||||
break;
|
||||
case 'p' : /* Print spec */
|
||||
printspec++;
|
||||
break;
|
||||
case 'G' : /* Print generated CLI syntax */
|
||||
printgen++;
|
||||
break;
|
||||
case 'L' : /* Debug print dynamic CLI syntax */
|
||||
logclisyntax++;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0], h);
|
||||
break;
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
/* Defer: Wait to the last minute to print help message */
|
||||
if (help)
|
||||
usage(argv[0], h);
|
||||
|
||||
/* Setup signal handlers */
|
||||
cli_signal_init(h);
|
||||
|
||||
/* Backward compatible mode, do not include keys in cgv-arrays in callbacks.
|
||||
Should be 0 but default is 1 since all legacy apps use 1
|
||||
Test legacy before shifting default to 0
|
||||
*/
|
||||
cv_exclude_keys(clicon_cli_varonly(h));
|
||||
|
||||
/* Parse db specification as cli*/
|
||||
if (yang_spec_main(h, stdout, printspec) < 0)
|
||||
goto done;
|
||||
|
||||
/* Check plugin directory */
|
||||
if (clicon_cli_dir(h) == NULL){
|
||||
clicon_err(OE_PLUGIN, 0, "clicon_cli_dir not defined");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Create tree generated from dataspec */
|
||||
if (clicon_cli_genmodel(h)){
|
||||
yang_spec *yspec; /* yang spec */
|
||||
parse_tree pt = {0,}; /* cli parse tree */
|
||||
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
clicon_err(OE_FATAL, 0, "No YANG DB_SPEC");
|
||||
goto done;
|
||||
}
|
||||
/* Create cli command tree from dbspec */
|
||||
if (yang2cli(h, yspec, &pt, clicon_cli_genmodel_type(h)) < 0)
|
||||
goto done;
|
||||
|
||||
treename = chunk_sprintf(__FUNCTION__, "datamodel:%s", clicon_dbspec_name(h));
|
||||
cli_tree_add(h, treename, pt);
|
||||
if (printgen)
|
||||
cligen_print(stdout, pt, 1);
|
||||
}
|
||||
|
||||
/* Initialize cli syntax */
|
||||
if (cli_syntax_load(h) < 0)
|
||||
goto done;
|
||||
|
||||
/* Set syntax mode if specified from command-line or config-file. */
|
||||
if (clicon_option_exists(h, "CLICON_CLI_MODE"))
|
||||
if ((tmp = clicon_cli_mode(h)) != NULL)
|
||||
if (cli_set_syntax_mode(h, tmp) == 0) {
|
||||
fprintf(stderr, "FATAL: Failed to set syntax mode '%s'\n", tmp);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!cli_syntax_mode(h)){
|
||||
fprintf (stderr, "FATAL: No cli mode set (use -m or CLICON_CLI_MODE)\n");
|
||||
goto done;
|
||||
}
|
||||
if (cli_tree(h, cli_syntax_mode(h)) == NULL){
|
||||
fprintf (stderr, "FATAL: No such cli mode: %s\n", cli_syntax_mode(h));
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Initialize databases */
|
||||
if ((running_db = clicon_running_db(h)) == NULL)
|
||||
goto done;
|
||||
|
||||
if (strlen(private_db))
|
||||
clicon_option_str_set(h, "CLICON_CANDIDATE_DB", private_db);
|
||||
|
||||
if (!cli_send2backend(h))
|
||||
if (db_init(running_db) < 0){
|
||||
fprintf (stderr, "FATAL: Could not init running_db. (Run as root?)\n");
|
||||
goto done;
|
||||
}
|
||||
/* A client does not have access to the candidate (and running)
|
||||
databases if both these conditions are true:
|
||||
1. clicon_sock_family(h) == AF_INET[6]
|
||||
2. cli_send2backend(h) == 1
|
||||
*/
|
||||
if (clicon_sock_family(h) == AF_UNIX || cli_send2backend(h)==0)
|
||||
if (init_candidate_db(h, dbtype) < 0)
|
||||
return -1;
|
||||
|
||||
if (logclisyntax)
|
||||
cli_logsyntax_set(h, logclisyntax);
|
||||
|
||||
if (debug)
|
||||
clicon_option_dump(h, debug);
|
||||
|
||||
/* Call start function in all plugins before we go interactive
|
||||
Pass all args after the standard options to plugin_start
|
||||
*/
|
||||
|
||||
tmp = *(argv-1);
|
||||
*(argv-1) = argv0;
|
||||
cli_plugin_start(h, argc+1, argv-1);
|
||||
*(argv-1) = tmp;
|
||||
|
||||
/* Launch interfactive event loop, unless -1 */
|
||||
if (once == 0)
|
||||
cli_interactive(h);
|
||||
done:
|
||||
// Gets in your face if we log on stderr
|
||||
clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
|
||||
clicon_log(LOG_NOTICE, "%s: %u Terminated\n", __PROGRAM__, getpid());
|
||||
cli_terminate(h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue