示例#1
0
int
cl_session_set_error(sr_session_ctx_t *session, const char *error_message, const char *error_path)
{
    CHECK_NULL_ARG(session);

    pthread_mutex_lock(&session->lock);

    if (0 == session->error_info_size) {
        /* need to allocate the space for the error */
        session->error_info = calloc(1, sizeof(*session->error_info));
        if (NULL == session->error_info) {
            SR_LOG_ERR_MSG("Unable to allocate error information.");
            pthread_mutex_unlock(&session->lock);
            return SR_ERR_NOMEM;
        }
        session->error_info_size = 1;
    } else {
        /* space for the error already allocated, release old error data */
        if (NULL != session->error_info[0].message) {
            free((void*)session->error_info[0].message);
            session->error_info[0].message = NULL;
        }
        if (NULL != session->error_info[0].xpath) {
            free((void*)session->error_info[0].xpath);
            session->error_info[0].xpath = NULL;
        }
    }
    if (NULL != error_message) {
        session->error_info[0].message = strdup(error_message);
        if (NULL == session->error_info[0].message) {
            SR_LOG_ERR_MSG("Unable to allocate error message.");
            pthread_mutex_unlock(&session->lock);
            return SR_ERR_NOMEM;
        }
    }
    if (NULL != error_path) {
        session->error_info[0].xpath = strdup(error_path);
        if (NULL == session->error_info[0].xpath) {
            SR_LOG_ERR_MSG("Unable to allocate error xpath.");
            pthread_mutex_unlock(&session->lock);
            return SR_ERR_NOMEM;
        }
    }

    session->error_cnt = 1;
    pthread_mutex_unlock(&session->lock);

    return SR_ERR_OK;
}
示例#2
0
int
cl_session_create(sr_conn_ctx_t *conn_ctx, sr_session_ctx_t **session_p)
{
    sr_session_ctx_t *session = NULL;
    int rc = 0;

    /* initialize session context */
    session = calloc(1, sizeof(*session));
    CHECK_NULL_NOMEM_RETURN(session);

    /* initialize session mutext */
    rc = pthread_mutex_init(&session->lock, NULL);
    if (0 != rc) {
        SR_LOG_ERR_MSG("Cannot initialize session mutex.");
        free(session);
        return SR_ERR_INIT_FAILED;
    }

    session->conn_ctx = conn_ctx;

    /* store the session in the connection */
    rc = cl_conn_add_session(conn_ctx, session);
    if (SR_ERR_OK != rc) {
        SR_LOG_WRN_MSG("Error by adding the session to the connection session list.");
    }

    *session_p = session;
    return SR_ERR_OK;
}
示例#3
0
/**
 * @brief Adds a new session to the session list of the connection.
 */
