Exemple #1
0
/*
 * Call backend plugin
 */
int
from_client_validate(clicon_handle h,
		     int s, 
		     struct clicon_msg *msg, 
		     const char *label)
{
    char *dbname;
    int retval = -1;

    if (clicon_msg_validate_decode(msg, &dbname, label) < 0){
	send_msg_err(s, clicon_errno, clicon_suberrno,
		     clicon_err_reason);
	goto err;
    }

    clicon_debug(1, "Validate %s",  dbname);

    if (candidate_validate(h, dbname, clicon_running_db(h)) < 0){
	clicon_debug(1, "Validate %s failed",  dbname);
	retval = 0; /* We ignore errors from commit, but maybe
		       we should fail on fatal errors? */
	goto err;
    }
    retval = 0;
    if (send_msg_ok(s) < 0)
	goto done;
    goto done;
  err:
    /* XXX: more elaborate errstring? */
    if (send_msg_err(s, clicon_errno, clicon_suberrno, "%s", clicon_err_reason) < 0)
	retval = -1;
  done:
    unchunk_group(__FUNCTION__);
    return retval;
} /* from_client_validate */
Exemple #2
0
/*! Unload all plugins: call exit function and close shared handle
 * @param[in]  h       Clicon handle
 */
int
clixon_plugin_exit(clicon_handle h)
{
    clixon_plugin *cp;
    plgexit_t     *exitfn;
    int            i;
    char          *error;
    
    for (i = 0; i < _clixon_nplugins; i++) {
	cp = &_clixon_plugins[i];
	if ((exitfn = cp->cp_api.ca_exit) == NULL)
	    continue;
	if (exitfn(h) < 0) {
	    clicon_debug(1, "plugin_exit() failed");
	    return -1;
	}
	if (dlclose(cp->cp_handle) != 0) {
	    error = (char*)dlerror();
	    clicon_err(OE_PLUGIN, errno, "dlclose: %s", error ? error : "Unknown error");
	}
    }
    if (_clixon_plugins){
	free(_clixon_plugins);
	_clixon_plugins = NULL;
    }
    _clixon_nplugins = 0;
    return 0;
}
Exemple #3
0
/*
 * \brief open a unix-domain socket and bind it to a file.
 *
 * The socket is accessed via CLICON_SOCK option, has 770 permissions
 * and group according to CLICON_SOCK_GROUP option.
 */
int
config_socket_init(clicon_handle h)
{
    int s;
    struct sockaddr_un addr;
    mode_t             old_mask;
    char              *config_sock;
    char              *config_group;
    gid_t              gid;
    struct stat        st;

    /* first find sockpath and remove it if it exists (it shouldn't) */
    if ((config_sock = clicon_sock(h)) == NULL)
	return -1;
    if (lstat(config_sock, &st) == 0 && unlink(config_sock) < 0){
	clicon_err(OE_UNIX, errno, "%s: unlink(%s)", __FUNCTION__, config_sock);
	return -1;
    }
    /* then find configuration group (for clients) and find its groupid */
    if ((config_group = clicon_sock_group(h)) == NULL)
	return -1;
    if (group_name2gid(config_group, &gid) < 0)
	return -1;
#if 0
    if (gid == 0) 
	clicon_log(LOG_WARNING, "%s: No such group: %s\n", __FUNCTION__, config_group);
#endif
    /* create unix socket */
    if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
	clicon_err(OE_UNIX, errno, "%s: socket", __FUNCTION__);
	return -1;
    }

