int
main(int argc, char **argv)
{
    sr_conn_ctx_t *connection = NULL;
    sr_session_ctx_t *session = NULL;
    int rc = SR_ERR_OK;

    /* connect to sysrepo */
    rc = sr_connect("example_application", SR_CONN_DEFAULT, &connection);
    if (SR_ERR_OK != rc) {
        fprintf(stderr, "Error by sr_connect: %s\n", sr_strerror(rc));
        goto cleanup;
    }

    /* start session */
    rc = sr_session_start(connection, SR_DS_RUNNING, SR_SESS_DEFAULT, &session);
    if (SR_ERR_OK != rc) {
        fprintf(stderr, "Error by sr_session_start: %s\n", sr_strerror(rc));
        goto cleanup;
    }

    /* run as an event notification subscriber */
    printf("This application will be a subscriber for the 'paused' event notification of 'turing-machine'.\n");
    printf("This notification is sent by the RPC handler in rpc_example and rpc_tree_example.\n");
    rc = event_notif_subscriber(session);

cleanup:
    if (NULL != session) {
        sr_session_stop(session);
    }
    if (NULL != connection) {
        sr_disconnect(connection);
    }
    return rc;
}
Example #2
0
/**
 * Add a device instance to a session.
 *
 * @param session The session to add to. Must not be NULL.
 * @param sdi The device instance to add to a session. Must not
 *            be NULL. Also, sdi->driver and sdi->driver->dev_open must
 *            not be NULL.
 *
 * @retval SR_OK Success.
 * @retval SR_ERR_ARG Invalid argument.
 *
 * @since 0.4.0
 */
SR_API int sr_session_dev_add(struct sr_session *session,
		struct sr_dev_inst *sdi)
{
	int ret;

	if (!sdi) {
		sr_err("%s: sdi was NULL", __func__);
		return SR_ERR_ARG;
	}

	if (!session) {
		sr_err("%s: session was NULL", __func__);
		return SR_ERR_ARG;
	}

	/* If sdi->session is not NULL, the device is already in this or
	 * another session. */
	if (sdi->session) {
		sr_err("%s: already assigned to session", __func__);
		return SR_ERR_ARG;
	}

	/* If sdi->driver is NULL, this is a virtual device. */
	if (!sdi->driver) {
		/* Just add the device, don't run dev_open(). */
		session->devs = g_slist_append(session->devs, sdi);
		sdi->session = session;
		return SR_OK;
	}

	/* sdi->driver is non-NULL (i.e. we have a real device). */
	if (!sdi->driver->dev_open) {
		sr_err("%s: sdi->driver->dev_open was NULL", __func__);
		return SR_ERR_BUG;
	}

	session->devs = g_slist_append(session->devs, sdi);
	sdi->session = session;

	/* TODO: This is invalid if the session runs in a different thread.
	 * The usage semantics and restrictions need to be documented.
	 */
	if (session->running) {
		/* Adding a device to a running session. Commit settings
		 * and start acquisition on that device now. */
		if ((ret = sr_config_commit(sdi)) != SR_OK) {
			sr_err("Failed to commit device settings before "
			       "starting acquisition in running session (%s)",
			       sr_strerror(ret));
			return ret;
		}
		if ((ret = sr_dev_acquisition_start(sdi)) != SR_OK) {
			sr_err("Failed to start acquisition of device in "
			       "running session (%s)", sr_strerror(ret));
			return ret;
		}
	}

	return SR_OK;
}
Example #3
0
static int
rpc_handler(sr_session_ctx_t *session)
{
    sr_subscription_ctx_t *subscription = NULL;
    int rc = SR_ERR_OK;

    /* subscribe for handling RPC */
    rc = sr_rpc_subscribe_tree(session, "/turing-machine:run-until", rpc_cb,
            (void *)session, SR_SUBSCR_DEFAULT, &subscription);
    if (SR_ERR_OK != rc) {
        fprintf(stderr, "Error by sr_rpc_subscribe: %s\n", sr_strerror(rc));
        goto cleanup;
    }

    printf("\n\n ========== SUBSCRIBED FOR HANDLING RPC ==========\n\n");

    /* loop until ctrl-c is pressed / SIGINT is received */
    signal(SIGINT, sigint_handler);
    signal(SIGPIPE, SIG_IGN);
    while (!exit_application) {
        sleep(1000);  /* or do some more useful work... */
    }

    printf("Application exit requested, exiting.\n");

cleanup:
    if (NULL != subscription) {
        sr_unsubscribe(session, subscription);
    }
    return rc;
}
Example #4
0
/**
 * @brief Callback called by the event loop watcher when health check timer expires.
 */
static void
sr_pd_health_check_timer_cb(struct ev_loop *loop, ev_timer *w, int revents)
{
    sr_pd_ctx_t *ctx = NULL;
    bool init_retry_needed = false;
    int rc = SR_ERR_OK;

    CHECK_NULL_ARG_VOID2(w, w->data);
    ctx = (sr_pd_ctx_t*)w->data;

    CHECK_NULL_ARG_VOID(ctx);

    for (size_t i = 0; i < ctx->plugins_cnt; i++) {
        if (ctx->plugins[i].initialized && (NULL != ctx->plugins[i].health_check_cb)) {
            rc = ctx->plugins[i].health_check_cb(ctx->session, ctx->plugins[i].private_ctx);
            if (SR_ERR_OK != rc) {
                SR_LOG_ERR("Health check of the plugin '%s' returned an error: %s", ctx->plugins[i].filename,
                        sr_strerror(rc));
                sr_pd_cleanup_plugin(ctx, &(ctx->plugins[i]));
                init_retry_needed = true;
            }
        }
    }

    if (init_retry_needed) {
        SR_LOG_DBG("Scheduling plugin init retry after %d seconds.", SR_PLUGIN_INIT_RETRY_TIMEOUT);
        ev_timer_start(ctx->event_loop, &ctx->init_retry_timer);
    }
}
static int
event_notif_subscriber(sr_session_ctx_t *session)
{
    sr_subscription_ctx_t *subscription = NULL;
    int rc = SR_ERR_OK;

    /* subscribe for the notification */
    rc = sr_event_notif_subscribe(session, "/turing-machine:paused", event_notif_cb, NULL,
            SR_SUBSCR_DEFAULT, &subscription);
    if (SR_ERR_OK != rc) {
        fprintf(stderr, "Error by sr_event_notif_subscribe: %s\n", sr_strerror(rc));
        goto cleanup;
    }

    printf("\n\n ========== SUBSCRIBED FOR EVENT NOTIFICATION ==========\n\n");

    /* loop until ctrl-c is pressed / SIGINT is received */
    signal(SIGINT, sigint_handler);
    signal(SIGPIPE, SIG_IGN);
    while (!exit_application) {
        sleep(1000);  /* or do some more useful work... */
    }

    printf("Application exit requested, exiting.\n");

cleanup:
    if (NULL != subscription) {
        sr_unsubscribe(session, subscription);
    }
    return rc;
}
Example #6
0
/**
 * @brief Check the session and reconnect if it is needed.
 */
