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, indicate
29   your decision by deleting the provisions above and replace them with the
30   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  * This file contains access functions for two types of clixon vars:
38  * - options, ie string based variables from Clixon configuration files.
39  *            Accessed with clicon_options(h).
40  * @see clixon_data.[ch] for free-type runtime get/set
41  */
42 #ifdef HAVE_CONFIG_H
43 #include "clixon_config.h" /* generated by config & autoconf */
44 #endif
45 
46 #include <stdio.h>
47 #include <string.h>
48 #include <fcntl.h>
49 #include <unistd.h>
50 #include <errno.h>
51 #include <stdlib.h>
52 #include <stdint.h>
53 #include <dirent.h>
54 #include <libgen.h> /* dirname */
55 #include <syslog.h>
56 #include <sys/types.h>
57 #include <sys/stat.h>
58 #include <sys/param.h>
59 #include <sys/socket.h>
60 
61 /* cligen */
62 #include <cligen/cligen.h>
63 
64 /* clixon */
65 #include "clixon_err.h"
66 #include "clixon_string.h"
67 #include "clixon_queue.h"
68 #include "clixon_hash.h"
69 #include "clixon_handle.h"
70 #include "clixon_file.h"
71 #include "clixon_log.h"
72 #include "clixon_yang.h"
73 #include "clixon_xml.h"
74 #include "clixon_options.h"
75 #include "clixon_data.h"
76 #include "clixon_xpath_ctx.h"
77 #include "clixon_xpath.h"
78 #include "clixon_yang_parse_lib.h"
79 #include "clixon_netconf_lib.h"
80 #include "clixon_xml_nsctx.h"
81 #include "clixon_xml_io.h"
82 #include "clixon_validate.h"
83 #include "clixon_xml_map.h"
84 
85 /* Mapping between Cli generation from Yang string <--> constants,
86    see clixon-config.yang type cli_genmodel_type */
87 static const map_str2int cli_genmodel_map[] = {
88     {"NONE",                 GT_NONE},
89     {"VARS",                 GT_VARS},
90     {"ALL",                  GT_ALL},
91     {"HIDE",                 GT_HIDE},
92     {NULL,                   -1}
93 };
94 
95 /* Mapping between Clicon startup modes string <--> constants,
96    see clixon-config.yang type startup_mode */
97 static const map_str2int startup_mode_map[] = {
98     {"none",     SM_NONE},
99     {"running",  SM_RUNNING},
100     {"startup",  SM_STARTUP},
101     {"init",     SM_INIT},
102     {NULL,       -1}
103 };
104 
105 /* Mapping between Clicon privileges modes string <--> constants,
106  * see clixon-config.yang type priv_mode */
107 static const map_str2int priv_mode_map[] = {
108     {"none",      PM_NONE},
109     {"drop_perm", PM_DROP_PERM},
110     {"drop_temp", PM_DROP_TEMP},
111     {NULL,        -1}
112 };
113 
114 
115 /* Mapping between Clicon nacm user credential string <--> constants,
116  * see clixon-config.yang type nacm_cred_mode */
117 static const map_str2int nacm_credentials_map[] = {
118     {"none",      NC_NONE},
119     {"exact",     NC_EXACT},
120     {"except",    NC_EXCEPT},
121     {NULL,        -1}
122 };
123 
124 /* Mapping between datastore cache string <--> constants,
125  * see clixon-config.yang type datastore_cache */
126 static const map_str2int datastore_cache_map[] = {
127     {"nocache",               DATASTORE_NOCACHE},
128     {"cache",                 DATASTORE_CACHE},
129     {"cache-zerocopy",        DATASTORE_CACHE_ZEROCOPY},
130     {NULL,                    -1}
131 };
132 
133 /* Mapping between regular expression type string <--> constants,
134  * see clixon-config.yang type regexp_mode */
135 static const map_str2int yang_regexp_map[] = {
136     {"posix",               REGEXP_POSIX},
137     {"libxml2",             REGEXP_LIBXML2},
138     {NULL,                 -1}
139 };
140 
141 /*! Print registry on file. For debugging.
142  * @param[in] h        Clicon handle
143  * @param[in] dbglevel Debug level
144  * @retval    0        OK
145  * @retval   -1        Error
146  * @note CLICON_FEATURE and CLICON_YANG_DIR are treated specially since they are lists
147  */
148 int
clicon_option_dump(clicon_handle h,int dbglevel)149 clicon_option_dump(clicon_handle h,
150 		   int           dbglevel)
151 {
152     int            retval = -1;
153     clicon_hash_t *hash = clicon_options(h);
154     int            i;
155     char         **keys = NULL;
156     void          *val;
157     size_t         klen;
158     size_t         vlen;
159     cxobj         *x = NULL;
160 
161     if (clicon_hash_keys(hash, &keys, &klen) < 0)
162 	goto done;
163     for(i = 0; i < klen; i++) {
164 	val = clicon_hash_value(hash, keys[i], &vlen);
165 	if (vlen){
166 	    if (((char*)val)[vlen-1]=='\0') /* assume string */
167 		clicon_debug(dbglevel, "%s =\t \"%s\"", keys[i], (char*)val);
168 	    else
169 		clicon_debug(dbglevel, "%s =\t 0x%p , length %zu", keys[i], val, vlen);
170 	}
171 	else
172 	    clicon_debug(dbglevel, "%s = NULL", keys[i]);
173     }
174     /* Next print CLICON_FEATURE and CLICON_YANG_DIR from config tree
175      * Since they are lists they are placed in the config tree.
176      */
177     x = NULL;
178     while ((x = xml_child_each(clicon_conf_xml(h), x, CX_ELMNT)) != NULL) {
179 	if (strcmp(xml_name(x), "CLICON_YANG_DIR") != 0)
180 	    continue;
181 	clicon_debug(dbglevel, "%s =\t \"%s\"", xml_name(x), xml_body(x));
182     }
183     x = NULL;
184     while ((x = xml_child_each(clicon_conf_xml(h), x, CX_ELMNT)) != NULL) {
185 	if (strcmp(xml_name(x), "CLICON_FEATURE") != 0)
186 	    continue;
187 	clicon_debug(dbglevel, "%s =\t \"%s\"", xml_name(x), xml_body(x));
188     }
189    retval = 0;
190  done:
191     if (keys)
192 	free(keys);
193     return retval;
194 }
195 
196 /*! Open and parse single config file
197  * @param[in]  filename
198  * @param[in]  yspec
199  * @param[out] xconfig   Pointer to xml config tree. Should be freed by caller
200  */
201 static int
parse_configfile_one(const char * filename,yang_stmt * yspec,cxobj ** xconfig)202 parse_configfile_one(const char *filename,
203 		     yang_stmt  *yspec,
204 		     cxobj     **xconfig)
205 {
206     int    retval = -1;
207     int    fd = -1;
208     cxobj *xt = NULL;
209     cxobj *xerr = NULL;
210     cxobj *xa;
211     cbuf  *cbret = NULL;
212     int    ret;
213 
214     if ((fd = open(filename, O_RDONLY)) < 0){
215 	clicon_err(OE_UNIX, errno, "open configure file: %s", filename);
216 	return -1;
217     }
218     clicon_debug(2, "%s: Reading config file %s", __FUNCTION__, filename);
219     if ((ret = clixon_xml_parse_file(fd, yspec?YB_MODULE:YB_NONE, yspec, NULL, &xt, &xerr)) < 0)
220 	goto done;
221     if (ret == 0){
222 	if ((cbret = cbuf_new()) ==NULL){
223 	    clicon_err(OE_XML, errno, "cbuf_new");
224 	    goto done;
225 	}
226 	if (netconf_err2cb(xerr, cbret) < 0)
227 	    goto done;
228 	/* Here one could make it more relaxing to not quit on unrecognized option? */
229 	clixon_netconf_error(xerr, NULL, NULL);
230 	goto done;
231     }
232     /* Ensure a single root */
233     if (xt == NULL || xml_child_nr(xt) != 1){
234 	clicon_err(OE_CFG, 0, "Config file %s: Lacks single top element", filename);
235 	goto done;
236     }
237     if (xml_rootchild(xt, 0, &xt) < 0)
238 	goto done;
239     /* Check well-formedness */
240     if (strcmp(xml_name(xt), "clixon-config") != 0 ||
241 	(xa = xml_find_type(xt, NULL, "xmlns", CX_ATTR)) == NULL ||
242 	strcmp(xml_value(xa), CLIXON_CONF_NS) != 0){
243 	clicon_err(OE_CFG, 0, "Config file %s: Lacks top-level \"clixon-config\" element\nClixon config files should begin with: <clixon-config xmlns=\"%s\">", filename, CLIXON_CONF_NS);
244 	goto done;
245     }
246     *xconfig = xt;
247     xt = NULL;
248     retval = 0;
249  done:
250     if (xt)
251 	xml_free(xt);
252     if (fd != -1)
253 	close(fd);
254     if (cbret)
255 	cbuf_free(cbret);
256     if (xerr)
257 	xml_free(xerr);
258     return retval;
259 }
260 
261 /*! Read filename and set values to global options registry. XML variant.
262  *
263  * @param[in]  h            Clixon handle
264  * @param[in]  filename     Main configuration file
265  * @param[in]  extraconfig0 Override (if set use that, othewrwise get from main file)
266  * @param[in]  yspec        Yang spec
267  * @param[out] xconfig      Pointer to xml config tree. Should be freed by caller
268  * @retval     0            OK
269  * @retval    -1            Error
270  */
271 static int
parse_configfile(clicon_handle h,const char * filename,char * extraconfdir0,yang_stmt * yspec,cxobj ** xconfig)272 parse_configfile(clicon_handle  h,
273 		 const char    *filename,
274 		 char          *extraconfdir0,
275 		 yang_stmt     *yspec,
276 		 cxobj        **xconfig)
277 {
278     int            retval = -1;
279     struct stat    st;
280     cxobj         *xt = NULL;
281     cxobj         *xc = NULL;
282     cxobj         *x = NULL;
283     char          *name;
284     char          *body;
285     clicon_hash_t *copt = clicon_options(h);
286     cbuf          *cbret = NULL;
287     cxobj         *xerr = NULL;
288     int            ret;
289     cvec          *nsc = NULL;
290     int            i;
291     int            ndp;
292     struct dirent *dp = NULL;
293     char           filename1[MAXPATHLEN];
294     char          *extraconfdir = NULL;
295     cxobj         *xe = NULL;
296     cxobj         *xec;
297     DIR           *dirp;
298 
299     if (filename == NULL || !strlen(filename)){
300 	clicon_err(OE_UNIX, 0, "Not specified");
301 	goto done;
302     }
303     if (stat(filename, &st) < 0){
304 	clicon_err(OE_UNIX, errno, "%s", filename);
305 	goto done;
306     }
307     if (!S_ISREG(st.st_mode)){
308 	clicon_err(OE_UNIX, 0, "%s is not a regular file", filename);
309 	goto done;
310     }
311     /* Parse main config file */
312     if (parse_configfile_one(filename, yspec, &xt) < 0)
313 	goto done;
314     /* xt is a single-rooted:  <clixon-config>...</clixon-config>
315      * If no override (eg from command-line)
316      * Bootstrap: Shortcut to read extra confdir inline */
317     if ((extraconfdir = extraconfdir0) == NULL)
318 	if ((xc = xpath_first(xt, 0, "CLICON_CONFIGDIR")) != NULL)
319 	    extraconfdir = xml_body(xc);
320     if (extraconfdir){ /* If extra dir, parse extra config files */
321 	/* A check it exists (also done in clicon_file_dirent) */
322 	if ((dirp = opendir(extraconfdir)) == NULL) {
323 	    clicon_err(OE_UNIX, errno, "CLICON_CONFIGDIR: %s opendir", extraconfdir);
324 	    goto done;
325 	}
326 	closedir(dirp);
327 	if((ndp = clicon_file_dirent(extraconfdir, &dp, NULL, S_IFREG)) < 0)  /* Read dir */
328 	    goto done;
329 	/* Loop through files */
330 	for (i = 0; i < ndp; i++){
331 	    snprintf(filename1, sizeof(filename1), "%s/%s", extraconfdir, dp[i].d_name);
332 	    if (parse_configfile_one(filename1, yspec, &xe) < 0)
333 		goto done;
334 	    /* Drain objects from extrafile and replace/append to main */
335 	    while ((xec = xml_child_i_type(xe, 0, CX_ELMNT)) != NULL) {
336 		name = xml_name(xec);
337 		body = xml_body(xec);
338 		/* Ignored from file due to bootstrapping */
339 		if (strcmp(name,"CLICON_CONFIGFILE")==0)
340 		    continue;
341 		/* List options for configure options that are leaf-lists: append to main */
342 		if (strcmp(name,"CLICON_FEATURE")==0 ||
343 		    strcmp(name,"CLICON_YANG_DIR")==0){
344 		    if (xml_addsub(xt, xec) < 0)
345 			goto done;
346 		    continue;
347 		}
348 		/* Remove existing in master if any */
349 		if ((x = xml_find_type(xt, NULL, name, CX_ELMNT)) != NULL)
350 		    xml_purge(x);
351 		/* Append to master (removed from xe) */
352 		if (xml_addsub(xt, xec) < 0)
353 		    goto done;
354 	    }
355 	    if (xe)
356 		xml_free(xe);
357 	    xe = NULL;
358 	}
359     }
360     if (xml_default_recurse(xt, 0) < 0)
361 	goto done;
362     if ((ret = xml_yang_validate_add(h, xt, &xerr)) < 0)
363 	goto done;
364     if (ret == 0){
365 	if ((cbret = cbuf_new()) ==NULL){
366 	    clicon_err(OE_XML, errno, "cbuf_new");
367 	    goto done;
368 	}
369 	if (netconf_err2cb(xerr, cbret) < 0)
370 	    goto done;
371 	clicon_err(OE_CFG, 0, "Config file validation: %s", cbuf_get(cbret));
372 	goto done;
373     }
374     x = NULL;
375     while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
376 	name = xml_name(x);
377 	body = xml_body(x);
378 	if (name == NULL || body == NULL){
379 	    clicon_log(LOG_WARNING, "%s option NULL: name:%s body:%s",
380 		       __FUNCTION__, name, body);
381 	    continue;
382 	}
383 	/* Ignored from file due to bootstrapping */
384 	if (strcmp(name,"CLICON_CONFIGFILE")==0)
385 	    continue;
386 	/* List options for configure options that are leaf-lists (not leaf)
387 	 * They must be accessed directly by looping over clicon_conf_xml(h)
388 	 */
389 	if (strcmp(name,"CLICON_FEATURE")==0)
390 	    continue;
391 	if (strcmp(name,"CLICON_YANG_DIR")==0)
392 	    continue;
393 
394 	if (clicon_hash_add(copt,
395 			    name,
396 			    body,
397 			    strlen(body)+1) == NULL)
398 	    goto done;
399     }
400     retval = 0;
401     *xconfig = xt;
402     xt = NULL;
403  done:
404     if (dp)
405 	free(dp);
406     if (nsc)
407 	xml_nsctx_free(nsc);
408     if (cbret)
409 	cbuf_free(cbret);
410     if (xerr)
411 	xml_free(xerr);
412     if (xt)
413 	xml_free(xt);
414     return retval;
415 }
416 
417 /*! Add configuration option overriding file setting
418  * Add to clicon_options hash, and to clicon_conf_xml tree
419  * Assumes clicon_conf_xml_set has been called
420  * @param[in]  h      Clicon handle
421  * @param[in]  name   Name of configuration option (see clixon-config.yang)
422  * @param[in]  value  String value
423  * @retval     0      OK
424  * @retval    -1      Error
425  * @see clicon_options_main  For loading options from file
426  */
427 int
clicon_option_add(clicon_handle h,const char * name,char * value)428 clicon_option_add(clicon_handle h,
429 		  const char   *name,
430 		  char         *value)
431 {
432     int            retval = -1;
433     clicon_hash_t *copt = clicon_options(h);
434     cxobj         *x;
435 
436     if (strcmp(name, "CLICON_FEATURE")==0 ||
437 	strcmp(name, "CLICON_YANG_DIR")==0){
438 	if ((x = clicon_conf_xml(h)) == NULL){
439 	    clicon_err(OE_UNIX, ENOENT, "option %s not found (clicon_conf_xml_set has not been called?)", name);
440 	    goto done;
441 	}
442 	if (clixon_xml_parse_va(YB_NONE, NULL, &x, NULL, "<%s>%s</%s>",
443 				name, value, name) < 0)
444 	    goto done;
445     }
446     if (clicon_hash_add(copt,
447 		 name,
448 		 value,
449 		 strlen(value)+1) == NULL)
450 	goto done;
451     retval = 0;
452  done:
453     return retval;
454 }
455 
456 /*! Parse clixon yang file. Parse XML config file. Initialize option values
457  *
458  * Set default options, Read config-file, Check that all values are set.
459  * Parse clixon yang file and save in yspec.
460  * Read clixon system config files
461  * @param[in]  h     clicon handle
462  * @param[in]  yspec Yang spec of clixon config file
463  * @note Due to Bug: Top-level Yang symbol cannot be called "config" in any
464  *       imported yang file, the config module needs to be isolated from all
465  *       other yang modules.
466  */
467 int
clicon_options_main(clicon_handle h)468 clicon_options_main(clicon_handle h)
469 {
470     int            retval = -1;
471     char          *configfile;
472     clicon_hash_t *copt = clicon_options(h);
473     char          *suffix;
474     char           xml = 0; /* Configfile is xml, otherwise legacy */
475     cxobj         *xconfig = NULL;
476     yang_stmt     *yspec = NULL;
477     char          *extraconfdir = NULL;
478 
479     /* Create configure yang-spec */
480     if ((yspec = yspec_new()) == NULL)
481 	goto done;
482     /*
483      * Set configure file if not set by command-line above
484      */
485     if (!clicon_hash_lookup(copt, "CLICON_CONFIGFILE")){
486 	clicon_option_str_set(h, "CLICON_CONFIGFILE", CLIXON_DEFAULT_CONFIG);
487     }
488     configfile = clicon_hash_value(copt, "CLICON_CONFIGFILE", NULL);
489     clicon_debug(1, "CLICON_CONFIGFILE=%s", configfile);
490     /* File must end with .xml */
491     if ((suffix = rindex(configfile, '.')) != NULL){
492 	suffix++;
493 	xml = strcmp(suffix, "xml") == 0;
494     }
495     if (xml == 0){
496 	clicon_err(OE_CFG, 0, "%s: suffix %s not recognized", configfile, suffix);
497 	goto done;
498     }
499 
500     /* Override extraconfdir */
501     if (clicon_option_str(h, "CLICON_CONFIGDIR") &&
502 	(extraconfdir = strdup(clicon_option_str(h, "CLICON_CONFIGDIR"))) == NULL){
503 	clicon_err(OE_UNIX, errno, "strdup");
504 	goto done;
505     }
506 
507     /* Read configfile first without yangspec, and without extra config dir for bootstrapping,
508      * see second time below with proper yangspec and extra config dir
509      * (You need to read the config-file to get the YANG_DIR to find the clixon yang-spec)
510      * Difference from parsing with yangspec is:
511      * - no default values
512      * - no sanity checks
513      * - no extra config dir
514      */
515     if (parse_configfile(h, configfile, extraconfdir, NULL, &xconfig) < 0)
516 	goto done;
517 
518     clicon_conf_xml_set(h, xconfig);
519 
520     /* Parse clixon yang spec */
521     if (yang_spec_parse_module(h, "clixon-config", NULL, yspec) < 0)
522 	goto done;
523     clicon_conf_xml_set(h, NULL);
524     if (xconfig){
525 	xml_free(xconfig);
526 	xconfig = NULL;
527     }
528 
529     /* Read configfile second time now with check yang spec */
530     if (parse_configfile(h, configfile, extraconfdir, yspec, &xconfig) < 0)
531 	goto done;
532     if (xml_spec(xconfig) == NULL){
533 	clicon_err(OE_CFG, 0, "Config file %s: did not find corresponding Yang specification\nHint: File does not begin with: <clixon-config xmlns=\"%s\"> or clixon-config.yang not found?", configfile, CLIXON_CONF_NS);
534 	goto done;
535     }
536     /* Set yang config spec (must store to free at exit, since conf_xml below uses it) */
537     if (clicon_config_yang_set(h, yspec) < 0)
538        goto done;
539     yspec = NULL;
540     /* Set clixon_conf pointer to handle */
541     if (clicon_conf_xml_set(h, xconfig) < 0)
542 	goto done;
543 
544     retval = 0;
545  done:
546     if (yspec)
547 	ys_free(yspec);
548     if (extraconfdir)
549 	free(extraconfdir);
550     return retval;
551 }
552 
553 /*! Check if a clicon option has a value
554  * @param[in] h     clicon_handle
555  * @param[in] name  option name
556  * @retval  !=0     option exists
557  * @retval    0     option does not exist
558  */
559 int
clicon_option_exists(clicon_handle h,const char * name)560 clicon_option_exists(clicon_handle h,
561 		     const char   *name)
562 {
563     clicon_hash_t *copt = clicon_options(h);
564 
565     return (clicon_hash_lookup(copt, (char*)name) != NULL);
566 }
567 
568 /*! Get a single string option string via handle
569  *
570  * @param[in] h       clicon_handle
571  * @param[in] name    option name
572  * @retval    NULL    If option not found, or value of option is NULL
573  * @retval    string  value of option if found
574  * clicon options should be strings.
575  * @note To differentiate the two reasons why NULL may be returned, use function
576  * clicon_option_exists() before the call
577  */
578 char *
clicon_option_str(clicon_handle h,const char * name)579 clicon_option_str(clicon_handle h,
580 		  const char   *name)
581 {
582     clicon_hash_t *copt = clicon_options(h);
583 
584     if (clicon_hash_lookup(copt, (char*)name) == NULL)
585 	return NULL;
586     return clicon_hash_value(copt, (char*)name, NULL);
587 }
588 
589 /*! Set a single string option via handle
590  * @param[in] h       clicon_handle
591  * @param[in] name    option name
592  * @param[in] val     option value, must be null-terminated string
593  * @retval    0       OK
594  * @retval   -1       Error
595  */
596 int
clicon_option_str_set(clicon_handle h,const char * name,char * val)597 clicon_option_str_set(clicon_handle h,
598 		      const char   *name,
599 		      char         *val)
600 {
601     clicon_hash_t *copt = clicon_options(h);
602 
603     return clicon_hash_add(copt, (char*)name, val, strlen(val)+1)==NULL?-1:0;
604 }
605 
606 /*! Get options as integer but stored as string
607  *
608  * @param[in] h    clicon handle
609  * @param[in] name name of option
610  * @retval    int  An integer as a result of atoi
611  * @retval    -1   If option does not exist
612  * @code
613  *  if (clicon_option_exists(h, "X"))
614  *	return clicon_option_int(h, "X");
615  *  else
616  *      return 0;
617  * @endcode
618  * Note that -1 can be both error and value.
619  * This means that it should be used together with clicon_option_exists() and
620  * supply a default value as shown in the example.
621  */
622 int
clicon_option_int(clicon_handle h,const char * name)623 clicon_option_int(clicon_handle h,
624 		  const char   *name)
625 {
626     char *s;
627 
628     if ((s = clicon_option_str(h, name)) == NULL)
629 	return -1;
630     return atoi(s);
631 }
632 
633 /*! Set option given as int.
634  * @param[in] h     Clicon handle
635  * @param[in] name  Name of option to set
636  * @param[in] val   Integer value
637  */
638 int
clicon_option_int_set(clicon_handle h,const char * name,int val)639 clicon_option_int_set(clicon_handle h,
640 		      const char   *name,
641 		      int           val)
642 {
643     char s[64];
644 
645     if (snprintf(s, sizeof(s)-1, "%u", val) < 0)
646 	return -1;
647     return clicon_option_str_set(h, name, s);
648 }
649 
650 /*! Get options as bool but stored as string
651  *
652  * @param[in] h    Clicon handle
653  * @param[in] name name of option
654  * @retval    0    false, or does not exist, or does not have a boolean value
655  * @retval    1    true
656  * @code
657  *  if (clicon_option_exists(h, "X")
658  *	return clicon_option_bool(h, "X");
659  *  else
660  *      return 0; # default false?
661  * @endcode
662  * Note that 0 can be both error and false.
663  * This means that it should be used together with clicon_option_exists() and
664  * supply a default value as shown in the example.
665  */
666 int
clicon_option_bool(clicon_handle h,const char * name)667 clicon_option_bool(clicon_handle h,
668 		   const char   *name)
669 {
670     char *s;
671 
672     if ((s = clicon_option_str(h, name)) == NULL)
673 	return 0;
674     if (strcmp(s,"true")==0)
675 	return 1;
676     if (strcmp(s,"1")==0)
677 	return 1;
678     return 0; /* Hopefully false, but anything else than "true" or "one" */
679 }
680 
681 /*! Set option given as bool
682  * @param[in] h     Clicon handle
683  * @param[in] name  Name of option to set
684  * @param[in] val   Boolean value, 0 or 1
685  */
686 int
clicon_option_bool_set(clicon_handle h,const char * name,int val)687 clicon_option_bool_set(clicon_handle h,
688 		      const char   *name,
689 		      int           val)
690 {
691     char s[64];
692 
693     if (val != 0 && val != 1){
694 	clicon_err(OE_CFG, EINVAL, "val is %d, 0 or 1 expected", val);
695 	return -1;
696     }
697     if (snprintf(s, sizeof(s)-1, "%s", val?"true":"false") < 0){
698 	clicon_err(OE_CFG, errno, "snprintf");
699 	return -1;
700     }
701     return clicon_option_str_set(h, name, s);
702 }
703 
704 /*! Delete option
705  * @param[in] h     Clicon handle
706  * @param[in] name  Name of option to delete
707  */
708 int
clicon_option_del(clicon_handle h,const char * name)709 clicon_option_del(clicon_handle h,
710 		  const char   *name)
711 {
712     clicon_hash_t *copt = clicon_options(h);
713 
714     return clicon_hash_del(copt, (char*)name);
715 }
716 
717 /*-----------------------------------------------------------------
718  * Specific option access functions for YANG configuration variables.
719  * Sometimes overridden by command-line options,
720  * such as -f for CLICON_CONFIGFILE
721  * @see yang/clixon-config@<date>.yang
722  * You can always use the basic access functions, such as
723  * clicon_option_str[_set]
724  * But sometimes there are type conversions, etc which makes it more
725  * convenient to make wrapper functions. Or not?
726  *-----------------------------------------------------------------*/
727 /*! Whether to generate CLIgen syntax from datamodel or not (0, 1 or 2)
728  * Must be used with a previous clicon_option_exists().
729  * @param[in] h     Clicon handle
730  * @retval    flag  If set, generate CLI code from yang model, otherwise not
731  * @see clixon-config@<date>.yang CLICON_CLI_GENMODEL
732  */
733 int
clicon_cli_genmodel(clicon_handle h)734 clicon_cli_genmodel(clicon_handle h)
735 {
736     char const *opt = "CLICON_CLI_GENMODEL";
737 
738     if (clicon_option_exists(h, opt))
739 	return clicon_option_int(h, opt);
740     else
741 	return 0;
742 }
743 
744 /*! Generate code for CLI completion of existing db symbols
745  * @param[in] h     Clicon handle
746  * @retval    flag  If set, generate auto-complete CLI specs
747  * @see clixon-config@<date>.yang CLICON_CLI_GENMODEL_COMPLETION
748  */
749 int
clicon_cli_genmodel_completion(clicon_handle h)750 clicon_cli_genmodel_completion(clicon_handle h)
751 {
752     char const *opt = "CLICON_CLI_GENMODEL_COMPLETION";
753 
754     if (clicon_option_exists(h, opt))
755 	return clicon_option_int(h, opt);
756     else
757 	return 0;
758 }
759 
760 /*! How to generate and show CLI syntax: VARS|ALL
761  * @param[in] h     Clicon handle
762  * @retval    mode
763  * @see clixon-config@<date>.yang CLICON_CLI_GENMODEL_TYPE
764  */
765 enum genmodel_type
clicon_cli_genmodel_type(clicon_handle h)766 clicon_cli_genmodel_type(clicon_handle h)
767 {
768     char *str;
769 
770     if ((str = clicon_option_str(h, "CLICON_CLI_GENMODEL_TYPE")) == NULL)
771 	return GT_VARS;
772     else
773 	return clicon_str2int(cli_genmodel_map, str);
774 }
775 
776 /*! Get "do not include keys in cvec" in cli vars callbacks
777  * @param[in] h     Clicon handle
778  * @retval    flag  If set, get only vars
779  * @see clixon-config@<date>.yang CLICON_CLI_VARONLY
780  */
781 int
clicon_cli_varonly(clicon_handle h)782 clicon_cli_varonly(clicon_handle h)
783 {
784     char const *opt = "CLICON_CLI_VARONLY";
785 
786     if (clicon_option_exists(h, opt))
787 	return clicon_option_int(h, opt);
788     else
789 	return 0;
790 }
791 
792 /*! Get family of backend socket: AF_UNIX, AF_INET or AF_INET6
793  * @see clixon-config@<date>.yang CLICON_SOCK_FAMILY
794  * @param[in] h     Clicon handle
795  * @retval    fam   Socket family
796  */
797 int
clicon_sock_family(clicon_handle h)798 clicon_sock_family(clicon_handle h)
799 {
800     char *s;
801 
802     if ((s = clicon_option_str(h, "CLICON_SOCK_FAMILY")) == NULL)
803 	return AF_UNIX;
804     else  if (strcmp(s, "IPv4")==0)
805 	return AF_INET;
806     else  if (strcmp(s, "IPv6")==0)
807 	return AF_INET6;
808     else
809 	return AF_UNIX; /* default */
810 }
811 
812 /*! Get port for backend socket in case of AF_INET or AF_INET6
813  * @param[in] h     Clicon handle
814  * @retval    port  Socket port
815  * @see clixon-config@<date>.yang CLICON_SOCK_PORT
816  */
817 int
clicon_sock_port(clicon_handle h)818 clicon_sock_port(clicon_handle h)
819 {
820     char *s;
821 
822     if ((s = clicon_option_str(h, "CLICON_SOCK_PORT")) == NULL)
823 	return -1;
824     return atoi(s);
825 }
826 
827 /*! Set if all configuration changes are committed automatically
828  * @param[in] h     Clicon handle
829  * @retval    flag  Autocommit (or not)
830  */
831 int
clicon_autocommit(clicon_handle h)832 clicon_autocommit(clicon_handle h)
833 {
834     char const *opt = "CLICON_AUTOCOMMIT";
835 
836     if (clicon_option_exists(h, opt))
837 	return clicon_option_int(h, opt);
838     else
839 	return 0;
840 }
841 
842 /*! Which method to boot/start clicon backend
843  * @param[in] h     Clicon handle
844  * @retval    mode  Startup mode
845  */
846 int
clicon_startup_mode(clicon_handle h)847 clicon_startup_mode(clicon_handle h)
848 {
849     char *mode;
850 
851     if ((mode = clicon_option_str(h, "CLICON_STARTUP_MODE")) == NULL)
852 	return -1;
853     return clicon_str2int(startup_mode_map, mode);
854 }
855 
856 /*! Which privileges drop method to use
857  * @param[in] h     Clicon handle
858  * @retval    mode  Privileges mode
859  */
860 enum priv_mode_t
clicon_backend_privileges_mode(clicon_handle h)861 clicon_backend_privileges_mode(clicon_handle h)
862 {
863     char *mode;
864 
865     if ((mode = clicon_option_str(h, "CLICON_BACKEND_PRIVILEGES")) == NULL)
866 	return -1;
867     return clicon_str2int(priv_mode_map, mode);
868 }
869 
870 /*! Which privileges drop method to use
871  * @param[in] h     Clicon handle
872  * @retval    mode  Privileges mode
873  */
874 enum nacm_credentials_t
clicon_nacm_credentials(clicon_handle h)875 clicon_nacm_credentials(clicon_handle h)
876 {
877     char *mode;
878 
879     if ((mode = clicon_option_str(h, "CLICON_NACM_CREDENTIALS")) == NULL)
880 	return -1;
881     return clicon_str2int(nacm_credentials_map, mode);
882 }
883 
884 /*! Which datastore cache method to use
885  * @param[in] h      Clicon handle
886  * @retval    method Datastore cache method
887  * @see clixon-config@<date>.yang CLICON_DATASTORE_CACHE
888  */
889 enum datastore_cache
clicon_datastore_cache(clicon_handle h)890 clicon_datastore_cache(clicon_handle h)
891 {
892     char *str;
893 
894     if ((str = clicon_option_str(h, "CLICON_DATASTORE_CACHE")) == NULL)
895 	return DATASTORE_CACHE;
896     else
897 	return clicon_str2int(datastore_cache_map, str);
898 }
899 
900 /*! Which Yang regexp/pattern engine to use
901  * @param[in] h     Clicon handle
902  * @retval    mode  Regexp engine to use
903  * @see clixon-config@<date>.yang CLICON_YANG_REGEXP
904  */
905 enum regexp_mode
clicon_yang_regexp(clicon_handle h)906 clicon_yang_regexp(clicon_handle h)
907 {
908     char *str;
909 
910     if ((str = clicon_option_str(h, "CLICON_YANG_REGEXP")) == NULL)
911 	return REGEXP_POSIX;
912     else
913 	return clicon_str2int(yang_regexp_map, str);
914 }
915 
916 /*---------------------------------------------------------------------
917  * Specific option access functions for non-yang options
918  * Typically dynamic values and more complex datatypes,
919  * Such as handles to plugins, API:s and parsed structures
920  *--------------------------------------------------------------------*/
921 
922 /*! Get quiet mode eg -q option, do not print notifications on stdout
923  * @param[in] h      Clicon handle
924  * @retval    flag   quiet mode on or off
925  */
926 int
clicon_quiet_mode(clicon_handle h)927 clicon_quiet_mode(clicon_handle h)
928 {
929     char *s;
930     if ((s = clicon_option_str(h, "CLICON_QUIET")) == NULL)
931 	return 0; /* default */
932     return atoi(s);
933 }
934 
935 /*! Set quiet mode
936  * @param[in] h      Clicon handle
937  * @param[in] val    Flag value
938  */
939 int
clicon_quiet_mode_set(clicon_handle h,int val)940 clicon_quiet_mode_set(clicon_handle h,
941 		      int           val)
942 {
943     return clicon_option_int_set(h, "CLICON_QUIET", val);
944 }
945 
946