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