static void
sr_pd_session_check(sr_pd_ctx_t *ctx)
{
    int rc = SR_ERR_OK;

    CHECK_NULL_ARG_VOID(ctx);

    rc = sr_session_check(ctx->session);

    if (SR_ERR_OK != rc) {
        SR_LOG_DBG_MSG("Reconnecting to Sysrepo Engine.");

        /* disconnect */
        sr_session_stop(ctx->session);
        sr_disconnect(ctx->connection);
        ctx->session = NULL;
        ctx->connection = NULL;

        /* reconnect */
        rc = sr_connect("sysrepo-plugind", connect_options, &ctx->connection);
        if (SR_ERR_OK == rc) {
            rc = sr_session_start(ctx->connection, SR_DS_STARTUP, SR_SESS_DEFAULT, &ctx->session);
        }
        if (SR_ERR_OK != rc) {
            SR_LOG_ERR("Error by reconnecting to Sysrepo Engine: %s", sr_strerror(rc));
        }
    }
}
Example #7
0
/**
 * Start a session.
 *
 * There can only be one session at a time.
 *
 * @return SR_OK upon success, SR_ERR upon errors.
 */
SR_API int sr_session_start(void)
{
	struct sr_dev_inst *sdi;
	GSList *l;
	int ret;

	if (!session) {
		sr_err("%s: session was NULL; a session must be "
		       "created before starting it.", __func__);
		return SR_ERR_BUG;
	}

	if (!session->devs) {
		sr_err("%s: session->devs was NULL; a session "
		       "cannot be started without devices.", __func__);
		return SR_ERR_BUG;
	}

	sr_info("Starting...");

	ret = SR_OK;
	for (l = session->devs; l; l = l->next) {
		sdi = l->data;
		if ((ret = sdi->driver->dev_acquisition_start(sdi, sdi)) != SR_OK) {
			sr_err("%s: could not start an acquisition "
			       "(%d)", __func__, sr_strerror(ret));
			break;
		}
	}

	/* TODO: What if there are multiple devices? Which return code? */

	return ret;
}
Example #8
0
/**
 * @brief Convert data tree difference of type LYD_DIFF_DELETED to corresponding set of Sysrepo public API calls.
 */
static int
srcfg_convert_lydiff_deleted(const char *xpath)
{
    CHECK_NULL_ARG(xpath);
    int rc = sr_delete_item(srcfg_session, xpath, SR_EDIT_STRICT);
    if (SR_ERR_OK != rc) {
        SR_LOG_ERR("Error returned from sr_delete_item: %s.", sr_strerror(rc));
    }
    return rc;
}
Example #9
0
/**
 * @brief Convert data tree difference of type LYD_DIFF_MOVEDAFTER1 or LYD_DIFF_MOVEDAFTER2 to corresponding
 * set of Sysrepo public API calls.
 */
static int
srcfg_convert_lydiff_movedafter(const char *target_xpath, const char *after_xpath)
{
    CHECK_NULL_ARG(target_xpath);
    int rc = sr_move_item(srcfg_session, target_xpath, after_xpath ? SR_MOVE_AFTER : SR_MOVE_FIRST, after_xpath);
    if (SR_ERR_OK != rc) {
        SR_LOG_ERR("Error returned from sr_move_item: %s.", sr_strerror(rc));
    }
    return rc;
}
Example #10
0
static int dev_open(struct sr_dev_inst *sdi)
{
	int ret;
	struct sr_scpi_dev_inst *scpi = sdi->conn;

	if ((ret = sr_scpi_open(scpi)) < 0) {
		sr_err("Failed to open SCPI device: %s.", sr_strerror(ret));
		return SR_ERR;
	}

	if ((ret = rigol_ds_get_dev_cfg(sdi)) < 0) {
		sr_err("Failed to get device config: %s.", sr_strerror(ret));
		return SR_ERR;
	}

	sdi->status = SR_ST_ACTIVE;

	return SR_OK;
}
Example #11
0
int
main(int argc, char **argv)
{
    sr_conn_ctx_t *connection = NULL;
    sr_session_ctx_t *session = NULL;
    int rc = SR_ERR_OK;

    /* connect to sysrepo */
    rc = sr_connect("example_application", SR_CONN_DEFAULT, &connection);
    if (SR_ERR_OK != rc) {
        fprintf(stderr, "Error by sr_connect: %s\n", sr_strerror(rc));
        goto cleanup;
    }

    /* start session */
    rc = sr_session_start(connection, SR_DS_RUNNING, SR_SESS_DEFAULT, &session);
    if (SR_ERR_OK != rc) {
        fprintf(stderr, "Error by sr_session_start: %s\n", sr_strerror(rc));
        goto cleanup;
    }

    if (1 == argc) {
        /* run as a RPC handler */
        printf("This application will be an RPC handler for 'run-until' operation of 'turing-machine'.\n");
        printf("Run the same executable (or rpc_example) with one (any) argument to execute the RPC.\n");
        rc = rpc_handler(session);
    } else {
        /* run as a RPC caller */
        printf("Executing RPC 'run-until' of 'turing-machine':\n");
        rc = rpc_caller(session);
    }

cleanup:
    if (NULL != session) {
        sr_session_stop(session);
    }
    if (NULL != connection) {
        sr_disconnect(connection);
    }
    return rc;
}
Example #12
0
/**
 * @brief Reports (prints to stderr) the (sysrepo) error stored within the session or given one.
 */
static void
srcfg_report_error(int rc)
{
    const sr_error_info_t *error = NULL;

    if (NULL == srcfg_session) {
        SR_LOG_ERR("%s.", sr_strerror(rc));
    } else {
        sr_get_last_error(srcfg_session, &error);
        SR_LOG_ERR("%s.", error->message);
    }
}
Example #13
0
/**
 * @brief Convert data tree difference of type LYD_DIFF_CHANGED to corresponding set of Sysrepo public API calls.
 */
