1 /*
2 *
3 ***** BEGIN LICENSE BLOCK *****
4
5 Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
6 Copyright (C) 2017-2019 Olof Hagsand
7 Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
8
9 This file is part of CLIXON.
10
11 Licensed under the Apache License, Version 2.0 (the "License");
12 you may not use this file except in compliance with the License.
13 You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22
23 Alternatively, the contents of this file may be used under the terms of
24 the GNU General Public License Version 3 or later (the "GPL"),
25 in which case the provisions of the GPL are applicable instead
26 of those above. If you wish to allow use of your version of this file only
27 under the terms of the GPL, and not to allow others to
28 use your version of this file under the terms of Apache License version 2,
29 indicate your decision by deleting the provisions above and replace them with
30 the notice and other provisions required by the GPL. If you do not delete
31 the provisions above, a recipient may use your version of this file under
32 the terms of any one of the Apache License version 2 or the GPL.
33
34 ***** END LICENSE BLOCK *****
35
36 *
37 */
38
39 #ifdef HAVE_CONFIG_H
40 #include "clixon_config.h" /* generated by config & autoconf */
41 #endif
42
43 #include <stdio.h>
44 #include <string.h>
45 #include <stdlib.h>
46 #include <stdarg.h>
47 #include <dlfcn.h>
48 #include <dirent.h>
49 #include <unistd.h>
50 #include <errno.h>
51 #include <signal.h>
52 #include <syslog.h>
53 #include <sys/types.h>
54 #include <sys/stat.h>
55 #include <sys/param.h>
56 #include <netinet/in.h>
57
58 /* cligen */
59 #include <cligen/cligen.h>
60
61 /* clicon */
62 #include <clixon/clixon.h>
63
64 #include "clixon_backend_transaction.h"
65 #include "backend_plugin.h"
66 #include "backend_commit.h"
67
68 /*! Request plugins to reset system state
69 * The system 'state' should be the same as the contents of running_db
70 * @param[in] cp Plugin handle
71 * @param[in] h Clicon handle
72 * @param[in] db Name of datastore
73 * @retval 0 OK
74 * @retval -1 Error
75 */
76 int
clixon_plugin_reset_one(clixon_plugin * cp,clicon_handle h,char * db)77 clixon_plugin_reset_one(clixon_plugin *cp,
78 clicon_handle h,
79 char *db)
80 {
81 int retval = -1;
82 plgreset_t *fn; /* callback */
83
84 if ((fn = cp->cp_api.ca_reset) != NULL){
85 if (fn(h, db) < 0) {
86 if (clicon_errno < 0)
87 clicon_log(LOG_WARNING, "%s: Internal error: Reset callback in plugin: %s returned -1 but did not make a clicon_err call",
88 __FUNCTION__, cp->cp_name);
89 goto done;
90 }
91 }
92 retval = 0;
93 done:
94 return retval;
95 }
96
97 /*! Call all plugins reset callbacks
98 * The system 'state' should be the same as the contents of running_db
99 * @param[in] h Clixon handle
100 * @param[in] db Name of datastore
101 * @retval 0 OK
102 * @retval -1 Error
103 */
104 int
clixon_plugin_reset_all(clicon_handle h,char * db)105 clixon_plugin_reset_all(clicon_handle h,
106 char *db)
107 {
108 int retval = -1;
109 clixon_plugin *cp = NULL;
110
111 /* Loop through all plugins, call callbacks in each */
112 while ((cp = clixon_plugin_each(h, cp)) != NULL) {
113 if (clixon_plugin_reset_one(cp, h, db) < 0)
114 goto done;
115 }
116 retval = 0;
117 done:
118 return retval;
119 }
120
121 /*! Call single plugin "post-" daemonize callback
122 * @param[in] cp Plugin handle
123 * @param[in] h Clixon handle
124 * @retval 0 OK
125 * @retval -1 Error
126 */
127 int
clixon_plugin_daemon_one(clixon_plugin * cp,clicon_handle h)128 clixon_plugin_daemon_one(clixon_plugin *cp,
129 clicon_handle h)
130 {
131 int retval = -1;
132 plgdaemon_t *fn; /* Daemonize plugin callback function */
133
134 if ((fn = cp->cp_api.ca_daemon) != NULL){
135 if (fn(h) < 0) {
136 if (clicon_errno < 0)
137 clicon_log(LOG_WARNING, "%s: Internal error: Daemon callback in plugin: %s returned -1 but did not make a clicon_err call",
138 __FUNCTION__, cp->cp_name);
139 goto done;
140 }
141 }
142 retval = 0;
143 done:
144 return retval;
145 }
146
147 /*! Call all plugins "post-" daemonize callbacks
148 * @param[in] h Clicon handle
149 * @retval 0 OK
150 * @retval -1 Error
151 */
152 int
clixon_plugin_daemon_all(clicon_handle h)153 clixon_plugin_daemon_all(clicon_handle h)
154 {
155 int retval = -1;
156 clixon_plugin *cp = NULL;
157
158 /* Loop through all plugins, call callbacks in each */
159 while ((cp = clixon_plugin_each(h, cp)) != NULL) {
160 if (clixon_plugin_daemon_one(cp, h) < 0)
161 goto done;
162 }
163 retval = 0;
164 done:
165 return retval;
166 }
167
168 /*! Call single backend statedata callback
169 *
170 * Create an xml state tree (xret) for one callback only on the form:
171 * <config>...</config>,
172 * call a user supplied function (ca_statedata) which can do two things:
173 * - Fill in state XML in the tree and return 0
174 * - Call cli_error() and return -1
175 * In the former case, this function returns the state XML tree to the caller (which
176 * typically merges the tree with other state trees).
177 * In the latter error case, this function returns 0 (invalid) to the caller with no tree
178 * If a fatal error occurs in this function, -1 is returned.
179 *
180 * @param[in] cp Plugin handle
181 * @param[in] h clicon handle
182 * @param[in] xpath String with XPATH syntax. or NULL for all
183 * @param[out] xret If retval=1, state tree created and returned: <config>...
184 * @retval -1 Fatal error
185 * @retval 0 Statedata callback failed. no XML tree returned
186 * @retval 1 OK if callback found (and called) xret is set
187 */
188 static int
clixon_plugin_statedata_one(clixon_plugin * cp,clicon_handle h,cvec * nsc,char * xpath,cxobj ** xp)189 clixon_plugin_statedata_one(clixon_plugin *cp,
190 clicon_handle h,
191 cvec *nsc,
192 char *xpath,
193 cxobj **xp)
194 {
195 int retval = -1;
196 plgstatedata_t *fn; /* Plugin statedata fn */
197 cxobj *x = NULL;
198
199 if ((fn = cp->cp_api.ca_statedata) != NULL){
200 if ((x = xml_new("config", NULL, CX_ELMNT)) == NULL)
201 goto done;
202 if (fn(h, nsc, xpath, x) < 0){
203 if (clicon_errno < 0)
204 clicon_log(LOG_WARNING, "%s: Internal error: State callback in plugin: %s returned -1 but did not make a clicon_err call",
205 __FUNCTION__, cp->cp_name);
206 goto fail; /* Dont quit here on user callbacks */
207 }
208
209 }
210 if (xp && x)
211 *xp = x;
212 retval = 1;
213 done:
214 return retval;
215 fail:
216 retval = 0;
217 goto done;
218 }
219
220 /*! Go through all backend statedata callbacks and collect state data
221 * This is internal system call, plugin is invoked (does not call) this function
222 * Backend plugins can register
223 * @param[in] h clicon handle
224 * @param[in] yspec Yang spec
225 * @param[in] nsc Namespace context
226 * @param[in] xpath String with XPATH syntax. or NULL for all
227 * @param[in,out] xret State XML tree is merged with existing tree.
228 * @retval -1 Error
229 * @retval 0 Statedata callback failed (xret set with netconf-error)
230 * @retval 1 OK
231 * @note xret can be replaced in this function
232 */
233 int
clixon_plugin_statedata_all(clicon_handle h,yang_stmt * yspec,cvec * nsc,char * xpath,cxobj ** xret)234 clixon_plugin_statedata_all(clicon_handle h,
235 yang_stmt *yspec,
236 cvec *nsc,
237 char *xpath,
238 cxobj **xret)
239 {
240 int retval = -1;
241 int ret;
242 cxobj *x = NULL;
243 clixon_plugin *cp = NULL;
244 cbuf *cberr = NULL;
245 cxobj *xerr = NULL;
246
247 clicon_debug(1, "%s", __FUNCTION__);
248 while ((cp = clixon_plugin_each(h, cp)) != NULL) {
249 if ((ret = clixon_plugin_statedata_one(cp, h, nsc, xpath, &x)) < 0)
250 goto done;
251 if (ret == 0){
252 if ((cberr = cbuf_new()) == NULL){
253 clicon_err(OE_UNIX, errno, "cbuf_new");
254 goto done;
255 }
256 /* error reason should be in clicon_err_reason */
257 cprintf(cberr, "Internal error, state callback in plugin %s returned invalid XML: %s",
258 cp->cp_name, clicon_err_reason);
259 if (netconf_operation_failed_xml(&xerr, "application", cbuf_get(cberr)) < 0)
260 goto done;
261 xml_free(*xret);
262 *xret = xerr;
263 xerr = NULL;
264 goto fail;
265 }
266 if (x == NULL)
267 continue;
268 if (xml_child_nr(x) == 0){
269 xml_free(x);
270 x = NULL;
271 continue;
272 }
273 #if 1
274 if (clicon_debug_get())
275 clicon_log_xml(LOG_DEBUG, x, "%s STATE:", __FUNCTION__);
276 #endif
277 /* XXX: ret == 0 invalid yang binding should be handled as internal error */
278 if ((ret = xml_bind_yang(x, YB_MODULE, yspec, &xerr)) < 0)
279 goto done;
280 if (ret == 0){
281 if (clixon_netconf_internal_error(xerr,
282 ". Internal error, state callback returned invalid XML from plugin: ",
283 cp->cp_name) < 0)
284 goto done;
285 xml_free(*xret);
286 *xret = xerr;
287 xerr = NULL;
288 goto fail;
289 }
290 if (xml_sort_recurse(x) < 0)
291 goto done;
292 /* Mark non-presence containers as XML_FLAG_DEFAULT */
293 if (xml_apply(x, CX_ELMNT, xml_nopresence_default_mark, (void*)XML_FLAG_DEFAULT) < 0)
294 goto done;
295 /* Clear XML tree of defaults */
296 if (xml_tree_prune_flagged(x, XML_FLAG_DEFAULT, 1) < 0)
297 goto done;
298 /* clear mark and change */
299 xml_apply0(x, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
300 (void*)(0xffff));
301 if (xml_default_recurse(x, 1) < 0)
302 goto done;
303 if ((ret = netconf_trymerge(x, yspec, xret)) < 0)
304 goto done;
305 if (ret == 0)
306 goto fail;
307 if (x){
308 xml_free(x);
309 x = NULL;
310 }
311 } /* while plugin */
312 retval = 1;
313 done:
314 if (xerr)
315 xml_free(xerr);
316 if (cberr)
317 cbuf_free(cberr);
318 if (x)
319 xml_free(x);
320 return retval;
321 fail:
322 retval = 0;
323 goto done;
324 }
325
326 /*! Create and initialize a validate/commit transaction
327 * @retval td New alloced transaction,
328 * @retval NULL Error
329 * @see transaction_free which deallocates the returned handle
330 */
331 transaction_data_t *
transaction_new(void)332 transaction_new(void)
333 {
334 transaction_data_t *td;
335 static uint64_t id = 0; /* Global transaction id */
336
337 if ((td = malloc(sizeof(*td))) == NULL){
338 clicon_err(OE_CFG, errno, "malloc");
339 return NULL;
340 }
341 memset(td, 0, sizeof(*td));
342 td->td_id = id++;
343 return td;
344 }
345
346 /*! Free transaction structure
347 *
348 * @param[in] td Transaction data will be deallocated after the call
349 */
350 int
transaction_free(transaction_data_t * td)351 transaction_free(transaction_data_t *td)
352 {
353 if (td->td_src)
354 xml_free(td->td_src);
355 if (td->td_target)
356 xml_free(td->td_target);
357 if (td->td_dvec)
358 free(td->td_dvec);
359 if (td->td_avec)
360 free(td->td_avec);
361 if (td->td_scvec)
362 free(td->td_scvec);
363 if (td->td_tcvec)
364 free(td->td_tcvec);
365 free(td);
366 return 0;
367 }
368
369 /*! Call single plugin transaction_begin() before a validate/commit.
370 * @param[in] cp Plugin handle
371 * @param[in] h Clixon handle
372 * @param[in] td Transaction data
373 * @retval 0 OK
374 * @retval -1 Error
375 */
376 int
plugin_transaction_begin_one(clixon_plugin * cp,clicon_handle h,transaction_data_t * td)377 plugin_transaction_begin_one(clixon_plugin *cp,
378 clicon_handle h,
379 transaction_data_t *td)
380 {
381 int retval = -1;
382 trans_cb_t *fn;
383
384 if ((fn = cp->cp_api.ca_trans_begin) != NULL){
385 if (fn(h, (transaction_data)td) < 0){
386 if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
387 clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error",
388 __FUNCTION__, cp->cp_name);
389 goto done;
390 }
391 }
392 retval = 0;
393 done:
394 return retval;
395 }
396
397 /* The plugin_transaction routines need access to struct plugin which is local to this file */
398
399 /*! Call transaction_begin() in all plugins before a validate/commit.
400 * @param[in] h Clicon handle
401 * @param[in] td Transaction data
402 * @retval 0 OK
403 * @retval -1 Error: one of the plugin callbacks returned error
404 */
405 int
plugin_transaction_begin_all(clicon_handle h,transaction_data_t * td)406 plugin_transaction_begin_all(clicon_handle h,
407 transaction_data_t *td)
408 {
409 int retval = -1;
410 clixon_plugin *cp = NULL;
411
412 while ((cp = clixon_plugin_each(h, cp)) != NULL) {
413 if (plugin_transaction_begin_one(cp, h, td) < 0)
414 goto done;
415 }
416 retval = 0;
417 done:
418 return retval;
419 }
420
421 /*! Call single plugin transaction_validate() in a validate/commit transaction
422 * @param[in] cp Plugin handle
423 * @param[in] h Clixon handle
424 * @param[in] td Transaction data
425 * @retval 0 OK
426 * @retval -1 Error
427 */
428 int
plugin_transaction_validate_one(clixon_plugin * cp,clicon_handle h,transaction_data_t * td)429 plugin_transaction_validate_one(clixon_plugin *cp,
430 clicon_handle h,
431 transaction_data_t *td)
432 {
433 int retval = -1;
434 trans_cb_t *fn;
435
436 if ((fn = cp->cp_api.ca_trans_validate) != NULL){
437 if (fn(h, (transaction_data)td) < 0){
438 if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
439 clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error",
440 __FUNCTION__, cp->cp_name);
441 goto done;
442 }
443 }
444 retval = 0;
445 done:
446 return retval;
447 }
448
449 /*! Call transaction_validate callbacks in all backend plugins
450 * @param[in] h Clicon handle
451 * @param[in] td Transaction data
452 * @retval 0 OK. Validation succeeded in all plugins
453 * @retval -1 Error: one of the plugin callbacks returned validation fail
454 */
455 int
plugin_transaction_validate_all(clicon_handle h,transaction_data_t * td)456 plugin_transaction_validate_all(clicon_handle h,
457 transaction_data_t *td)
458 {
459 int retval = -1;
460 clixon_plugin *cp = NULL;
461
462 while ((cp = clixon_plugin_each(h, cp)) != NULL) {
463 if (plugin_transaction_validate_one(cp, h, td) < 0)
464 goto done;
465 }
466 retval = 0;
467 done:
468 return retval;
469 }
470
471 /*! Call single plugin transaction_complete() in a validate/commit transaction
472 * complete is called after validate (before commit)
473 * @param[in] cp Plugin handle
474 * @param[in] h Clixon handle
475 * @param[in] td Transaction data
476 * @retval 0 OK
477 * @retval -1 Error
478 */
479 int
plugin_transaction_complete_one(clixon_plugin * cp,clicon_handle h,transaction_data_t * td)480 plugin_transaction_complete_one(clixon_plugin *cp,
481 clicon_handle h,
482 transaction_data_t *td)
483 {
484 int retval = -1;
485 trans_cb_t *fn;
486
487 if ((fn = cp->cp_api.ca_trans_complete) != NULL){
488 if (fn(h, (transaction_data)td) < 0){
489 if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
490 clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error",
491 __FUNCTION__, cp->cp_name);
492 goto done;
493 }
494 }
495 retval = 0;
496 done:
497 return retval;
498 }
499
500 /*! Call transaction_complete() in all plugins after validation (before commit)
501 * @param[in] h Clicon handle
502 * @param[in] td Transaction data
503 * @retval 0 OK
504 * @retval -1 Error: one of the plugin callbacks returned error
505 * @note Call plugins which have commit dependencies?
506 * @note Rename to transaction_complete?
507 */
508 int
plugin_transaction_complete_all(clicon_handle h,transaction_data_t * td)509 plugin_transaction_complete_all(clicon_handle h,
510 transaction_data_t *td)
511 {
512 int retval = -1;
513 clixon_plugin *cp = NULL;
514
515 while ((cp = clixon_plugin_each(h, cp)) != NULL) {
516 if (plugin_transaction_complete_one(cp, h, td) < 0)
517 goto done;
518 }
519 retval = 0;
520 done:
521 return retval;
522 }
523
524 /*! Revert a commit
525 * @param[in] h CLICON handle
526 * @param[in] td Transaction data
527 * @param[in] nr The plugin where an error occured.
528 * @retval 0 OK
529 * @retval -1 Error
530 * The revert is made in plugin before this one. Eg if error occurred in
531 * plugin 2, then the revert will be made in plugins 1 and 0.
532 */
533 static int
plugin_transaction_revert_all(clicon_handle h,transaction_data_t * td,int nr)534 plugin_transaction_revert_all(clicon_handle h,
535 transaction_data_t *td,
536 int nr)
537 {
538 int retval = 0;
539 clixon_plugin *cp = NULL;
540 trans_cb_t *fn;
541
542 while ((cp = clixon_plugin_each_revert(h, cp, nr)) != NULL) {
543 if ((fn = cp->cp_api.ca_trans_revert) == NULL)
544 continue;
545 if ((retval = fn(h, (transaction_data)td)) < 0){
546 clicon_log(LOG_NOTICE, "%s: Plugin '%s' trans_revert callback failed",
547 __FUNCTION__, cp->cp_name);
548 break;
549 }
550 }
551 return retval; /* ignore errors */
552 }
553
554
555 /*! Call single plugin transaction_commit() in a commit transaction
556 * @param[in] cp Plugin handle
557 * @param[in] h Clixon handle
558 * @param[in] td Transaction data
559 * @retval 0 OK
560 * @retval -1 Error
561 */
562 int
plugin_transaction_commit_one(clixon_plugin * cp,clicon_handle h,transaction_data_t * td)563 plugin_transaction_commit_one(clixon_plugin *cp,
564 clicon_handle h,
565 transaction_data_t *td)
566 {
567 int retval = -1;
568 trans_cb_t *fn;
569
570 if ((fn = cp->cp_api.ca_trans_commit) != NULL){
571 if (fn(h, (transaction_data)td) < 0){
572 if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
573 clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error",
574 __FUNCTION__, cp->cp_name);
575 goto done;
576 }
577 }
578 retval = 0;
579 done:
580 return retval;
581 }
582
583 /*! Call transaction_commit callbacks in all backend plugins
584 * @param[in] h Clicon handle
585 * @param[in] td Transaction data
586 * @retval 0 OK
587 * @retval -1 Error: one of the plugin callbacks returned error
588 * If any of the commit callbacks fail by returning -1, a revert of the
589 * transaction is tried by calling the commit callbacsk with reverse arguments
590 * and in reverse order.
591 */
592 int
plugin_transaction_commit_all(clicon_handle h,transaction_data_t * td)593 plugin_transaction_commit_all(clicon_handle h,
594 transaction_data_t *td)
595 {
596 int retval = -1;
597 clixon_plugin *cp = NULL;
598 int i=0;
599
600 while ((cp = clixon_plugin_each(h, cp)) != NULL) {
601 i++;
602 if (plugin_transaction_commit_one(cp, h, td) < 0){
603 /* Make an effort to revert transaction */
604 plugin_transaction_revert_all(h, td, i-1);
605 goto done;
606 }
607 }
608 retval = 0;
609 done:
610 return retval;
611 }
612
613
614 /*! Call single plugin transaction_commit_done() in a commit transaction
615 * @param[in] cp Plugin handle
616 * @param[in] h Clixon handle
617 * @param[in] td Transaction data
618 * @retval 0 OK
619 * @retval -1 Error
620 */
621 int
plugin_transaction_commit_done_one(clixon_plugin * cp,clicon_handle h,transaction_data_t * td)622 plugin_transaction_commit_done_one(clixon_plugin *cp,
623 clicon_handle h,
624 transaction_data_t *td)
625 {
626 int retval = -1;
627 trans_cb_t *fn;
628
629 if ((fn = cp->cp_api.ca_trans_commit_done) != NULL){
630 if (fn(h, (transaction_data)td) < 0){
631 if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
632 clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error",
633 __FUNCTION__, cp->cp_name);
634 goto done;
635 }
636 }
637 retval = 0;
638 done:
639 return retval;
640 }
641
642 /*! Call transaction_commit_done callbacks in all backend plugins
643 * @param[in] h Clicon handle
644 * @param[in] td Transaction data
645 * @retval 0 OK
646 * @retval -1 Error: one of the plugin callbacks returned error
647 * @note no revert is done
648 */
649 int
plugin_transaction_commit_done_all(clicon_handle h,transaction_data_t * td)650 plugin_transaction_commit_done_all(clicon_handle h,
651 transaction_data_t *td)
652 {
653 int retval = -1;
654 clixon_plugin *cp = NULL;
655
656 while ((cp = clixon_plugin_each(h, cp)) != NULL) {
657 if (plugin_transaction_commit_done_one(cp, h, td) < 0)
658 goto done;
659 }
660 retval = 0;
661 done:
662 return retval;
663 }
664
665 /*! Call single plugin transaction_end() in a commit/validate transaction
666 * @param[in] cp Plugin handle
667 * @param[in] h Clixon handle
668 * @param[in] td Transaction data
669 * @retval 0 OK
670 * @retval -1 Error
671 */
672 int
plugin_transaction_end_one(clixon_plugin * cp,clicon_handle h,transaction_data_t * td)673 plugin_transaction_end_one(clixon_plugin *cp,
674 clicon_handle h,
675 transaction_data_t *td)
676 {
677 int retval = -1;
678 trans_cb_t *fn;
679
680 if ((fn = cp->cp_api.ca_trans_end) != NULL){
681 if (fn(h, (transaction_data)td) < 0){
682 if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
683 clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error",
684 __FUNCTION__, cp->cp_name);
685 goto done;
686 }
687 }
688 retval = 0;
689 done:
690 return retval;
691 }
692
693 /*! Call transaction_end() in all plugins after a successful commit.
694 * @param[in] h Clicon handle
695 * @param[in] td Transaction data
696 * @retval 0 OK
697 * @retval -1 Error
698 */
699 int
plugin_transaction_end_all(clicon_handle h,transaction_data_t * td)700 plugin_transaction_end_all(clicon_handle h,
701 transaction_data_t *td)
702 {
703 int retval = -1;
704 clixon_plugin *cp = NULL;
705
706 while ((cp = clixon_plugin_each(h, cp)) != NULL) {
707 if (plugin_transaction_end_one(cp, h, td) < 0)
708 goto done;
709 }
710 retval = 0;
711 done:
712 return retval;
713 }
714
715 int
plugin_transaction_abort_one(clixon_plugin * cp,clicon_handle h,transaction_data_t * td)716 plugin_transaction_abort_one(clixon_plugin *cp,
717 clicon_handle h,
718 transaction_data_t *td)
719 {
720 int retval = -1;
721 trans_cb_t *fn;
722
723 if ((fn = cp->cp_api.ca_trans_abort) != NULL){
724 if (fn(h, (transaction_data)td) < 0){
725 if (!clicon_errno) /* sanity: log if clicon_err() is not called ! */
726 clicon_log(LOG_NOTICE, "%s: Plugin '%s' callback does not make clicon_err call on error",
727 __FUNCTION__, cp->cp_name);
728 goto done;
729 }
730 }
731 retval = 0;
732 done:
733 return retval;
734 }
735
736 /*! Call transaction_abort() in all plugins after a failed validation/commit.
737 * @param[in] h Clicon handle
738 * @param[in] td Transaction data
739 * @retval 0 OK
740 * @retval -1 Error
741 */
742 int
plugin_transaction_abort_all(clicon_handle h,transaction_data_t * td)743 plugin_transaction_abort_all(clicon_handle h,
744 transaction_data_t *td)
745 {
746 int retval = -1;
747 clixon_plugin *cp = NULL;
748
749 while ((cp = clixon_plugin_each(h, cp)) != NULL) {
750 if (plugin_transaction_abort_one(cp, h, td) < 0)
751 ; /* dont abort on error */
752 }
753 retval = 0;
754 return retval;
755 }
756
757