//    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void*)&one, sizeof(one));
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, config_sock, sizeof(addr.sun_path)-1);
    old_mask = umask(S_IRWXO | S_IXGRP | S_IXUSR);
    if (bind(s, (struct sockaddr *)&addr, SUN_LEN(&addr)) < 0){
	clicon_err(OE_UNIX, errno, "%s: bind", __FUNCTION__);
	umask(old_mask); 
	goto err;
    }
    umask(old_mask); 
    /* change socket path file group */
    if (lchown(config_sock, -1, gid) < 0){
	clicon_err(OE_UNIX, errno, "%s: lchown(%s, %s)", __FUNCTION__, 
		config_sock, config_group);
	goto err;
    }
    clicon_debug(1, "Listen on server socket at %s", addr.sun_path);
    if (listen(s, 5) < 0){
	clicon_err(OE_UNIX, errno, "%s: listen", __FUNCTION__);
	goto err;
    }
    return s;
  err:
    close(s);
    return -1;
}
Exemple #4
0
/*
 * from_client_commit
 * Handle an incoming commit message from a client.
 * XXX: If commit succeeds and snapshot/startup fails, we have strange state:
 *   the commit has succeeded but an error message is returned.
 */
int
from_client_commit(clicon_handle h,
		   int s, 
		   struct clicon_msg *msg, 
		   const char *label)
{
    char *candidate, *running;
    uint32_t snapshot, startup;
    int retval = -1;
    char *snapshot_0;

    if (clicon_msg_commit_decode(msg, &candidate, &running, 
				&snapshot, &startup, label) < 0)
	goto err;

    if (candidate_commit(h, candidate, running) < 0){
	clicon_debug(1, "Commit %s failed",  candidate);
	retval = 0; /* We ignore errors from commit, but maybe
		       we should fail on fatal errors? */
	goto err;
    }
    clicon_debug(1, "Commit %s",  candidate);

    if (snapshot && config_snapshot(clicon_dbspec_key(h), running, clicon_archive_dir(h)) < 0)
	goto err;

    if (startup){
	snapshot_0 = chunk_sprintf(__FUNCTION__, "%s/0", 
				   clicon_archive_dir(h));
	if (file_cp(snapshot_0, clicon_startup_config(h)) < 0){
	    clicon_err(OE_PROTO, errno, "%s: Error when creating startup", 
		    __FUNCTION__);
		goto err;
	}
    }
    retval = 0;
    if (send_msg_ok(s) < 0)
	goto done;
    goto done;
  err:
    /* XXX: more elaborate errstring? */
    if (send_msg_err(s, clicon_errno, clicon_suberrno, "%s", clicon_err_reason) < 0)
	retval = -1;
  done:
    unchunk_group(__FUNCTION__);
    return retval; /* may be zero if we ignoring errors from commit */
} /* from_client_commit */
Exemple #5
0
/*
 * Plugin initialization
 */
int
plugin_init(clicon_handle h)
{
    int i;
    char *key;
    int retval = -1;

    for (i = 0; resolv_conf_fmts[i].lm_key; i++) {
	key = resolv_conf_fmts[i].lm_key;
	if (dbdep(h, TRANS_CB_COMMIT, dns_commit, (void *)NULL, 1, key) == NULL) {
	    clicon_debug(1, "Failed to create dependency '%s'", key);
	    goto done;
	}
	clicon_debug(1, "Created dependency '%s'", key);
    }
    retval = 0;
  done:
    return retval;
}
Exemple #6
0
/*! Load a set of plugin objects from a directory and and call their init-function
 * @param[in]  h     Clicon handle
 * @param[in]  function Which function symbol to load and call (eg CLIXON_PLUGIN_INIT)
 * @param[in]  dir   Directory. .so files in this dir will be loaded.
 * @param[in]  regexp Regexp for matching files in plugin directory. Default *.so.
 * @retval     0     OK
 * @retval     -1    Error
 */