static int
srcfg_convert_lydiff_changed(const char *xpath, struct lyd_node *node)
{
    int rc = SR_ERR_INTERNAL;
    sr_val_t value = { 0, SR_UNKNOWN_T };
    struct lyd_node_leaf_list *data_leaf = NULL;

    CHECK_NULL_ARG2(xpath, node);

    switch (node->schema->nodetype) {
        case LYS_LEAF:
        case LYS_LEAFLIST:
            data_leaf = (struct lyd_node_leaf_list *) node;
            value.type = sr_libyang_leaf_get_type(data_leaf);
            rc = sr_libyang_leaf_copy_value(data_leaf, &value);
            if (SR_ERR_OK != rc) {
                SR_LOG_ERR("Error returned from sr_libyang_leaf_copy_value: %s.", sr_strerror(rc));
                goto cleanup;
            }
            break;
        case LYS_ANYXML:
            SR_LOG_ERR_MSG("The anyxml statement is not yet supported by Sysrepo.");
            goto cleanup;
        default:
            SR_LOG_ERR_MSG("Unexpected node type for LYD_DIFF_CHANGED.");
            goto cleanup;
    }
    rc = sr_set_item(srcfg_session, xpath, &value, SR_EDIT_NON_RECURSIVE);
    if (SR_ERR_OK != rc) {
        SR_LOG_ERR("Error returned from sr_set_item: %s.", sr_strerror(rc));
    }

cleanup:
    sr_free_val_content(&value);
    return rc;
}
Example #14
0
static void get_option(void)
{
	struct sr_dev_inst *sdi;
	struct sr_channel_group *cg;
	const struct sr_config_info *ci;
	GSList *devices;
	GVariant *gvar;
	GHashTable *devargs;
	int ret;
	char *s;
	struct sr_dev_driver *driver;

	if (!(devices = device_scan())) {
		g_critical("No devices found.");
		return;
	}
	sdi = devices->data;
	g_slist_free(devices);

	driver = sr_dev_inst_driver_get(sdi);

	if (sr_dev_open(sdi) != SR_OK) {
		g_critical("Failed to open device.");
		return;
	}

	cg = select_channel_group(sdi);
	if (!(ci = sr_config_info_name_get(opt_get)))
		g_critical("Unknown option '%s'", opt_get);

	if ((devargs = parse_generic_arg(opt_config, FALSE)))
		set_dev_options(sdi, devargs);
	else devargs = NULL;

	if ((ret = maybe_config_get(driver, sdi, cg, ci->key, &gvar)) != SR_OK)
		g_critical("Failed to get '%s': %s", opt_get, sr_strerror(ret));
	s = g_variant_print(gvar, FALSE);
	printf("%s\n", s);
	g_free(s);

	g_variant_unref(gvar);
	sr_dev_close(sdi);
	if (devargs)
		g_hash_table_destroy(devargs);
}
Example #15
0
int
cl_request_process(sr_session_ctx_t *session, Sr__Msg *msg_req, Sr__Msg **msg_resp,
        sr_mem_ctx_t *sr_mem_resp, const Sr__Operation expected_response_op)
{
    int rc = SR_ERR_OK;
    struct timeval tv = { 0, };

    CHECK_NULL_ARG4(session, session->conn_ctx, msg_req, msg_resp);

    SR_LOG_DBG("Sending %s request.", sr_gpb_operation_name(expected_response_op));

    pthread_mutex_lock(&session->conn_ctx->lock);
    /* some operation may take more time, raise the timeout */
    if (SR__OPERATION__COMMIT == expected_response_op || SR__OPERATION__COPY_CONFIG == expected_response_op ||
            SR__OPERATION__RPC == expected_response_op || SR__OPERATION__ACTION == expected_response_op) {
        tv.tv_sec = SR_LONG_REQUEST_TIMEOUT;
        tv.tv_usec = 0;
        rc = setsockopt(session->conn_ctx->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
        if (-1 == rc) {
            SR_LOG_WRN("Unable to set timeout for socket operations: %s", sr_strerror_safe(errno));
        }
    }

    /* send the request */
    rc = cl_message_send(session->conn_ctx, msg_req);
    if (SR_ERR_OK != rc) {
        SR_LOG_ERR("Unable to send the message with request (session id=%"PRIu32", operation=%s).",
                session->id, sr_gpb_operation_name(msg_req->request->operation));
        pthread_mutex_unlock(&session->conn_ctx->lock);
        return rc;
    }

    SR_LOG_DBG("%s request sent, waiting for response.", sr_gpb_operation_name(expected_response_op));

    /* receive the response */
    rc = cl_message_recv(session->conn_ctx, msg_resp, sr_mem_resp);
    if (SR_ERR_OK != rc) {
        SR_LOG_ERR("Unable to receive the message with response (session id=%"PRIu32", operation=%s).",
                session->id, sr_gpb_operation_name(msg_req->request->operation));
        pthread_mutex_unlock(&session->conn_ctx->lock);
        return rc;
    }

    /* change socket timeout to the standard value */
    if (SR__OPERATION__COMMIT == expected_response_op || SR__OPERATION__COPY_CONFIG == expected_response_op ||
            SR__OPERATION__RPC == expected_response_op || SR__OPERATION__ACTION == expected_response_op) {
        tv.tv_sec = SR_REQUEST_TIMEOUT;
        rc = setsockopt(session->conn_ctx->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv));
        if (-1 == rc) {
            SR_LOG_WRN("Unable to set timeout for socket operations: %s", sr_strerror_safe(errno));
        }
    }

    pthread_mutex_unlock(&session->conn_ctx->lock);

    SR_LOG_DBG("%s response received, processing.", sr_gpb_operation_name(expected_response_op));

    /* validate the response */
    rc = sr_gpb_msg_validate(*msg_resp, SR__MSG__MSG_TYPE__RESPONSE, expected_response_op);
    if (SR_ERR_OK != rc) {
        SR_LOG_ERR("Malformed message with response received (session id=%"PRIu32", operation=%s).",
                session->id, sr_gpb_operation_name(msg_req->request->operation));
        return rc;
    }

    /* check for errors */
    if (SR_ERR_OK != (*msg_resp)->response->result) {
        if (NULL != (*msg_resp)->response->error) {
            /* set detailed error information into session */
            rc = cl_session_set_error(session, (*msg_resp)->response->error->message, (*msg_resp)->response->error->xpath);
        }
        /* log the error (except expected ones) */
        if (SR_ERR_NOT_FOUND != (*msg_resp)->response->result &&
                SR_ERR_VALIDATION_FAILED != (*msg_resp)->response->result &&
                SR_ERR_UNAUTHORIZED != (*msg_resp)->response->result &&
                SR_ERR_OPERATION_FAILED != (*msg_resp)->response->result) {
            SR_LOG_ERR("Error by processing of the %s request (session id=%"PRIu32"): %s.",
                    sr_gpb_operation_name(msg_req->request->operation), session->id,
                (NULL != (*msg_resp)->response->error && NULL != (*msg_resp)->response->error->message) ?
                        (*msg_resp)->response->error->message : sr_strerror((*msg_resp)->response->result));
        }
        return (*msg_resp)->response->result;
    }

    return rc;
}
Example #16
0
/**
 * Session start / stop negative test.
 */
