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; }
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; }
/** * @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; }
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; }
/** * @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; }
/** * @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; }
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; }
/* * @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; }
/** * @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; }
/** * @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; }
/** * @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; }