int
clixon_plugins_load(clicon_handle h,
    		    char         *function,
		    char         *dir,
    		    char         *regexp)
{
    int            retval = -1;
    int            ndp;
    struct dirent *dp = NULL;
    int            i;
    char           filename[MAXPATHLEN];
    clixon_plugin *cp;
    int            ret;

    clicon_debug(1, "%s", __FUNCTION__); 
    /* Get plugin objects names from plugin directory */
    if((ndp = clicon_file_dirent(dir, &dp, regexp?regexp:"(.so)$", S_IFREG)) < 0)
	goto done;
    /* Load all plugins */
    for (i = 0; i < ndp; i++) {
	snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name);
	clicon_debug(1, "DEBUG: Loading plugin '%.*s' ...", 
		     (int)strlen(filename), filename);
	if ((ret = plugin_load_one(h, filename, function, RTLD_NOW, &cp)) < 0)
	    goto done;
	if (ret == 0)
	    continue;
	_clixon_nplugins++;
	if ((_clixon_plugins = realloc(_clixon_plugins, _clixon_nplugins*sizeof(clixon_plugin))) == NULL) {
	    clicon_err(OE_UNIX, errno, "realloc");
	    goto done;
	}
	_clixon_plugins[_clixon_nplugins-1] = *cp;
	free(cp);
    }
    retval = 0;
done:
    if (dp)
	free(dp);
    return retval;
}
Exemple #7
0
/*
 * Plugin initialization
 */
int
plugin_init(clicon_handle h)

{
    char *dir;
    int retval = -1;

    PyImport_AppendInittab("_cliconcli", MOD_INITFUNC(_cli));
    Py_InitializeEx(0);

    /* Append application plugin directory */
    if ((dir = clicon_option_str(h, "CLICON_CLI_DIR")) == NULL) {
	clicon_err(OE_PLUGIN, 0, "CLICON_CLI_DIR not set");
	goto quit;
    }
    if (syspath_insert(dir) < 0)
	goto quit;

    /* Append CLICON python module directory */
    if (syspath_insert(PYCLICON_MODDIR) < 0)
	goto quit;
    
    retval = plugin_call(h, "_plugin_init");

    
 quit:
    if (retval <= 0)  /* Error or no loaded plugins */
	Py_Finalize();

    if (debug && retval == 0)
	clicon_debug(1, "DEBUG: No python plugins loaded");
    else if (retval > 0)
	clicon_debug(1, "DEBUG: Loaded %d python plugins", retval);

    return (retval < 0) ? -1 : 0;
}
Exemple #8
0
/*! Call plugin_start in all plugins
 * @param[in]  h       Clicon handle
 * Call plugin start functions (if defined)
 * @note  Start functions used to have argc/argv. Use clicon_argv_get() instead
 */
int
clixon_plugin_start(clicon_handle h)
{
    clixon_plugin *cp;
    int            i;
    plgstart_t    *startfn;          /* Plugin start */
    
    for (i = 0; i < _clixon_nplugins; i++) {
	cp = &_clixon_plugins[i];
	if ((startfn = cp->cp_api.ca_start) == NULL)
	    continue;
	if (startfn(h) < 0) {
	    clicon_debug(1, "plugin_start() failed");
	    return -1;
	}
    }
    return 0;
}
Exemple #9
0
/*
 * Call plugin_start in all plugins
 */
int
netconf_plugin_start(clicon_handle h, int argc, char **argv)
{
    int i;
    plgstart_t *startfn;

    for (i = 0; i < nplugins; i++) {
	/* Call exit function is it exists */
	if ((startfn = dlsym(plugins[i], "plugin_start")) == NULL)
	    break;
	optind = 0;
	if (startfn(h, argc, argv) < 0) {
	    clicon_debug(1, "plugin_start() failed\n");
	    return -1;
	}
    }
    return 0;
}
Exemple #10
0
/*
 * netconf_plugin_load
 * Load allplugins you can find in CLICON_NETCONF_DIR
 */