static int
cl_conn_add_session(sr_conn_ctx_t *connection, sr_session_ctx_t *session)
{
    sr_session_list_t *session_item = NULL, *tmp = NULL;

    CHECK_NULL_ARG2(connection, session);

    session_item = calloc(1, sizeof(*session_item));
    if (NULL == session_item) {
        SR_LOG_ERR_MSG("Cannot allocate memory for new session list entry.");
        return SR_ERR_NOMEM;
    }
    session_item->session = session;

    pthread_mutex_lock(&connection->lock);

    /* append session entry at the end of list */
    if (NULL == connection->session_list) {
        connection->session_list = session_item;
    } else {
        tmp = connection->session_list;
        while (NULL != tmp->next) {
            tmp = tmp->next;
        }
        tmp->next = session_item;
    }

    pthread_mutex_unlock(&connection->lock);

    return SR_ERR_OK;
}
示例#4
0
int
cl_session_set_errors(sr_session_ctx_t *session, Sr__Error **errors, size_t error_cnt)
{
    sr_error_info_t *tmp_info = NULL;

    CHECK_NULL_ARG2(session, errors);

    pthread_mutex_lock(&session->lock);

    /* first release already allocated space for errors */
    for (size_t i = 0; i < session->error_info_size; i++) {
        if (NULL != session->error_info[i].message) {
            free((void*)session->error_info[i].message);
            session->error_info[i].message = NULL;
        }
        if (NULL != session->error_info[i].xpath) {
            free((void*)session->error_info[i].xpath);
            session->error_info[i].xpath = NULL;
        }
    }

    if (session->error_info_size < error_cnt) {
        tmp_info = realloc(session->error_info, (error_cnt * sizeof(*tmp_info)));
        if (NULL == tmp_info) {
            SR_LOG_ERR_MSG("Unable to allocate error information.");
            pthread_mutex_unlock(&session->lock);
            return SR_ERR_NOMEM;
        }
        session->error_info = tmp_info;
        session->error_info_size = error_cnt;
    }
    for (size_t i = 0; i < error_cnt; i++) {
        if (NULL != errors[i]->message) {
            session->error_info[i].message = strdup(errors[i]->message);
            if (NULL == session->error_info[i].message) {
                SR_LOG_WRN_MSG("Unable to allocate error message, will be left NULL.");
            }
        } else {
            session->error_info[i].message = NULL;
        }
        if (NULL != errors[i]->xpath) {
            session->error_info[i].xpath = strdup(errors[i]->xpath);
            if (NULL == session->error_info[i].xpath) {
                SR_LOG_WRN_MSG("Unable to allocate error xpath, will be left NULL.");
            }
        } else {
            session->error_info[i].xpath = NULL;
        }
    }

    session->error_cnt = error_cnt;
    pthread_mutex_unlock(&session->lock);

    return SR_ERR_OK;
}
示例#5
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;
}
示例#6
0
/**
 * @brief Sends a message via provided connection.
 */
static int
cl_message_send(sr_conn_ctx_t *conn_ctx, Sr__Msg *msg)
{
    size_t msg_size = 0;
    int pos = 0, sent = 0;
    int rc = SR_ERR_OK;

    CHECK_NULL_ARG2(conn_ctx, msg);

    /* find out required message size */
    msg_size = sr__msg__get_packed_size(msg);
    if ((msg_size <= 0) || (msg_size > SR_MAX_MSG_SIZE)) {
        SR_LOG_ERR("Unable to send the message of size %zuB.", msg_size);
        return SR_ERR_INTERNAL;
    }

    /* expand the buffer if needed */
    rc = cl_conn_msg_buf_expand(conn_ctx, msg_size + SR_MSG_PREAM_SIZE);
    if (SR_ERR_OK != rc) {
        SR_LOG_ERR_MSG("Cannot expand buffer for the message.");
        return rc;
    }

    /* write 4-byte length */
    sr_uint32_to_buff(msg_size, conn_ctx->msg_buf);

    /* pack the message */
    sr__msg__pack(msg, (conn_ctx->msg_buf + SR_MSG_PREAM_SIZE));

    /* send the message */
    do {
        sent = send(conn_ctx->fd, (conn_ctx->msg_buf + pos), (msg_size + SR_MSG_PREAM_SIZE - pos), 0);
        if (sent > 0) {
            pos += sent;
        } else {
            if (errno == EINTR) {
                continue;
            }
            SR_LOG_ERR("Error by sending of the message: %s.", sr_strerror_safe(errno));
            return SR_ERR_DISCONNECT;
        }
    } while ((pos < (msg_size + SR_MSG_PREAM_SIZE)) && (sent > 0));

    return SR_ERR_OK;
}
示例#7
0
int
cl_connection_create(sr_conn_ctx_t **conn_ctx_p)
{
    sr_conn_ctx_t *connection = NULL;
    int rc = 0;

    /* initialize the context */
    connection = calloc(1, sizeof(*connection));
    CHECK_NULL_NOMEM_RETURN(connection);

    /* init connection mutext */
    rc = pthread_mutex_init(&connection->lock, NULL);
    if (0 != rc) {
        SR_LOG_ERR_MSG("Cannot initialize connection mutex.");
        free(connection);
        return SR_ERR_INIT_FAILED;
    }

    connection->fd = -1;

    *conn_ctx_p = connection;
    return SR_ERR_OK;
}
示例#8
0
/*
 * @brief Receives a message on provided connection (blocks until a message is received).
 */
