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 <syslog.h>
52 #include <ifaddrs.h>
53 #include <sys/time.h>
54 #include <sys/socket.h>
55 #include <sys/stat.h>
56 #include <sys/param.h>
57 #include <sys/types.h>
58 #include <grp.h>
59 #include <netinet/in.h>
60 #include <arpa/inet.h>
61 #include <netinet/in.h>
62 #include <libgen.h>
63 
64 /* cligen */
65 #include <cligen/cligen.h>
66 
67 /* clicon */
68 #include <clixon/clixon.h>
69 
70 #include "clixon_backend_handle.h"
71 #include "backend_socket.h"
72 #include "backend_client.h"
73 #include "backend_plugin.h"
74 #include "backend_commit.h"
75 #include "backend_handle.h"
76 #include "backend_startup.h"
77 
78 /* Command line options to be passed to getopt(3) */
79 #define BACKEND_OPTS "hD:f:E:l:d:p:b:Fza:u:P:1qs:c:U:g:y:o:"
80 
81 #define BACKEND_LOGFILE "/usr/local/var/clixon_backend.log"
82 
83 /*! Clean and close all state of backend (but dont exit).
84  * Cannot use h after this
85  * @param[in]  h  Clixon handle
86  */
87 static int
backend_terminate(clicon_handle h)88 backend_terminate(clicon_handle h)
89 {
90     yang_stmt *yspec;
91     char      *pidfile = clicon_backend_pidfile(h);
92     int       sockfamily = clicon_sock_family(h);
93     char      *sockpath = clicon_sock(h);
94     cxobj     *x;
95     struct stat st;
96     int        ss;
97     cvec      *nsctx;
98 
99     clicon_debug(1, "%s", __FUNCTION__);
100     if ((ss = clicon_socket_get(h)) != -1)
101 	close(ss);
102     /* Disconnect datastore */
103     xmldb_disconnect(h);
104     /* Clear module state caches */
105     if ((x = clicon_modst_cache_get(h, 0)) != NULL)
106 	xml_free(x);
107     if ((x = clicon_modst_cache_get(h, 1)) != NULL)
108 	xml_free(x);
109     /* Free changelog */
110     if ((x = clicon_xml_changelog_get(h)) != NULL)
111 	xml_free(x);
112     if ((yspec = clicon_dbspec_yang(h)) != NULL)
113 	yspec_free(yspec);
114     if ((yspec = clicon_config_yang(h)) != NULL)
115 	yspec_free(yspec);
116     if ((yspec = clicon_nacm_ext_yang(h)) != NULL)
117 	yspec_free(yspec);
118     if ((nsctx = clicon_nsctx_global_get(h)) != NULL)
119 	cvec_free(nsctx);
120     if ((x = clicon_nacm_ext(h)) != NULL)
121 	xml_free(x);
122     if ((x = clicon_conf_xml(h)) != NULL)
123 	xml_free(x);
124     stream_publish_exit();
125     clixon_plugin_exit_all(h);
126     /* Delete all backend plugin RPC callbacks */
127     rpc_callback_delete_all(h);
128     /* Delete all backend plugin upgrade callbacks */
129     upgrade_callback_delete_all(h);
130     xpath_optimize_exit();
131 
132     if (pidfile)
133 	unlink(pidfile);
134     if (sockfamily==AF_UNIX && lstat(sockpath, &st) == 0)
135 	unlink(sockpath);
136     backend_handle_exit(h); /* Also deletes streams. Cannot use h after this. */
137     clixon_event_exit();
138     clicon_debug(1, "%s done", __FUNCTION__);
139     clicon_log_exit();
140     return 0;
141 }
142 
143 /*! Unlink pidfile and quit
144  */
145 static void
backend_sig_term(int arg)146 backend_sig_term(int arg)
147 {
148     static int i=0;
149 
150     if (i++ == 0)
151 	clicon_log(LOG_NOTICE, "%s: %s: pid: %u Signal %d",
152 		   __PROGRAM__, __FUNCTION__, getpid(), arg);
153     clicon_exit_set(); /* checked in clixon_event_loop() */
154 }
155 
156 /*! Create backend server socket and register callback
157  * @param[in]  h    Clicon handle
158  * @retval     s    Server socket file descriptor (see socket(2))
159  * @retval    -1    Error
160  */
161 static int
backend_server_socket(clicon_handle h)162 backend_server_socket(clicon_handle h)
163 {
164     int ss;
165 
166     /* Open control socket */
167     if ((ss = backend_socket_init(h)) < 0)
168 	return -1;
169     /* ss is a server socket that the clients connect to. The callback
170        therefore accepts clients on ss */
171     if (clixon_event_reg_fd(ss, backend_accept_client, h, "server socket") < 0) {
172 	close(ss);
173 	return -1;
174     }
175     return ss;
176 }
177 
178 /*! Load external NACM file
179  */
180 static int
nacm_load_external(clicon_handle h)181 nacm_load_external(clicon_handle h)
182 {
183     int         retval = -1;
184     char       *filename; /* NACM config file */
185     yang_stmt  *yspec = NULL;
186     cxobj      *xt = NULL;
187     struct stat st;
188     FILE       *f = NULL;
189     int         fd;
190 
191     filename = clicon_option_str(h, "CLICON_NACM_FILE");
192     if (filename == NULL || strlen(filename)==0){
193 	clicon_err(OE_UNIX, errno, "CLICON_NACM_FILE not set in NACM external mode");
194 	goto done;
195     }
196     if (stat(filename, &st) < 0){
197 	clicon_err(OE_UNIX, errno, "%s", filename);
198 	goto done;
199     }
200     if (!S_ISREG(st.st_mode)){
201 	clicon_err(OE_UNIX, 0, "%s is not a regular file", filename);
202 	goto done;
203     }
204     if ((f = fopen(filename, "r")) == NULL) {
205 	clicon_err(OE_UNIX, errno, "configure file: %s", filename);
206 	return -1;
207     }
208     if ((yspec = yspec_new()) == NULL)
209 	goto done;
210     if (yang_spec_parse_module(h, "ietf-netconf-acm", NULL, yspec) < 0)
211 	goto done;
212     fd = fileno(f);
213     /* Read configfile */
214     if (clixon_xml_parse_file(fd, YB_MODULE, yspec, NULL, &xt, NULL) < 0)
215 	goto done;
216     if (xt == NULL){
217 	clicon_err(OE_XML, 0, "No xml tree in %s", filename);
218 	goto done;
219     }
220     if (clicon_nacm_ext_yang_set(h, yspec) < 0)
221 	goto done;
222     if (clicon_nacm_ext_set(h, xt) < 0)
223 	goto done;
224 
225     retval = 0;
226  done:
227     if (f)
228 	fclose(f);
229     return retval;
230 }
231 
232 static int
xmldb_drop_priv(clicon_handle h,const char * db,uid_t uid,gid_t gid)233 xmldb_drop_priv(clicon_handle h,
234 		const char   *db,
235 		uid_t         uid,
236 		gid_t         gid)
237 {
238     int         retval = -1;
239     char       *filename = NULL;
240 
241     if (xmldb_db2file(h, db, &filename) < 0)
242 	goto done;
243     if (chown(filename, uid, gid) < 0){
244 	clicon_err(OE_UNIX, errno, "chown");
245 	goto done;
246     }
247     retval = 0;
248  done:
249     if (filename)
250 	free(filename);
251     return retval;
252 }
253 
254 /*! Drop root privileges uid and gid to Clixon user/group and
255  *
256  * If config options are right, drop process uid/guid privileges and change some
257  * file ownerships.
258  * "Right" means:
259  * - uid is currently 0 (started as root)
260  * - CLICON_BACKEND_USER is set
261  * - CLICON_BACKEND_PRIVILEGES is not "none"
262  * @param[in] h   Clicon handle
263  * @param[in] gid Group id (assume already known)
264  * @retval    0   OK
265  * @retval   -1   Error
266  */
267 static int
check_drop_priv(clicon_handle h,gid_t gid)268 check_drop_priv(clicon_handle h,
269 		gid_t         gid)
270 {
271     int              retval = -1;
272     uid_t            uid;
273     uid_t            newuid = -1;
274     enum priv_mode_t priv_mode = PM_NONE;
275     char            *backend_user = NULL;
276 
277     /* Get privileges mode (for dropping privileges) */
278     priv_mode = clicon_backend_privileges_mode(h);
279     if (priv_mode == PM_NONE)
280 	goto ok;
281 
282     /* From here, drop privileges */
283     /* Check backend user exists */
284     if ((backend_user = clicon_backend_user(h)) == NULL){
285 	clicon_err(OE_DAEMON, EPERM, "Privileges cannot be dropped without specifying CLICON_BACKEND_USER\n");
286 	goto done;
287     }
288     /* Get (wanted) new backend user id */
289     if (name2uid(backend_user, &newuid) < 0){
290 	clicon_err(OE_DAEMON, errno, "'%s' is not a valid user .\n", backend_user);
291 	goto done;
292     }
293     /* get current backend userid, if already at this level OK */
294     if ((uid = getuid()) == newuid)
295 	goto ok;
296     if (uid != 0){
297 	clicon_err(OE_DAEMON, EPERM, "Privileges can only be dropped from root user (uid is %u)\n", uid);
298 	goto done;
299     }
300     /* When dropping privileges, datastores are created if they do not exist.
301      * But when drops are not made, datastores are created on demand.
302      * XXX: move the creation to top-level so they are always created at init?
303      */
304     if (xmldb_exists(h, "running") != 1)
305 	if (xmldb_create(h, "running") < 0)
306 	    goto done;
307     if (xmldb_drop_priv(h, "running", newuid, gid) < 0)
308 	goto done;
309     if (xmldb_exists(h, "candidate") != 1)
310 	if (xmldb_create(h, "candidate") < 0)
311 	    goto done;
312     if (xmldb_drop_priv(h, "candidate", newuid, gid) < 0)
313 	goto done;
314     if (xmldb_exists(h, "startup") != 1)
315 	if (xmldb_create(h, "startup") < 0)
316 	    goto done;
317     if (xmldb_drop_priv(h, "startup", newuid, gid) < 0)
318 	goto done;
319 
320     if (setgid(gid) == -1) {
321 	clicon_err(OE_DAEMON, errno, "setgid %d", gid);
322 	goto done;
323     }
324     switch (priv_mode){
325     case PM_DROP_PERM:
326 	if (drop_priv_perm(newuid) < 0)
327 	    goto done;
328 	/* Verify you cannot regain root privileges */
329 	if (setuid(0) != -1){
330 	    clicon_err(OE_DAEMON, EPERM, "Could regain root privilieges");
331 	    goto done;
332 	}
333 	break;
334     case PM_DROP_TEMP:
335 	if (drop_priv_temp(newuid) < 0)
336 	    goto done;
337 	break;
338     case PM_NONE:
339 	break; /* catched above */
340     }
341  ok:
342     retval = 0;
343  done:
344     return retval;
345 }
346 
347 /*! Given a retval, transform to status or fatal error
348  *
349  * @param[in]  ret    Return value from xml validation function
350  * @param[out] status Transform status according to rules below
351  * @retval    0       OK, status set
352  * @retval   -1       Fatal error outside scope of startup_status
353  * Transformation rules:
354  * 1) retval -1 assume clicon_errno/suberrno set. Special case from xml parser
355  * is clicon_suberrno = XMLPARSE_ERRNO which assumes an XML (non-fatal) parse
356  * error which translates to -> STARTUP_ERR
357  * All other error cases translates to fatal error
358  * 2) retval 0 is xml validation fails -> STARTUP_INVALID
359  * 3) retval 1 is OK -> STARTUP_OK
360  * 4) any other retval translates to fatal error
361  */
362 static int
ret2status(int ret,enum startup_status * status)363 ret2status(int                  ret,
364 	   enum startup_status *status)
365 {
366     int retval = -1;
367 
368     switch (ret){
369     case -1:
370 	if (clicon_suberrno != XMLPARSE_ERRNO)
371 	    goto done;
372 	clicon_err_reset();
373 	*status = STARTUP_ERR;
374 	break;
375     case 0:
376 	*status = STARTUP_INVALID;
377 	break;
378     case 1:
379 	*status = STARTUP_OK;
380 	break;
381     default:
382 	clicon_err(OE_CFG, EINVAL, "No such retval %d", retval);
383     } /* switch */
384     retval = 0;
385  done:
386     return retval;
387 }
388 
389 /*! usage
390  */
391 static void
usage(clicon_handle h,char * argv0)392 usage(clicon_handle h,
393       char         *argv0)
394 {
395     char *plgdir   = clicon_backend_dir(h);
396     char *confsock = clicon_sock(h);
397     char *confpid  = clicon_backend_pidfile(h);
398     char *group    = clicon_sock_group(h);
399 
400     fprintf(stderr, "usage:%s <options>*\n"
401 	    "where options are\n"
402             "\t-h\t\tHelp\n"
403     	    "\t-D <level>\tDebug level\n"
404     	    "\t-f <file>\tCLICON config file\n"
405 	    "\t-E <dir> \tExtra configuration file directory\n"
406 	    "\t-l (s|e|o|f<file>)  Log on (s)yslog, std(e)rr or std(o)ut (stderr is default) Only valid if -F, if background syslog is on syslog.\n"
407 	    "\t-d <dir>\tSpecify backend plugin directory (default: %s)\n"
408 	    "\t-p <dir>\tYang directory path (see CLICON_YANG_DIR)\n"
409 	    "\t-b <dir>\tSpecify XMLDB database directory\n"
410     	    "\t-F\t\tRun in foreground, do not run as daemon\n"
411     	    "\t-z\t\tKill other config daemon and exit\n"
412     	    "\t-a UNIX|IPv4|IPv6  Internal backend socket family\n"
413     	    "\t-u <path|addr>\tInternal socket domain path or IP addr (see -a)(default: %s)\n"
414     	    "\t-P <file>\tPid filename (default: %s)\n"
415     	    "\t-1\t\tRun once and then quit (dont wait for events)\n"
416 	    "\t-s <mode>\tSpecify backend startup mode: none|startup|running|init)\n"
417 	    "\t-c <file>\tLoad extra xml configuration, but don't commit.\n"
418 	    "\t-q \t\tQuit startup directly after upgrading and print result on stdout\n"
419 	    "\t-U <user>\tRun backend daemon as this user AND drop privileges permanently\n"
420 	    "\t-g <group>\tClient membership required to this group (default: %s)\n"
421 
422 	    "\t-y <file>\tLoad yang spec file (override yang main module)\n"
423 	    "\t-o \"<option>=<value>\"\tGive configuration option overriding config file (see clixon-config.yang)\n",
424 	    argv0,
425 	    plgdir ? plgdir : "none",
426 	    confsock ? confsock : "none",
427 	    confpid ? confpid : "none",
428 	    group ? group : "none"
429 	    );
430     exit(-1);
431 }
432 
433 int
main(int argc,char ** argv)434 main(int    argc,
435      char **argv)
436 {
437     int           retval = -1;
438     int           c;
439     int           zap;
440     int           foreground;
441     int           once;
442     enum startup_mode_t startup_mode;
443     char         *extraxml_file;
444     char         *backend_group = NULL;
445     char         *argv0 = argv[0];
446     struct stat   st;
447     clicon_handle h;
448     int           help = 0;
449     int           pid;
450     char         *pidfile;
451     char         *sock;
452     int           sockfamily;
453     char         *nacm_mode;
454     int           logdst = CLICON_LOG_SYSLOG|CLICON_LOG_STDERR;
455     yang_stmt    *yspec = NULL;
456     char         *str;
457     int           ss = -1; /* server socket */
458     cbuf         *cbret = NULL; /* startup cbuf if invalid */
459     enum startup_status status = STARTUP_ERR; /* Startup status */
460     int           ret;
461     char         *dir;
462     gid_t         gid = -1;
463     cvec         *nsctx_global = NULL; /* Global namespace context */
464     size_t        cligen_buflen;
465     size_t        cligen_bufthreshold;
466     int           dbg;
467 
468     /* In the startup, logs to stderr & syslog and debug flag set later */
469     clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
470     /* Initiate CLICON handle */
471     if ((h = backend_handle_init()) == NULL)
472 	return -1;
473 
474     foreground = 0;
475     once = 0;
476     zap = 0;
477     extraxml_file = NULL;
478     dbg = 0;
479 
480     /*
481      * Command-line options for help, debug, and config-file
482      */
483     opterr = 0;
484     optind = 1;
485     while ((c = getopt(argc, argv, BACKEND_OPTS)) != -1)
486 	switch (c) {
487 	case 'h':
488 	    /* Defer the call to usage() to later. Reason is that for helpful
489 	       text messages, default dirs, etc, are not set until later.
490 	       But this measn that we need to check if 'help' is set before
491 	       exiting, and then call usage() before exit.
492 	    */
493 	    help = 1;
494 	    break;
495 	case 'D' : /* debug */
496 	    if (sscanf(optarg, "%d", &dbg) != 1)
497 		usage(h, argv[0]);
498 	    break;
499 	case 'f': /* config file */
500 	    if (!strlen(optarg))
501 		usage(h, argv[0]);
502 	    clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg);
503 	    break;
504 	case 'E': /* extra config directory */
505 	    if (!strlen(optarg))
506 		usage(h, argv[0]);
507 	    clicon_option_str_set(h, "CLICON_CONFIGDIR", optarg);
508 	    break;
509 	case 'l': /* Log destination: s|e|o */
510 	    if ((logdst = clicon_log_opt(optarg[0])) < 0)
511 		usage(h, argv[0]);
512 	    if (logdst == CLICON_LOG_FILE &&
513 		strlen(optarg)>1 &&
514 		clicon_log_file(optarg+1) < 0)
515 		goto done;
516 	    break;
517 	}
518     /*
519      * Here we have the debug flag settings, use that.
520      * Syslogs also to stderr, but later turn stderr off in daemon mode.
521      * error only to syslog. debug to syslog
522      * XXX: if started in a start-daemon script, there will be irritating
523      * double syslogs until fork below.
524      */
525     clicon_log_init(__PROGRAM__, dbg?LOG_DEBUG:LOG_INFO, logdst);
526     clicon_debug_init(dbg, NULL);
527 
528     /* Find and read configfile */
529     if (clicon_options_main(h) < 0){
530 	if (help)
531 	    usage(h, argv[0]);
532 	goto done;
533     }
534     /* External NACM file? */
535     nacm_mode = clicon_option_str(h, "CLICON_NACM_MODE");
536     if (nacm_mode && strcmp(nacm_mode, "external") == 0)
537 	if (nacm_load_external(h) < 0)
538 	    goto done;
539 
540     /* Now run through the operational args */
541     opterr = 1;
542     optind = 1;
543     while ((c = getopt(argc, argv, BACKEND_OPTS)) != -1)
544 	switch (c) {
545 	case 'h' : /* help */
546 	case 'D' : /* debug */
547 	case 'f': /* config file */
548 	case 'E': /* extra config dir */
549 	case 'l' :
550 	    break; /* see above */
551 	case 'd':  /* Plugin directory */
552 	    if (!strlen(optarg))
553 		usage(h, argv[0]);
554 	    if (clicon_option_add(h, "CLICON_BACKEND_DIR", optarg) < 0)
555 		goto done;
556 	    break;
557 	case 'b':  /* XMLDB database directory */
558 	    if (!strlen(optarg))
559 		usage(h, argv[0]);
560 	    if (clicon_option_add(h, "CLICON_XMLDB_DIR", optarg) < 0)
561 		goto done;
562 	    break;
563 	case 'p' : /* yang dir path */
564 	    if (clicon_option_add(h, "CLICON_YANG_DIR", optarg) < 0)
565 		goto done;
566 	    break;
567 	case 'F' : /* foreground */
568 	    foreground = 1;
569 	    break;
570 	case 'z': /* Zap other process */
571 	    zap++;
572 	    break;
573 	case 'a': /* internal backend socket address family */
574 	    if (clicon_option_add(h, "CLICON_SOCK_FAMILY", optarg) < 0)
575 		goto done;
576 	    break;
577 	case 'u': /* config unix domain path / ip address */
578 	    if (!strlen(optarg))
579 		usage(h, argv[0]);
580 	    if (clicon_option_add(h, "CLICON_SOCK", optarg) < 0)
581 		goto done;
582 	    break;
583 	case 'P': /* pidfile */
584 	    if (clicon_option_add(h, "CLICON_BACKEND_PIDFILE", optarg) < 0)
585 		goto done;
586 	    break;
587 	case '1' : /* Quit after reading database once - dont wait for events */
588 	    once = 1;
589 	    break;
590 	case 'q': /* Quit directly after startup upgrading and print result on stdout */
591 	    if (clicon_quit_upgrade_set(h, 1) < 0)
592 		goto done;
593 	    break;
594 	case 's' : /* startup mode */
595 	    if (clicon_option_add(h, "CLICON_STARTUP_MODE", optarg) < 0)
596 		goto done;
597 	    if (clicon_startup_mode(h) < 0){
598 		fprintf(stderr, "Invalid startup mode: %s\n", optarg);
599 		usage(h, argv[0]);
600 	    }
601 	    break;
602 	case 'c': /* Load application config */
603 	    extraxml_file = optarg;
604 	    break;
605 	case 'U': /* config user (for socket and drop privileges) */
606 	    if (clicon_option_add(h, "CLICON_USER", optarg) < 0)
607 		goto done;
608 	    if (clicon_option_add(h, "CLICON_BACKEND_PRIVILEGES", "drop_permanent") < 0)
609 		goto done;
610 	    break;
611 	case 'g': /* config socket group */
612 	    if (clicon_option_add(h, "CLICON_SOCK_GROUP", optarg) < 0)
613 		goto done;
614 	    break;
615 	case 'y' : /* Load yang absolute filename */
616 	    if (clicon_option_add(h, "CLICON_YANG_MAIN_FILE", optarg) < 0)
617 		goto done;
618 	    break;
619 	case 'o':{ /* Configuration option */
620 	    char          *val;
621 	    if ((val = index(optarg, '=')) == NULL)
622 		usage(h, argv0);
623 	    *val++ = '\0';
624 	    if (clicon_option_add(h, optarg, val) < 0)
625 		goto done;
626 	    break;
627 	}
628 	default:
629 	    usage(h, argv[0]);
630 	    break;
631 	}
632 
633     argc -= optind;
634     argv += optind;
635 
636     /* Access the remaining argv/argc options (after --) w clicon-argv_get() */
637     clicon_argv_set(h, argv0, argc, argv);
638 
639     clicon_log_init(__PROGRAM__, dbg?LOG_DEBUG:LOG_INFO, logdst);
640 
641     /* Defer: Wait to the last minute to print help message */
642     if (help)
643 	usage(h, argv[0]);
644 
645     /* Init cligen buffers */
646     cligen_buflen = clicon_option_int(h, "CLICON_CLI_BUF_START");
647     cligen_bufthreshold = clicon_option_int(h, "CLICON_CLI_BUF_THRESHOLD");
648     cbuf_alloc_set(cligen_buflen, cligen_bufthreshold);
649 
650 #ifndef HAVE_LIBXML2
651     if (clicon_yang_regexp(h) ==  REGEXP_LIBXML2){
652 	clicon_err(OE_FATAL, 0, "CLICON_YANG_REGEXP set to libxml2, but HAVE_LIBXML2 not set (Either change CLICON_YANG_REGEXP to posix, or run: configure --with-libxml2))");
653 	goto done;
654     }
655 #endif
656     /* Check pid-file, if zap kil the old daemon, else return here */
657     if ((pidfile = clicon_backend_pidfile(h)) == NULL){
658 	clicon_err(OE_FATAL, 0, "pidfile not set");
659 	goto done;
660     }
661     sockfamily = clicon_sock_family(h);
662     if ((sock = clicon_sock(h)) == NULL){
663 	clicon_err(OE_FATAL, 0, "sock not set");
664 	goto done;
665     }
666     if (pidfile_get(pidfile, &pid) < 0)
667 	return -1;
668     if (zap){
669 	if (pid && pidfile_zapold(pid) < 0)
670 	    return -1;
671 	if (lstat(pidfile, &st) == 0)
672 	    unlink(pidfile);
673 	if (sockfamily==AF_UNIX && lstat(sock, &st) == 0)
674 	    unlink(sock);
675 	backend_terminate(h);
676 	exit(0); /* OK */
677     }
678     else
679 	if (pid){
680 	    clicon_err(OE_DAEMON, 0, "Daemon already running with pid %d\n(Try killing it with %s -z)",
681 		       pid, argv0);
682 	    return -1; /* goto done deletes pidfile */
683 	}
684 
685     /* After this point we can goto done on error
686      * Here there is either no old process or we have killed it,..
687      */
688     if (lstat(pidfile, &st) == 0)
689 	unlink(pidfile);
690     if (sockfamily==AF_UNIX && lstat(sock, &st) == 0)
691 	unlink(sock);
692 
693     /* Sanity check: backend group exists */
694     if ((backend_group = clicon_sock_group(h)) == NULL){
695 	clicon_err(OE_FATAL, 0, "clicon_sock_group option not set");
696 	return -1;
697     }
698     if (group_name2gid(backend_group, &gid) < 0){
699 	clicon_log(LOG_ERR, "'%s' does not seem to be a valid user group.\n" /* \n required here due to multi-line log */
700 		   "The config demon requires a valid group to create a server UNIX socket\n"
701 		   "Define a valid CLICON_SOCK_GROUP in %s or via the -g option\n"
702 		   "or create the group and add the user to it. Check documentation for how to do this on your platform",
703 		   backend_group,
704 		   clicon_configfile(h));
705 	goto done;
706     }
707 
708     /* Treat unknown XML as anydata */
709     if (clicon_option_bool(h, "CLICON_YANG_UNKNOWN_ANYDATA") == 1)
710 	xml_bind_yang_unknown_anydata(1);
711 
712     /* Publish stream on pubsub channels.
713      * CLICON_STREAM_PUB should be set to URL to where streams are published
714      * and configure should be run with --enable-publish
715      */
716     if (clicon_option_exists(h, "CLICON_STREAM_PUB") &&
717 	stream_publish_init() < 0)
718 	goto done;
719     /* Connect to plugin to get a handle */
720     if (xmldb_connect(h) < 0)
721 	goto done;
722 
723     /* Set default namespace according to CLICON_NAMESPACE_NETCONF_DEFAULT */
724     xml_nsctx_namespace_netconf_default(h);
725 
726     /* Add (hardcoded) netconf features in case ietf-netconf loaded here
727      * Otherwise it is loaded in netconf_module_load below
728      */
729     if (netconf_module_features(h) < 0)
730 	goto done;
731 
732     /* Create top-level yang spec and store as option */
733     if ((yspec = yspec_new()) == NULL)
734 	goto done;
735     clicon_dbspec_yang_set(h, yspec);
736 
737     /* Load backend plugins before yangs are loaded (eg extension callbacks) */
738     if ((dir = clicon_backend_dir(h)) != NULL &&
739 	clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir,
740 			    clicon_option_str(h, "CLICON_BACKEND_REGEXP")) < 0)
741 	goto done;
742 
743     /* Load Yang modules
744      * 1. Load a yang module as a specific absolute filename */
745     if ((str = clicon_yang_main_file(h)) != NULL)
746 	if (yang_spec_parse_file(h, str, yspec) < 0)
747 	    goto done;
748     /* 2. Load a (single) main module */
749     if ((str = clicon_yang_module_main(h)) != NULL)
750 	if (yang_spec_parse_module(h, str, clicon_yang_module_revision(h),
751 				   yspec) < 0)
752 	    goto done;
753     /* 3. Load all modules in a directory (will not overwrite file loaded ^) */
754     if ((str = clicon_yang_main_dir(h)) != NULL)
755 	if (yang_spec_load_dir(h, str, yspec) < 0)
756 	    goto done;
757     /* Load clixon lib yang module */
758     if (yang_spec_parse_module(h, "clixon-lib", NULL, yspec) < 0)
759 	goto done;
760     /* Load yang module library, RFC7895 */
761     if (yang_modules_init(h) < 0)
762 	goto done;
763     /* Add netconf yang spec, used by netconf client and as internal protocol
764      */
765     if (netconf_module_load(h) < 0)
766 	goto done;
767     /* Load yang restconf module */
768     if (yang_spec_parse_module(h, "ietf-restconf", NULL, yspec)< 0)
769 	goto done;
770     /* Load yang Restconf stream discovery */
771     if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC8040") &&
772 	yang_spec_parse_module(h, "ietf-restconf-monitoring", NULL, yspec)< 0)
773 	goto done;
774     /* Load yang Netconf stream discovery */
775     if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") &&
776 	yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0)
777 	goto done;
778     /* Load yang YANG module state */
779     if (clicon_option_bool(h, "CLICON_XMLDB_MODSTATE") &&
780 	yang_spec_parse_module(h, "ietf-yang-library", NULL, yspec)< 0)
781 	goto done;
782     /* Here all modules are loaded
783      * Compute and set canonical namespace context
784      */
785     if (xml_nsctx_yangspec(yspec, &nsctx_global) < 0)
786 	goto done;
787     if (clicon_nsctx_global_set(h, nsctx_global) < 0)
788 	goto done;
789 
790     /* Initialize server socket and save it to handle */
791     if (backend_rpc_init(h) < 0)
792 	goto done;
793 
794     /* Must be after netconf_module_load, but before startup code */
795     if (clicon_option_bool(h, "CLICON_XML_CHANGELOG"))
796 	if (clixon_xml_changelog_init(h) < 0)
797 	    goto done;
798 
799     /* Save modules state of the backend (server). Compare with startup XML */
800     if (startup_module_state(h, yspec) < 0)
801 	goto done;
802     /* Startup mode needs to be defined,  */
803     startup_mode = clicon_startup_mode(h);
804     if ((int)startup_mode == -1){
805 	clicon_log(LOG_ERR, "Startup mode undefined. Specify option CLICON_STARTUP_MODE or specify -s option to clicon_backend.");
806 	goto done;
807     }
808     /* Check that netconf :startup is enabled */
809     if (startup_mode == SM_STARTUP &&
810 	!if_feature(yspec, "ietf-netconf", "startup")){
811 	clicon_log(LOG_ERR, "Startup mode selected but Netconf :startup feature is not enabled. Enable with option: <CLICON_FEATURE>ietf-netconf:startup</CLICON_FEATURE>");
812 	goto done;
813     }
814 
815     /* Init running db if it is not there
816      */
817     if (xmldb_exists(h, "running") != 1)
818 	if (xmldb_create(h, "running") < 0)
819 	    return -1;
820     xmldb_delete(h, "candidate");
821     /* If startup fails, lib functions report invalidation info in a cbuf */
822     if ((cbret = cbuf_new()) == NULL){
823 	clicon_err(OE_XML, errno, "cbuf_new");
824 	goto done;
825     }
826     switch (startup_mode){
827     case SM_INIT: /* Scratch running and start from empty */
828 	/* [Delete and] create running db */
829 	if (xmldb_db_reset(h, "running") < 0)
830 	    goto done;
831     case SM_NONE: /* Fall through *
832 		   * Load plugins and call plugin_init() */
833 	status = STARTUP_OK;
834 	break;
835     case SM_RUNNING: /* Use running as startup */
836 	/* Copy original running to tmp as backup (restore if error) */
837 	if (xmldb_copy(h, "running", "tmp") < 0)
838 	    goto done;
839 	ret = startup_mode_startup(h, "tmp", cbret);
840 	/* If ret fails, copy tmp back to running */
841 	if (ret != 1)
842 	    if (xmldb_copy(h, "tmp", "running") < 0)
843 		goto done;
844 	if (ret2status(ret, &status) < 0)
845 	    goto done;
846 	break;
847     case SM_STARTUP:
848 	/* Copy original running to tmp as backup (restore if error) */
849 	if (xmldb_copy(h, "running", "tmp") < 0)
850 	    goto done;
851 	/* Load and commit from startup */
852 	ret = startup_mode_startup(h, "startup", cbret);
853 	/* If ret fails, copy tmp back to running */
854 	if (ret != 1)
855 	    if (xmldb_copy(h, "tmp", "running") < 0)
856 		goto done;
857 	/* clear startup dbcache */
858 	xmldb_clear(h, "startup");
859 	if (ret2status(ret, &status) < 0)
860 	    goto done;
861 	/* if status = STARTUP_INVALID, cbret contains info */
862     }
863     /* Quit after upgrade catch-all, running/startup quits in upgrade code */
864     if (clicon_quit_upgrade_get(h) == 1)
865 	goto done;
866 
867     /*
868      * Disable unknown to anydata auto-creation after startup
869      */
870     if (clicon_option_bool(h, "CLICON_YANG_UNKNOWN_ANYDATA") == 1){
871 	clicon_option_bool_set(h, "CLICON_YANG_UNKNOWN_ANYDATA", 0);
872 	xml_bind_yang_unknown_anydata(0);
873     }
874     /* Merge extra XML from file and reset function to running
875      */
876     if (status == STARTUP_OK && startup_mode != SM_NONE){
877 	if ((ret = startup_extraxml(h, extraxml_file, cbret)) < 0)
878 	    goto done;
879 	if (ret2status(ret, &status) < 0)
880 	    goto done;
881 	/* if status = STARTUP_INVALID, cbret contains info */
882     }
883 
884     if (status != STARTUP_OK){
885 	if (cbuf_len(cbret))
886 	    clicon_log(LOG_NOTICE, "%s: %u %s", __PROGRAM__, getpid(), cbuf_get(cbret));
887 	if (startup_failsafe(h) < 0){
888 	    goto done;
889 	}
890     }
891 
892     /* Initiate the shared candidate. */
893     if (xmldb_copy(h, "running", "candidate") < 0)
894 	goto done;
895     xmldb_modified_set(h, "candidate", 0);
896 
897     /* Set startup status */
898     if (clicon_startup_status_set(h, status) < 0)
899 	goto done;
900 
901     if (status == STARTUP_INVALID && cbuf_len(cbret))
902 	clicon_log(LOG_NOTICE, "%s: %u %s", __PROGRAM__, getpid(), cbuf_get(cbret));
903 
904     /* Call backend plugin_start with user -- options */
905     if (clixon_plugin_start_all(h) < 0)
906 	goto done;
907     /* -1 option to run only once */
908     if (once)
909 	goto ok;
910 
911     /* Daemonize and initiate logging. Note error is initiated here to make
912        demonized errors OK. Before this stage, errors are logged on stderr
913        also */
914     if (foreground==0){
915 	clicon_log_init(__PROGRAM__, dbg?LOG_DEBUG:LOG_INFO,
916 			logdst==CLICON_LOG_FILE?CLICON_LOG_FILE:CLICON_LOG_SYSLOG);
917 	if (daemon(0, 0) < 0){
918 	    fprintf(stderr, "config: daemon");
919 	    exit(-1);
920 	}
921 
922     }
923     /* Call plugin callbacks when in background and before dropped privileges */
924     if (clixon_plugin_daemon_all(h) < 0)
925 	goto done;
926 
927     /* Write pid-file */
928     if ((pid = pidfile_write(pidfile)) <  0)
929 	goto done;
930 
931     clicon_log(LOG_NOTICE, "%s: %u Started", __PROGRAM__, getpid());
932     if (set_signal(SIGTERM, backend_sig_term, NULL) < 0){
933 	clicon_err(OE_DAEMON, errno, "Setting signal");
934 	goto done;
935     }
936     if (set_signal(SIGINT, backend_sig_term, NULL) < 0){
937 	clicon_err(OE_DAEMON, errno, "Setting signal");
938 	goto done;
939     }
940 
941     /* Initialize server socket and save it to handle */
942     if ((ss = backend_server_socket(h)) < 0)
943 	goto done;
944     if (clicon_socket_set(h, ss) < 0)
945 	goto done;
946     if (dbg)
947 	clicon_option_dump(h, dbg);
948     /* Depending on configure setting, privileges may be dropped here after
949      * initializations */
950     if (check_drop_priv(h, gid) < 0)
951 	goto done;
952 
953     /* Start session-id for clients */
954     clicon_session_id_set(h, 0);
955 
956     if (stream_timer_setup(0, h) < 0)
957 	goto done;
958     if (clixon_event_loop() < 0)
959 	goto done;
960  ok:
961     retval = 0;
962  done:
963     if (cbret)
964 	cbuf_free(cbret);
965     clicon_log(LOG_NOTICE, "%s: %u Terminated retval:%d", __PROGRAM__, getpid(), retval);
966     backend_terminate(h); /* Cannot use h after this */
967 
968     return retval;
969 }
970