/** * @brief Performs the --import operation. */ static int srcfg_import_operation(const char *module_name, srcfg_datastore_t datastore, const char *filepath, LYD_FORMAT format, bool permanent) { int rc = SR_ERR_INTERNAL, ret = 0; struct ly_ctx *ly_ctx = NULL; int fd_in = STDIN_FILENO; CHECK_NULL_ARG(module_name); /* init libyang context */ ret = srcfg_ly_init(&ly_ctx, module_name); CHECK_RC_MSG_GOTO(ret, fail, "Failed to initialize libyang context."); if (filepath) { /* try to open the input file */ fd_in = open(filepath, O_RDONLY); CHECK_NOT_MINUS1_LOG_GOTO(fd_in, rc, SR_ERR_INTERNAL, fail, "Unable to open the input file '%s': %s.", filepath, sr_strerror_safe(errno)); } else { /* read configuration from stdin */ printf("Please enter the new configuration:\n"); } /* import datastore data */ ret = srcfg_import_datastore(ly_ctx, fd_in, module_name, datastore, format, permanent); if (SR_ERR_OK != ret) { goto fail; } rc = SR_ERR_OK; printf("The new configuration was successfully applied.\n"); goto cleanup; fail: printf("Errors were encountered during importing. Cancelling the operation.\n"); cleanup: if (STDIN_FILENO != fd_in && -1 != fd_in) { close(fd_in); } if (ly_ctx) { ly_ctx_destroy(ly_ctx, NULL); } return rc; }
/** * @brief Performs the --export operation. */ static int srcfg_export_operation(const char *module_name, const char *filepath, LYD_FORMAT format) { int rc = SR_ERR_INTERNAL, ret = 0; struct ly_ctx *ly_ctx = NULL; int fd_out = STDOUT_FILENO; CHECK_NULL_ARG(module_name); /* init libyang context */ ret = srcfg_ly_init(&ly_ctx, module_name); CHECK_RC_MSG_GOTO(ret, fail, "Failed to initialize libyang context."); /* try to open/create the output file if needed */ if (filepath) { fd_out = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0666); CHECK_NOT_MINUS1_LOG_GOTO(fd_out, rc, SR_ERR_INTERNAL, fail, "Unable to open the output file '%s': %s.", filepath, sr_strerror_safe(errno)); } /* export diatastore data */ ret = srcfg_export_datastore(ly_ctx, fd_out, module_name, format); if (SR_ERR_OK != ret) { goto fail; } rc = SR_ERR_OK; if (filepath) { /* do not clutter the output sent to stdout */ printf("The configuration was successfully exported.\n"); } goto cleanup; fail: printf("Errors were encountered during exporting. Cancelling the operation.\n"); cleanup: if (STDOUT_FILENO != fd_out && -1 != fd_out) { close(fd_out); } if (ly_ctx) { ly_ctx_destroy(ly_ctx, NULL); } return rc; }
int cl_version_verify(sr_conn_ctx_t *connection) { int rc = SR_ERR_OK; Sr__Msg *msg_req = NULL, *msg_resp = NULL; sr_mem_ctx_t *sr_mem = NULL; /* prepare version-verification request */ rc = sr_mem_new(0, &sr_mem); CHECK_RC_MSG_GOTO(rc, cleanup, "Failed to create a new Sysrepo memory context."); rc = sr_gpb_req_alloc(sr_mem, SR__OPERATION__VERSION_VERIFY, /* undefined session id */ 0, &msg_req); CHECK_RC_MSG_GOTO(rc, cleanup, "Cannot allocate GPB message."); /* set argument */ sr_mem_edit_string(sr_mem, &msg_req->request->version_verify_req->soname, SR_COMPAT_VERSION); CHECK_NULL_NOMEM_GOTO(msg_req->request->version_verify_req->soname, rc, cleanup); /* send the request */ SR_LOG_DBG("Sending %s request.", sr_gpb_operation_name(SR__OPERATION__VERSION_VERIFY)); pthread_mutex_lock(&connection->lock); rc = cl_message_send(connection, msg_req); if (SR_ERR_OK != rc) { SR_LOG_ERR("Unable to send the message with request (operation=%s).", sr_gpb_operation_name(msg_req->request->operation)); pthread_mutex_unlock(&connection->lock); goto cleanup; } SR_LOG_DBG("%s request sent, waiting for response.", sr_gpb_operation_name(SR__OPERATION__VERSION_VERIFY)); /* receive the response */ rc = cl_message_recv(connection, &msg_resp, NULL); if (SR_ERR_OK != rc) { SR_LOG_ERR("Unable to receive the message with response (operation=%s).", sr_gpb_operation_name(msg_req->request->operation)); pthread_mutex_unlock(&connection->lock); goto cleanup; } pthread_mutex_unlock(&connection->lock); SR_LOG_DBG("%s response received, processing.", sr_gpb_operation_name(SR__OPERATION__VERSION_VERIFY)); /* validate the response */ rc = sr_gpb_msg_validate(msg_resp, SR__MSG__MSG_TYPE__RESPONSE, SR__OPERATION__VERSION_VERIFY); if (SR_ERR_OK != rc) { SR_LOG_ERR("Malformed message with response received (operation=%s).", sr_gpb_operation_name(msg_req->request->operation)); goto cleanup; } /* process the result */ if (SR_ERR_OK != msg_resp->response->result) { SR_LOG_ERR("Sysrepod's \"%s\" version is not compatible with version \""SR_COMPAT_VERSION"\" in use.", msg_resp->response->version_verify_resp->soname); rc = msg_resp->response->result; goto cleanup; } cleanup: if (NULL != msg_req) { sr_msg_free(msg_req); } else { sr_mem_free(sr_mem); } if (NULL != msg_resp) { sr_msg_free(msg_resp); } return rc; }
/** * @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 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, ¤t_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; }