static int
cl_message_recv(sr_conn_ctx_t *conn_ctx, Sr__Msg **msg, sr_mem_ctx_t *sr_mem_resp)
{
    size_t len = 0, pos = 0;
    size_t msg_size = 0;
    sr_mem_ctx_t *sr_mem = sr_mem_resp;
    int rc = 0;

    /* expand the buffer if needed */
    rc = cl_conn_msg_buf_expand(conn_ctx, SR_MSG_PREAM_SIZE);
    if (SR_ERR_OK != rc) {
        SR_LOG_ERR_MSG("Cannot expand buffer for the message.");
        return rc;
    }

    /* read at least first 4 bytes with length of the message */
    while (pos < SR_MSG_PREAM_SIZE) {
        len = recv(conn_ctx->fd, conn_ctx->msg_buf, conn_ctx->msg_buf_size, 0);
        if (-1 == len) {
            if (errno == EINTR) {
                continue;
            }
            if (EAGAIN == errno || EWOULDBLOCK == errno) {
                SR_LOG_ERR_MSG("While waiting for a response, timeout has expired.");
                return SR_ERR_TIME_OUT;
            }
            SR_LOG_ERR("Error by receiving of the message: %s.", sr_strerror_safe(errno));
            return SR_ERR_DISCONNECT;
        }
        if (0 == len) {
            SR_LOG_ERR_MSG("Sysrepo server disconnected.");
            return SR_ERR_DISCONNECT;
        }
        pos += len;
    }
    msg_size = sr_buff_to_uint32(conn_ctx->msg_buf);

    /* check message size bounds */
    if ((msg_size <= 0) || (msg_size > SR_MAX_MSG_SIZE)) {
        SR_LOG_ERR("Invalid message size in the message preamble (%zu).", msg_size);
        return SR_ERR_MALFORMED_MSG;
    }

    /* expand the buffer if needed */
    rc = cl_conn_msg_buf_expand(conn_ctx, (msg_size + SR_MSG_PREAM_SIZE));
    if (SR_ERR_OK != rc) {
        SR_LOG_ERR_MSG("Cannot expand buffer for the message.");
        return rc;
    }

    /* read the rest of the message */
    while (pos < (msg_size + SR_MSG_PREAM_SIZE)) {
        len = recv(conn_ctx->fd, (conn_ctx->msg_buf + pos), (conn_ctx->msg_buf_size - pos), 0);
        if (-1 == len) {
            if (errno == EINTR) {
                continue;
            }
            if (EAGAIN == errno || EWOULDBLOCK == errno) {
                SR_LOG_ERR_MSG("While waiting for a response, timeout has expired.");
                return SR_ERR_TIME_OUT;
            }
            SR_LOG_ERR("Error by receiving of the message: %s.", sr_strerror_safe(errno));
            return SR_ERR_DISCONNECT;
        }
        if (0 == len) {
            SR_LOG_ERR_MSG("Sysrepo server disconnected.");
            return SR_ERR_DISCONNECT;
        }
        pos += len;
    }

    /* unpack the message */
    if (NULL == sr_mem) {
        rc = sr_mem_new(msg_size, &sr_mem);
        CHECK_RC_MSG_RETURN(rc, "Failed to create a new Sysrepo memory context.");
    }
    ProtobufCAllocator allocator = sr_get_protobuf_allocator(sr_mem);
    *msg = sr__msg__unpack(&allocator, msg_size, (const uint8_t*)(conn_ctx->msg_buf + SR_MSG_PREAM_SIZE));
    if (NULL == *msg) {
        if (NULL == sr_mem_resp) {
            sr_mem_free(sr_mem);
        }
        SR_LOG_ERR_MSG("Malformed message received.");
        return SR_ERR_MALFORMED_MSG;
    }

    /* associate message with context */
    if (NULL != sr_mem) {
        (*msg)->_sysrepo_mem_ctx = (uint64_t)sr_mem;
        ++sr_mem->obj_count;
    }

    return SR_ERR_OK;
}
示例#9
0
/**
 * @brief Performs the program's main operation: lets user to edit specified module and datastore
 * using the preferred editor. New configuration is validated before it is saved.
 */