static void
cm_session_neg_test(void **state) {
    Sr__Msg *msg = NULL;
    uint8_t *msg_buf = NULL;
    size_t msg_size = 0;
    int fd1 = 0, fd2 = 0;
    uint32_t session_id1 = 0, session_id2 = 0;

    fd1 = cm_connect_to_server(0);

    /* try invalid version  */
    cm_version_verify_generate("invalid", &msg_buf, &msg_size);
    cm_message_send(fd1, msg_buf, msg_size);
    free(msg_buf);
    msg = cm_message_recv(fd1);
    assert_non_null(msg);
    assert_int_equal(msg->type, SR__MSG__MSG_TYPE__RESPONSE);
    assert_non_null(msg->response);
    assert_int_equal(msg->response->result, SR_ERR_VERSION_MISMATCH);
    assert_int_equal(msg->response->operation, SR__OPERATION__VERSION_VERIFY);
    assert_non_null(msg->response->version_verify_resp);
    assert_string_equal_bt(msg->response->version_verify_resp->soname, SR_COMPAT_VERSION);
    sr__msg__free_unpacked(msg, NULL);

    /* disconnect expected */
    msg = cm_message_recv(fd1);
    assert_null(msg);
    close(fd1);

    fd1 = cm_connect_to_server(1);

    /* try a message with NULL request  */
    msg = calloc(1, sizeof(*msg));
    assert_non_null(msg);
    sr__msg__init(msg);
    msg->type = SR__MSG__MSG_TYPE__REQUEST;
    /* send the message */
    cm_msg_pack_to_buff(msg, &msg_buf, &msg_size);
    cm_message_send(fd1, msg_buf, msg_size);
    free(msg_buf);
    /* receive the response */
    msg = cm_message_recv(fd1);
    /* disconnect expected */
    assert_null(msg);
    close(fd1);

    fd1 = cm_connect_to_server(1);

    /* try a message with bad session id */
    cm_session_stop_generate(999, &msg_buf, &msg_size);
    cm_message_send(fd1, msg_buf, msg_size);
    free(msg_buf);
    /* receive the response */
    msg = cm_message_recv(fd1);
    /* disconnect expected */
    assert_null(msg);
    close(fd1);

    fd1 = cm_connect_to_server(1);

    /* try a session_start request with non-existing username */
    cm_session_start_generate("non-existing-username", &msg_buf, &msg_size);
    cm_message_send(fd1, msg_buf, msg_size);
    free(msg_buf);
    /* receive the response */
    msg = cm_message_recv(fd1);
    assert_non_null(msg);
    assert_non_null(msg->response);
    assert_non_null(msg->response->session_start_resp);
    /* expect invalid user error */
    assert_int_equal(msg->response->result, SR_ERR_INVAL_USER);
    sr__msg__free_unpacked(msg, NULL);
    close(fd1);

    fd1 = cm_connect_to_server(1);
    fd2 = cm_connect_to_server(1);

    /* try to stop session via another connection */
    /* session_start request */
    cm_session_start_generate("nobody", &msg_buf, &msg_size);
    cm_message_send(fd1, msg_buf, msg_size);
    free(msg_buf);
    /* receive the response */
    msg = cm_message_recv(fd1);
    assert_non_null(msg);
    assert_non_null(msg->response);
    assert_non_null(msg->response->session_start_resp);
    session_id1 = msg->response->session_start_resp->session_id;
    sr__msg__free_unpacked(msg, NULL);
    /* stop via another connection */
    cm_session_stop_generate(session_id1, &msg_buf, &msg_size);
    cm_message_send(fd2, msg_buf, msg_size);
    free(msg_buf);
    /* receive the response */
    msg = cm_message_recv(fd2);
    /* disconnect expected */
    assert_null(msg);
    close(fd2);

    fd2 = cm_connect_to_server(1);

    /* try sending a response */
    /* session_start request */
    cm_session_start_generate("nobody", &msg_buf, &msg_size);
    cm_message_send(fd2, msg_buf, msg_size);
    free(msg_buf);
    /* receive the response */
    msg = cm_message_recv(fd2);
    assert_non_null(msg);
    assert_non_null(msg->response);
    assert_non_null(msg->response->session_start_resp);
    session_id2 = msg->response->session_start_resp->session_id;
    sr__msg__free_unpacked(msg, NULL);
    /* send BAD response */
    sr_gpb_resp_alloc(NULL, SR__OPERATION__SESSION_STOP, session_id2, &msg);
    cm_msg_pack_to_buff(msg, &msg_buf, &msg_size);
    cm_message_send(fd2, msg_buf, msg_size);
    free(msg_buf);
    /* receive the response */
    msg = cm_message_recv(fd2);
    /* disconnect expected */
    assert_null(msg);
    close(fd2);

    /* try to stop another session id */
    sr_gpb_req_alloc(NULL, SR__OPERATION__SESSION_STOP, session_id1, &msg);
    assert_non_null(msg);
    assert_non_null(msg->request);
    assert_non_null(msg->request->session_stop_req);
    msg->request->session_stop_req->session_id = 0; /* should be invalid */
    cm_msg_pack_to_buff(msg, &msg_buf, &msg_size);
    cm_message_send(fd1, msg_buf, msg_size);
    free(msg_buf);
    /* receive the response - error is expected */
    msg = cm_message_recv(fd1);
    assert_non_null(msg);
    assert_non_null(msg->response);
    assert_int_not_equal(msg->response->result, SR_ERR_OK);
    assert_non_null(sr_strerror(msg->response->result));
    assert_non_null(msg->response->error);
    assert_non_null(msg->response->error->message);
    sr__msg__free_unpacked(msg, NULL);

    /* try sending a message with invalid type */
    sr_gpb_resp_alloc(NULL, SR__OPERATION__SESSION_STOP, session_id1, &msg);
    msg->type = 53;
    cm_msg_pack_to_buff(msg, &msg_buf, &msg_size);
    cm_message_send(fd1, msg_buf, msg_size);
    free(msg_buf);
    /* receive the response */
    msg = cm_message_recv(fd1);
    /* disconnect expected */
    assert_null(msg);
    close(fd1);

    fd2 = cm_connect_to_server(1);

    /* try not closing a connection with an open session (auto cleanup) */
    /* session_start request */
    cm_session_start_generate(NULL, &msg_buf, &msg_size);
    cm_message_send(fd2, msg_buf, msg_size);
    free(msg_buf);
    /* receive the response */
    msg = cm_message_recv(fd2);
    assert_non_null(msg);
    assert_non_null(msg->response);
    assert_non_null(msg->response->session_start_resp);
    session_id2 = msg->response->session_start_resp->session_id;
    sr__msg__free_unpacked(msg, NULL);

    cm_teardown(state);
    close(fd2);
}
Example #17
0
/**
 * @brief Main routine of the sysrepo daemon.
 */
