Inital commit

This commit is contained in:
Olof hagsand 2016-02-22 22:17:30 +01:00
parent edc5e091bb
commit d6e393ea58
145 changed files with 58117 additions and 0 deletions

407
apps/cli/cli_main.c Normal file
View 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;
}