static int
srcfg_edit_operation(const char *module_name, srcfg_datastore_t datastore, LYD_FORMAT format,
                     const char *editor, bool keep, bool permanent)
{
    int rc = SR_ERR_INTERNAL, ret = 0;
    struct ly_ctx *ly_ctx = NULL;
    char tmpfile_path[PATH_MAX] = { 0, }, cmd[2*PATH_MAX+4] = { 0, };
    char *dest = NULL;
    int fd_tmp = -1;
    bool locked = false;
    pid_t child_pid = -1;
    int child_status = 0, first_attempt = 1;

    CHECK_NULL_ARG2(module_name, editor);

    /* init libyang context */
    ret = srcfg_ly_init(&ly_ctx, module_name);
    CHECK_RC_MSG_GOTO(ret, fail, "Failed to initialize libyang context.");

    /* lock module for the time of editing if requested */
    if (keep) {
        rc = sr_lock_module(srcfg_session, module_name);
        if (SR_ERR_OK != rc) {
            srcfg_report_error(rc);
            goto fail;
        }
        locked = true;
    }

/* export: */
    /* create temporary file for datastore editing */
    mode_t orig_umask = umask(S_IRWXO|S_IRWXG);
    snprintf(tmpfile_path, PATH_MAX, "/tmp/srcfg.%s%s.XXXXXX", module_name,
             datastore == SRCFG_STORE_RUNNING ? SR_RUNNING_FILE_EXT : SR_STARTUP_FILE_EXT);
    fd_tmp = mkstemp(tmpfile_path);
    umask(orig_umask);
    CHECK_NOT_MINUS1_MSG_GOTO(fd_tmp, rc, SR_ERR_INTERNAL, fail,
                              "Failed to create temporary file for datastore editing.");

    /* export datastore content into a temporary file */
    ret = srcfg_export_datastore(ly_ctx, fd_tmp, module_name, format);
    if (SR_ERR_OK != ret) {
        goto fail;
    }
    close(fd_tmp);
    fd_tmp = -1;

edit:
    if (!first_attempt) {
        if (!srcfg_prompt("Unable to apply the changes. "
                          "Would you like to continue editing the configuration?", "y", "n")) {
            goto save;
        }
    }
    first_attempt = 0;

    /* Open the temporary file inside the preferred text editor */
    child_pid = fork();
    if (0 <= child_pid) { /* fork succeeded */
        if (0 == child_pid) { /* child process */
            /* Open text editor */
            return execlp(editor, editor, tmpfile_path, (char *)NULL);
         } else { /* parent process */
             /* wait for the child to exit */
             ret = waitpid(child_pid, &child_status, 0);
             if (child_pid != ret) {
                 SR_LOG_ERR_MSG("Unable to wait for the editor to exit.");
                 goto save;
             }
             /* Check return status from the child */
             if (!WIFEXITED(child_status) || 0 != WEXITSTATUS(child_status)) {
                 SR_LOG_ERR_MSG("Text editor didn't start/terminate properly.");
                 goto save;
             }
         }
    }
    else /* fork failed */
    {
        SR_LOG_ERR_MSG("Failed to fork a new process for the text editor.");
        goto fail;
    }

/* import: */
    /* re-open temporary file */
    fd_tmp = open(tmpfile_path, O_RDONLY);
    CHECK_NOT_MINUS1_MSG_GOTO(fd_tmp, rc, SR_ERR_INTERNAL, save,
                              "Unable to re-open the configuration after it was edited using the text editor.");

    /* import temporary file content into the datastore */
    ret = srcfg_import_datastore(ly_ctx, fd_tmp, module_name, datastore, format, permanent);
    close(fd_tmp);
    fd_tmp = -1;
    if (SR_ERR_OK != ret) {
        goto edit;
    }

    /* operation succeeded */
    rc = SR_ERR_OK;
    printf("The new configuration was successfully applied.\n");
    goto cleanup;

save:
    /* save to a (ordinary) file if requested */
    if (srcfg_prompt("Failed to commit the new configuration. "
                     "Would you like to save your changes to a file?", "y", "n")) {
        /* copy whatever is in the temporary file right now */
        snprintf(cmd, PATH_MAX + 4, "cp %s ", tmpfile_path);
        dest = cmd + strlen(cmd);
        do {
            printf("Enter a file path: ");
            ret = scanf("%" PATH_MAX_STR "s", dest);
            if (EOF == ret) {
                SR_LOG_ERR_MSG("Scanf failed: end of the input stream.");
                goto discard;
            }
            sr_str_trim(dest);
            ret = system(cmd);
            if (0 != ret) {
                printf("Unable to save the configuration to '%s'. ", dest);
                if (!srcfg_prompt("Retry?", "y", "n")) {
                    goto discard;
                }
            }
        } while (0 != ret);
        printf("Your changes have been saved to '%s'. "
               "You may try to apply them again using the import operation.\n", dest);
        goto cleanup;
    }

discard:
    printf("Your changes were discarded.\n");
    goto cleanup;

fail:
    printf("Errors were encountered during editing. Cancelling the operation.\n");

cleanup:
    if (-1 != fd_tmp) {
        close(fd_tmp);
    }
    if ('\0' != tmpfile_path[0]) {
        unlink(tmpfile_path);
    }
    if (locked) {
        rc = sr_unlock_module(srcfg_session, module_name);
        if (SR_ERR_OK != rc) {
            srcfg_report_error(rc);
        }
    }
    if (ly_ctx) {
        ly_ctx_destroy(ly_ctx, NULL);
    }
    return rc;
}
示例#10
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;
}
示例#11
0
/**
 * @brief Loads all plugins in plugins directory.
 */
