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 #ifdef HAVE_CONFIG_H
39 #include "clixon_config.h" /* generated by config & autoconf */
40 #endif
41
42 #include <stdio.h>
43 #include <string.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <stdarg.h>
47 #include <errno.h>
48 #include <signal.h>
49 #include <fcntl.h>
50 #include <time.h>
51 #include <pwd.h>
52 #include <syslog.h>
53 #include <sys/stat.h>
54 #include <sys/time.h>
55 #include <sys/socket.h>
56 #include <sys/param.h>
57 #include <sys/types.h>
58 #include <netinet/in.h>
59 #include <arpa/inet.h>
60 #include <netinet/in.h>
61
62 /* cligen */
63 #include <cligen/cligen.h>
64
65 /* clicon */
66 #include <clixon/clixon.h>
67
68 #include "clixon_backend_transaction.h"
69 #include "backend_plugin.h"
70 #include "backend_handle.h"
71 #include "backend_commit.h"
72 #include "backend_startup.h"
73
74 /*! Merge db1 into db2 without commit
75 * @retval -1 Error
76 * @retval 0 Validation failed (with cbret set)
77 * @retval 1 Validation OK
78 */
79 static int
db_merge(clicon_handle h,const char * db1,const char * db2,cbuf * cbret)80 db_merge(clicon_handle h,
81 const char *db1,
82 const char *db2,
83 cbuf *cbret)
84 {
85 int retval = -1;
86 cxobj *xt = NULL;
87
88 /* Get data as xml from db1 */
89 if (xmldb_get0(h, (char*)db1, YB_MODULE, NULL, NULL, 0, &xt, NULL) < 0)
90 goto done;
91 /* Merge xml into db2. Without commit */
92 retval = xmldb_put(h, (char*)db2, OP_MERGE, xt, clicon_username_get(h), cbret);
93 done:
94 xmldb_get0_free(h, &xt);
95 return retval;
96 }
97
98 /*! Clixon startup startup mode: Commit startup configuration into running state
99 * @param[in] h Clixon handle
100 * @param[in] db tmp or startup
101 * @param[out] cbret If status is invalid contains error message
102 * @retval -1 Error
103 * @retval 0 Validation failed
104 * @retval 1 OK
105
106 OK:
107 reset
108 running |--------+------------> RUNNING
109 parse validate OK / commit
110 startup -------+--+-------+------------+
111
112
113 INVALID (requires manual edit of candidate)
114 failsafe ----------------------+
115 reset \ commit
116 running |-------+---------------> RUNNING FAILSAFE
117 parse validate fail
118 startup ---+-------------------------------------> INVALID XML
119
120 ERR: (requires repair of startup) NYI
121 failsafe ----------------------+
122 reset \ commit
123 running |-------+---------------> RUNNING FAILSAFE
124 parse fail
125 startup --+-------------------------------------> BROKEN XML
126
127 * @note: if commit fails, copy factory to running
128 */
129 int
startup_mode_startup(clicon_handle h,char * db,cbuf * cbret)130 startup_mode_startup(clicon_handle h,
131 char *db,
132 cbuf *cbret)
133 {
134 int retval = -1;
135 int ret;
136
137 if (strcmp(db, "running")==0){
138 clicon_err(OE_FATAL, 0, "Invalid startup db: %s", db);
139 goto done;
140 }
141 /* If startup does not exist, create it empty */
142 if (xmldb_exists(h, db) != 1){ /* diff */
143 if (xmldb_create(h, db) < 0) /* diff */
144 return -1;
145 }
146 if ((ret = startup_commit(h, db, cbret)) < 0)
147 goto done;
148 if (ret == 0)
149 goto fail;
150 retval = 1;
151 done:
152 return retval;
153 fail:
154 retval = 0;
155 goto done;
156 }
157
158 /*! Merge xml in filename into database
159 * @retval -1 Error
160 * @retval 0 Validation failed (with cbret set)
161 * @retval 1 Validation OK
162 */
163 static int
load_extraxml(clicon_handle h,char * filename,const char * db,cbuf * cbret)164 load_extraxml(clicon_handle h,
165 char *filename,
166 const char *db,
167 cbuf *cbret)
168 {
169 int retval = -1;
170 cxobj *xt = NULL;
171 int fd = -1;
172 yang_stmt *yspec = NULL;
173
174 if (filename == NULL)
175 return 1;
176 if ((fd = open(filename, O_RDONLY)) < 0){
177 clicon_err(OE_UNIX, errno, "open(%s)", filename);
178 goto done;
179 }
180 yspec = clicon_dbspec_yang(h);
181 if (clixon_xml_parse_file(fd, YB_MODULE, yspec, NULL, &xt, NULL) < 0)
182 goto done;
183 /* Replace parent w first child */
184 if (xml_rootchild(xt, 0, &xt) < 0)
185 goto done;
186 /* Merge user reset state */
187 retval = xmldb_put(h, (char*)db, OP_MERGE, xt, clicon_username_get(h), cbret);
188 done:
189 if (fd != -1)
190 close(fd);
191 if (xt)
192 xml_free(xt);
193 return retval;
194 }
195
196 /*! Load extra XML via file and/or reset callback, and merge with current
197 * An application can add extra XML either via the -c <file> option or
198 * via the .ca_reset callback. This XML is "merged" into running, that is,
199 * it does not trigger validation calbacks.
200 * The function uses an extra "tmp" database, loads the file to it, and calls
201 * the reset function on it.
202 * @param[in] h Clicon handle
203 * @param[in] file (Optional) extra xml file
204 * @param[out] status Startup status
205 * @param[out] cbret If status is invalid contains error message
206 * @retval -1 Error
207 * @retval 0 Validation failed
208 * @retval 1 OK
209
210 running -----------------+----+------>
211 reset loadfile / merge
212 tmp |-------+-----+-----+
213 reset extrafile
214 */
215 int
startup_extraxml(clicon_handle h,char * file,cbuf * cbret)216 startup_extraxml(clicon_handle h,
217 char *file,
218 cbuf *cbret)
219 {
220 int retval = -1;
221 char *tmp_db = "tmp";
222 int ret;
223 cxobj *xt0 = NULL;
224 cxobj *xt = NULL;
225
226 /* Clear tmp db */
227 if (xmldb_db_reset(h, tmp_db) < 0)
228 goto done;
229 /* Application may define extra xml in its reset function */
230 if (clixon_plugin_reset_all(h, tmp_db) < 0)
231 goto done;
232 /* Extra XML can also be added via file */
233 if (file){
234 /* Parse and load file into tmp db */
235 if ((ret = load_extraxml(h, file, tmp_db, cbret)) < 0)
236 goto done;
237 if (ret == 0)
238 goto fail;
239 }
240 /*
241 * Check if tmp db is empty.
242 * It should be empty if extra-xml is null and reset plugins did nothing
243 * then skip validation.
244 */
245 if (xmldb_get(h, tmp_db, NULL, NULL, &xt0) < 0)
246 goto done;
247 if (xmldb_empty_get(h, tmp_db))
248 goto ok;
249 xt = NULL;
250 /* Validate the tmp db and return possibly upgraded xml in xt
251 */
252 if ((ret = startup_validate(h, tmp_db, &xt, cbret)) < 0)
253 goto done;
254 if (ret == 0)
255 goto fail;
256 if (xt==NULL || xml_child_nr(xt)==0)
257 goto ok;
258 /* Merge tmp into running (no commit) */
259 if ((ret = db_merge(h, tmp_db, "running", cbret)) < 0)
260 goto fail;
261 if (ret == 0)
262 goto fail;
263 ok:
264 retval = 1;
265 done:
266 if (xt0)
267 xml_free(xt0);
268 xmldb_get0_free(h, &xt);
269 if (xmldb_delete(h, tmp_db) != 0 && errno != ENOENT)
270 return -1;
271 return retval;
272 fail:
273 retval = 0;
274 goto done;
275 }
276
277 /*! Reset running and start in failsafe mode. If no failsafe then quit.
278 Typically done when startup status is not OK so
279
280 failsafe ----------------------+
281 reset \ commit
282 running ----|-------+---------------> RUNNING FAILSAFE
283 \
284 tmp |---------------------->
285 */
286 int
startup_failsafe(clicon_handle h)287 startup_failsafe(clicon_handle h)
288 {
289 int retval = -1;
290 int ret;
291 char *db = "failsafe";
292 cbuf *cbret = NULL;
293
294 if ((cbret = cbuf_new()) == NULL){
295 clicon_err(OE_XML, errno, "cbuf_new");
296 goto done;
297 }
298 if ((ret = xmldb_exists(h, db)) < 0)
299 goto done;
300 if (ret == 0){ /* No it does not exist, fail */
301 clicon_err(OE_DB, 0, "Startup failed and no Failsafe database found, exiting");
302 goto done;
303 }
304 /* Copy original running to tmp as backup (restore if error) */
305 if (xmldb_copy(h, "running", "tmp") < 0)
306 goto done;
307 if (xmldb_db_reset(h, "running") < 0)
308 goto done;
309 ret = candidate_commit(h, db, cbret);
310 if (ret != 1)
311 if (xmldb_copy(h, "tmp", "running") < 0)
312 goto done;
313 if (ret < 0)
314 goto done;
315 if (ret == 0){
316 clicon_err(OE_DB, 0, "Startup failed, Failsafe database validation failed %s", cbuf_get(cbret));
317 goto done;
318 }
319 clicon_log(LOG_NOTICE, "Startup failed, Failsafe database loaded ");
320 retval = 0;
321 done:
322 if (cbret)
323 cbuf_free(cbret);
324 return retval;
325 }
326
327 /*! Init modules state of the backend (server). To compare with startup XML
328 * Set the modules state as setopt to the datastore module.
329 * Only if CLICON_XMLDB_MODSTATE is enabled
330 * @retval -1 Error
331 * @retval 0 OK
332 */
333 int
startup_module_state(clicon_handle h,yang_stmt * yspec)334 startup_module_state(clicon_handle h,
335 yang_stmt *yspec)
336 {
337 int retval = -1;
338 cxobj *x = NULL;
339 int ret;
340
341 if (!clicon_option_bool(h, "CLICON_XMLDB_MODSTATE"))
342 goto ok;
343 /* Set up cache
344 * Now, access brief module cache with clicon_modst_cache_get(h, 1) */
345 if ((ret = yang_modules_state_get(h, yspec, NULL, NULL, 1, &x)) < 0)
346 goto done;
347 if (ret == 0)
348 goto fail;
349 ok:
350 retval = 1;
351 done:
352 if (x)
353 xml_free(x);
354 return retval;
355 fail:
356 retval = 0;
357 goto done;
358 }
359