int 
netconf_plugin_load(clicon_handle h)
{
    int            retval = -1;
    char          *dir;
    int            ndp;
    struct dirent *dp;
    int            i;
    char          *filename;
    plghndl_t     *handle;

    if ((dir = clicon_netconf_dir(h)) == NULL)
	goto quit;

    /* Get plugin objects names from plugin directory */
    if((ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG, __FUNCTION__))<0)
	goto quit;

    /* Load all plugins */
    for (i = 0; i < ndp; i++) {
	filename = chunk_sprintf(__FUNCTION__, "%s/%s", dir, dp[i].d_name);
	clicon_debug(1, "DEBUG: Loading plugin '%.*s' ...", 
		     (int)strlen(filename), filename);
	if (filename == NULL) {
	    clicon_err(OE_UNIX, errno, "chunk");
	    goto quit;
	}
	if ((handle = plugin_load (h, filename, RTLD_NOW, __FUNCTION__)) == NULL)
	    goto quit;
	if ((plugins = rechunk(plugins, (nplugins+1) * sizeof (*plugins), NULL)) == NULL) {
	    clicon_err(OE_UNIX, errno, "chunk");
	    goto quit;
	}
	plugins[nplugins++] = handle;
	unchunk (filename);
    }
    retval = 0;
quit:
    unchunk_group(__FUNCTION__);
    return retval;
}
Exemple #11
0
/*! Run the restconf user-defined credentials callback if present
 * Find first authentication callback and call that, then return.
 * The callback is to set the authenticated user
 * @param[in]  h    Clicon handle
 * @param[in]  arg  Argument, such as fastcgi handler for restconf
 * @retval    -1    Error
 * @retval     0    Not authenticated
 * @retval     1    Authenticated 
 * @note If authenticated either a callback was called and clicon_username_set() 
 *       Or no callback was found.
 */
int
clixon_plugin_auth(clicon_handle h, 
		   void         *arg)
{
    clixon_plugin *cp;
    int            i;
    plgauth_t     *authfn;          /* Plugin auth */
    int            retval = 1;
    
    for (i = 0; i < _clixon_nplugins; i++) {
	cp = &_clixon_plugins[i];
	if ((authfn = cp->cp_api.ca_auth) == NULL)
	    continue;
	if ((retval = authfn(h, arg)) < 0) {
	    clicon_debug(1, "plugin_auth() failed");
	    return -1;
	}
	break;
    }
    return retval;
}
Exemple #12
0
/*
 * plugin_modify_key_value
 * A wrapper function for invoking the plugin dependency set/del call
 * for a changed a key value. 
 * The routine logs on debug.
 * It also checks whether an error was properly registered using clicon_err().
 * Arguments:
 *   db:   The database which contains the key value, most relevant in a set operation
 *         but may possibly in some cases be important in delete operations, although
 *         I cannot think of any,..
 *   key:  The name of the key in the database above.
 *   op:   Either set or delete
 *   dep:  plugin dependency information. Contains function and argument pointers.
 *
 * Returns:
 *  0: OK
 * -1: An error occured in the plugin commit function. It is assumed that 
 *     clicon_err() has been called there. Here, we interpret the clicon_err
 *     as a 'commit' error and does not handle it fatally. 
 */
static int
plugin_modify_key_value(clicon_handle h, 
			char *db, 
			char *key,
			enum trans_cb_type type,
			lv_op_t op,
			dbdep_t *dp)
{
    int retval = -1;

    clicon_debug(2, "commit diff %c%s", (op==LV_SET)?'+':'-', key);
    clicon_err_reset();
    if (dp->dp_callback(h, db, type, op, key, dp->dp_arg) < 0){
	if (!clicon_errno) 	/* sanity: log if clicon_err() is not called ! */
	    clicon_err(OE_DB, 0, "Unknown error: %c%s: plugin does not make clicon_err call on error",
		    (op==LV_SET)?'+':'-', key);
	goto done;
    }
    retval = 0;
  done:
    return retval;
}
Exemple #13
0
/*! Load a dynamic plugin object and call its init-function
 * @param[in]  h       Clicon handle
 * @param[in]  file    Which plugin to load
 * @param[in]  function Which function symbol to load and call
 * @param[in]  dlflags See man(3) dlopen
 * @param[out] cpp      Clixon plugin structure (if retval is 1)
 * @retval     1     OK
 * @retval     0     Failed load, log, skip and continue with other plugins
 * @retval     -1    Error
 * @see clixon_plugins_load  Load all plugins
 */