int
main(int argc, char* argv[])
{
    sr_pd_ctx_t ctx = { 0, };
    pid_t parent_pid = 0;
    int pidfile_fd = -1;
    int c = 0;
    bool debug_mode = false;
    int log_level = -1;
    int rc = SR_ERR_OK;

    while ((c = getopt (argc, argv, "hvdDl:")) != -1) {
        switch (c) {
            case 'v':
                sr_pd_print_version();
                return 0;
                break;
            case 'd':
                debug_mode = true;
                break;
            case 'D':
                connect_options |= SR_CONN_DAEMON_START;
                break;
            case 'l':
                log_level = atoi(optarg);
                break;
            default:
                sr_pd_print_help();
                return 0;
        }
    }

    /* init logger */
    sr_logger_init("sysrepo-plugind");

    /* daemonize the process */
    parent_pid = sr_daemonize(debug_mode, log_level, SR_PLUGIN_DAEMON_PID_FILE, &pidfile_fd);

    SR_LOG_DBG_MSG("Sysrepo plugin daemon initialization started.");

    /* init the event loop */
    ctx.event_loop = ev_loop_new(EVFLAG_AUTO);

    /* init signal watchers */
    ev_signal_init(&ctx.signal_watcher[0], sr_pd_signal_cb, SIGTERM);
    ev_signal_start(ctx.event_loop, &ctx.signal_watcher[0]);
    ev_signal_init(&ctx.signal_watcher[1], sr_pd_signal_cb, SIGINT);
    ev_signal_start(ctx.event_loop, &ctx.signal_watcher[1]);

    /* init timers */
    ev_timer_init(&ctx.health_check_timer, sr_pd_health_check_timer_cb, SR_PLUGIN_HEALTH_CHECK_TIMEOUT,
            SR_PLUGIN_HEALTH_CHECK_TIMEOUT);
    ctx.health_check_timer.data = &ctx;
    ev_timer_init(&ctx.init_retry_timer, sr_pd_init_retry_timer_cb, SR_PLUGIN_INIT_RETRY_TIMEOUT, SR_PLUGIN_INIT_RETRY_TIMEOUT);
    ctx.init_retry_timer.data = &ctx;

    /* connect to sysrepo */
    rc = sr_connect("sysrepo-plugind", connect_options, &ctx.connection);
    CHECK_RC_LOG_GOTO(rc, cleanup, "Unable to connect to sysrepod: %s", sr_strerror(rc));

    /* start the session */
    rc = sr_session_start(ctx.connection, SR_DS_STARTUP, SR_SESS_DEFAULT, &ctx.session);
    CHECK_RC_LOG_GOTO(rc, cleanup, "Unable to connect to sysrepo: %s", sr_strerror(rc));

    /* tell the parent process that we are okay */
    if (!debug_mode) {
        sr_daemonize_signal_success(parent_pid);
    }

    /* load the plugins */
    rc = sr_pd_load_plugins(&ctx);

    SR_LOG_INF_MSG("Sysrepo plugin daemon initialized successfully.");

    /* start health check timer */
    ev_timer_start(ctx.event_loop, &ctx.health_check_timer);

    /* run the event loop */
    ev_run(ctx.event_loop, 0);

    ev_loop_destroy(ctx.event_loop);

    /* check whether the session is still valid & reconnect if needed */
    sr_pd_session_check(&ctx);

cleanup:
    sr_pd_cleanup_plugins(&ctx);

    if (NULL != ctx.session) {
        sr_session_stop(ctx.session);
    }
    if (NULL != ctx.connection) {
        sr_disconnect(ctx.connection);
    }

    SR_LOG_INF_MSG("Sysrepo plugin daemon terminated.");
    sr_logger_cleanup();

    unlink(SR_PLUGIN_DAEMON_PID_FILE);
    if (-1 != pidfile_fd) {
        close(pidfile_fd);
    }

    exit((SR_ERR_OK == rc) ? EXIT_SUCCESS : EXIT_FAILURE);
}
Example #18
0
/**
 * @brief Import content of the specified datastore for the given module from a file
 * referenced by the descriptor 'fd_in'
 */
