1 /*
2  *
3   ***** BEGIN LICENSE BLOCK *****
4 
5   Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
6   Copyright (C) 2017-2019 Olof Hagsand
7   Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
8 
9   This file is part of CLIXON.
10 
11   Licensed under the Apache License, Version 2.0 (the "License");
12   you may not use this file except in compliance with the License.
13   You may obtain a copy of the License at
14 
15     http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22 
23   Alternatively, the contents of this file may be used under the terms of
24   the GNU General Public License Version 3 or later (the "GPL"),
25   in which case the provisions of the GPL are applicable instead
26   of those above. If you wish to allow use of your version of this file only
27   under the terms of the GPL, and not to allow others to
28   use your version of this file under the terms of Apache License version 2,
29   indicate your decision by deleting the provisions above and replace them with
30   the  notice and other provisions required by the GPL. If you do not delete
31   the provisions above, a recipient may use your version of this file under
32   the terms of any one of the Apache License version 2 or the GPL.
33 
34   ***** END LICENSE BLOCK *****
35 
36  *
37  */
38 
39 #ifdef HAVE_CONFIG_H
40 #include "clixon_config.h" /* generated by config & autoconf */
41 #endif
42 
43 #include <stdio.h>
44 #include <string.h>
45 #include <stdlib.h>
46 #include <stdarg.h>
47 #include <dlfcn.h>
48 #include <dirent.h>
49 #include <unistd.h>
50 #include <errno.h>
51 #include <signal.h>
52 #include <syslog.h>
53 #include <sys/types.h>
54 #include <sys/stat.h>
55 #include <sys/param.h>
56 #include <netinet/in.h>
57 #include <pwd.h>
58 #include <libgen.h>
59 #include <wordexp.h>
60 
61 /* cligen */
62 #include <cligen/cligen.h>
63 
64 /* clicon */
65 #include <clixon/clixon.h>
66 
67 #include "clixon_cli_api.h"
68 
69 #include "cli_plugin.h"
70 #include "cli_generate.h"
71 #include "cli_common.h"
72 #include "cli_handle.h"
73 
74 /* Command line options to be passed to getopt(3) */
75 #define CLI_OPTS "hD:f:E:l:F:1a:u:d:m:qp:GLy:c:U:o:"
76 
77 /*! Check if there is a CLI history file and if so dump the CLI histiry to it
78  * Just log if file does not exist or is not readable
79  * @param[in]  h    CLICON handle
80  */
81 static int
cli_history_load(clicon_handle h)82 cli_history_load(clicon_handle h)
83 {
84     int       retval = -1;
85     int       lines;
86     char     *filename;
87     FILE     *f = NULL;
88     wordexp_t result = {0,}; /* for tilde expansion */
89 
90     /* Get history size from clixon option, if not use cligen default. */
91     if (clicon_option_exists(h, "CLICON_CLI_HIST_SIZE"))
92 	lines = clicon_option_int(h,"CLICON_CLI_HIST_SIZE");
93     else
94 	lines = CLIGEN_HISTSIZE_DEFAULT;
95     /* Re-init history with clixon lines (1st time was w cligen defaults) */
96     if (cligen_hist_init(cli_cligen(h), lines) < 0)
97 	goto done;
98     if ((filename = clicon_option_str(h,"CLICON_CLI_HIST_FILE")) == NULL)
99 	goto ok; /* ignore */
100     if (wordexp(filename, &result, 0) < 0){
101 	clicon_err(OE_UNIX, errno, "wordexp");
102 	goto done;
103     }
104     if ((f = fopen(result.we_wordv[0], "r")) == NULL){
105 	clicon_log(LOG_DEBUG, "Warning: Could not open CLI history file for reading: %s: %s",
106 		   result.we_wordv[0], strerror(errno));
107 	goto ok;
108     }
109     if (cligen_hist_file_load(cli_cligen(h), f) < 0){
110 	clicon_err(OE_UNIX, errno, "cligen_hist_file_load");
111 	goto done;
112     }
113  ok:
114     retval = 0;
115  done:
116     wordfree(&result);
117     if (f)
118 	fclose(f);
119     return retval;
120 }
121 
122 /*! Start CLI history and load from file
123  * Just log if file does not exist or is not readable
124  * @param[in]  h    CLICON handle
125  */
126 static int
cli_history_save(clicon_handle h)127 cli_history_save(clicon_handle h)
128 {
129     int       retval = -1;
130     char     *filename;
131     FILE     *f = NULL;
132     wordexp_t result = {0,}; /* for tilde expansion */
133 
134     if ((filename = clicon_option_str(h, "CLICON_CLI_HIST_FILE")) == NULL)
135 	goto ok; /* ignore */
136     if (wordexp(filename, &result, 0) < 0){
137 	clicon_err(OE_UNIX, errno, "wordexp");
138 	goto done;
139     }
140     if ((f = fopen(result.we_wordv[0], "w+")) == NULL){
141 	clicon_log(LOG_DEBUG, "Warning: Could not open CLI history file for writing: %s: %s",
142 		   result.we_wordv[0], strerror(errno));
143 	goto ok;
144     }
145     if (cligen_hist_file_save(cli_cligen(h), f) < 0){
146 	clicon_err(OE_UNIX, errno, "cligen_hist_file_save");
147 	goto done;
148     }
149  ok:
150     retval = 0;
151  done:
152     wordfree(&result);
153     if (f)
154 	fclose(f);
155     return retval;
156 }
157 
158 
159 /*! Clean and close all state of cli process (but dont exit).
160  * Cannot use h after this
161  * @param[in]  h  Clixon handle
162  */
163 static int
cli_terminate(clicon_handle h)164 cli_terminate(clicon_handle h)
165 {
166     yang_stmt  *yspec;
167     cvec       *nsctx;
168     cxobj      *x;
169 
170     clicon_rpc_close_session(h);
171     if ((yspec = clicon_dbspec_yang(h)) != NULL)
172 	yspec_free(yspec);
173     if ((yspec = clicon_config_yang(h)) != NULL)
174 	yspec_free(yspec);
175     if ((nsctx = clicon_nsctx_global_get(h)) != NULL)
176 	cvec_free(nsctx);
177     if ((x = clicon_conf_xml(h)) != NULL)
178 	xml_free(x);
179     clicon_data_cvec_del(h, "cli-edit-cvv");;
180     xpath_optimize_exit();
181     cli_plugin_finish(h);
182     cli_history_save(h);
183     cli_handle_exit(h);
184     clicon_log_exit();
185     return 0;
186 }
187 
188 /*! Unlink pidfile and quit
189 */
190 static void
cli_sig_term(int arg)191 cli_sig_term(int arg)
192 {
193     clicon_log(LOG_NOTICE, "%s: %u Terminated (killed by sig %d)",
194 	    __PROGRAM__, getpid(), arg);
195     exit(1);
196 }
197 
198 /*! Setup signal handlers
199  */
200 static void
cli_signal_init(clicon_handle h)201 cli_signal_init (clicon_handle h)
202 {
203 	cli_signal_block(h);
204 	set_signal(SIGTERM, cli_sig_term, NULL);
205 }
206 
207 /*! Interactive CLI command loop
208  * @param[in]  h    CLICON handle
209  * @retval     0
210  * @retval    -1
211  * @see cligen_loop
212  */
213 static int
cli_interactive(clicon_handle h)214 cli_interactive(clicon_handle h)
215 {
216     int           retval = -1;
217     char         *cmd;
218     char         *new_mode;
219     cligen_result result;
220 
221     /* Loop through all commands */
222     while(!cligen_exiting(cli_cligen(h))) {
223 	new_mode = cli_syntax_mode(h);
224 	cmd = NULL;
225 	if (clicon_cliread(h, &cmd) < 0)
226 	    goto done;
227 	if (cmd == NULL) { /* EOF */
228 	    cligen_exiting_set(cli_cligen(h), 1);
229 	    continue;
230 	}
231 	if (clicon_parse(h, cmd, &new_mode, &result, NULL) < 0)
232 	    goto done;
233 	/* Why not check result? */
234     }
235     retval = 0;
236  done:
237     return retval;
238 }
239 
240 /*! Generate one autocli clispec tree
241  *
242  * @param[in]  h        Clixon handle
243  * @param[in]  name     Name of tree
244  * @param[in]  gt       genmodel-type, ie HOW to generate the CLI
245  * @param[in]  printgen Print CLI syntax to stderr
246  * @param[in]  show_tree Is tree for show cli command (1 - yes. 0 - no)
247  *
248  * Generate clispec (datamodel) from YANG dataspec and add to the set of cligen trees
249  * (as a separate mode)
250  * This tree is referenced from the main CLI spec (CLICON_CLISPEC_DIR) using the "tree reference"
251  * syntax, ie @datamodel
252  * @param[in]  h        Clixon handle
253  * @param[in]  printgen Print CLI syntax generated from dbspec
254  * @retval     0        OK
255  * @retval    -1        Error
256  *
257  * @note that yang2cli generates syntax for ALL modules under the loaded yangspec.
258  */
259 static int
autocli_tree(clicon_handle h,char * name,enum genmodel_type gt,int state,int printgen,int show_tree)260 autocli_tree(clicon_handle      h,
261 	     char              *name,
262 	     enum genmodel_type gt,
263 	     int                state,
264 	     int                printgen,
265          int                show_tree)
266 {
267     int           retval = -1;
268     parse_tree   *pt = NULL;  /* cli parse tree */
269     yang_stmt    *yspec;
270     pt_head      *ph;
271 
272     if ((pt = pt_new()) == NULL){
273 	clicon_err(OE_UNIX, errno, "pt_new");
274 	goto done;
275     }
276     yspec = clicon_dbspec_yang(h);
277     /* Generate tree (this is where the action is) */
278     if (yang2cli(h, yspec, gt, printgen, state, show_tree, pt) < 0)
279 	goto done;
280     /* Append cligen tree and name it */
281     if ((ph = cligen_ph_add(cli_cligen(h), name)) == NULL)
282 	goto done;
283     if (cligen_ph_parsetree_set(ph, pt) < 0)
284 	goto done;
285     retval = 0;
286  done:
287     return retval;
288 }
289 
290 /*! Generate autocli, ie if enabled, generate clispec from YANG and add to cligen parse-trees
291  *
292  * Generate clispec (datamodel) from YANG dataspec and add to the set of cligen trees
293  * (as a separate mode)
294  * This tree is referenced from the main CLI spec (CLICON_CLISPEC_DIR) using the "tree reference"
295  * syntax, ie @datamodel
296  * Also (if enabled) generate a second "state" tree called @datamodelstate
297  *
298  * @param[in]  h        Clixon handle
299  * @param[in]  printgen Print CLI syntax generated from dbspec
300  * @retval     0        OK
301  * @retval    -1        Error
302  */
303 static int
autocli_start(clicon_handle h,int printgen)304 autocli_start(clicon_handle h,
305 	      int           printgen)
306 {
307     int                retval = -1;
308     int                autocli_model = 0;
309     cbuf              *show_treename = NULL, *treename = NULL;
310     enum genmodel_type gt;
311 
312     /* If autocli disabled quit */
313     if ((autocli_model = clicon_cli_genmodel(h)) == 0)
314 	goto ok;
315     /* Get the autocli type, ie HOW the cli is generated (could be much more here) */
316     gt = clicon_cli_genmodel_type(h);
317     /* Create show_treename cbuf */
318     if ((show_treename = cbuf_new()) == NULL){
319 	clicon_err(OE_UNIX, errno, "cbuf_new");
320 	goto done;
321     }
322     /* Create treename cbuf */
323     if ((treename = cbuf_new()) == NULL){
324 	clicon_err(OE_UNIX, errno, "cbuf_new");
325 	goto done;
326     }
327     /* The tree name is by default @datamodel but can be changed by option (why would one do that?) */
328 	cprintf(treename, "%s", clicon_cli_model_treename(h));
329 	if (autocli_tree(h, cbuf_get(treename), gt, 0, printgen, 0) < 0)
330 	    goto done;
331 
332     /* The tree name is by default @datamodelshow but can be changed by option (why would one do that?) */
333     cprintf(show_treename, "%s", clicon_cli_model_treename(h));
334     cprintf(show_treename, "show");
335     if (autocli_tree(h, cbuf_get(show_treename), gt, 0, printgen, 1) < 0)
336 	goto done;
337 
338     /* Create a tree for config+state. This tree's name has appended "state" to @datamodel (XXX)
339      */
340     if (autocli_model > 1){
341 	cprintf(treename, "state");
342 	if (autocli_tree(h, cbuf_get(treename), gt, 1, printgen, 1) < 0)
343 	    goto done;
344     }
345 
346  ok:
347     retval = 0;
348  done:
349     if (show_treename)
350 	cbuf_free(show_treename);
351     if (treename)
352 	cbuf_free(treename);
353     return retval;
354 }
355 
356 static void
usage(clicon_handle h,char * argv0)357 usage(clicon_handle h,
358       char         *argv0)
359 {
360     char *plgdir = clicon_cli_dir(h);
361 
362     fprintf(stderr, "usage:%s [options] [commands]\n"
363 	    "where commands is a CLI command or options passed to the main plugin\n"
364 	    "where options are\n"
365             "\t-h \t\tHelp\n"
366     	    "\t-D <level> \tDebug level\n"
367 	    "\t-f <file> \tConfig-file (mandatory)\n"
368 	    "\t-E <dir>  \tExtra configuration file directory\n"
369     	    "\t-F <file> \tRead commands from file (default stdin)\n"
370 	    "\t-1\t\tDo not enter interactive mode\n"
371     	    "\t-a UNIX|IPv4|IPv6\tInternal backend socket family\n"
372     	    "\t-u <path|addr>\tInternal socket domain path or IP addr (see -a)\n"
373 	    "\t-d <dir>\tSpecify plugin directory (default: %s)\n"
374             "\t-m <mode>\tSpecify plugin syntax mode\n"
375 	    "\t-q \t\tQuiet mode, dont print greetings or prompt, terminate on ctrl-C\n"
376 	    "\t-p <dir>\tYang directory path (see CLICON_YANG_DIR)\n"
377 	    "\t-G \t\tPrint CLI syntax generated from dbspec (if CLICON_CLI_GENMODEL enabled)\n"
378 	    "\t-L \t\tDebug print dynamic CLI syntax including completions and expansions\n"
379 	    "\t-l <s|e|o|f<file>> \tLog on (s)yslog, std(e)rr, std(o)ut or (f)ile (stderr is default)\n"
380 	    "\t-y <file>\tOverride yang spec file (dont include .yang suffix)\n"
381 	    "\t-c <file>\tSpecify cli spec file.\n"
382 	    "\t-U <user>\tOver-ride unix user with a pseudo user for NACM.\n"
383 	    "\t-o \"<option>=<value>\"\tGive configuration option overriding config file (see clixon-config.yang)\n",
384 	    argv0,
385 	    plgdir ? plgdir : "none"
386 	);
387     exit(1);
388 }
389 
390 /*
391  */
392 int
main(int argc,char ** argv)393 main(int    argc,
394      char **argv)
395 {
396     int            retval = -1;
397     int            c;
398     int            once;
399     char	  *tmp;
400     char	  *argv0 = argv[0];
401     clicon_handle  h;
402     int            printgen  = 0;
403     int            logclisyntax  = 0;
404     int            help = 0;
405     int            logdst = CLICON_LOG_STDERR;
406     char          *restarg = NULL; /* what remains after options */
407     yang_stmt     *yspec;
408     struct passwd *pw;
409     char          *str;
410     int            tabmode;
411     char          *dir;
412     cvec          *nsctx_global = NULL; /* Global namespace context */
413     size_t         cligen_buflen;
414     size_t         cligen_bufthreshold;
415     int            dbg=0;
416     int            nr;
417 
418     /* Defaults */
419     once = 0;
420 
421     /* In the startup, logs to stderr & debug flag set later */
422     clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
423 
424     /* Initiate CLICON handle. CLIgen is also initialized */
425     if ((h = cli_handle_init()) == NULL)
426 	goto done;
427 
428     /* Set username to clicon handle. Use in all communication to backend
429      * Note, can be overridden by -U
430      */
431     if ((pw = getpwuid(getuid())) == NULL){
432 	clicon_err(OE_UNIX, errno, "getpwuid");
433 	goto done;
434     }
435     if (clicon_username_set(h, pw->pw_name) < 0)
436 	goto done;
437 
438     cligen_comment_set(cli_cligen(h), '#'); /* Default to handle #! clicon_cli scripts */
439 
440     /*
441      * First-step command-line options for help, debug, config-file and log,
442      */
443     optind = 1;
444     opterr = 0;
445     while ((c = getopt(argc, argv, CLI_OPTS)) != -1)
446 	switch (c) {
447 	case 'h':
448 	    /* Defer the call to usage() to later. Reason is that for helpful
449 	       text messages, default dirs, etc, are not set until later.
450 	       But this means that we need to check if 'help' is set before
451 	       exiting, and then call usage() before exit.
452 	    */
453 	    help = 1;
454 	    break;
455 	case 'D' : /* debug */
456 	    if (sscanf(optarg, "%d", &dbg) != 1)
457 		usage(h, argv[0]);
458 	    break;
459 	case 'f': /* config file */
460 	    if (!strlen(optarg))
461 		usage(h, argv[0]);
462 	    clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg);
463 	    break;
464 	case 'E': /* extra config directory */
465 	    if (!strlen(optarg))
466 		usage(h, argv[0]);
467 	    clicon_option_str_set(h, "CLICON_CONFIGDIR", optarg);
468 	    break;
469 	case 'l': /* Log destination: s|e|o|f */
470 	    if ((logdst = clicon_log_opt(optarg[0])) < 0)
471 		usage(h, argv[0]);
472 	    if (logdst == CLICON_LOG_FILE &&
473 		strlen(optarg)>1 &&
474 		clicon_log_file(optarg+1) < 0)
475 		goto done;
476 	    break;
477 	}
478     /*
479      * Logs, error and debug to stderr or syslog, set debug level
480      */
481     clicon_log_init(__PROGRAM__, dbg?LOG_DEBUG:LOG_INFO, logdst);
482 
483     clicon_debug_init(dbg, NULL);
484 
485     /* Find, read and parse configfile */
486     if (clicon_options_main(h) < 0){
487         if (help)
488 	    usage(h, argv[0]);
489 	goto done;
490     }
491     /* Now rest of options */
492     opterr = 0;
493     optind = 1;
494     while ((c = getopt(argc, argv, CLI_OPTS)) != -1){
495 	switch (c) {
496 	case 'D' : /* debug */
497 	case 'f': /* config file */
498 	case 'E': /* extra config dir */
499 	case 'l': /* Log destination */
500 	    break; /* see above */
501 	case 'F': /* read commands from file */
502 	    if (freopen(optarg, "r", stdin) == NULL){
503 		fprintf(stderr, "freopen: %s\n", strerror(errno));
504 		return -1;
505 	    }
506 	    break;
507 	case '1' : /* Quit after reading database once - dont wait for events */
508 	    once = 1;
509 	    break;
510 	case 'a': /* internal backend socket address family */
511 	    if (clicon_option_add(h, "CLICON_SOCK_FAMILY", optarg) < 0)
512 		goto done;
513 	    break;
514 	case 'u': /* internal backend socket unix domain path or ip host */
515 	    if (!strlen(optarg))
516 		usage(h, argv[0]);
517 	    if (clicon_option_add(h, "CLICON_SOCK", optarg) < 0)
518 		goto done;
519 	    break;
520 	case 'd':  /* Plugin directory: overrides configfile */
521 	    if (!strlen(optarg))
522 		usage(h, argv[0]);
523 	    if (clicon_option_add(h, "CLICON_CLI_DIR", optarg) < 0)
524 		goto done;
525 	    break;
526 	case 'm': /* CLI syntax mode */
527 	    if (!strlen(optarg))
528 		usage(h, argv[0]);
529 	    if (clicon_option_add(h, "CLICON_CLI_MODE", optarg) < 0)
530 		goto done;
531 	    break;
532 	case 'q' : /* Quiet mode */
533 	    clicon_quiet_mode_set(h, 1);
534 	    break;
535 	case 'p' : /* yang dir path */
536 	    if (clicon_option_add(h, "CLICON_YANG_DIR", optarg) < 0)
537 		goto done;
538 	    break;
539 	case 'G' : /* Print generated CLI syntax */
540 	    printgen++;
541 	    break;
542 	case 'L' : /* Debug print dynamic CLI syntax */
543 	    logclisyntax++;
544 	    break;
545 	case 'y' : /* Load yang absolute filename */
546 	    if (clicon_option_add(h, "CLICON_YANG_MAIN_FILE", optarg) < 0)
547 		goto done;
548 	    break;
549 	case 'c' : /* Overwrite clispec with absolute filename */
550 	    if (clicon_option_add(h, "CLICON_CLISPEC_FILE", optarg) < 0)
551 		goto done;
552 	    break;
553 	case 'U': /* Clixon 'pseudo' user */
554 	    if (!strlen(optarg))
555 		usage(h, argv[0]);
556 	    if (clicon_username_set(h, optarg) < 0)
557 		goto done;
558 	    break;
559 	case 'o':{ /* Configuration option */
560 	    char          *val;
561 	    if ((val = index(optarg, '=')) == NULL)
562 		usage(h, argv0);
563 	    *val++ = '\0';
564 	    if (clicon_option_add(h, optarg, val) < 0)
565 		goto done;
566 	    break;
567 	}
568 	default:
569 	    usage(h, argv[0]);
570 	    break;
571 	}
572     }
573     argc -= optind;
574     argv += optind;
575 
576     /* Access the remaining argv/argc options (after --) w clicon-argv_get() */
577     clicon_argv_set(h, argv0, argc, argv);
578 
579     /* Defer: Wait to the last minute to print help message */
580     if (help)
581 	usage(h, argv[0]);
582 
583     /* Init cligen buffers */
584     cligen_buflen = clicon_option_int(h, "CLICON_CLI_BUF_START");
585     cligen_bufthreshold = clicon_option_int(h, "CLICON_CLI_BUF_THRESHOLD");
586     cbuf_alloc_set(cligen_buflen, cligen_bufthreshold);
587 
588     /* Init row numbers for raw terminals */
589     if (clicon_option_exists(h, "CLICON_CLI_LINES_DEFAULT")){
590 	nr = clicon_option_int(h, "CLICON_CLI_LINES_DEFAULT");
591 	cligen_terminal_rows_set(cli_cligen(h), nr);
592     }
593 
594     if (clicon_yang_regexp(h) == REGEXP_LIBXML2){
595 #ifdef HAVE_LIBXML2
596 	/* Enable XSD libxml2 regex engine */
597 	cligen_regex_xsd_set(cli_cligen(h), 1);
598 #else
599 	clicon_err(OE_FATAL, 0, "CLICON_YANG_REGEXP set to libxml2, but HAVE_LIBXML2 not set (Either change CLICON_YANG_REGEXP to posix, or run: configure --with-libxml2))");
600 	goto done;
601 #endif
602     }
603 
604     /* CLIgen help string setting for long and multi-line strings */
605     nr = clicon_option_int(h, "CLICON_CLI_HELPSTRING_TRUNCATE");
606     cligen_helpstring_truncate_set(cli_cligen(h), nr);
607     nr = clicon_option_int(h, "CLICON_CLI_HELPSTRING_LINES");
608     cligen_helpstring_lines_set(cli_cligen(h), nr);
609 
610     /* Setup signal handlers */
611     cli_signal_init(h);
612 
613     /* Backward compatible mode, do not include keys in cgv-arrays in callbacks.
614        Should be 0 but default is 1 since all legacy apps use 1
615        Test legacy before shifting default to 0
616      */
617     cv_exclude_keys(clicon_cli_varonly(h));
618 
619     /* Load cli plugins before yangs are loaded (eg extension callbacks) */
620     if ((dir = clicon_cli_dir(h)) != NULL &&
621 	clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir, NULL) < 0)
622 	goto done;
623 
624     /* Add (hardcoded) netconf features in case ietf-netconf loaded here
625      * Otherwise it is loaded in netconf_module_load below
626      */
627     if (netconf_module_features(h) < 0)
628 	goto done;
629 
630     /* Set default namespace according to CLICON_NAMESPACE_NETCONF_DEFAULT */
631     xml_nsctx_namespace_netconf_default(h);
632 
633     /* Treat unknwon XML as anydata */
634     if (clicon_option_bool(h, "CLICON_YANG_UNKNOWN_ANYDATA") == 1)
635 	xml_bind_yang_unknown_anydata(1);
636 
637     /* Create top-level and store as option */
638     if ((yspec = yspec_new()) == NULL)
639 	goto done;
640     clicon_dbspec_yang_set(h, yspec);
641 
642     /* Load Yang modules
643      * 1. Load a yang module as a specific absolute filename */
644     if ((str = clicon_yang_main_file(h)) != NULL){
645 	if (yang_spec_parse_file(h, str, yspec) < 0)
646 	    goto done;
647     }
648     /* 2. Load a (single) main module */
649     if ((str = clicon_yang_module_main(h)) != NULL){
650 	if (yang_spec_parse_module(h, str, clicon_yang_module_revision(h),
651 				   yspec) < 0)
652 	    goto done;
653     }
654     /* 3. Load all modules in a directory */
655     if ((str = clicon_yang_main_dir(h)) != NULL){
656 	if (yang_spec_load_dir(h, str, yspec) < 0)
657 	    goto done;
658     }
659     /* Load clixon lib yang module */
660     if (yang_spec_parse_module(h, "clixon-lib", NULL, yspec) < 0)
661 	goto done;
662 
663      /* Load yang module library, RFC7895 */
664     if (yang_modules_init(h) < 0)
665 	goto done;
666 
667     /* Add netconf yang spec, used as internal protocol */
668     if (netconf_module_load(h) < 0)
669 	goto done;
670 
671     /* Here all modules are loaded
672      * Compute and set canonical namespace context
673      */
674     if (xml_nsctx_yangspec(yspec, &nsctx_global) < 0)
675 	goto done;
676     if (clicon_nsctx_global_set(h, nsctx_global) < 0)
677 	goto done;
678 
679     /* Create autocli from YANG */
680     if (autocli_start(h, printgen) < 0)
681 	goto done;
682 
683     /* Initialize cli syntax */
684     if (cli_syntax_load(h) < 0)
685 	goto done;
686 
687     /* Set syntax mode if specified from command-line or config-file. */
688     if (clicon_option_exists(h, "CLICON_CLI_MODE"))
689 	if ((tmp = clicon_cli_mode(h)) != NULL)
690 	    if (cli_set_syntax_mode(h, tmp) == 0) {
691 		fprintf(stderr, "FATAL: Failed to set syntax mode '%s'\n", tmp);
692 		goto done;
693 	    }
694 
695     if (!cli_syntax_mode(h)){
696 	fprintf(stderr, "FATAL: No cli mode set (use -m or CLICON_CLI_MODE)\n");
697 	goto done;
698     }
699     if (cligen_ph_find(cli_cligen(h), cli_syntax_mode(h)) == NULL)
700 	clicon_log(LOG_WARNING, "No such cli mode: %s (Specify cli mode with CLICON_CLI_MODE in config file or -m <mode> on command line", cli_syntax_mode(h));
701     /* CLIgen tab mode, ie how <tab>s behave */
702     if ((tabmode = clicon_cli_tab_mode(h)) < 0){
703 	fprintf(stderr, "FATAL: CLICON_CLI_TAB_MODE not set\n");
704 	goto done;
705     }
706     cligen_tabmode_set(cli_cligen(h), tabmode);
707 
708     if (logclisyntax)
709 	cli_logsyntax_set(h, logclisyntax);
710 
711     if (dbg)
712 	clicon_option_dump(h, dbg);
713 
714     /* Join rest of argv to a single command */
715     restarg = clicon_strjoin(argc, argv, " ");
716 
717     /* If several cligen object variables match same preference, select first */
718     cligen_preference_mode_set(cli_cligen(h), 1);
719 
720     /* Call start function in all plugins before we go interactive
721      */
722     if (clixon_plugin_start_all(h) < 0)
723 	goto done;
724 
725     cligen_line_scrolling_set(cli_cligen(h), clicon_option_int(h,"CLICON_CLI_LINESCROLLING"));
726     /*! Start CLI history and load from file */
727     if (cli_history_load(h) < 0)
728 	goto done;
729     /* Experimental utf8 mode */
730     cligen_utf8_set(cli_cligen(h), clicon_option_int(h,"CLICON_CLI_UTF8"));
731     /* Launch interfactive event loop, unless -1 */
732     if (restarg != NULL && strlen(restarg)){
733 	char         *mode = cli_syntax_mode(h);
734 	cligen_result result;            /* match result */
735 	int           evalresult = 0;    /* if result == 1, calback result */
736 
737 	if (clicon_parse(h, restarg, &mode, &result, &evalresult) < 0)
738 	    goto done;
739 	if (result != 1) /* Not unique match */
740 	    goto done;
741 	if (evalresult < 0)
742 	    goto done;
743     }
744 
745     /* Go into event-loop unless -1 command-line */
746     if (!once)
747 	retval = cli_interactive(h);
748     else
749 	retval = 0;
750   done:
751     if (restarg)
752 	free(restarg);
753     // Gets in your face if we log on stderr
754     clicon_log_init(__PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
755     clicon_log(LOG_NOTICE, "%s: %u Terminated", __PROGRAM__, getpid());
756     if (h)
757 	cli_terminate(h);
758     return retval;
759 }
760