static int
sr_pd_load_plugins(sr_pd_ctx_t *ctx)
{
    DIR *dir;
    struct dirent entry, *result;
    char *env_str = NULL;
    char plugins_dir[PATH_MAX - 256] = { 0, };
    char plugin_filename[PATH_MAX + 1] = { 0, };
    sr_pd_plugin_ctx_t *tmp = NULL;
    bool init_retry_needed = false;
    int ret = 0;
    int rc = SR_ERR_OK;

    CHECK_NULL_ARG(ctx);

    /* get plugins dir from environment variable, or use default one */
    env_str = getenv("SR_PLUGINS_DIR");
    if (NULL != env_str) {
        strncat(plugins_dir, env_str, PATH_MAX - 257);
    } else {
        strncat(plugins_dir, SR_PLUGINS_DIR, PATH_MAX - 257);
    }

    SR_LOG_DBG("Loading plugins from '%s'.", plugins_dir);

    dir = opendir(plugins_dir);
    if (NULL == dir) {
        SR_LOG_ERR("Error by opening plugin directory: %s.", sr_strerror_safe(errno));
        return SR_ERR_INVAL_ARG;
    }
    do {
        ret = readdir_r(dir, &entry, &result);
        if (0 != ret) {
            SR_LOG_ERR("Error by reading plugin directory: %s.", sr_strerror_safe(errno));
            break;
        }
        if ((NULL != result) && (DT_DIR != entry.d_type)
                && (0 != strcmp(entry.d_name, ".")) && (0 != strcmp(entry.d_name, ".."))) {
            SR_LOG_DBG("Loading plugin from file '%s'.", entry.d_name);
            snprintf(plugin_filename, PATH_MAX, "%s/%s", plugins_dir, entry.d_name);

            /* realloc plugins array */
            tmp = realloc(ctx->plugins, sizeof(*ctx->plugins) * (ctx->plugins_cnt + 1));
            if (NULL == tmp) {
                SR_LOG_ERR_MSG("Unable to realloc plugins array, skipping the rest of plugins.");
                break;
            }
            ctx->plugins = tmp;

            /* load the plugin */
            rc = sr_pd_load_plugin(ctx->session, plugin_filename, &(ctx->plugins[ctx->plugins_cnt]));
            if (SR_ERR_OK != rc) {
                SR_LOG_WRN("Ignoring the file '%s'.", plugin_filename);
                continue;
            }

            /* initialize the plugin */
            rc = sr_pd_init_plugin(ctx->session, &(ctx->plugins[ctx->plugins_cnt]));
            if (SR_ERR_OK != rc) {
                init_retry_needed = true;
            }
            ctx->plugins_cnt += 1;
        }
    } while (NULL != result);
    closedir(dir);

    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);
    }

    return SR_ERR_OK;
}