static int
srcfg_import_datastore(struct ly_ctx *ly_ctx, int fd_in, const char *module_name, srcfg_datastore_t datastore,
                       LYD_FORMAT format, bool permanent)
{
    int rc = SR_ERR_INTERNAL;
    unsigned i = 0;
    struct lyd_node *new_data_tree = NULL;
    struct lyd_node *current_data_tree = NULL;
    struct lyd_difflist *diff = NULL;
    char *first_xpath = NULL, *second_xpath = NULL;
    char *input_data = NULL;
    int ret = 0;
    struct stat info;

    CHECK_NULL_ARG2(ly_ctx, module_name);

    /* parse input data */
    ret = fstat(fd_in, &info);
    CHECK_NOT_MINUS1_LOG_GOTO(ret, rc, SR_ERR_INTERNAL, cleanup,
                              "Unable to obtain input file info: %s.", sr_strerror_safe(errno));
    ly_errno = LY_SUCCESS;
    if (S_ISREG(info.st_mode)) {
        /* load (using mmap) and parse the input data in one step */
        new_data_tree = lyd_parse_fd(ly_ctx, fd_in, format, LYD_OPT_STRICT | LYD_OPT_CONFIG);
    } else { /* most likely STDIN */
        /* load input data into the memory first */
        ret = srcfg_read_file_content(fd_in, &input_data);
        CHECK_RC_MSG_GOTO(ret, cleanup, "Unable to read the input data.");
        /* parse the input data stored inside memory buffer */
        new_data_tree = lyd_parse_mem(ly_ctx, input_data, format, LYD_OPT_STRICT | LYD_OPT_CONFIG);
    }
    if (NULL == new_data_tree && LY_SUCCESS != ly_errno) {
        SR_LOG_ERR("Unable to parse the input data: %s", ly_errmsg());
        goto cleanup;
    }

    /* validate input data */
    if (NULL != new_data_tree) {
        ret = lyd_validate(&new_data_tree, LYD_OPT_STRICT | LYD_OPT_CONFIG | LYD_WD_IMPL_TAG);
        CHECK_ZERO_LOG_GOTO(ret, rc, SR_ERR_INTERNAL, cleanup, "Input data are not valid: %s", ly_errmsg());
    }

    /* remove default nodes */
    lyd_wd_cleanup(&new_data_tree, 0);

    /* get data tree of currently stored configuration */
    rc = srcfg_get_module_data(ly_ctx, module_name, &current_data_tree);
    if (SR_ERR_OK != rc) {
        goto cleanup;
    }

    /* get the list of changes made by the user */
    diff = lyd_diff(current_data_tree, new_data_tree, 0);
    if (NULL == diff) {
        SR_LOG_ERR("Unable to get the list of changes: %s", ly_errmsg());
        goto cleanup;
    }

    /* iterate over the list of differences and for each issue corresponding Sysrepo command(s) */
    while (diff->type && LYD_DIFF_END != diff->type[i]) {
        if (NULL != diff->first[i]) {
            first_xpath = lyd_path(diff->first[i]);
            if (NULL == first_xpath) {
                SR_LOG_ERR("Error returned from lyd_path: %s.", ly_errmsg());
                goto cleanup;
            }
        }
        if (NULL != diff->second[i]) {
            second_xpath = lyd_path(diff->second[i]);
            if (NULL == second_xpath) {
                free(first_xpath);
                first_xpath = NULL;
                SR_LOG_ERR("Error returned from lyd_path: %s.", ly_errmsg());
                goto cleanup;
            }
        }
        switch (diff->type[i]) {
            case LYD_DIFF_DELETED:
                SR_LOG_DBG("<LYD_DIFF_DELETED> node: %s", first_xpath);
                rc = srcfg_convert_lydiff_deleted(first_xpath);
                break;
            case LYD_DIFF_CHANGED:
                SR_LOG_DBG("<LYD_DIFF_CHANGED> orig: %s, new: %s", first_xpath, second_xpath);
                rc = srcfg_convert_lydiff_changed(first_xpath, diff->second[i]);
                break;
            case LYD_DIFF_MOVEDAFTER1:
                SR_LOG_DBG("<LYD_DIFF_MOVEDAFTER1> moved: %s, after: %s", first_xpath, second_xpath);
                rc = srcfg_convert_lydiff_movedafter(first_xpath, second_xpath);
                break;
            case LYD_DIFF_CREATED:
                SR_LOG_DBG("<LYD_DIFF_CREATED> parent: %s, new node: %s", first_xpath, second_xpath);
                rc = srcfg_convert_lydiff_created(diff->second[i]);
                break;
            case LYD_DIFF_MOVEDAFTER2:
                SR_LOG_DBG("<LYD_DIFF_MOVEDAFTER2> after: %s, this new node was inserted: %s", first_xpath, second_xpath);
                rc = srcfg_convert_lydiff_movedafter(second_xpath, first_xpath);
                break;
            default:
                assert(0 && "not reachable");
        }
        free(first_xpath);
        free(second_xpath);
        first_xpath = second_xpath = NULL;
        if (SR_ERR_OK != rc) {
            goto cleanup;
        }
        ++i;
    }
    if (0 == i) {
        SR_LOG_DBG_MSG("No changes were made.");
    } else {
        /* commit the changes */
        rc = sr_commit(srcfg_session);
        if (SR_ERR_OK != rc) {
            SR_LOG_ERR("Error returned from sr_commit: %s.", sr_strerror(rc));
            goto cleanup;
        }
        if (SRCFG_STORE_RUNNING == datastore && permanent) {
            /* copy running datastore data into the startup datastore */
            rc = sr_copy_config(srcfg_session, module_name, SR_DS_RUNNING, SR_DS_STARTUP);
            if (SR_ERR_OK != rc) {
                SR_LOG_ERR("Error returned from sr_copy_config: %s.", sr_strerror(rc));
                goto cleanup;
            }
        }
    }

    rc = SR_ERR_OK;

cleanup:
    if (NULL != diff) {
        lyd_free_diff(diff);
    }
    if (NULL != current_data_tree) {
        lyd_free_withsiblings(current_data_tree);
    }
    if (NULL != new_data_tree) {
        lyd_free_withsiblings(new_data_tree);
    }
    if (input_data) {
        free(input_data);
    }
    return rc;
}
Example #19
0
/**
 * @brief Initializes a plugin.
 */
