blob: d6ec444ddeab4ab1864a89c5e86662facf370893 [file] [log] [blame]
Michal Vasko7bcb48e2016-01-15 10:28:54 +01001/**
2 * \file messages.c
3 * \author Radek Krejci <rkrejci@cesnet.cz>
4 * \brief libnetconf2 - NETCONF messages functions
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of the Company nor the names of its contributors
18 * may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 */
22
23#include <ctype.h>
24#include <stdlib.h>
25#include <string.h>
26#include <stdarg.h>
27
28#include <libyang/libyang.h>
29
30#include "libnetconf.h"
Michal Vasko7bcb48e2016-01-15 10:28:54 +010031
32const char *rpcedit_dfltop2str[] = {NULL, "merge", "replace", "none"};
33const char *rpcedit_testopt2str[] = {NULL, "test-then-set", "set", "test-only"};
34const char *rpcedit_erropt2str[] = {NULL, "stop-on-error", "continue-on-error", "rollback-on-error"};
35
36API NC_RPC_TYPE
37nc_rpc_get_type(const struct nc_rpc *rpc)
38{
Michal Vasko7f1c78b2016-01-19 09:52:14 +010039 if (!rpc) {
40 ERRARG;
41 return 0;
42 }
43
Michal Vasko7bcb48e2016-01-15 10:28:54 +010044 return rpc->type;
45}
46
47API struct nc_rpc *
48nc_rpc_generic(const struct lyd_node *data, NC_PARAMTYPE paramtype)
49{
50 struct nc_rpc_generic *rpc;
51
Michal Vasko7f1c78b2016-01-19 09:52:14 +010052 if (!data) {
53 ERRARG;
54 return NULL;
55 }
56
Michal Vasko7bcb48e2016-01-15 10:28:54 +010057 if (data->next || (data->prev != data)) {
58 ERR("Generic RPC must have a single root node.");
59 return NULL;
60 }
61
62 rpc = malloc(sizeof *rpc);
63 if (!rpc) {
64 ERRMEM;
65 return NULL;
66 }
67
68 rpc->type = NC_RPC_GENERIC;
69 rpc->has_data = 1;
70 if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) {
71 rpc->content.data = lyd_dup(data, 1);
72 } else {
73 rpc->content.data = (struct lyd_node *)data;
74 }
75 rpc->free = (paramtype == NC_PARAMTYPE_CONST ? 0 : 1);
76
77 return (struct nc_rpc *)rpc;
78}
79
80API struct nc_rpc *
81nc_rpc_generic_xml(const char *xml_str, NC_PARAMTYPE paramtype)
82{
83 struct nc_rpc_generic *rpc;
84
Michal Vasko7f1c78b2016-01-19 09:52:14 +010085 if (!xml_str) {
86 ERRARG;
87 return NULL;
88 }
89
Michal Vasko7bcb48e2016-01-15 10:28:54 +010090 rpc = malloc(sizeof *rpc);
91 if (!rpc) {
92 ERRMEM;
93 return NULL;
94 }
95
96 rpc->type = NC_RPC_GENERIC;
97 rpc->has_data = 0;
98 if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) {
99 rpc->content.xml_str = strdup(xml_str);
100 } else {
101 rpc->content.xml_str = (char *)xml_str;
102 }
103 rpc->free = (paramtype == NC_PARAMTYPE_CONST ? 0 : 1);
104
105 return (struct nc_rpc *)rpc;
106}
107
108API struct nc_rpc *
109nc_rpc_getconfig(NC_DATASTORE source, const char *filter, NC_WD_MODE wd_mode, NC_PARAMTYPE paramtype)
110{
111 struct nc_rpc_getconfig *rpc;
112
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100113 if (!source) {
114 ERRARG;
115 return NULL;
116 }
117
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100118 if (filter && (filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) {
119 ERR("Filter must either be an XML subtree or an XPath expression.");
120 return NULL;
121 }
122
123 rpc = malloc(sizeof *rpc);
124 if (!rpc) {
125 ERRMEM;
126 return NULL;
127 }
128
129 rpc->type = NC_RPC_GETCONFIG;
130 rpc->source = source;
131 if (filter && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) {
132 rpc->filter = strdup(filter);
133 } else {
134 rpc->filter = (char *)filter;
135 }
136 rpc->wd_mode = wd_mode;
137 rpc->free = (paramtype == NC_PARAMTYPE_CONST ? 0 : 1);
138
139 return (struct nc_rpc *)rpc;
140}
141
142API struct nc_rpc *
143nc_rpc_edit(NC_DATASTORE target, NC_RPC_EDIT_DFLTOP default_op, NC_RPC_EDIT_TESTOPT test_opt,
144 NC_RPC_EDIT_ERROPT error_opt, const char *edit_content, NC_PARAMTYPE paramtype)
145{
146 struct nc_rpc_edit *rpc;
147
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100148 if (!target || !edit_content) {
149 ERRARG;
150 return NULL;
151 }
152
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100153 if ((edit_content[0] != '<') && !isalpha(edit_content[0])) {
154 ERR("<edit-config> content must either be a URL or a config (XML).");
155 return NULL;
156 }
157
158 rpc = malloc(sizeof *rpc);
159 if (!rpc) {
160 ERRMEM;
161 return NULL;
162 }
163
164 rpc->type = NC_RPC_EDIT;
165 rpc->target = target;
166 rpc->default_op = default_op;
167 rpc->test_opt = test_opt;
168 rpc->error_opt = error_opt;
169 if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) {
170 rpc->edit_cont = strdup(edit_content);
171 } else {
172 rpc->edit_cont = (char *)edit_content;
173 }
174 rpc->free = (paramtype == NC_PARAMTYPE_CONST ? 0 : 1);
175
176 return (struct nc_rpc *)rpc;
177}
178
179API struct nc_rpc *
180nc_rpc_copy(NC_DATASTORE target, const char *url_trg, NC_DATASTORE source, const char *url_or_config_src,
181 NC_WD_MODE wd_mode, NC_PARAMTYPE paramtype)
182{
183 struct nc_rpc_copy *rpc;
184
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100185 if (!target || !source) {
186 ERRARG;
187 return NULL;
188 }
189
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100190 if (url_or_config_src && (url_or_config_src[0] != '<') && !isalpha(url_or_config_src[0])) {
191 ERR("<copy-config> source is neither a URL nor a config (XML).");
192 return NULL;
193 }
194
195 rpc = malloc(sizeof *rpc);
196 if (!rpc) {
197 ERRMEM;
198 return NULL;
199 }
200
201 rpc->type = NC_RPC_COPY;
202 rpc->target = target;
203 if (url_trg && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) {
204 rpc->url_trg = strdup(url_trg);
205 } else {
206 rpc->url_trg = (char *)url_trg;
207 }
208 rpc->source = source;
209 if (url_or_config_src && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) {
210 rpc->url_config_src = strdup(url_or_config_src);
211 } else {
212 rpc->url_config_src = (char *)url_or_config_src;
213 }
214 rpc->wd_mode = wd_mode;
215 rpc->free = (paramtype == NC_PARAMTYPE_CONST ? 0 : 1);
216
217 return (struct nc_rpc *)rpc;
218}
219
220API struct nc_rpc *
221nc_rpc_delete(NC_DATASTORE target, const char *url, NC_PARAMTYPE paramtype)
222{
223 struct nc_rpc_delete *rpc;
224
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100225 if (!target) {
226 ERRARG;
227 return NULL;
228 }
229
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100230 rpc = malloc(sizeof *rpc);
231 if (!rpc) {
232 ERRMEM;
233 return NULL;
234 }
235
236 rpc->type = NC_RPC_DELETE;
237 rpc->target = target;
238 if (url && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) {
239 rpc->url = strdup(url);
240 } else {
241 rpc->url = (char *)url;
242 }
243 rpc->free = (paramtype == NC_PARAMTYPE_CONST ? 0 : 1);
244
245 return (struct nc_rpc *)rpc;
246}
247
248API struct nc_rpc *
249nc_rpc_lock(NC_DATASTORE target)
250{
251 struct nc_rpc_lock *rpc;
252
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100253 if (!target) {
254 ERRARG;
255 return NULL;
256 }
257
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100258 rpc = malloc(sizeof *rpc);
259 if (!rpc) {
260 ERRMEM;
261 return NULL;
262 }
263
264 rpc->type = NC_RPC_LOCK;
265 rpc->target = target;
266
267 return (struct nc_rpc *)rpc;
268}
269
270API struct nc_rpc *
271nc_rpc_unlock(NC_DATASTORE target)
272{
273 struct nc_rpc_lock *rpc;
274
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100275 if (!target) {
276 ERRARG;
277 return NULL;
278 }
279
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100280 rpc = malloc(sizeof *rpc);
281 if (!rpc) {
282 ERRMEM;
283 return NULL;
284 }
285
286 rpc->type = NC_RPC_UNLOCK;
287 rpc->target = target;
288
289 return (struct nc_rpc *)rpc;
290}
291
292API struct nc_rpc *
293nc_rpc_get(const char *filter, NC_WD_MODE wd_mode, NC_PARAMTYPE paramtype)
294{
295 struct nc_rpc_get *rpc;
296
297 if (filter && (filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) {
298 ERR("Filter must either be an XML subtree or an XPath expression.");
299 return NULL;
300 }
301
302 rpc = malloc(sizeof *rpc);
303 if (!rpc) {
304 ERRMEM;
305 return NULL;
306 }
307
308 rpc->type = NC_RPC_GET;
309 if (filter && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) {
310 rpc->filter = strdup(filter);
311 } else {
312 rpc->filter = (char *)filter;
313 }
314 rpc->wd_mode = wd_mode;
315 rpc->free = (paramtype == NC_PARAMTYPE_CONST ? 0 : 1);
316
317 return (struct nc_rpc *)rpc;
318}
319
320API struct nc_rpc *
321nc_rpc_kill(uint32_t session_id)
322{
323 struct nc_rpc_kill *rpc;
324
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100325 if (!session_id) {
326 ERRARG;
327 return NULL;
328 }
329
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100330 rpc = malloc(sizeof *rpc);
331 if (!rpc) {
332 ERRMEM;
333 return NULL;
334 }
335
336 rpc->type = NC_RPC_KILL;
337 rpc->sid = session_id;
338
339 return (struct nc_rpc *)rpc;
340}
341
342API struct nc_rpc *
343nc_rpc_commit(int confirmed, uint32_t confirm_timeout, const char *persist, const char *persist_id,
344 NC_PARAMTYPE paramtype)
345{
346 struct nc_rpc_commit *rpc;
347
348 rpc = malloc(sizeof *rpc);
349 if (!rpc) {
350 ERRMEM;
351 return NULL;
352 }
353
354 rpc->type = NC_RPC_COMMIT;
355 rpc->confirmed = confirmed;
356 rpc->confirm_timeout = confirm_timeout;
357 if (persist && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) {
358 rpc->persist = strdup(persist);
359 } else {
360 rpc->persist = (char *)persist;
361 }
362 if (persist_id && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) {
363 rpc->persist_id = strdup(persist_id);
364 } else {
365 rpc->persist_id = (char *)persist_id;
366 }
367 rpc->free = (paramtype == NC_PARAMTYPE_CONST ? 0 : 1);
368
369 return (struct nc_rpc *)rpc;
370}
371
372API struct nc_rpc *
373nc_rpc_discard(void)
374{
375 struct nc_rpc *rpc;
376
377 rpc = malloc(sizeof *rpc);
378 if (!rpc) {
379 ERRMEM;
380 return NULL;
381 }
382
383 rpc->type = NC_RPC_DISCARD;
384
385 return rpc;
386}
387
388API struct nc_rpc *
389nc_rpc_cancel(const char *persist_id, NC_PARAMTYPE paramtype)
390{
391 struct nc_rpc_cancel *rpc;
392
393 rpc = malloc(sizeof *rpc);
394 if (!rpc) {
395 ERRMEM;
396 return NULL;
397 }
398
399 rpc->type = NC_RPC_CANCEL;
400 if (persist_id && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) {
401 rpc->persist_id = strdup(persist_id);
402 } else {
403 rpc->persist_id = (char *)persist_id;
404 }
405 rpc->free = (paramtype == NC_PARAMTYPE_CONST ? 0 : 1);
406
407 return (struct nc_rpc *)rpc;
408}
409
410API struct nc_rpc *
411nc_rpc_validate(NC_DATASTORE source, const char *url_or_config, NC_PARAMTYPE paramtype)
412{
413 struct nc_rpc_validate *rpc;
414
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100415 if (!source) {
416 ERRARG;
417 return NULL;
418 }
419
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100420 if (url_or_config && (url_or_config[0] != '<') && !isalpha(url_or_config[0])) {
421 ERR("<validate> source is neither a URL nor a config (XML).");
422 return NULL;
423 }
424
425 rpc = malloc(sizeof *rpc);
426 if (!rpc) {
427 ERRMEM;
428 return NULL;
429 }
430
431 rpc->type = NC_RPC_VALIDATE;
432 rpc->source = source;
433 if (url_or_config && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) {
434 rpc->url_config_src = strdup(url_or_config);
435 } else {
436 rpc->url_config_src = (char *)url_or_config;
437 }
438 rpc->free = (paramtype == NC_PARAMTYPE_CONST ? 0 : 1);
439
440 return (struct nc_rpc *)rpc;
441}
442
443API struct nc_rpc *
444nc_rpc_getschema(const char *identifier, const char *version, const char *format, NC_PARAMTYPE paramtype)
445{
446 struct nc_rpc_getschema *rpc;
447
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100448 if (!identifier) {
449 ERRARG;
450 return NULL;
451 }
452
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100453 rpc = malloc(sizeof *rpc);
454 if (!rpc) {
455 ERRMEM;
456 return NULL;
457 }
458
459 rpc->type = NC_RPC_GETSCHEMA;
460 if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) {
461 rpc->identifier = strdup(identifier);
462 } else {
463 rpc->identifier = (char *)identifier;
464 }
465 if (version && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) {
466 rpc->version = strdup(version);
467 } else {
468 rpc->version = (char *)version;
469 }
470 if (format && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) {
471 rpc->format = strdup(format);
472 } else {
473 rpc->format = (char *)format;
474 }
475 rpc->free = (paramtype == NC_PARAMTYPE_CONST ? 0 : 1);
476
477 return (struct nc_rpc *)rpc;
478}
479
480API struct nc_rpc *
481nc_rpc_subscribe(const char *stream_name, const char *filter, const char *start_time, const char *stop_time,
482 NC_PARAMTYPE paramtype)
483{
484 struct nc_rpc_subscribe *rpc;
485
486 if (filter && (filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) {
487 ERR("Filter must either be an XML subtree or an XPath expression.");
488 return NULL;
489 }
490
491 rpc = malloc(sizeof *rpc);
492 if (!rpc) {
493 ERRMEM;
494 return NULL;
495 }
496
497 rpc->type = NC_RPC_SUBSCRIBE;
498 if (stream_name && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) {
499 rpc->stream = strdup(stream_name);
500 } else {
501 rpc->stream = (char *)stream_name;
502 }
503 if (filter && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) {
504 rpc->filter = strdup(filter);
505 } else {
506 rpc->filter = (char *)filter;
507 }
508 if (start_time && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) {
509 rpc->start = strdup(start_time);
510 } else {
511 rpc->start = (char *)start_time;
512 }
513 if (stop_time && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) {
514 rpc->stop = strdup(stop_time);
515 } else {
516 rpc->stop = (char *)stop_time;
517 }
518 rpc->free = (paramtype == NC_PARAMTYPE_CONST ? 0 : 1);
519
520 return (struct nc_rpc *)rpc;
521}
522
523API void
524nc_rpc_free(struct nc_rpc *rpc)
525{
526 struct nc_rpc_generic *rpc_generic;
527 struct nc_rpc_getconfig *rpc_getconfig;
528 struct nc_rpc_edit *rpc_edit;
529 struct nc_rpc_copy *rpc_copy;
530 struct nc_rpc_delete *rpc_delete;
531 struct nc_rpc_get *rpc_get;
532 struct nc_rpc_commit *rpc_commit;
533 struct nc_rpc_cancel *rpc_cancel;
534 struct nc_rpc_validate *rpc_validate;
535 struct nc_rpc_getschema *rpc_getschema;
536 struct nc_rpc_subscribe *rpc_subscribe;
537
538 if (!rpc) {
539 return;
540 }
541
542 switch (rpc->type) {
543 case NC_RPC_GENERIC:
544 rpc_generic = (struct nc_rpc_generic *)rpc;
545 if (rpc_generic->free) {
546 if (rpc_generic->has_data) {
547 lyd_free(rpc_generic->content.data);
548 } else {
549 free(rpc_generic->content.xml_str);
550 }
551 }
552 break;
553 case NC_RPC_GETCONFIG:
554 rpc_getconfig = (struct nc_rpc_getconfig *)rpc;
555 if (rpc_getconfig->free) {
556 free(rpc_getconfig->filter);
557 }
558 break;
559 case NC_RPC_EDIT:
560 rpc_edit = (struct nc_rpc_edit *)rpc;
561 if (rpc_edit->free) {
562 free(rpc_edit->edit_cont);
563 }
564 break;
565 case NC_RPC_COPY:
566 rpc_copy = (struct nc_rpc_copy *)rpc;
567 if (rpc_copy->free) {
568 free(rpc_copy->url_config_src);
569 }
570 break;
571 case NC_RPC_DELETE:
572 rpc_delete = (struct nc_rpc_delete *)rpc;
573 if (rpc_delete->free) {
574 free(rpc_delete->url);
575 }
576 break;
577 case NC_RPC_GET:
578 rpc_get = (struct nc_rpc_get *)rpc;
579 if (rpc_get->free) {
580 free(rpc_get->filter);
581 }
582 break;
583 case NC_RPC_COMMIT:
584 rpc_commit = (struct nc_rpc_commit *)rpc;
585 if (rpc_commit->free) {
586 free(rpc_commit->persist);
587 free(rpc_commit->persist_id);
588 }
589 break;
590 case NC_RPC_CANCEL:
591 rpc_cancel = (struct nc_rpc_cancel *)rpc;
592 if (rpc_cancel->free) {
593 free(rpc_cancel->persist_id);
594 }
595 break;
596 case NC_RPC_VALIDATE:
597 rpc_validate = (struct nc_rpc_validate *)rpc;
598 if (rpc_validate->free) {
599 free(rpc_validate->url_config_src);
600 }
601 break;
602 case NC_RPC_GETSCHEMA:
603 rpc_getschema = (struct nc_rpc_getschema *)rpc;
604 if (rpc_getschema->free) {
605 free(rpc_getschema->identifier);
606 free(rpc_getschema->version);
607 free(rpc_getschema->format);
608 }
609 break;
610 case NC_RPC_SUBSCRIBE:
611 rpc_subscribe = (struct nc_rpc_subscribe *)rpc;
612 if (rpc_subscribe->free) {
613 free(rpc_subscribe->stream);
614 free(rpc_subscribe->filter);
615 free(rpc_subscribe->start);
616 free(rpc_subscribe->stop);
617 }
618 break;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100619 default:
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100620 /* nothing special needed */
621 break;
622 }
623
624 free(rpc);
625}
626
627API void
628nc_reply_free(struct nc_reply *reply)
629{
Michal Vasko1a38c862016-01-15 15:50:07 +0100630 struct nc_client_reply_error *error;
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100631 struct nc_reply_data *data;
632 uint32_t i, j;
633
634 if (!reply) {
635 return;
636 }
637
638 switch (reply->type) {
639 case NC_RPL_DATA:
640 data = (struct nc_reply_data *)reply;
641 lyd_free_withsiblings(data->data);
642 break;
643
644 case NC_RPL_OK:
645 /* nothing to free */
646 break;
647
648 case NC_RPL_ERROR:
Michal Vasko1a38c862016-01-15 15:50:07 +0100649 error = (struct nc_client_reply_error *)reply;
Michal Vasko7bcb48e2016-01-15 10:28:54 +0100650 for (i = 0; i < error->count; ++i) {
651 lydict_remove(error->ctx, error->err[i].type);
652 lydict_remove(error->ctx, error->err[i].tag);
653 lydict_remove(error->ctx, error->err[i].severity);
654 lydict_remove(error->ctx, error->err[i].apptag);
655 lydict_remove(error->ctx, error->err[i].path);
656 lydict_remove(error->ctx, error->err[i].message);
657 lydict_remove(error->ctx, error->err[i].message_lang);
658 lydict_remove(error->ctx, error->err[i].sid);
659 for (j = 0; j < error->err[i].attr_count; ++j) {
660 lydict_remove(error->ctx, error->err[i].attr[j]);
661 }
662 free(error->err[i].attr);
663 for (j = 0; j < error->err[i].elem_count; ++j) {
664 lydict_remove(error->ctx, error->err[i].elem[j]);
665 }
666 free(error->err[i].elem);
667 for (j = 0; j < error->err[i].ns_count; ++j) {
668 lydict_remove(error->ctx, error->err[i].ns[j]);
669 }
670 free(error->err[i].ns);
671 for (j = 0; j < error->err[i].other_count; ++j) {
672 lyxml_free(error->ctx, error->err[i].other[j]);
673 }
674 free(error->err[i].other);
675 }
676 free(error->err);
677 break;
678
679 case NC_RPL_NOTIF:
680 nc_notif_free((struct nc_notif *)reply);
681 break;
682 }
683
684 free(reply);
685}
Michal Vasko495c9462016-01-15 11:27:43 +0100686
687API void
688nc_notif_free(struct nc_notif *notif)
689{
690 if (!notif) {
691 return;
692 }
693
694 lydict_remove(notif->tree->schema->module->ctx, notif->datetime);
695 lyd_free(notif->tree);
696 free(notif);
697}