1 /*
2  *
3   ***** BEGIN LICENSE BLOCK *****
4 
5   Copyright (C) 2009-2019 Olof Hagsand
6   Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
7 
8   This file is part of CLIXON.
9 
10   Licensed under the Apache License, Version 2.0 (the "License");
11   you may not use this file except in compliance with the License.
12   You may obtain a copy of the License at
13 
14     http://www.apache.org/licenses/LICENSE-2.0
15 
16   Unless required by applicable law or agreed to in writing, software
17   distributed under the License is distributed on an "AS IS" BASIS,
18   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19   See the License for the specific language governing permissions and
20   limitations under the License.
21 
22   Alternatively, the contents of this file may be used under the terms of
23   the GNU General Public License Version 3 or later (the "GPL"),
24   in which case the provisions of the GPL are applicable instead
25   of those above. If you wish to allow use of your version of this file only
26   under the terms of the GPL, and not to allow others to
27   use your version of this file under the terms of Apache License version 2,
28   indicate your decision by deleting the provisions above and replace them with
29   the  notice and other provisions required by the GPL. If you do not delete
30   the provisions above, a recipient may use your version of this file under
31   the terms of any one of the Apache License version 2 or the GPL.
32 
33   ***** END LICENSE BLOCK *****
34 
35  * Yang functions
36  * @see https://tools.ietf.org/html/rfc6020 YANG 1.0
37  * @see https://tools.ietf.org/html/rfc7950 YANG 1.1
38  *
39  * CALLING ORDER OF YANG PARSE FILES
40  * =================================
41  *                                      yang_spec_parse_module
42  *                                     |                       |
43  *                                     v                       v   v
44  * yang_spec_parse_file-> yang_parse_post->yang_parse_recurse->yang_parse_module
45  *                    \   /                                         v
46  * yang_spec_load_dir ------------------------------------> yang_parse_filename
47  *                                                                 v
48  *                                                          yang_parse_file
49  *                                                                 v
50  *                                                          yang_parse_str
51  */
52 
53 #ifdef HAVE_CONFIG_H
54 #include "clixon_config.h" /* generated by config & autoconf */
55 #endif
56 
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <errno.h>
60 #include <limits.h>
61 #include <ctype.h>
62 #include <unistd.h>
63 #include <string.h>
64 #include <arpa/inet.h>
65 #include <regex.h>
66 #include <dirent.h>
67 #include <sys/types.h>
68 #include <fcntl.h>
69 #include <syslog.h>
70 #include <assert.h>
71 #include <libgen.h>
72 #include <sys/stat.h>
73 #include <sys/param.h>
74 #include <netinet/in.h>
75 #include <libgen.h>
76 
77 /* cligen */
78 #include <cligen/cligen.h>
79 
80 /* clicon */
81 #include "clixon_log.h"
82 #include "clixon_err.h"
83 #include "clixon_string.h"
84 #include "clixon_queue.h"
85 #include "clixon_hash.h"
86 #include "clixon_handle.h"
87 #include "clixon_file.h"
88 #include "clixon_yang.h"
89 #include "clixon_yang_internal.h"
90 #include "clixon_hash.h"
91 #include "clixon_xml.h"
92 #include "clixon_xml_nsctx.h"
93 #include "clixon_xpath_ctx.h"
94 #include "clixon_xpath.h"
95 #include "clixon_yang_module.h"
96 #include "clixon_plugin.h"
97 #include "clixon_data.h"
98 #include "clixon_options.h"
99 #include "clixon_yang_type.h"
100 #include "clixon_yang_parse.h"
101 #include "clixon_yang_cardinality.h"
102 #include "clixon_yang_parse_lib.h"
103 
104 /* Size of json read buffer when reading from file*/
105 #define BUFLEN 1024
106 
107 /*! Resolve a grouping name from a module, includes looking in submodules
108  */
109 static yang_stmt *
ys_grouping_module_resolve(yang_stmt * ymod,yang_stmt * yspec,char * name)110 ys_grouping_module_resolve(yang_stmt *ymod,
111 			   yang_stmt *yspec,
112 			   char      *name)
113 {
114     yang_stmt *yinc;
115     yang_stmt *ysubm;
116     yang_stmt *yrealmod = NULL;
117     yang_stmt *ygrouping = NULL;
118     char      *submname;
119 
120     /* Find grouping from own sub/module */
121     if ((ygrouping = yang_find(ymod, Y_GROUPING, name)) != NULL)
122 	goto done;
123     /* Find top-level module */
124     if (ys_real_module(ymod, &yrealmod) < 0)
125 	goto done;
126     if (yrealmod == ymod) /* skip if module, continue if submodule */
127 	goto done;
128     /* Find grouping from real module */
129     if ((ygrouping = yang_find(yrealmod, Y_GROUPING, name)) != NULL)
130 	goto done;
131     /* Find grouping from sub-modules */
132     yinc = NULL;
133     while ((yinc = yn_each(yrealmod, yinc)) != NULL){
134 	if (yang_keyword_get(yinc) != Y_INCLUDE)
135 	    continue;
136 	submname = yang_argument_get(yinc);
137 	if ((ysubm = yang_find_module_by_name(yspec, submname)) == NULL)
138 	    continue;
139 	if (ysubm == ymod)
140 	    continue;
141 	if ((ygrouping = yang_find(ysubm, Y_GROUPING, name)) != NULL)
142 	    break;
143     }
144  done:
145     return ygrouping;
146 }
147 
148 /*! Resolve a grouping name from a point in the yang tree
149  * @param[in]  ys         Yang statement of "uses" statement doing the lookup
150  * @param[in]  prefix     Prefix of grouping to look for
151  * @param[in]  name       Name of grouping to look for
152  * @param[out] ygrouping0 A found grouping yang structure as result
153  * @retval     0          OK, ygrouping may be NULL
154  * @retval    -1          Error, with clicon_err called
155  */
156 static int
ys_grouping_resolve(yang_stmt * yuses,char * prefix,char * name,yang_stmt ** ygrouping0)157 ys_grouping_resolve(yang_stmt  *yuses,
158 		    char       *prefix,
159 		    char       *name,
160 		    yang_stmt **ygrouping0)
161 {
162     int             retval = -1;
163     yang_stmt      *ymod;
164     yang_stmt      *ygrouping = NULL;
165     yang_stmt      *yp;
166     yang_stmt      *ys;
167     yang_stmt      *yspec;
168     enum rfc_6020   keyw;
169 
170     yspec = ys_spec(yuses);
171     /* find the grouping associated with argument and expand(?) */
172     if (prefix){ /* Go to top and find import that matches */
173 	if ((ymod = yang_find_module_by_prefix(yuses, prefix)) != NULL)
174 	    ygrouping = ys_grouping_module_resolve(ymod, yspec, name);
175     }
176     else {
177 	ys = yuses; 	    /* Check upwards in hierarchy for matching groupings */
178 	while (1){
179 	    if ((yp = yang_parent_get(ys)) == NULL)
180 		break;
181 	    if ((keyw = yang_keyword_get(yp)) == Y_SPEC)
182 		break;
183 	    else if (keyw == Y_MODULE || keyw == Y_SUBMODULE){ /* Try submodules */
184 		ygrouping = ys_grouping_module_resolve(yp, yspec, name);
185 		break;
186 	    }
187 	    else if ((ygrouping = yang_find(yp, Y_GROUPING, name)) != NULL) /* Here find grouping */
188 		break;
189 	    ys = (yang_stmt*)yp; 	    /* Proceed to next level */
190 	}
191     }
192     *ygrouping0 = ygrouping;
193     retval = 0;
194     // done:
195     return retval;
196 }
197 
198 /*! This is an augment node, augment the original datamodel.
199  * @param[in] ys    The augment statement
200  * @param[in] yspec Yang specification
201  * @see RFC7950 Sec 7.17
202  * The target node MUST be either a container, list, choice, case, input,
203  * output, or notification node.
204  * If the "augment" statement is on the top level the absolute form MUST be
205  * used.
206  * All data nodes defined in the "augment" statement are defined as XML
207  * elements in the XML namespace of the module where the "augment" is
208  * specified.
209  *
210  * @note If the augment has a when statement, which is commonplace, the when statement is not copied as
211  * datanodes are, since it should not apply to the target node. Instead it is added as a special "when"
212  * struct to the yang statements being inserted.
213  */
214 static int
yang_augment_node(yang_stmt * ys)215 yang_augment_node(yang_stmt *ys)
216 {
217     int           retval = -1;
218     char         *schema_nodeid;
219     yang_stmt    *ytarget = NULL;
220     yang_stmt    *yc0;
221     yang_stmt    *yc;
222     yang_stmt    *ymod;
223     yang_stmt    *ywhen;
224     char         *wxpath = NULL; /* xpath of when statement */
225     cvec         *wnsc = NULL;   /* namespace context of when statement */
226     enum rfc_6020 targetkey;
227     enum rfc_6020 childkey;
228 
229     if ((ymod = ys_module(ys)) == NULL){
230 	clicon_err(OE_YANG, 0, "My yang module not found");
231 	goto done;
232     }
233     /* */
234     schema_nodeid = yang_argument_get(ys);
235     clicon_debug(2, "%s %s", __FUNCTION__, schema_nodeid);
236     /* Find the target */
237     if (yang_abs_schema_nodeid(ys, schema_nodeid, &ytarget) < 0)
238 	goto done;
239 
240     if (ytarget == NULL){
241 #if 0 /* Lots of yang models fail here */
242 	clicon_err(OE_YANG, 0, "Augment failed in module %s: target node %s not found",
243 		   yang_argument_get(ys_module(ys)),
244 		   schema_nodeid);
245 	goto done;
246 #else
247 	clicon_log(LOG_WARNING, "Warning: Augment failed in module %s: target node %s not found",
248 		   yang_argument_get(ys_module(ys)),
249 		   schema_nodeid);
250 	goto ok;
251 #endif
252     }
253     /* The target node MUST be either a container, list, choice, case, input, output, or notification node.
254      * which means it is slightly different than a schema-nodeid ? */
255     targetkey = yang_keyword_get(ytarget);
256     if (targetkey != Y_CONTAINER && targetkey != Y_LIST && targetkey != Y_CHOICE &&
257 	targetkey != Y_CASE && targetkey != Y_INPUT &&
258 	targetkey != Y_OUTPUT && targetkey != Y_NOTIFICATION){
259 	clicon_log(LOG_WARNING, "Warning: Augment failed in module %s: target node %s has wrong type %s",
260 		   yang_argument_get(ys_module(ys)),
261 		   schema_nodeid,
262 		   yang_key2str(targetkey));
263 	goto ok;
264     }
265 
266     /* Find when statement, if present */
267     if ((ywhen = yang_find(ys, Y_WHEN, NULL)) != NULL){
268 	wxpath = yang_argument_get(ywhen);
269 	if (xml_nsctx_yang(ywhen, &wnsc) < 0)
270 	    goto done;
271     }
272     /* Extend ytarget with ys' schemanode children */
273     yc0 = NULL;
274     while ((yc0 = yn_each(ys, yc0)) != NULL) {
275 	if (!yang_schemanode(yc0))
276 	    continue;
277 	childkey = yang_keyword_get(yc0);
278 	switch (targetkey){
279 	case Y_CONTAINER:
280 	case Y_LIST:
281 	    /* If the target node is a container or list node, the "action" and
282 	       "notification" statements can be used within the "augment" statement.
283 	    */
284 	    if (childkey != Y_ACTION && childkey != Y_NOTIFICATION &&
285 		childkey != Y_CONTAINER && childkey != Y_LEAF && childkey != Y_LIST &&
286 		childkey != Y_LEAF_LIST && childkey != Y_USES && childkey != Y_CHOICE){
287 		clicon_log(LOG_WARNING, "Warning: A Augment failed in module %s: node %s %d cannot be added to target node %s",
288 			   yang_argument_get(ys_module(ys)),
289 			   yang_key2str(childkey),
290 			   childkey,
291 			   schema_nodeid);
292 		goto ok;
293 	    }
294 	    break;
295 	case Y_CASE:
296 	case Y_INPUT:
297 	case Y_OUTPUT:
298 	case Y_NOTIFICATION:
299 	    /* If the target node is a container, list, case, input, output, or
300 	       notification node, the "container", "leaf", "list", "leaf-list",
301 	       "uses", and "choice" statements can be used within the "augment"
302 	       statement. */
303 	    if (childkey != Y_CONTAINER && childkey != Y_LEAF && childkey != Y_LIST &&
304 		childkey != Y_LEAF_LIST && childkey != Y_USES && childkey != Y_CHOICE){
305 		clicon_log(LOG_WARNING, "Warning: B Augment failed in module %s: node %s %d cannot be added to target node %s",
306 			   yang_argument_get(ys_module(ys)),
307 			   yang_key2str(childkey),
308 			   childkey,
309 			   schema_nodeid);
310 		goto ok;
311 	    }
312 	    break;
313 	case Y_CHOICE:
314 	    /* If the target node is a choice node, the "case" statement or a
315 	       shorthand "case" statement (see Section 7.9.2) can be used within the
316 	       "augment" statement.
317 	       XXX could be more or less anything?
318 	       As a shorthand, the "case" statement can be omitted if the branch
319 	       contains a single "anydata", "anyxml", "choice", "container", "leaf",
320 	       "list", or "leaf-list" statement.
321 	    */
322 	    if (childkey != Y_CASE && childkey != Y_ANYDATA && childkey != Y_ANYXML &&
323 		childkey != Y_CHOICE && childkey != Y_CONTAINER && childkey != Y_LEAF &&
324 		childkey != Y_LIST && childkey != Y_LEAF_LIST){
325 
326 		clicon_log(LOG_WARNING, "Warning: C Augment failed in module %s: node %s %d cannot be added to target node %s",
327 			   yang_argument_get(ys_module(ys)),
328 			   yang_key2str(childkey),
329 			   childkey,
330 			   schema_nodeid);
331 		goto ok;
332 	    }
333 	    break;
334 	default:
335 	    break;
336 	}
337 
338 	if ((yc = ys_dup(yc0)) == NULL)
339 	    goto done;
340 	yc->ys_mymodule = ymod;
341 
342 	if (yn_insert(ytarget, yc) < 0)
343 	    goto done;
344 	/* If there is an associated when statement, add a special when struct to the yang */
345 	if (ywhen){
346 	    if (yang_when_xpath_set(yc, wxpath) < 0)
347 		goto done;
348 	    if (yang_when_nsc_set(yc, wnsc) < 0)
349 		goto done;
350 	}
351     }
352  ok:
353    retval = 0;
354  done:
355     if (wnsc)
356 	cvec_free(wnsc);
357     return retval;
358 }
359 
360 /*! Find all top-level augments in a module and change original datamodels.
361  * @param[in]  ymod  Yang statement of type module/sub-module
362  * @retval     0     OK
363  * @retval    -1     Error
364  * Note there is an ordering problem, where an augment in one module depends on an augment in
365  * another module not yet augmented.
366  */
367 static int
yang_augment_module(yang_stmt * ymod)368 yang_augment_module(yang_stmt *ymod)
369 
370 {
371     int        retval = -1;
372     yang_stmt *ys;
373 
374     ys = NULL;
375     while ((ys = yn_each(ymod, ys)) != NULL){
376 	switch (yang_keyword_get(ys)){
377 	case Y_AUGMENT: /* top-level */
378 	    if (yang_augment_node(ys) < 0)
379 		goto done;
380 	    break;
381 	default:
382 	    break;
383 	}
384     }
385     retval = 0;
386  done:
387     return retval;
388 }
389 
390 /*! Given a refine node, perform the refinement action on the target refine node
391  * The RFC is somewhat complicate in the rules for refine.
392  * Most nodes will be replaced, but some are added
393  * @param[in]  yr   Refine node
394  * @param[in]  yt   Refine target node (will be modified)
395  * @see RFC7950 Sec 7.13.2
396  * There may be some missed cornercases
397  */
398 static int
ys_do_refine(yang_stmt * yr,yang_stmt * yt)399 ys_do_refine(yang_stmt *yr,
400 	     yang_stmt *yt)
401 {
402     int           retval = -1;
403     yang_stmt    *yrc; /* refine child */
404     yang_stmt    *yrc1;
405     yang_stmt    *ytc; /* target child */
406     enum rfc_6020 keyw;
407     int           i;
408 
409     /* Loop through refine node children. First if remove do that first
410      * In some cases remove a set of nodes.
411      */
412     yrc = NULL;
413     while ((yrc = yn_each(yr, yrc)) != NULL) {
414 	keyw = yang_keyword_get(yrc);
415 	switch (keyw){
416 	case Y_DEFAULT: /* remove old, add new */
417 	case Y_DESCRIPTION:
418 	case Y_REFERENCE:
419 	case Y_CONFIG:
420 	case Y_MANDATORY:
421 	case Y_PRESENCE:
422 	case Y_MIN_ELEMENTS:
423 	case Y_MAX_ELEMENTS:
424 	case Y_EXTENSION:
425 	    /* Remove old matching, dont increment due to prune in loop */
426 	    for (i=0; i<yang_len_get(yt); ){
427 		ytc = yt->ys_stmt[i];
428 		if (keyw != yang_keyword_get(ytc)){
429 		    i++;
430 		    continue;
431 		}
432 		ys_prune(yt, i);
433 		ys_free(ytc);
434 	    }
435 	    /* fall through and add if not found */
436 	case Y_MUST:   /* keep old, add new */
437 	case Y_IF_FEATURE:
438 	    break;
439 	default:
440 	    break;
441 	}
442     }
443     /* Second, add the node(s) */
444     yrc = NULL;
445     while ((yrc = yn_each(yr, yrc)) != NULL) {
446 	keyw = yang_keyword_get(yrc);
447 	/* Make copy */
448 	if ((yrc1 = ys_dup(yrc)) == NULL)
449 	    goto done;
450 	if (yn_insert(yt, yrc1) < 0)
451 	    goto done;
452     }
453     retval = 0;
454  done:
455     return retval;
456 }
457 
458 /*! Macro expansion of grouping/uses done in step 2 of yang parsing
459  * RFC7950:
460  * Identifiers appearing inside the grouping are resolved
461  * relative to the scope in which the  grouping is defined, not where it is
462  * used.  Prefix mappings, type names, grouping  names, and extension usage are
463  * evaluated in the hierarchy where the "grouping" statement appears.
464  *   The identifiers defined in the grouping are not bound to a namespace
465  * until the contents of the grouping are added to the schema tree via a
466  * "uses" statement that does not appear inside a "grouping" statement,
467  * at which point they are bound to the namespace of the current module.
468  */
469 static int
yang_expand_grouping(yang_stmt * yn)470 yang_expand_grouping(yang_stmt *yn)
471 {
472     int        retval = -1;
473     yang_stmt *ys = NULL;
474     yang_stmt *ygrouping;  /* grouping original */
475     yang_stmt *ygrouping2; /* grouping copy */
476     yang_stmt *yg;         /* grouping child */
477     yang_stmt *yr;         /* refinement */
478     int        glen;
479     int        i;
480     int        j;
481     int        k;
482     char      *id = NULL;
483     char      *prefix = NULL;
484     size_t     size;
485     yang_stmt *yp;
486 
487     /* Cannot use yang_apply here since child-list is modified (is destructive) */
488     i = 0;
489     while (i < yang_len_get(yn)){
490 	ys = yn->ys_stmt[i];
491 	switch (yang_keyword_get(ys)){
492 	case Y_USES:
493 	    /* Split argument into prefix and name */
494 	    if (nodeid_split(yang_argument_get(ys), &prefix, &id) < 0)
495 		goto done;
496 	    if (ys_grouping_resolve(ys, prefix, id, &ygrouping) < 0)
497 		goto done;
498 	    if (prefix){
499 		free(prefix);
500 		prefix = NULL;
501 	    }
502 	    if (id){
503 		free(id);
504 		id = NULL;
505 	    }
506 	    if (ygrouping == NULL){
507 		clicon_log(LOG_NOTICE, "%s: Yang error : grouping \"%s\" not found in module \"%s\"",
508 			   __FUNCTION__, yang_argument_get(ys), yang_argument_get(ys_module(ys)));
509 		goto done;
510 		break;
511 	    }
512 	    /* Check so that this uses statement is not a descendant of the grouping
513 	     */
514 	    yp = yn;
515 	    do {
516 		if (yp == ygrouping){
517 		    clicon_err(OE_YANG, EFAULT, "Yang use of grouping %s in module %s is defined inside the grouping (recursion), see RFC 7950 Sec 7.12: A grouping MUST NOT reference itself",
518 			       yang_argument_get(ys),
519 			       yang_argument_get(ys_module(yn))
520 			       );
521 		    goto done;
522 		}
523 	    } while((yp = yang_parent_get(yp)) != NULL);
524 	    if (yang_flag_get(ygrouping, YANG_FLAG_MARK) == 0){
525 		/* Check mark flag to see if this grouping has been expanded before,
526 		 * here below in the traverse section
527 		 * A mark could be completely normal (several uses) or it could be a recursion.
528 		 */
529 		yang_flag_set(ygrouping, YANG_FLAG_MARK); /* Mark as (being)  expanded */
530 		if (yang_expand_grouping(ygrouping) < 0)
531 		    goto done;
532 	    }
533 	    /* Make a copy of the grouping, then make refinements to this copy
534 	     * Note this ygrouping2 object does not gave a parent and does not work in many
535 	     * functions which assume a full hierarchy, use the original ygrouping in those cases.
536 	     */
537 	    if ((ygrouping2 = ys_dup(ygrouping)) == NULL)
538 		goto done;
539 
540 	    /* Only replace data/schemanodes:
541 	     * Compute the number of such nodes, and extend the child vector with that below
542 	     */
543 	    glen = 0;
544 	    yg = NULL;
545 	    while ((yg = yn_each(ygrouping2, yg)) != NULL) {
546 		if (yang_schemanode(yg))
547 		    glen++;
548 	    }
549 	    /*
550 	     * yn is parent: the children of ygrouping replaces ys.
551 	     * Is there a case when glen == 0?  YES AND THIS BREAKS
552 	     */
553 	    if (glen != 1){
554 		size = (yang_len_get(yn) - i - 1)*sizeof(struct yang_stmt *);
555 		yn->ys_len += glen - 1;
556 		if (glen && (yn->ys_stmt = realloc(yn->ys_stmt, (yang_len_get(yn))*sizeof(yang_stmt *))) == 0){
557 		    clicon_err(OE_YANG, errno, "realloc");
558 		    goto done;
559 		}
560 		/* Then move all existing elements up from i+1 (not uses-stmt) */
561 		if (size)
562 		    memmove(&yn->ys_stmt[i+glen],
563 			    &yn->ys_stmt[i+1],
564 			    size);
565 	    }
566 	    /* Iterate through refinements and modify grouping copy
567 	     * See RFC 7950 7.13.2 yrt is the refine target node
568 	     */
569 	    yr = NULL;
570 	    while ((yr = yn_each(ys, yr)) != NULL) {
571 		yang_stmt *yrt; /* refine target node */
572 		if (yang_keyword_get(yr) != Y_REFINE)
573 		    continue;
574 		/* Find a node */
575 		if (yang_desc_schema_nodeid(ygrouping, /* Cannot use ygrouping2 */
576 					    yang_argument_get(yr),
577 					    &yrt) < 0)
578 		    goto done;
579 		/* Not found, try next */
580 		if (yrt == NULL)
581 		    continue;
582 		/* Do the actual refinement */
583 		if (ys_do_refine(yr, yrt) < 0)
584 		    goto done;
585 		/* RFC: The argument is a string that identifies a node in the
586 		 * grouping.  I interpret that as only one node --> break */
587 		break;
588 	    }
589 	    /* Then copy and insert each child element from ygrouping2 to yn */
590 	    k=0;
591 	    for (j=0; j<yang_len_get(ygrouping2); j++){
592 		yg = ygrouping2->ys_stmt[j]; /* Child of refined copy */
593 		/* Only replace data/schemanodes */
594 		if (!yang_schemanode(yg)){
595 		    ys_free(yg);
596 		    continue;
597 		}
598 		yn->ys_stmt[i+k] = yg;
599 		yg->ys_parent = yn;
600 		k++;
601 	    }
602 	    /* Remove 'uses' node */
603 	    ys_free(ys);
604 	    /* Remove the grouping copy */
605 	    ygrouping2->ys_len = 0; /* Cant do with get access function */
606 	    ys_free(ygrouping2);
607 	    break; /* Note same child is re-iterated since it may be changed */
608 	default:
609 	    i++;
610 	    break;
611 	}
612     }
613     /* Second pass since length may have changed */
614     for (i=0; i<yang_len_get(yn); i++){
615 	ys = yn->ys_stmt[i];
616 	if (yang_keyword_get(ys) == Y_GROUPING){
617 	    /* Check mark flag to see if this grouping has been expanded before, here or in the
618 	     * 'uses' section
619 	     * A mark could be completely normal (several uses) or it could be a recursion.
620 	     */
621 	    if (yang_flag_get(ys, YANG_FLAG_MARK) == 0){
622 		yang_flag_set(ys, YANG_FLAG_MARK); /* Mark as (being)  expanded */
623 		if (yang_expand_grouping(ys) < 0)
624 		    goto done;
625 	    }
626 	}
627 	else{
628 	    if (yang_expand_grouping(ys) < 0)
629 		goto done;
630 	}
631     }
632     retval = 0;
633  done:
634     if (prefix)
635 	free(prefix);
636     if (id)
637 	free(id);
638     return retval;
639 }
640 
641 /*! Parse a string containing a YANG spec into a parse-tree
642  *
643  * Syntax parsing. A string is input and a YANG syntax-tree is returned (or error).
644  * As a side-effect, Yang modules present in the text will be inserted under the global Yang
645  * specification
646  * @param[in] str    String of yang statements
647  * @param[in] name   Log string, typically filename
648  * @param[in] yspec  Yang specification.
649  * @retval    ymod   Top-level yang (sub)module
650  * @retval    NULL   Error encountered
651  * See top of file for diagram of calling order
652  */
653 static yang_stmt *
yang_parse_str(char * str,const char * name,yang_stmt * yspec)654 yang_parse_str(char         *str,
655 	       const char   *name, /* just for errs */
656 	       yang_stmt    *yspec)
657 {
658     clixon_yang_yacc yy = {0,};
659     yang_stmt       *ymod = NULL;
660 
661     if (yspec == NULL){
662 	clicon_err(OE_YANG, 0, "Yang parse need top level yang spec");
663 	goto done;
664     }
665     yy.yy_name         = (char*)name;
666     yy.yy_linenum      = 1;
667     yy.yy_parse_string = str;
668     yy.yy_stack        = NULL;
669     yy.yy_module       = NULL; /* this is the return value - the module/sub-module */
670     if (ystack_push(&yy, yspec) == NULL)
671 	goto done;
672     if (strlen(str)){ /* Not empty */
673 	if (yang_scan_init(&yy) < 0)
674 	    goto done;
675 	if (yang_parse_init(&yy) < 0)
676 	    goto done;
677 	if (clixon_yang_parseparse(&yy) != 0) { /* yacc returns 1 on error */
678 	    clicon_log(LOG_NOTICE, "Yang error: %s on line %d", name, yy.yy_linenum);
679 	    if (clicon_errno == 0)
680 		clicon_err(OE_YANG, 0, "yang parser error with no error code (should not happen)");
681 	    yang_parse_exit(&yy);
682 	    yang_scan_exit(&yy);
683 	    goto done;
684 	}
685 	if (yang_parse_exit(&yy) < 0)
686 	    goto done;
687 	if (yang_scan_exit(&yy) < 0)
688 	    goto done;
689     }
690     ymod = yy.yy_module;
691   done:
692     ystack_pop(&yy);
693     if (yy.yy_stack)
694 	free (yy.yy_stack);
695     return ymod;  /* top-level (sub)module */
696 }
697 
698 /*! Parse yang spec from an open file descriptor
699  * @param[in] fd     File descriptor containing the YANG file as ASCII characters
700  * @param[in] name   For debug, eg filename
701  * @param[in] yspec  Yang specification. Should have been created by caller using yspec_new
702  * @retval ymod      Top-level yang (sub)module
703  * @retval NULL      Error
704  * @note this function simply parse a yang spec, no dependencies or checks
705  */
706 yang_stmt *
yang_parse_file(int fd,const char * name,yang_stmt * yspec)707 yang_parse_file(int         fd,
708 		const char *name,
709 		yang_stmt  *yspec)
710 {
711     char         *buf = NULL;
712     int           i;
713     char          c;
714     int           len;
715     yang_stmt    *ymod = NULL;
716     int           ret;
717 
718     len = BUFLEN; /* any number is fine */
719     if ((buf = malloc(len)) == NULL){
720 	clicon_err(OE_XML, errno, "malloc");
721 	goto done;
722     }
723     memset(buf, 0, len);
724     i = 0; /* position in buf */
725     while (1){ /* read the whole file */
726 	if ((ret = read(fd, &c, 1)) < 0){
727 	    clicon_err(OE_XML, errno, "read");
728 	    break;
729 	}
730 	if (ret == 0)
731 	    break; /* eof */
732 	if (i == len-1){
733 	    if ((buf = realloc(buf, 2*len)) == NULL){
734 		clicon_err(OE_XML, errno, "realloc");
735 		goto done;
736 	    }
737 	    memset(buf+len, 0, len);
738 	    len *= 2;
739 	}
740 	buf[i++] = (char)(c&0xff);
741     } /* read a line */
742     if ((ymod = yang_parse_str(buf, name, yspec)) < 0)
743 	goto done;
744   done:
745     if (buf != NULL)
746 	free(buf);
747     return ymod; /* top-level (sub)module */
748 }
749 
750 /*! Given a yang filename, extract the revision as an integer as YYYYMMDD
751  * @param[in]  filename  Filename on the form: name [+ @rev ] + .yang
752  * @param[out] basep     "Base" filename, stripped: [+ @rev ] + .yang (malloced)
753  * @param[out] revp      Revision as YYYYMMDD (0 if not found)
754  */
755 static int
filename2revision(const char * filename,char ** basep,uint32_t * revp)756 filename2revision(const char *filename,
757 		  char      **basep,
758 		  uint32_t   *revp)
759 {
760     int      retval = -1;
761     char    *base = NULL;
762     char    *p;
763 
764     /* base = module name [+ @rev ] + .yang */
765     if ((base = strdup(filename)) == NULL){
766 	clicon_err(OE_UNIX, errno, "strdup");
767 	goto done;
768     }
769     clicon_debug(2, "%s %s", __FUNCTION__, base);
770     if ((p = rindex(base, '.')) != NULL) /* strip postfix .yang */
771 	*p = '\0';
772     if ((p = index(base, '@')) != NULL){ /* extract revision date */
773 	*p++ = '\0';
774 	if (revp && ys_parse_date_arg(p, revp) < 0)
775 	    goto done;
776     }
777     if (basep){
778 	*basep = base;
779 	base = NULL;
780     }
781     retval = 0;
782  done:
783     if (base)
784 	free(base);
785     return retval;
786 }
787 
788 /*! No specific revision give. Match a yang file given module
789  * @param[in]  h        CLICON handle
790  * @param[in]  module   Name of main YANG module.
791  * @param[in]  revision Revision or NULL
792  * @param[out] revactual Actual revision (if retval=1)
793  * @param[out] fbuf     Buffer containing filename (if retval=1)
794  * @retval     1        Match found, Most recent entry returned in fbuf and revactual
795  * @retval     0        No matching entry found
796  * @retval    -1        Error
797  * @note for bootstrapping, dir may have to be set.
798 */
799 static int
yang_parse_find_match(clicon_handle h,const char * module,const char * revision,uint32_t * revactual,cbuf * fbuf)800 yang_parse_find_match(clicon_handle h,
801 		      const char   *module,
802 		      const char   *revision,
803 		      uint32_t     *revactual,
804 		      cbuf         *fbuf)
805 {
806     int            retval = -1;
807     struct dirent *dp = NULL;
808     int            ndp;
809     cbuf          *regex = NULL;
810     cxobj         *x;
811     cxobj         *xc;
812     char          *dir;
813 
814     /* get clicon config file in xml form */
815     if ((x = clicon_conf_xml(h)) == NULL)
816 	goto ok;
817     if ((regex = cbuf_new()) == NULL){
818 	clicon_err(OE_YANG, errno, "cbuf_new");
819 	goto done;
820     }
821     /* RFC 6020: The name of the file SHOULD be of the form:
822      * module-or-submodule-name ['@' revision-date] ( '.yang' / '.yin' )
823      * revision-date ::= 4DIGIT "-" 2DIGIT "-" 2DIGIT
824      */
825     if (revision)
826 	cprintf(regex, "^%s@%s(.yang)$", module, revision);
827     else
828 	cprintf(regex, "^%s(@[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9])?(.yang)$",
829 		module);
830     xc = NULL;
831     while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL) {
832 	if (strcmp(xml_name(xc), "CLICON_YANG_DIR") != 0)
833 	    continue;
834 	dir = xml_body(xc);
835 	/* get all matching files in this directory */
836 	if ((ndp = clicon_file_dirent(dir,
837 				      &dp,
838 				      cbuf_get(regex),
839 				      S_IFREG)) < 0)
840 	    goto done;
841 	/* Entries are sorted, last entry should be most recent date
842 	 */
843 	if (ndp != 0){
844 	    cprintf(fbuf, "%s/%s", dir, dp[ndp-1].d_name);
845 	    retval = 1;
846 	    goto done;
847 	}
848     }
849  ok:
850     retval = 0;
851  done:
852     if (regex)
853 	cbuf_free(regex);
854     if (dp)
855 	free(dp);
856     return retval;
857 }
858 
859 /*! Open a file, read into a string and invoke yang parsing
860  *
861  * Similar to clicon_yang_str(), just read a file first
862  * @param[in] filename Name of file
863  * @param[in] yspec    Yang specification. Should have been created by caller using yspec_new
864  * @retval ymod        Top-level yang (sub)module
865  * @retval NULL        Error encountered
866 
867  * The database symbols are inserted in alphabetical order.
868  * See top of file for diagram of calling order
869  */
870 yang_stmt *
yang_parse_filename(const char * filename,yang_stmt * yspec)871 yang_parse_filename(const char *filename,
872 		    yang_stmt  *yspec)
873 {
874     yang_stmt    *ymod = NULL;
875     int           fd = -1;
876     struct stat   st;
877 
878     clicon_debug(1, "%s %s", __FUNCTION__, filename);
879     if (stat(filename, &st) < 0){
880 	clicon_err(OE_YANG, errno, "%s not found", filename);
881 	goto done;
882     }
883     if ((fd = open(filename, O_RDONLY)) < 0){
884 	clicon_err(OE_YANG, errno, "open(%s)", filename);
885 	goto done;
886     }
887     if ((ymod = yang_parse_file(fd, filename, yspec)) < 0)
888 	goto done;
889   done:
890     if (fd != -1)
891 	close(fd);
892     return ymod; /* top-level (sub)module */
893 }
894 
895 /*! Given a (sub)module, parse all (sub)modules in turn recursively
896  *
897  * Find a yang module file, and then recursively parse all its imported modules.
898  * @param[in] h        CLICON handle
899  * @param[in] module   Module name
900  * @param[in] revision Revision (or NULL)
901  * @param[in] yspec    Yang statement
902  * @retval    0        OK
903  * @retval   -1        Error
904  *
905  * See top of file for diagram of calling order
906  */
907 static yang_stmt *
yang_parse_module(clicon_handle h,const char * module,const char * revision,yang_stmt * yspec)908 yang_parse_module(clicon_handle h,
909 		  const char   *module,
910 		  const char   *revision,
911 		  yang_stmt    *yspec)
912 {
913     cbuf      *fbuf = NULL;
914     char      *filename;
915     int        nr;
916     yang_stmt *ymod = NULL;
917     yang_stmt *yrev; /* yang revision */
918     uint32_t   revf = 0; /* revision in filename */
919     uint32_t   revm = 0; /* revision in parsed new module (should be same as revf) */
920 
921     if ((fbuf = cbuf_new()) == NULL){
922 	clicon_err(OE_YANG, errno, "cbuf_new");
923 	goto done;
924     }
925     /* Match a yang file with or without revision in yang-dir list */
926     if ((nr = yang_parse_find_match(h, module, revision, &revf, fbuf)) < 0)
927 	goto done;
928     if (nr == 0){
929 	if (revision)
930 	    clicon_err(OE_YANG, errno, "No yang files found matching \"%s@%s\" in the list of CLICON_YANG_DIRs",
931 		       module, revision);
932 	else
933 	    clicon_err(OE_YANG, errno, "No yang files found matching \"%s\" in the list of CLICON_YANG_DIRs", module);
934 	goto done;
935     }
936     filename = cbuf_get(fbuf);
937     if ((ymod = yang_parse_filename(filename, yspec)) == NULL)
938 	goto done;
939     /* Sanity check that requested module name matches loaded module
940      * If this does not match, the filename and containing module do not match
941      * RFC 7950 Sec 5.2
942      */
943     if (strcmp(yang_argument_get(ymod), module) != 0){
944 	clicon_err(OE_YANG, EINVAL, "File %s contains yang module \"%s\" which does not match expected module %s",
945 		   filename,
946 		   yang_argument_get(ymod),
947 		   module);
948 	ymod = NULL;
949 	goto done;
950     }
951     if ((yrev = yang_find(ymod, Y_REVISION, NULL)) != NULL)
952 	revm = cv_uint32_get(yang_cv_get(yrev));
953     if (filename2revision(filename, NULL, &revf) < 0)
954 	goto done;
955     /* Sanity check that file revision does not match internal rev stmt */
956     if (revf && revm && revm != revf){
957 	clicon_err(OE_YANG, EINVAL, "Yang module file revision and in yang does not match: %s vs %u", filename, revm);
958 	ymod = NULL;
959 	goto done;
960     }
961   done:
962     if (fbuf)
963 	cbuf_free(fbuf);
964     return ymod; /* top-level (sub)module */
965 }
966 
967 /*! Given a (sub)module, parse all (sub)modules in turn recursively
968  *
969  * Find a yang module file, and then recursively parse all its imported modules.
970  * @param[in] h        CLICON handle
971  * @param[in] ymod     Yang module.
972  * @param[in] yspec    Yang specification.
973  * @retval    0        OK
974  * @retval   -1        Error
975  *
976  * See top of file for diagram of calling order
977  */
978 static int
yang_parse_recurse(clicon_handle h,yang_stmt * ymod,yang_stmt * ysp)979 yang_parse_recurse(clicon_handle h,
980 		   yang_stmt    *ymod,
981 		   yang_stmt    *ysp)
982 {
983     int         retval = -1;
984     yang_stmt  *yi = NULL; /* import */
985     yang_stmt  *yrev;
986     char       *submodule;
987     char       *subrevision;
988     yang_stmt  *subymod;
989     enum rfc_6020 keyw;
990 
991     /* go through all import (modules) and include(submodules) of ysp */
992     while ((yi = yn_each(ymod, yi)) != NULL){
993 	keyw = yang_keyword_get(yi);
994 	if (keyw != Y_IMPORT && keyw != Y_INCLUDE)
995 	    continue;
996 	/* common part */
997 	submodule = yang_argument_get(yi);
998 	/* Is there a specific revision (or just latest)? */
999 	if ((yrev = yang_find(yi, Y_REVISION_DATE, NULL)) != NULL)
1000 	    subrevision = yang_argument_get(yrev);
1001 	else
1002 	    subrevision = NULL;
1003 	/* if already loaded, ignore, else parse the file */
1004 	if (yang_find(ysp,
1005 		      keyw==Y_IMPORT?Y_MODULE:Y_SUBMODULE,
1006 		      submodule) == NULL){
1007 	    /* recursive call */
1008 	    if ((subymod = yang_parse_module(h, submodule, subrevision, ysp)) == NULL)
1009 		goto done;
1010 	    /* Go through its sub-modules recursively */
1011 	    if (yang_parse_recurse(h, subymod, ysp) < 0){
1012 		ymod = NULL;
1013 		goto done;
1014 	    }
1015 	}
1016     }
1017     retval = 0;
1018   done:
1019     return retval; /* top-level (sub)module */
1020 }
1021 
1022 /*!
1023  * @param[in] ys      Yang statement
1024  * @param[in] dummy   Necessary for called in yang_apply
1025  * @see yang_applyfn_t
1026  */
1027 static int
ys_schemanode_check(yang_stmt * ys,void * dummy)1028 ys_schemanode_check(yang_stmt *ys,
1029 		    void      *dummy)
1030 {
1031     int           retval = -1;
1032     yang_stmt    *yres = NULL;
1033     yang_stmt    *yp;
1034     char         *arg;
1035     enum rfc_6020 keyword;
1036     char **vec = NULL;
1037     char  *v;
1038     int    nvec;
1039     int    i;
1040 
1041     yp = yang_parent_get(ys);
1042     arg = yang_argument_get(ys);
1043     keyword = yang_keyword_get(ys);
1044     switch (yang_keyword_get(ys)){
1045     case Y_AUGMENT:
1046 	if (yang_keyword_get(yp) == Y_MODULE || /* Not top-level */
1047 	    yang_keyword_get(yp) == Y_SUBMODULE)
1048 	    break;
1049 	/* fallthru */
1050     case Y_REFINE:
1051 	if (yang_desc_schema_nodeid(yp, arg, &yres) < 0)
1052 	    goto done;
1053 	if (yres == NULL){
1054 	    clicon_err(OE_YANG, 0, "schemanode sanity check of %s %s",
1055 		       yang_key2str(keyword), arg);
1056 	    goto done;
1057 	}
1058 	break;
1059     case Y_UNIQUE:{
1060 	/* Unique: Sec 7.8.3 It takes as an argument a string that contains a space-
1061 	   separated list of schema node identifiers */
1062 	if ((vec = clicon_strsep(arg, " \t\n", &nvec)) == NULL)
1063 	    goto done;
1064 	for (i=0; i<nvec; i++){
1065 	    v = vec[i];
1066 	    if (yang_desc_schema_nodeid(yp, v, &yres) < 0)
1067 		goto done;
1068 	    if (yres == NULL){
1069 		clicon_err(OE_YANG, 0, "schemanode sanity check of %s %s",
1070 			   yang_key2str(yang_keyword_get(ys)), v);
1071 		goto done;
1072 	    }
1073 	}
1074 	break;
1075     }
1076     case Y_DEVIATION:
1077 	if (yang_abs_schema_nodeid(ys, arg, &yres) < 0)
1078 	    goto done;
1079 	if (yres == NULL){
1080 	    clicon_err(OE_YANG, 0, "schemanode sanity check of %s", arg);
1081 	    goto done;
1082 	}
1083 	break;
1084     default:
1085 	break;
1086     }
1087     retval = 0;
1088  done:
1089     if (vec)
1090 	free(vec);
1091     return retval;
1092 }
1093 
1094 /*! Check lists: non-config lists MUST have keys
1095  * @param[in] h   Clicon handle
1096  * @param[in] ys  Yang statement
1097  * Verify the following rule:
1098  * RFC 7950 7.8.2: The "key" statement, which MUST be present if the list represents
1099  *                 configuration and MAY be present otherwise
1100  * Unless CLICON_YANG_LIST_CHECK is false
1101  * OR it is the "errors" rule of the ietf-restconf spec which seems to be a special case.
1102  */
1103 static int
ys_list_check(clicon_handle h,yang_stmt * ys)1104 ys_list_check(clicon_handle h,
1105 	      yang_stmt    *ys)
1106 {
1107     int           retval = -1;
1108     yang_stmt    *ymod;
1109     yang_stmt    *yc = NULL;
1110     enum rfc_6020 keyw;
1111     yang_stmt    *yroot;
1112 
1113     /* This node is state, not config */
1114     if (yang_config_ancestor(ys) == 0)
1115 	goto ok;
1116     /* Find root, examine if this node is part of a rpc declaration */
1117     if ((yroot = yang_myroot(ys)) != NULL &&
1118 	yang_keyword_get(yroot) == Y_RPC)
1119 	goto ok;
1120 
1121     keyw = yang_keyword_get(ys);
1122     /* Check if list and if keys do not exist */
1123     if (keyw == Y_LIST &&
1124 	yang_find(ys, Y_KEY, NULL) == 0){
1125 	ymod = ys_module(ys);
1126 #if 1
1127 	/* Except restconf error extension from sanity check, dont know why it has no keys */
1128 	if (strcmp(yang_find_mynamespace(ys),"urn:ietf:params:xml:ns:yang:ietf-restconf")==0 &&
1129 	    strcmp(yang_argument_get(ys),"error") == 0)
1130 	    ;
1131 	else
1132 #endif
1133 	    {
1134 		if (clicon_option_bool(h, "CLICON_YANG_LIST_CHECK")){
1135 		    clicon_log(LOG_ERR, "Error: LIST \"%s\" in module \"%s\" lacks key statement which MUST be present (See RFC 7950 Sec 7.8.2)",
1136 			   yang_argument_get(ys),
1137 			   yang_argument_get(ymod)
1138 			   );
1139 
1140 		    goto done;
1141 		}
1142 		else
1143 		    clicon_log(LOG_WARNING, "Warning: LIST \"%s\" in module \"%s\" lacks key statement which MUST be present (See RFC 7950 Sec 7.8.2)",
1144 			   yang_argument_get(ys),
1145 			   yang_argument_get(ymod)
1146 			   );
1147 	    }
1148     }
1149     /* Traverse subs */
1150     if (yang_schemanode(ys) || keyw == Y_MODULE || keyw == Y_SUBMODULE){
1151 	yc = NULL;
1152 	while ((yc = yn_each(ys, yc)) != NULL){
1153 	    if (ys_list_check(h, yc) < 0)
1154 		goto done;
1155 	}
1156     }
1157  ok:
1158     retval = 0;
1159  done:
1160     return retval;
1161 }
1162 
1163 /*! Depth-first topological sort
1164  * Topological sort of a DAG
1165  * @param[in]  yn    Yang module node
1166  * @param[out] ylist Result list of sorted nodes with "least significant" first
1167  * @param[out] ylen  Length of ylist
1168  * @retval     0     OK
1169  * @retval    -1     Error. Eg circular
1170  * see eg https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search
1171  */
1172 static int
ys_visit(struct yang_stmt * yn,struct yang_stmt *** ylist,int * ylen)1173 ys_visit(struct yang_stmt   *yn,
1174 	 struct yang_stmt ***ylist,
1175 	 int                *ylen)
1176 {
1177     int               retval = -1;
1178     int               i;
1179     struct yang_stmt *yi; /* import / include */
1180     struct yang_stmt *yspec;
1181     struct yang_stmt *ymod;
1182 
1183     if (yn == NULL ||
1184 	(yang_keyword_get(yn) != Y_MODULE && yang_keyword_get(yn) != Y_SUBMODULE)){
1185 	clicon_err(OE_YANG, EINVAL, "Expected module or submodule");
1186 	goto done;
1187     }
1188     yspec = ys_spec(yn);
1189     /* if n has a permanent mark then return */
1190     if (yang_flag_get(yn, YANG_FLAG_MARK))
1191 	return 0;
1192     /* if n has a temporary mark then stop (not a DAG) */
1193     if (yang_flag_get(yn, YANG_FLAG_TMP)){
1194 	clicon_err(OE_YANG, EFAULT, "Yang module %s import/include is circular", yang_argument_get(yn));
1195 	goto done;
1196     }
1197     /* mark n with a temporary mark */
1198     yang_flag_set(yn, YANG_FLAG_TMP);
1199 
1200     /* Loop through import and include statements and visit each */
1201     yi = NULL;
1202     for (i=0; i<yang_len_get(yn); i++){
1203 	yi = yang_child_i(yn, i);
1204 	if (yang_keyword_get(yi) != Y_IMPORT &&
1205 	    yang_keyword_get(yi) != Y_INCLUDE)
1206 	    continue;
1207 	if ((ymod = yang_find(yspec, Y_MODULE, yang_argument_get(yi))) == NULL &&
1208 	    (ymod = yang_find(yspec, Y_SUBMODULE, yang_argument_get(yi))) == NULL){
1209 	    clicon_err(OE_YANG, EFAULT, "Yang module %s import/include not found",
1210 		       yang_argument_get(yi)); /* shouldnt happen */
1211 	    goto done;
1212 	}
1213 	if (ys_visit(ymod, ylist, ylen) < 0)
1214 	    goto done;
1215     }
1216     /* remove temporary mark from n */
1217     yang_flag_reset(yn, YANG_FLAG_TMP);
1218     /* mark n with a permanent mark */
1219     yang_flag_set(yn, YANG_FLAG_MARK);
1220     /* add n to head of L. NB reversed */
1221     (*ylen)++;
1222     if ((*ylist = realloc(*ylist, (*ylen)*sizeof(yang_stmt *))) == 0){
1223 	clicon_err(OE_YANG, errno, "realloc");
1224 	goto done;
1225     }
1226     (*ylist)[*ylen - 1] = yn;
1227     retval = 0;
1228  done:
1229     return retval;
1230 }
1231 
1232 /*! Sort module/submodules according to import/include order and cycle detect
1233  * Topological sort of a DAG
1234  * @param[in]  yspec   Yang specification.
1235  * @param[in]  modmin  Start of interval of yspec:s module children
1236  * @param[in]  modmax  End of interval
1237  * @param[out] ylist   Result list of sorted nodes with "least significant" first
1238  * @param[out] ylen    Length of ylist
1239  * see eg https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search
1240  */
1241 static int
yang_sort_modules(yang_stmt * yspec,int modmin,int modmax,struct yang_stmt *** ylist,int * ylen)1242 yang_sort_modules(yang_stmt          *yspec,
1243 		  int                 modmin,
1244 		  int                 modmax,
1245 		  struct yang_stmt ***ylist,
1246 		  int                *ylen)
1247 {
1248     int               retval = -1;
1249     int               i;
1250     struct yang_stmt *yn;
1251 
1252 
1253     for (i=modmin; i<modmax; i++){
1254 	yn = yang_child_i(yspec, i);
1255 	/* select an unmarked node n */
1256 	if (yang_flag_get(yn, YANG_FLAG_MARK|YANG_FLAG_TMP) == 0){
1257 	    if (ys_visit(yn, ylist, ylen) < 0)
1258 		goto done;
1259 	}
1260     }
1261     if (*ylen != modmax-modmin){
1262 	clicon_err(OE_YANG, EFAULT, "Internal error: mismatch sort vector lengths");
1263     }
1264     /* Unmark all nodes */
1265     for (i=modmin; i<modmax; i++){
1266 	yn = yang_child_i(yspec, i);
1267 	yang_flag_set(yn, YANG_FLAG_MARK|YANG_FLAG_TMP);
1268     }
1269     retval = 0;
1270  done:
1271     return retval;
1272 }
1273 
1274 /*! Parse top yang module including all its sub-modules. Expand and populate yang tree
1275  *
1276  * Perform secondary actions after yang parsing. These actions cannot be made at
1277  * parse-time for various reasons:
1278  * - Detect imported yang specs that are not loaded and load and parse them too
1279  * - Check cardinality of yang (that nr of children match)
1280  * - Check features: remove disabled
1281  * - "Populate" yang, which means things like initiating caches, resolving references
1282  * - Resolve types
1283  * - Augments
1284  * - Defaults
1285  * There is some complexity in how modules are loaded vs how they need to be augmented
1286  * Therefore, after full loading, a topological sort is made to ensure the modules are
1287  * non-circular (a DAG) and that the rest of the operations are made in the topology order.
1288  * The loading order of the yang models (under yang spec) is kept.
1289  *
1290  * @param[in] h      CLICON handle
1291  * @param[in] yspec  Yang specification.
1292  * @param[in] modmin  Perform checks after this number, prior are already complete
1293  * @retval    0      Everything OK
1294  * @retval   -1      Error encountered
1295  */
1296 static int
yang_parse_post(clicon_handle h,yang_stmt * yspec,int modmin)1297 yang_parse_post(clicon_handle h,
1298 		yang_stmt    *yspec,
1299 		int           modmin)
1300 {
1301     int                retval = -1;
1302     int                i;
1303     int                modmax;
1304     struct yang_stmt **ylist = NULL; /* Topology sorted modules */
1305     int                ylen = 0;     /* Length of ylist */
1306 
1307     /* 1: Parse from text to yang parse-tree.
1308      * Iterate through modules and detect module/submodules to parse
1309      * NOTE: the list may grow on each iteration */
1310     for (i=modmin; i<yang_len_get(yspec); i++)
1311 	if (yang_parse_recurse(h, yang_child_i(yspec, i), yspec) < 0)
1312 	    goto done;
1313 
1314     modmax = yang_len_get(yspec);
1315     /* The set of modules [modmin..maxmax] is here complete wrt imports/includes and is a DAG
1316      * Example: A imports B, C and D, and C and D imports B
1317      * In some operations below (eg augment) need to be in topology order, eg B first.
1318      * Therefore the modules are sorted into a separate list that is used henceforth
1319      */
1320     if (yang_sort_modules(yspec, modmin, yang_len_get(yspec), &ylist, &ylen) < 0)
1321 	goto done;
1322 
1323     /* 2. Check cardinality a first time (done again last) */
1324     for (i=modmin; i<modmax; i++)
1325 	if (yang_cardinality(h, yang_child_i(yspec, i), yang_argument_get(yspec->ys_stmt[i])) < 0)
1326 	    goto done;
1327 
1328     /* 3: Check features/if-features: check if enabled and remove disabled features */
1329     for (i=modmin; i<modmax; i++)
1330 	if (yang_features(h, yang_child_i(yspec, i)) < 0)
1331 	    goto done;
1332 
1333     /* 4: Go through parse tree and populate it with cv types */
1334     for (i=modmin; i<modmax; i++){
1335 	if (ys_populate(yang_child_i(yspec, i), h) < 0) /* Alt: make a yang_apply0 */
1336 	    goto done;
1337 	if (yang_apply(yang_child_i(yspec, i), -1, ys_populate, (void*)h) < 0)
1338 	    goto done;
1339     }
1340 
1341     /* 5: Resolve all types: populate type caches. Requires eg length/range cvecs
1342      * from ys_populate step.
1343      * Must be done using static binding.
1344      */
1345     for (i=modmin; i<modmax; i++)
1346 	if (yang_apply(yang_child_i(yspec, i), Y_TYPE, ys_resolve_type, h) < 0)
1347 	    goto done;
1348 
1349     /* Up to here resolving is made in the context they are defined, rather
1350      * than the context they are used (except for submodules being merged w
1351      * modules). Like static scoping.
1352      * After this we expand all grouping/uses and unfold all macros into a
1353      * single tree as they are used.
1354      */
1355 
1356     /* 6: Macro expansion of all grouping/uses pairs. Expansion needs marking */
1357     for (i=0; i<ylen; i++){
1358 	if (yang_expand_grouping(ylist[i]) < 0)
1359 	    goto done;
1360 	yang_apply(ylist[i], -1, (yang_applyfn_t*)yang_flag_reset, (void*)YANG_FLAG_MARK);
1361     }
1362 
1363     /* 7: Top-level augmentation of all modules.
1364      * Note: Clixon does not implement augment in USES
1365      * Note: There is an ordering problem, where an augment in one module depends on an augment in
1366      * another module not yet augmented.
1367      */
1368     for (i=0; i<ylen; i++)
1369 	if (yang_augment_module(ylist[i]) < 0)
1370 	    goto done;
1371 
1372     /* 4: Go through parse tree and do 2nd step populate (eg default) */
1373     for (i=0; i<ylen; i++)
1374 	if (yang_apply(ylist[i], -1, ys_populate2, (void*)h) < 0)
1375 	    goto done;
1376 
1377     /* 8: sanity checks of expanded yangs need more here */
1378     for (i=0; i<ylen; i++){
1379 	/* Check schemanode references */
1380 	if (yang_apply(ylist[i], -1, ys_schemanode_check, NULL) < 0)
1381 	    goto done;
1382 	/* Check list key values */
1383 	if (ys_list_check(h, ylist[i]) < 0)
1384 	    goto done;
1385     }
1386     /* 9. Check cardinality a second time after grouping/augment etc */
1387     for (i=0; i<ylen; i++)
1388 	if (yang_cardinality(h, ylist[i], yang_argument_get(ylist[i])) < 0)
1389 	    goto done;
1390     retval = 0;
1391  done:
1392     if (ylist)
1393 	free(ylist);
1394     return retval;
1395 }
1396 
1397 /*! Parse yang specification and its dependencies recursively given module
1398  * @param[in]  h         clicon handle
1399  * @param[in]  module    Module name, or absolute filename (including dir)
1400  * @param[in]  revision  Revision, or NULL
1401  * @param[in]  yspec     Modules parse are added to this yangspec
1402  * @retval     0         OK
1403  * @retval    -1         Error
1404  * @see yang_spec_parse_file
1405  */
1406 int
yang_spec_parse_module(clicon_handle h,const char * module,const char * revision,yang_stmt * yspec)1407 yang_spec_parse_module(clicon_handle h,
1408 		       const char   *module,
1409 		       const char   *revision,
1410 		       yang_stmt    *yspec)
1411 {
1412     int         retval = -1;
1413     int         modmin;       /* Existing number of modules */
1414     char       *base = NULL;;
1415 
1416     if (yspec == NULL){
1417 	clicon_err(OE_YANG, EINVAL, "yang spec is NULL");
1418 	goto done;
1419     }
1420     if (module == NULL){
1421 	clicon_err(OE_YANG, EINVAL, "yang module not set");
1422 	goto done;
1423     }
1424     /* Apply steps 2.. on new modules, ie ones after modmin. */
1425     modmin = yang_len_get(yspec);
1426     /* Do not load module if it already exists */
1427     if (yang_find(yspec, Y_MODULE, module) != NULL)
1428 	goto ok;
1429     if (yang_parse_module(h, module, revision, yspec) == NULL)
1430 	goto done;
1431     if (yang_parse_post(h, yspec, modmin) < 0)
1432 	goto done;
1433  ok:
1434     retval = 0;
1435  done:
1436     if (base)
1437 	free(base);
1438     return retval;
1439 }
1440 
1441 /*! Parse yang specification and its dependencies recursively given filename
1442  * @param[in]  h         clicon handle
1443  * @param[in]  filename  Actual filename (including dir and revision)
1444  * @param[in]  yspec     Modules parse are added to this yangspec
1445  * @retval     0         OK
1446  * @retval    -1         Error
1447  * @see yang_spec_parse_module for yang dir,module,revision instead of
1448  *       actual filename
1449  * @see yang_spec_load_dir For loading all files in a directory
1450  */
1451 int
yang_spec_parse_file(clicon_handle h,char * filename,yang_stmt * yspec)1452 yang_spec_parse_file(clicon_handle h,
1453 		     char         *filename,
1454 		     yang_stmt    *yspec)
1455 {
1456     int         retval = -1;
1457     int         modmin;       /* Existing number of modules */
1458     char       *base = NULL;;
1459 
1460     /* Apply steps 2.. on new modules, ie ones after modmin. */
1461     modmin = yang_len_get(yspec);
1462     /* Find module, and do not load file if module already exists */
1463     if (basename(filename) == NULL){
1464 	clicon_err(OE_YANG, errno, "No basename");
1465 	goto done;
1466     }
1467     if ((base = strdup(basename(filename))) == NULL){
1468 	clicon_err(OE_YANG, errno, "strdup");
1469 	goto done;
1470     }
1471     if (index(base, '@') != NULL)
1472 	*index(base, '@') = '\0';
1473     if (yang_find(yspec, Y_MODULE, base) != NULL)
1474 	goto ok;
1475     if (yang_parse_filename(filename, yspec) == NULL)
1476 	goto done;
1477     if (yang_parse_post(h, yspec, modmin) < 0)
1478 	goto done;
1479  ok:
1480     retval = 0;
1481  done:
1482     if (base)
1483 	free(base);
1484     return retval;
1485 }
1486 
1487 /*! Load all yang modules in directory
1488  * @param[in]  h     Clicon handle
1489  * @param[in]  dir   Load all yang modules in this directory
1490  * @param[in]  yspec Modules parse are added to this yangspec
1491  * @retval     0     OK
1492  * @retval    -1     Error
1493  * @see yang_spec_parse_file
1494  * Load all yang files in a directory as primary objects.
1495  * Some details if several same yang module x exists:
1496  * 1) If x is already loaded (eg via direct file loading) skip it
1497  * 2) Prefer x.yang over x@rev.yang (no revision)
1498  * 3) If only x@rev.yang's found, prefer newest (newest revision)
1499  * There is also an extra failsafe which may not be necessary, which removes
1500  * the oldest module if 1-3 for some reason fails.
1501  */
1502 int
yang_spec_load_dir(clicon_handle h,char * dir,yang_stmt * yspec)1503 yang_spec_load_dir(clicon_handle h,
1504 		   char         *dir,
1505 		   yang_stmt    *yspec)
1506 {
1507     int            retval = -1;
1508     int            ndp;
1509     struct dirent *dp = NULL;
1510     int            i;
1511     int            j;
1512     char           filename[MAXPATHLEN];
1513     char          *base = NULL; /* filename without dir */
1514     int            modmin;
1515     yang_stmt     *ym;   /* yang module */
1516     yang_stmt     *ym0;  /* (existing) yang module */
1517     yang_stmt     *yrev; /* yang revision */
1518     uint32_t       revf = 0; /* revision in filename */
1519     uint32_t       revm = 0; /* revision in parsed new module (should be same as revf) */
1520     uint32_t       rev0; /* revision in existing module */
1521     char          *oldbase = NULL;
1522     int            taken = 0;
1523 
1524     /* Get yang files names from yang module directory. Note that these
1525      * are sorted alphatetically:
1526      * a.yang,
1527      * a@2000-01-01.yang,
1528      * a@2111-11-11.yang
1529      */
1530     if((ndp = clicon_file_dirent(dir, &dp, "(.yang)$", S_IFREG)) < 0)
1531 	goto done;
1532     if (ndp == 0)
1533 	goto ok;
1534     /* Apply post steps on new modules, ie ones after modmin. */
1535     modmin = yang_len_get(yspec);
1536     /* Load all yang files in dir */
1537     for (i = 0; i < ndp; i++) {
1538 	/* base = module name [+ @rev ] + .yang */
1539 	if (oldbase)
1540 	    free(oldbase);
1541 	oldbase = base;
1542 	base = NULL;
1543 	revf = 0;
1544 	if (filename2revision(dp[i].d_name, &base, &revf) < 0)
1545 	    goto done;
1546 	if (oldbase && strcmp(base, oldbase)) /* new yang file basename */
1547 	    taken = 0;
1548 	if (revf == 0) /* No revision: a.yang - take that */
1549 	    taken = 1;
1550 	else{ /* a@xxx.yang */
1551 	    if (taken)
1552 		continue; /* skip if already taken */
1553 	    /* Look forward: is there anyone else later? (assume sorted revision dates) */
1554 	    if (i+1 < ndp){ /* not last in list */
1555 		char *nextbase = NULL; /* XXX suboptimal algorithm, could combione old/next/base */
1556 		if (filename2revision(dp[i+1].d_name, &nextbase, NULL) < 0)
1557 		    goto done;
1558 		if (nextbase && strcmp(base, nextbase) == 0){
1559 		    free(nextbase);
1560 		    nextbase = NULL;
1561 		    continue; /* same base: skip; */
1562 		}
1563 		if (nextbase)
1564 		    free(nextbase);
1565 	    }
1566 	    taken = 1; /* last in line and not taken */
1567 	}
1568 	/* Here only a single file is reached(taken)
1569 	 * Check if module already exists -> ym0/rev0 */
1570 	rev0 = 0;
1571 	if ((ym0 = yang_find(yspec, Y_MODULE, base)) != NULL ||
1572 	    (ym0 = yang_find(yspec, Y_SUBMODULE, base)) != NULL){
1573 	    yrev = yang_find(ym0, Y_REVISION, NULL);
1574 	    rev0 = cv_uint32_get(yang_cv_get(yrev));
1575 	    continue; /* skip if already added by specific file or module */
1576 	}
1577 	/* Create full filename */
1578 	snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name);
1579 	if ((ym = yang_parse_filename(filename, yspec)) == NULL)
1580 	    goto done;
1581 	revm = 0;
1582 	if ((yrev = yang_find(ym, Y_REVISION, NULL)) != NULL)
1583 	    revm = cv_uint32_get(yang_cv_get(yrev));
1584 	/* Sanity check that file revision does not match internal rev stmt */
1585 	if (revf && revm && revm != revf){ /* XXX */
1586 	    clicon_err(OE_YANG, EINVAL, "Yang module file revision and in yang does not match: %s(%u) vs %u", filename, revf, revm);
1587 	    goto done;
1588 	}
1589 	/* If ym0 and ym exists, delete the yang with oldest revision
1590 	 * This is a failsafe in case anything else fails
1591 	 */
1592 	if (revm && rev0){
1593 	    if (revm > rev0) /* Loaded module is older or eq -> remove ym */
1594 		ym = ym0;
1595 	    for (j=0; j<yang_len_get(yspec); j++)
1596 		if (yspec->ys_stmt[j] == ym)
1597 		    break;
1598 	    ys_prune(yspec, j);
1599 	    ys_free(ym);
1600 	}
1601     }
1602     if (yang_parse_post(h, yspec, modmin) < 0)
1603 	goto done;
1604  ok:
1605     retval = 0;
1606   done:
1607     if (dp)
1608 	free(dp);
1609     if (base)
1610 	free(base);
1611     if (oldbase)
1612 	free(oldbase);
1613     return retval;
1614 }
1615 
1616 /*! parse yang date-arg string and return a uint32 useful for arithmetics
1617  * @param[in]  datearg  yang revision string as "YYYY-MM-DD"
1618  * @param[out] dateint  Integer version as YYYYMMDD
1619  * @retval     0        OK
1620  * @retval    -1        Error, eg str is not on the format "YYYY-MM-DD"
1621  */
1622 int
ys_parse_date_arg(char * datearg,uint32_t * dateint)1623 ys_parse_date_arg(char     *datearg,
1624 		  uint32_t *dateint)
1625 {
1626     int      retval = -1;
1627     int      i;
1628     uint32_t d = 0;
1629 
1630     if (strlen(datearg) != 10 || datearg[4] != '-' || datearg[7] != '-'){
1631 	clicon_err(OE_YANG, EINVAL, "Revision date %s, but expected: YYYY-MM-DD", datearg);
1632 	goto done;
1633     }
1634     if ((i = cligen_tonum(4, datearg)) < 0){
1635 	clicon_err(OE_YANG, EINVAL, "Revision date %s, but expected: YYYY-MM-DD", datearg);
1636 	goto done;
1637     }
1638     d = i*10000; /* year */
1639     if ((i = cligen_tonum(2, &datearg[5])) < 0){
1640 	clicon_err(OE_YANG, EINVAL, "Revision date %s, but expected: YYYY-MM-DD", datearg);
1641 	goto done;
1642     }
1643     d += i*100; /* month */
1644     if ((i = cligen_tonum(2, &datearg[8])) < 0){
1645 	clicon_err(OE_YANG, EINVAL, "Revision date %s, but expected: YYYY-MM-DD", datearg);
1646 	goto done;
1647     }
1648     d += i; /* day */
1649     *dateint = d;
1650     retval = 0;
1651  done:
1652     return retval;
1653 }
1654 
1655 /*! Parse argument as CV and save result in yang cv variable
1656  *
1657  * Note that some CV:s are parsed directly (eg fraction-digits) while others are parsed
1658  * in third pass (ys_populate). The reason being that all information is not
1659  * available in the first pass. Prefer to do stuff in ys_populate
1660  */
1661 cg_var *
ys_parse(yang_stmt * ys,enum cv_type cvtype)1662 ys_parse(yang_stmt   *ys,
1663 	 enum cv_type cvtype)
1664 {
1665     int             cvret;
1666     char           *reason = NULL;
1667 
1668     assert(yang_cv_get(ys) == NULL); /* Cv:s are parsed in different places, difficult to separate */
1669     if ((ys->ys_cv = cv_new(cvtype)) == NULL){
1670 	clicon_err(OE_YANG, errno, "cv_new");
1671 	goto done;
1672     }
1673     if ((cvret = cv_parse1(yang_argument_get(ys), ys->ys_cv, &reason)) < 0){ /* error */
1674 	clicon_err(OE_YANG, errno, "parsing cv");
1675 	ys->ys_cv = NULL;
1676 	goto done;
1677     }
1678     if (cvret == 0){ /* parsing failed */
1679 	clicon_err(OE_YANG, errno, "Parsing CV: %s", reason);
1680 	ys->ys_cv = NULL;
1681 	goto done;
1682     }
1683     /* cvret == 1 means parsing is OK */
1684   done:
1685     if (reason)
1686 	free(reason);
1687     return ys->ys_cv;
1688 }
1689 
1690 /*! First round yang syntactic statement specific checks. No context checks.
1691  *
1692  * Specific syntax checks  and variable creation for stand-alone yang statements.
1693  * That is, siblings, etc, may not be there. Complete checks are made in
1694  * ys_populate instead.
1695  * @param[in] ys    yang statement
1696  * @param[in] extra Yang extra for cornercases (unknown/extension). Is consumed
1697  *
1698  * The cv:s created in parse-tree as follows:
1699  * fraction-digits : Create cv as uint8, check limits [1:8] (must be made in 1st pass)
1700  * revision: cv as uint32 date: Integer version as YYYYMMDD
1701  * min-elements: cv as uint32
1702  * max-elements: cv as uint32, '0' means unbounded
1703  * @see ys_populate
1704  */
1705 int
ys_parse_sub(yang_stmt * ys,char * extra)1706 ys_parse_sub(yang_stmt *ys,
1707 	     char      *extra)
1708 {
1709     int        retval = -1;
1710     uint8_t    fd;
1711     uint32_t   date = 0;
1712     char      *arg;
1713     enum rfc_6020 keyword;
1714     char      *reason = NULL;
1715     int        ret;
1716     uint32_t   minmax;
1717 
1718     arg = yang_argument_get(ys);
1719     keyword = yang_keyword_get(ys);
1720     switch (keyword){
1721     case Y_FRACTION_DIGITS:
1722 	if (ys_parse(ys, CGV_UINT8) == NULL)
1723 	    goto done;
1724 	fd = cv_uint8_get(ys->ys_cv);
1725 	if (fd < 1 || fd > 18){
1726 	    clicon_err(OE_YANG, errno, "%u: Out of range, should be [1:18]", fd);
1727 	    goto done;
1728 	}
1729 	break;
1730     case Y_MUST:
1731     case Y_WHEN:
1732 	if (xpath_parse(yang_argument_get(ys), NULL) < 0)
1733 	    goto done;
1734 	break;
1735     case Y_REVISION:
1736     case Y_REVISION_DATE:  /* YYYY-MM-DD encoded as uint32 YYYYMMDD */
1737 	if (ys_parse_date_arg(arg, &date) < 0)
1738 	    goto done;
1739 	if ((ys->ys_cv = cv_new(CGV_UINT32)) == NULL){
1740 	    clicon_err(OE_YANG, errno, "cv_new");
1741 	    goto done;
1742 	}
1743 	cv_uint32_set(ys->ys_cv, date);
1744 	break;
1745     case Y_STATUS: /* RFC7950 7.21.2: "current", "deprecated", or "obsolete". */
1746 	if (strcmp(arg, "current") &&
1747 	    strcmp(arg, "deprecated") &&
1748 	    strcmp(arg, "obsolete")){
1749 	    clicon_err(OE_YANG, errno, "Invalid status: \"%s\", expected current, deprecated, or obsolete", arg);
1750 	    goto done;
1751 
1752 	}
1753 	break;
1754     case Y_MAX_ELEMENTS:
1755     case Y_MIN_ELEMENTS:
1756 	if ((ys->ys_cv = cv_new(CGV_UINT32)) == NULL){
1757 	    clicon_err(OE_YANG, errno, "cv_new");
1758 	    goto done;
1759 	}
1760 	if (keyword == Y_MAX_ELEMENTS &&
1761 	    strcmp(arg, "unbounded") == 0)
1762 	    cv_uint32_set(ys->ys_cv, 0); /* 0 means unbounded for max */
1763 	else{
1764 	    if ((ret = parse_uint32(arg, &minmax, &reason)) < 0){
1765 		clicon_err(OE_YANG, errno, "parse_uint32");
1766 		goto done;
1767 	    }
1768 	    if (ret == 0){
1769 		clicon_err(OE_YANG, EINVAL, "element-min/max parse error: %s", reason);
1770 		if (reason)
1771 		    free(reason);
1772 		goto done;
1773 	    }
1774 	    cv_uint32_set(ys->ys_cv, minmax);
1775 	}
1776 	break;
1777     case Y_MODIFIER:
1778 	if (strcmp(yang_argument_get(ys), "invert-match")){
1779 	    clicon_err(OE_YANG, EINVAL, "modifier %s, expected invert-match", yang_argument_get(ys));
1780 	    goto done;
1781 	}
1782 	break;
1783     case Y_UNKNOWN:{ /* save (optional) argument in ys_cv */
1784 	if (extra == NULL)
1785 	    break;
1786 	if ((ys->ys_cv = cv_new(CGV_STRING)) == NULL){
1787 	    clicon_err(OE_YANG, errno, "cv_new");
1788 	    goto done;
1789 	}
1790 	if ((ret = cv_parse1(extra, ys->ys_cv, &reason)) < 0){ /* error */
1791 	    clicon_err(OE_YANG, errno, "parsing cv");
1792 	    goto done;
1793 	}
1794 	if (ret == 0){ /* parsing failed */
1795 	    clicon_err(OE_YANG, errno, "Parsing CV: %s", reason);
1796 	    goto done;
1797 	}
1798 	break;
1799     }
1800     default:
1801 	break;
1802     }
1803     retval = 0;
1804   done:
1805     if (extra)
1806 	free(extra);
1807     return retval;
1808 }
1809