static int
sr_pd_init_plugin(sr_session_ctx_t *session, sr_pd_plugin_ctx_t *plugin_ctx)
{
    int rc = SR_ERR_OK;

    CHECK_NULL_ARG2(session, plugin_ctx);

    /* call init callback */
    rc = plugin_ctx->init_cb(session, &plugin_ctx->private_ctx);

    if (SR_ERR_OK != rc) {
        SR_LOG_ERR("'%s' in '%s' returned an error: %s.", SR_PLUGIN_INIT_FN_NAME, plugin_ctx->filename, sr_strerror(rc));
        plugin_ctx->initialized = false;
    } else {
        plugin_ctx->initialized = true;
    }

    return rc;
}
Example #20
0
int
cl_request_process(sr_session_ctx_t *session, Sr__Msg *msg_req, Sr__Msg **msg_resp,
        sr_mem_ctx_t *sr_mem_resp, const Sr__Operation expected_response_op)
{
    int rc = SR_ERR_OK;

    CHECK_NULL_ARG4(session, session->conn_ctx, msg_req, msg_resp);

    SR_LOG_DBG("Sending %s request.", sr_gpb_operation_name(expected_response_op));

    pthread_mutex_lock(&session->conn_ctx->lock);

    /* send the request */
    rc = cl_message_send(session->conn_ctx, msg_req);
    if (SR_ERR_OK != rc) {
        SR_LOG_ERR("Unable to send the message with request (session id=%"PRIu32", operation=%s).",
                session->id, sr_gpb_operation_name(msg_req->request->operation));
        pthread_mutex_unlock(&session->conn_ctx->lock);
        return rc;
    }

    SR_LOG_DBG("%s request sent, waiting for response.", sr_gpb_operation_name(expected_response_op));

    /* receive the response */
    rc = cl_message_recv(session->conn_ctx, msg_resp, sr_mem_resp);
    if (SR_ERR_OK != rc) {
        SR_LOG_ERR("Unable to receive the message with response (session id=%"PRIu32", operation=%s).",
                session->id, sr_gpb_operation_name(msg_req->request->operation));
        pthread_mutex_unlock(&session->conn_ctx->lock);
        return rc;
    }

    pthread_mutex_unlock(&session->conn_ctx->lock);

    SR_LOG_DBG("%s response received, processing.", sr_gpb_operation_name(expected_response_op));

    /* validate the response */
    rc = sr_gpb_msg_validate(*msg_resp, SR__MSG__MSG_TYPE__RESPONSE, expected_response_op);
    if (SR_ERR_OK != rc) {
        SR_LOG_ERR("Malformed message with response received (session id=%"PRIu32", operation=%s).",
                session->id, sr_gpb_operation_name(msg_req->request->operation));
        return rc;
    }

    /* check for errors */
    if (SR_ERR_OK != (*msg_resp)->response->result) {
        if (NULL != (*msg_resp)->response->error) {
            /* set detailed error information into session */
            rc = cl_session_set_error(session, (*msg_resp)->response->error->message, (*msg_resp)->response->error->xpath);
        }
        /* log the error (except expected ones) */
        if (SR_ERR_NOT_FOUND != (*msg_resp)->response->result &&
                SR_ERR_VALIDATION_FAILED != (*msg_resp)->response->result &&
                SR_ERR_UNAUTHORIZED != (*msg_resp)->response->result &&
                SR_ERR_OPERATION_FAILED != (*msg_resp)->response->result) {
            SR_LOG_ERR("Error by processing of the %s request (session id=%"PRIu32"): %s.",
                    sr_gpb_operation_name(msg_req->request->operation), session->id,
                (NULL != (*msg_resp)->response->error && NULL != (*msg_resp)->response->error->message) ?
                        (*msg_resp)->response->error->message : sr_strerror((*msg_resp)->response->result));
        }
        return (*msg_resp)->response->result;
    }

    return rc;
}
Example #21
0
/**
 * @brief Get complete libyang data tree of a specified module from sysrepo.
 */
static int
srcfg_get_module_data(struct ly_ctx *ly_ctx, const char *module_name, struct lyd_node **data_tree)
{
    int rc = SR_ERR_OK, ret = 0;
    sr_val_t *value = NULL;
    sr_val_iter_t *iter = NULL;
    struct lyd_node *node = NULL;
    const struct lys_node *schema = NULL;
    char query[PATH_MAX] = { 0, };
    char *string_val = NULL;

    snprintf(query, PATH_MAX, "/%s:*//.", module_name);
    rc = sr_get_items_iter(srcfg_session, query, &iter);
    if (SR_ERR_OK != rc) {
        SR_LOG_ERR("Error by sr_get_items_iter: %s", sr_strerror(rc));
        goto cleanup;
    }

    *data_tree = NULL;
    ly_errno = LY_SUCCESS;
    ly_diminish_errors = true;
    while (SR_ERR_OK == (rc = sr_get_item_next(srcfg_session, iter, &value))) {
        ly_diminish_errors = false;
        if (NULL == value) {
            goto next;
        }
        /* get node schema */
        schema = ly_ctx_get_node2(ly_ctx, NULL, value->xpath, 0);
        if (!schema) {
            SR_LOG_ERR("Error by ly_ctx_get_node2: %s", ly_errmsg());
            goto fail;
        }

        /* skip default values */
        if (schema->nodetype == LYS_LEAF && value->dflt) {
            goto next;
        }

        /* skip non-presence containers */
        if (value->type == SR_CONTAINER_T) {
            goto next;
        }

        /* convert value to string */
        rc = sr_val_to_str(value, schema, &string_val);
        if (SR_ERR_OK != rc) {
            SR_LOG_ERR("Error by sr_val_to_str: %s", sr_strerror(rc));
            goto fail;
        }

        /* add node to data tree */
        ly_errno = LY_SUCCESS;
        node = lyd_new_path(*data_tree, ly_ctx, value->xpath, string_val, LYD_PATH_OPT_UPDATE);
        if (!node && LY_SUCCESS != ly_errno) {
            SR_LOG_ERR("Error by lyd_new_path: %s", ly_errmsg());
            goto fail;
        }
        if (NULL == *data_tree) {
            *data_tree = node;
        }
next:
        /* cleanup before next iteration */
        if (NULL != string_val) {
            free(string_val);
            string_val = NULL;
        }
        if (NULL != value) {
            sr_free_val(value);
            value = NULL;
        }
        ly_diminish_errors = true;
    }
    ly_diminish_errors = false;

    if (SR_ERR_NOT_FOUND == rc) {
        rc = SR_ERR_OK;
    }
    if (SR_ERR_OK == rc) {
        if (NULL != *data_tree) {
            /* validate returned data, but most importantly resolve leafrefs */
            ret = lyd_validate(data_tree, LYD_OPT_STRICT | LYD_OPT_CONFIG | LYD_WD_IMPL_TAG);
            CHECK_ZERO_LOG_GOTO(ret, rc, SR_ERR_INTERNAL, fail, "Received data tree from sysrepo is not valid: %s", ly_errmsg());
            /* remove default nodes added by validation */
            lyd_wd_cleanup(data_tree, 0);
        }
        goto cleanup;
    }

fail:
    rc = SR_ERR_INTERNAL;
    if (NULL != *data_tree) {
        lyd_free_withsiblings(*data_tree);
        *data_tree = NULL;
    }

cleanup:
    if (NULL != string_val) {
        free(string_val);
    }
    if (NULL != value) {
        sr_free_val(value);
    }
    if (NULL != iter) {
        sr_free_val_iter(iter);
    }
    return rc;
}
Example #22
0
/**
 * @brief Convert data tree difference of type LYD_DIFF_CREATED to corresponding set of Sysrepo public API calls.
 */
