| // SPDX-License-Identifier: MIT or LGPL-2.1-only |
| |
| #include <config.h> |
| |
| #include <iostream> |
| #include "nlohmann/json.hpp" |
| #include "ublksrv_priv.h" |
| |
| #define parse_json(j, jbuf) \ |
| try { \ |
| j = json::parse(std::string(jbuf)); \ |
| } catch (json::parse_error& ex) { \ |
| std::cerr << "parse error at byte " << ex.byte << std::endl; \ |
| return -EINVAL; \ |
| } \ |
| |
| using json = nlohmann::json; |
| |
| static inline int dump_json_to_buf(json &j, char *jbuf, int len) |
| { |
| std::string s; |
| int j_len; |
| |
| s = j.dump(); |
| j_len = s.length(); |
| if (j_len < len) { |
| strcpy(jbuf, s.c_str()); |
| return j_len; |
| } |
| return -EINVAL; |
| } |
| |
| NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ublksrv_ctrl_dev_info, |
| nr_hw_queues, |
| queue_depth, |
| state, |
| pad0, |
| max_io_buf_bytes, |
| dev_id, |
| ublksrv_pid, |
| pad1, |
| flags, |
| ublksrv_flags, |
| owner_uid, |
| owner_gid, |
| reserved1, |
| reserved2) |
| |
| /* |
| * build one json string with dev_info head, and result is stored |
| * in 'buf'. |
| */ |
| int ublksrv_json_write_dev_info(const struct ublksrv_ctrl_dev *cdev, |
| char *jbuf, int len) |
| { |
| const struct ublksrv_ctrl_dev_info *info = &cdev->dev_info; |
| json j_info = *info; |
| json j; |
| |
| j["dev_info"] = j_info; |
| |
| return dump_json_to_buf(j, jbuf, len); |
| } |
| |
| /* Fill 'info' from the json string pointed by 'json_buf' */ |
| int ublksrv_json_read_dev_info(const char *jbuf, |
| struct ublksrv_ctrl_dev_info *info) |
| { |
| json j; |
| |
| parse_json(j, jbuf); |
| |
| if (!j.contains("dev_info")) |
| return -EINVAL; |
| |
| auto sj = j["dev_info"]; |
| |
| *info = sj.get<struct ublksrv_ctrl_dev_info>(); |
| |
| return 0; |
| } |
| |
| NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(struct ublk_param_basic, |
| attrs, |
| logical_bs_shift, |
| physical_bs_shift, |
| io_opt_shift, |
| io_min_shift, |
| max_sectors, |
| chunk_sectors, |
| dev_sectors, |
| virt_boundary_mask) |
| |
| NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(struct ublk_param_discard, |
| discard_alignment, |
| discard_granularity, |
| max_discard_sectors, |
| max_write_zeroes_sectors, |
| max_discard_segments, |
| reserved0) |
| NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(struct ublk_params, |
| len, types, basic, discard) |
| |
| int ublksrv_json_write_params(const struct ublk_params *p, |
| char *jbuf, int len) |
| { |
| json j; |
| std::string s; |
| |
| parse_json(j, jbuf); |
| |
| j["params"] = *p; |
| |
| return dump_json_to_buf(j, jbuf, len); |
| } |
| |
| int ublksrv_json_read_params(struct ublk_params *p, |
| const char *jbuf) |
| { |
| json j, sj; |
| std::string s; |
| |
| parse_json(j, jbuf); |
| |
| if (!j.contains("params")) |
| return -EINVAL; |
| |
| *p = j["params"]; |
| |
| return 0; |
| } |
| |
| int ublksrv_json_dump_params(const char *jbuf) |
| { |
| json j; |
| std::string s; |
| |
| parse_json(j, jbuf); |
| |
| if (!j.contains("params")) |
| return -EINVAL; |
| |
| std::cout << std::setw(4) << j["params"] << '\n'; |
| |
| return 0; |
| } |
| |
| int ublksrv_json_read_target_str_info(const char *jbuf, int len, |
| const char *name, char *val) |
| { |
| json j; |
| std::string s; |
| |
| parse_json(j, jbuf); |
| |
| if (!j.contains("target")) |
| return -EINVAL; |
| |
| auto tj = j["target"]; |
| |
| if (!tj.contains(name)) |
| return -EINVAL; |
| |
| std::string str = tj[std::string(name)]; |
| if (str.length() < (unsigned)len) { |
| strcpy(val, str.c_str()); |
| return 0; |
| } |
| |
| return -EINVAL; |
| } |
| |
| int ublksrv_json_read_target_ulong_info(const char *jbuf, |
| const char *name, long *val) |
| { |
| json j; |
| std::string s; |
| |
| parse_json(j, jbuf); |
| |
| if (!j.contains("target")) |
| return -EINVAL; |
| |
| auto tj = j["target"]; |
| |
| if (!tj.contains(name)) |
| return -EINVAL; |
| |
| *val = tj[std::string(name)]; |
| |
| return 0; |
| } |
| |
| int ublksrv_json_write_target_str_info(char *jbuf, int len, |
| const char *name, const char *val) |
| { |
| json j; |
| std::string s; |
| |
| parse_json(j, jbuf); |
| |
| j["target"][std::string(name)] = val;; |
| |
| return dump_json_to_buf(j, jbuf, len); |
| } |
| |
| int ublksrv_json_write_target_long_info(char *jbuf, int len, |
| const char *name, long val) |
| { |
| json j; |
| std::string s; |
| |
| parse_json(j, jbuf); |
| |
| j["target"][std::string(name)] = val;; |
| |
| return dump_json_to_buf(j, jbuf, len); |
| } |
| |
| int ublksrv_json_write_target_ulong_info(char *jbuf, int len, const char *name, |
| unsigned long val) |
| { |
| json j; |
| std::string s; |
| |
| parse_json(j, jbuf); |
| |
| j["target"][std::string(name)] = val;; |
| |
| return dump_json_to_buf(j, jbuf, len); |
| } |
| |
| int ublksrv_json_write_target_base_info(char *jbuf, int len, |
| const struct ublksrv_tgt_base_json *tgt) |
| { |
| json j; |
| std::string s; |
| |
| parse_json(j, jbuf); |
| |
| j["target"]["name"] = tgt->name; |
| j["target"]["type"] = tgt->type; |
| j["target"]["dev_size"] = tgt->dev_size; |
| |
| return dump_json_to_buf(j, jbuf, len); |
| } |
| |
| int ublksrv_json_read_target_base_info(const char *jbuf, |
| struct ublksrv_tgt_base_json *tgt) |
| { |
| json j; |
| std::string s; |
| |
| parse_json(j, jbuf); |
| |
| if (!j.contains("target")) |
| return -EINVAL; |
| |
| auto tj = j["target"]; |
| |
| if (!tj.contains("name") || !tj.contains("type") || |
| !tj.contains("dev_size")) |
| return -EINVAL; |
| |
| std::string str = tj["name"]; |
| if (str.length() >= UBLKSRV_TGT_NAME_MAX_LEN) |
| return -EINVAL; |
| strcpy(tgt->name, str.c_str()); |
| tgt->type = tj["type"]; |
| tgt->dev_size = tj["dev_size"]; |
| |
| return 0; |
| } |
| |
| int ublksrv_json_read_target_info(const char *jbuf, char *tgt_buf, int len) |
| { |
| json j; |
| |
| parse_json(j, jbuf); |
| |
| if (j.contains("target")) { |
| auto tj = j["target"]; |
| |
| return dump_json_to_buf(tj, tgt_buf, len); |
| } |
| return 0; |
| } |
| |
| int ublksrv_json_write_queue_info(const struct ublksrv_ctrl_dev *cdev, |
| char *jbuf, int len, int qid, int ubq_daemon_tid) |
| { |
| json j; |
| std::string s; |
| char name[16]; |
| char cpus[4096]; |
| cpu_set_t *cpuset = ublksrv_get_queue_affinity(cdev, qid); |
| |
| parse_json(j, jbuf); |
| |
| snprintf(name, 16, "%d", qid); |
| |
| ublksrv_build_cpu_str(cpus, 512, cpuset); |
| |
| j["queues"][std::string(name)]["qid"] = qid; |
| j["queues"][std::string(name)]["tid"] = ubq_daemon_tid; |
| j["queues"][std::string(name)]["affinity"] = cpus; |
| |
| return dump_json_to_buf(j, jbuf, len); |
| } |
| |
| int ublksrv_json_read_queue_info(const char *jbuf, int qid, unsigned *tid, |
| char *affinity_buf, int len) |
| { |
| json j; |
| char name[16]; |
| std::string str; |
| |
| parse_json(j, jbuf); |
| |
| snprintf(name, 16, "%d", qid); |
| |
| auto qj = j["queues"][name]; |
| |
| *tid = qj["tid"]; |
| str = qj["affinity"]; |
| |
| if (str.length() < (unsigned)len) { |
| strcpy(affinity_buf, str.c_str()); |
| return 0; |
| } |
| return -EINVAL; |
| } |
| |
| void ublksrv_json_dump(const char *jbuf) |
| { |
| auto j = json::parse(jbuf); |
| |
| std::cout << std::setw(4) << j << '\n'; |
| } |
| |
| /* the end null character is always counted */ |
| int ublksrv_json_get_length(const char *jbuf) |
| { |
| auto j = json::parse(jbuf); |
| |
| return j.dump().length() + 1; |
| } |