static int
plugin_load_one(clicon_handle   h, 
		char           *file,
		char           *function,
		int             dlflags,
		clixon_plugin **cpp)
{
    int                retval = -1;
    char              *error;
    void              *handle = NULL;
    plginit2_t        *initfn;
    clixon_plugin_api *api = NULL;
    clixon_plugin     *cp = NULL;
    char              *name;
    char              *p;

    clicon_debug(1, "%s file:%s function:%s", __FUNCTION__, file, function);
    dlerror();    /* Clear any existing error */
    if ((handle = dlopen(file, dlflags)) == NULL) {
        error = (char*)dlerror();
	clicon_err(OE_PLUGIN, errno, "dlopen: %s", error ? error : "Unknown error");
	goto done;
    }
    /* call plugin_init() if defined, eg CLIXON_PLUGIN_INIT or CLIXON_BACKEND_INIT */
    if ((initfn = dlsym(handle, function)) == NULL){
	clicon_err(OE_PLUGIN, errno, "Failed to find %s when loading clixon plugin %s", CLIXON_PLUGIN_INIT, file);
	goto done;
    }
    if ((error = (char*)dlerror()) != NULL) {
	clicon_err(OE_UNIX, 0, "dlsym: %s: %s", file, error);
	goto done;
    }
    clicon_err_reset();
    if ((api = initfn(h)) == NULL) {
	if (!clicon_errno){ 	/* if clicon_err() is not called then log and continue */
	    clicon_log(LOG_DEBUG, "Warning: failed to initiate %s", strrchr(file,'/')?strchr(file, '/'):file);
	    retval = 0;
	    goto done;
	}
	else{
	    clicon_err(OE_PLUGIN, errno, "Failed to initiate %s", strrchr(file,'/')?strchr(file, '/'):file);
	    goto done;
	}
    }
    /* Note: sizeof clixon_plugin_api which is largest of clixon_plugin_api:s */
    if ((cp = (clixon_plugin *)malloc(sizeof(struct clixon_plugin))) == NULL){
	clicon_err(OE_UNIX, errno, "malloc");
	goto done;
    }
    memset(cp, 0, sizeof(struct clixon_plugin));
    cp->cp_handle = handle;
    /* Extract string after last '/' in filename, if any */
    name = strrchr(file, '/') ? strrchr(file, '/')+1 : file;
    /* strip extension, eg .so from name */
    if ((p=strrchr(name, '.')) != NULL)
	*p = '\0';
    /* Copy name to struct */
    memcpy(cp->cp_name, name, strlen(name)+1);

    snprintf(cp->cp_name, sizeof(cp->cp_name), "%*s",
	     (int)strlen(name), name);
    cp->cp_api = *api;
    clicon_debug(1, "%s", __FUNCTION__);
    if (cp){
	*cpp = cp;
	cp = NULL;
    }
    retval = 1;
 done:
    if (retval != 1 && handle)
	dlclose(handle);
    if (cp)
	free(cp);
    return retval;
}
Exemple #14
0
/*
 * config_accept_client
 * XXX: credentials not properly implemented
 */