static int
srcfg_convert_lydiff_created(struct lyd_node *node)
{
    int rc = SR_ERR_INTERNAL;
    struct lyd_node *elem = node;
    bool process_children = true;
    sr_val_t value = { 0, SR_UNKNOWN_T };
    struct lyd_node_leaf_list *data_leaf = NULL;
    struct lys_node_list *slist = NULL;
    char *xpath = NULL, *delim = NULL;

    CHECK_NULL_ARG(node);

    /* non-recursive DFS post-order */
    do {
        /* go as deep as possible */
        if (process_children) {
            while (!(elem->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYXML)) && elem->child) {
                elem = elem->child;
            }
        }

        /* get appropriate xpath and value */
        free(xpath);
        xpath = value.xpath = NULL;
        value.type = SR_UNKNOWN_T;
        switch (elem->schema->nodetype) {
            case LYS_LEAF: /* e.g.: /test-module:user[name='nameE']/name */
                /* get value */
                data_leaf = (struct lyd_node_leaf_list *)elem;
                value.type = sr_libyang_leaf_get_type(data_leaf);
                rc = sr_libyang_leaf_copy_value(data_leaf, &value);
                if (SR_ERR_OK != rc) {
                    SR_LOG_ERR("Error returned from sr_libyang_leaf_copy_value: %s.", sr_strerror(rc));
                    goto cleanup;
                }
                /* get xpath */
                xpath = lyd_path(elem);
                if (NULL == xpath) {
                    SR_LOG_ERR("Error returned from lyd_path: %s.", ly_errmsg());
                    goto cleanup;
                }
                /* key value of a list cannot be set directly */
                if (elem->parent && (elem->parent->schema->nodetype == LYS_LIST)) {
                    slist = (struct lys_node_list *)elem->parent->schema;
                    for (unsigned i = 0; i < slist->keys_size; ++i) {
                        if (slist->keys[i]->name == elem->schema->name) {
                            /* key */
                            if (i == 0) {
                                delim = strrchr(xpath, '/');
                                if (delim) {
                                    *delim = '\0';
                                }
                                goto set_value;
                            } else {
                                /* create list instance (directly) only once - with the first key */
                                goto next_node;
                            }
                        }
                    }
                }
                break;

            case LYS_LEAFLIST: /* e.g.: /test-module:main/numbers[.='10'] */
                /* get value */
                data_leaf = (struct lyd_node_leaf_list *)elem;
                value.type = sr_libyang_leaf_get_type(data_leaf);
                rc = sr_libyang_leaf_copy_value(data_leaf, &value);
                if (SR_ERR_OK != rc) {
                    SR_LOG_ERR("Error returned from sr_libyang_leaf_copy_value: %s.", sr_strerror(rc));
                    goto cleanup;
                }
                /* get xpath */
                xpath = lyd_path(elem);
                if (NULL == xpath) {
                    SR_LOG_ERR("Error returned from lyd_path: %s.", ly_errmsg());
                    goto cleanup;
                }
                /* strip away the predicate */
                delim = strrchr(xpath, '[');
                if (delim) {
                    *delim = '\0';
                }
                break;

            case LYS_ANYXML:
                SR_LOG_ERR_MSG("The anyxml statement is not yet supported by Sysrepo.");
                goto cleanup;

            case LYS_CONTAINER:
                /* explicitly create only presence containers */
                if (((struct lys_node_container *)elem->schema)->presence) {
                    xpath = lyd_path(elem);
                } else {
                    goto next_node;
                }
                break;

            default:
                /* no data to set */
                goto next_node;
        }

set_value:
        /* set value */
        rc = sr_set_item(srcfg_session, xpath, SR_UNKNOWN_T != value.type ? &value : NULL, SR_EDIT_DEFAULT);
        if (SR_ERR_OK != rc) {
            SR_LOG_ERR("Error returned from sr_set_item: %s.", sr_strerror(rc));
            goto cleanup;
        }

next_node:
        /* backtracking + automatically moving to the next sibling if there is any */
        if (elem != node) {
            if (elem->next) {
                elem = elem->next;
                process_children = true;
            } else {
                assert(elem->parent);
                elem = elem->parent;
                process_children = false;
            }
        } else {
            break;
        }
    } while (true);

    rc = SR_ERR_OK;

cleanup:
    if (NULL != xpath) {
        free(xpath);
    }
    sr_free_val_content(&value);
    return rc;
}
Example #23
0
/**
 * Start a session.
 *
 * @param session The session to use. Must not be NULL.
 *
 * @retval SR_OK Success.
 * @retval SR_ERR_ARG Invalid session passed.
 *
 * @since 0.4.0
 */
SR_API int sr_session_start(struct sr_session *session)
{
	struct sr_dev_inst *sdi;
	struct sr_channel *ch;
	GSList *l, *c;
	int enabled_channels, ret;

	if (!session) {
		sr_err("%s: session was NULL", __func__);
		return SR_ERR_ARG;
	}

	if (!session->devs) {
		sr_err("%s: session->devs was NULL; a session "
		       "cannot be started without devices.", __func__);
		return SR_ERR_ARG;
	}

	if (session->trigger && verify_trigger(session->trigger) != SR_OK)
		return SR_ERR;

	ret = set_main_context(session);
	if (ret != SR_OK)
		return ret;

	session->running = TRUE;

	sr_info("Starting.");

	for (l = session->devs; l; l = l->next) {
		sdi = l->data;
		enabled_channels = 0;
		for (c = sdi->channels; c; c = c->next) {
			ch = c->data;
			if (ch->enabled) {
				enabled_channels++;
				break;
			}
		}
		if (enabled_channels == 0) {
			ret = SR_ERR;
			sr_err("%s using connection %s has no enabled channels!",
					sdi->driver->name, sdi->connection_id);
			break;
		}

		if ((ret = sr_config_commit(sdi)) != SR_OK) {
			sr_err("Failed to commit device settings before "
			       "starting acquisition (%s)", sr_strerror(ret));
			break;
		}
		if ((ret = sdi->driver->dev_acquisition_start(sdi, sdi)) != SR_OK) {
			sr_err("%s: could not start an acquisition "
			       "(%s)", __func__, sr_strerror(ret));
			break;
		}
	}

	if (ret != SR_OK) {
		unset_main_context(session);
		session->running = FALSE;
	}
	/* TODO: What if there are multiple devices? Which return code? */

	return ret;
}