int
config_accept_client(int fd, void *arg)
{
    clicon_handle h = (clicon_handle)arg;
    int s;
    struct sockaddr_un from;
    socklen_t     len;
    struct client_entry *ce;
#ifdef DONT_WORK /* XXX HAVE_SYS_UCRED_H */
    struct xucred credentials; 	/* FreeBSD. */
    socklen_t     clen;
#elif defined(SO_PEERCRED)
    struct ucred credentials; 	/* Linux. */
    socklen_t     clen;
#endif
    char         *config_group;
    struct group *gr;
    char         *mem;
    int           i;

    len = sizeof(from);
    if ((s = accept(fd, (struct sockaddr*)&from, &len)) < 0){
	clicon_err(OE_UNIX, errno, "%s: accept", __FUNCTION__);
	return -1;
    }

#if defined(SO_PEERCRED)
    /* fill in the user data structure */
    clen =  sizeof(credentials);
    if(getsockopt(s, SOL_SOCKET, SO_PEERCRED/* XXX finns ej i freebsd*/, &credentials, &clen)){
	clicon_err(OE_UNIX, errno, "%s: getsockopt", __FUNCTION__);
	return 1;
    }
#endif   
    if ((ce = ce_add(&ce_list, (struct sockaddr*)&from)) == NULL)
	return -1;
#if defined(SO_PEERCRED)
    ce->ce_pid = credentials.pid;
    ce->ce_uid = credentials.uid;
#endif
    ce->ce_handle = h;

    /* check credentials of caller (not properly implemented yet) */
    if ((config_group = clicon_sock_group(h)) == NULL)
	return -1;
    if ((gr = getgrnam(config_group)) != NULL){
	i = 0; /* one of mem should correspond to ce->ce_uid */
	while ((mem = gr->gr_mem[i++]) != NULL)
	    ;
    }

#if 0
    { /* XXX */
	int ii;
	struct client_entry *c;
	for (c = ce_list, ii=0; c; c = c->ce_next, ii++);
	clicon_debug(1, "Open client socket (nr:%d pid:%d [Total: %d])",
		ce->ce_nr, ce->ce_pid, ii);
    }
#endif
    ce->ce_s = s;

    /*
     * Here we register callbacks for actual data socket 
     */
    if (event_reg_fd(s, from_client, (void*)ce, "client socket") < 0)
	return -1;

    return 0;
}
Exemple #15
0
/*! Send internal netconf rpc from client to backend
 * @param[in]    h      CLICON handle
 * @param[in]    msg    Encoded message. Deallocate woth free
 * @param[out]   xret   Return value from backend as xml tree. Free w xml_free
 * @param[inout] sock0  If pointer exists, do not close socket to backend on success 
 *                      and return it here. For keeping a notify socket open
 * @note sock0 is if connection should be persistent, like a notification/subscribe api
 * @note xret is populated with yangspec according to standard handle yangspec
 */
int
clicon_rpc_msg(clicon_handle      h, 
	       struct clicon_msg *msg, 
	       cxobj            **xret0,
	       int               *sock0)
{
    int                retval = -1;
    char              *sock;
    int                port;
    char              *retdata = NULL;
    cxobj             *xret = NULL;
    yang_stmt         *yspec;

#ifdef RPC_USERNAME_ASSERT
    assert(strstr(msg->op_body, "username")!=NULL); /* XXX */
#endif
    clicon_debug(1, "%s request:%s", __FUNCTION__, msg->op_body);
    if ((sock = clicon_sock(h)) == NULL){
	clicon_err(OE_FATAL, 0, "CLICON_SOCK option not set");
	goto done;
    }
    /* What to do if inet socket? */
    switch (clicon_sock_family(h)){
    case AF_UNIX:
	if (clicon_rpc_connect_unix(msg, sock, &retdata, sock0) < 0){
#if 0
	    if (errno == ESHUTDOWN)
		/* Maybe could reconnect on a higher layer, but lets fail
		   loud and proud */
		cligen_exiting_set(cli_cligen(h), 1);
#endif
	    goto done;
	}
	break;
    case AF_INET:
	if ((port = clicon_sock_port(h)) < 0){
	    clicon_err(OE_FATAL, 0, "CLICON_SOCK option not set");
	    goto done;
	}
	if (port < 0){
	    clicon_err(OE_FATAL, 0, "CLICON_SOCK_PORT not set");
	    goto done;
	}
	if (clicon_rpc_connect_inet(msg, sock, port, &retdata, sock0) < 0)
	    goto done;
	break;
    }
    clicon_debug(1, "%s retdata:%s", __FUNCTION__, retdata);

    if (retdata){
 	yspec = clicon_dbspec_yang(h);
	if (xml_parse_string(retdata, yspec, &xret) < 0)
	    goto done;
    }
    if (xret0){
	*xret0 = xret;
	xret = NULL;
    }
    retval = 0;
 done:
    if (retdata)
	free(retdata);
    if (xret)
	xml_free(xret);
    return retval;
}