bool complete;
bool async;
unsigned int indent;
+ unsigned int version;
struct yaml_nl_node *cur;
struct yaml_nl_node *root;
};
LNKF_SEQUENCE)
indent += 2;
- len = snprintf(data->buffer, *size,
- "%*s%s:\n",
- data->indent, "",
- keys[i].lkp_value);
+ if (keys[i].lkp_value) {
+ len = snprintf(data->buffer,
+ *size,
+ "%*s%s:\n",
+ data->indent, "",
+ keys[i].lkp_value);
+ } else {
+ len = 0;
+ }
}
if (len < 0)
goto unwind;
strcat((char *)data->buffer, ", ");
len += 2;
} else {
+ if ((mapping == LNKF_SEQUENCE) &&
+ !keys[i].lkp_value)
+ ((char *)data->buffer)[len++] = ':';
+
((char *)data->buffer)[len++] = '\n';
}
data->buffer += len;
struct nlmsghdr *nlh = nlmsg_hdr(msg);
if (nlh->nlmsg_flags & NLM_F_CREATE) {
+ struct genlmsghdr *ghdr = genlmsg_hdr(nlh);
struct nlattr *attrs[LN_SCALAR_MAX + 1];
if (genlmsg_parse(nlh, 0, attrs, LN_SCALAR_MAX + 1,
strncpy(data->buffer, start_doc, len);
data->buffer += len - 1;
}
+ data->version = ghdr->version;
} else {
uint16_t maxtype = data->cur->keys.lkl_maxattr;
struct nla_policy policy[maxtype];
return NL_OK;
}
+/* This is the libnl callback for when an error has happened
+ * kernel side. An error message is sent back to the user.
+ */
+static int yaml_netlink_msg_error(struct sockaddr_nl *who,
+ struct nlmsgerr *errmsg, void *arg)
+{
+ struct nlmsghdr *nlh = (void *)errmsg - NLMSG_HDRLEN;
+ struct yaml_netlink_input *data = arg;
+
+ if ((nlh->nlmsg_type == NLMSG_ERROR ||
+ nlh->nlmsg_flags & NLM_F_ACK_TLVS) && errmsg->error) {
+ /* libyaml stomps on the reader error so we need to
+ * cache the source of the error.
+ */
+ const char *errstr = nl_geterror(nl_syserr2nlerr(errmsg->error));
+
+#ifdef HAVE_USRSPC_NLMSGERR
+ /* Newer kernels support NLM_F_ACK_TLVS in nlmsg_flags
+ * which gives greater detail why we failed.
+ */
+ if ((nlh->nlmsg_flags & NLM_F_ACK_TLVS) &&
+ !(nlh->nlmsg_flags & NLM_F_CAPPED)) {
+ struct nlattr *head = ((void *)&errmsg->msg);
+ struct nlattr *tb[NLMSGERR_ATTR_MAX];
+
+ if (nla_parse(tb, NLMSGERR_ATTR_MAX, head,
+ nlmsg_attrlen(nlh, 0), NULL) == 0) {
+ if (tb[NLMSGERR_ATTR_MSG])
+ errstr = nla_strdup(tb[NLMSGERR_ATTR_MSG]);
+ }
+ }
+#endif /* HAVE_USRSPC_NLMSGERR */
+ data->errmsg = errstr;
+ data->parser->error = YAML_READER_ERROR;
+ data->complete = true;
+ }
+ return NL_STOP;
+}
+
static bool cleanup_children(struct yaml_nl_node *parent)
{
struct yaml_nl_node *child;
{
struct yaml_netlink_input *data = arg;
struct nlmsghdr *nlh = nlmsg_hdr(msg);
- struct nlmsgerr *errmsg = nlmsg_data(nlh);
- if ((nlh->nlmsg_type == NLMSG_ERROR ||
- nlh->nlmsg_flags & NLM_F_ACK_TLVS) && errmsg->error) {
- /* libyaml stomps on the reader error so we need to
- * cache the source of the error.
- */
- data->errmsg = nl_geterror(nl_syserr2nlerr(errmsg->error));
-#ifdef HAVE_USRSPC_NLMSGERR
- /* Newer kernels support NLM_F_ACK_TLVS in nlmsg_flags
- * which gives greater detail why we failed.
- */
- if (nlh->nlmsg_flags & NLM_F_ACK_TLVS &&
- !(nlh->nlmsg_flags & NLM_F_CAPPED)) {
- struct nlattr *head = ((void *)&errmsg->msg);
- struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
+ /* For the case of NLM_F_DUMP the kernel will send error msgs
+ * yet not be labled NLMSG_ERROR which results in this code
+ * path being executed.
+ */
+ yaml_netlink_msg_error(NULL, nlmsg_data(nlh), arg);
+ if (data->parser->error == YAML_READER_ERROR)
+ return NL_STOP;
- if (nla_parse(tb, NLMSGERR_ATTR_MAX + 1, head,
- nlmsg_attrlen(nlh, 0), NULL) == 0) {
- if (tb[NLMSGERR_ATTR_MSG])
- data->errmsg = nla_strdup(tb[NLMSGERR_ATTR_MSG]);
- }
- }
-#endif /* HAVE_USRSPC_NLMSGERR */
- data->parser->error = YAML_READER_ERROR;
- data->complete = true;
+ /* Free internal data. */
+ if (data->root) {
+ cleanup_children(data->root);
+ free(data->root);
+ data->root = NULL;
+ }
- return NL_STOP;
- } else {
- if (data->root) {
- cleanup_children(data->root);
- free(data->root);
- data->root = NULL;
- }
- /* For streaming insert '...' to define end of
- * YAML document
- */
- if (data->async) {
- char *end_doc = "...\n";
- size_t len = strlen(end_doc) + 1;
+ /* For streaming insert '...' to define end of
+ * YAML document
+ */
+ if (data->async) {
+ char *end_doc = "...\n";
+ size_t len = strlen(end_doc) + 1;
- strncpy(data->buffer, end_doc, len);
- data->buffer += len - 1;
- } else
- data->complete = true;
+ strncpy(data->buffer, end_doc, len);
+ data->buffer += len - 1;
+ } else {
+ data->complete = true;
}
return data->async ? NL_OK : NL_STOP;
if (rc == -NLE_INTR) {
*size_read = 0;
return 1;
- } else if (rc < 0) {
+ } else if (!data->errmsg && rc < 0) {
data->errmsg = nl_geterror(rc);
return 0;
} else if (data->parser->error) {
return buf->errmsg;
}
+YAML_DECLARE(int)
+yaml_parser_get_reader_proto_version(yaml_parser_t *parser)
+{
+ struct yaml_netlink_input *buf = parser->read_handler_data;
+
+ if (!buf)
+ return 0;
+
+ return buf->version;
+}
+
/* yaml_parser_set_input_netlink() mirrors the libyaml function
* yaml_parser_set_input_file(). Internally it does setup of the
* libnl socket callbacks to parse the Netlink messages received
yaml_parser_set_reader_error(reply,
"netlink msg cleanup setup failed",
0, -rc);
+ goto failed;
+ }
+
+ rc = nl_socket_modify_err_cb(nl, NL_CB_CUSTOM, yaml_netlink_msg_error,
+ buf);
+ if (rc < 0) {
+ yaml_parser_set_reader_error(reply,
+ "failed to register error handling",
+ 0, -rc);
failed:
free(buf);
}
+
return rc < 0 ? false : true;
}
static enum lnet_nl_key_format yaml_format_type(yaml_emitter_t *emitter,
char *line,
- unsigned int *offset,
- enum lnet_nl_key_format prev)
+ unsigned int *offset)
{
+ unsigned int indent = *offset, new_indent = 0;
enum lnet_nl_key_format fmt = 0;
- unsigned int indent = *offset;
- unsigned int new_indent = 0;
-
- if (strchr(line, '{') || strchr(line, '[')) {
- fmt = LNKF_FLOW;
- if (strchr(line, '{'))
- fmt |= LNKF_MAPPING;
- return fmt;
- }
+ char *tmp;
new_indent = indent_level(line);
if (new_indent < indent) {
}
if (strncmp(line + new_indent, "- ", 2) == 0) {
- *offset = new_indent + emitter->best_indent;
- fmt = LNKF_SEQUENCE;
+ memset(line + new_indent, ' ', 2);
+ new_indent += 2;
+ }
- if (strstr(line + new_indent, ": "))
- fmt |= LNKF_MAPPING;
- return fmt;
+ /* hdr: [ a : 1, b : 2, c : 3 ] */
+ tmp = strstr(line + new_indent, ": ");
+ if (!tmp)
+ tmp = line + new_indent;
+ else
+ fmt |= LNKF_MAPPING;
+
+ if (strchr(tmp, '{') || strchr(tmp, '[')) {
+ fmt |= LNKF_FLOW;
+ } else if (strchr(tmp, '}') || strchr(tmp, ']')) {
+ fmt &= ~LNKF_MAPPING;
+ fmt |= LNKF_FLOW;
}
if (indent != new_indent) {
*offset = new_indent;
- if (prev != LNKF_MAPPING)
- return LNKF_MAPPING;
+ fmt |= LNKF_SEQUENCE;
}
return fmt;
{
char *sep = strchr(line, ':');
int rc = 0;
+ long num;
if (sep)
*sep++ = '\0';
- NLA_PUT_STRING(msg, LN_SCALAR_ATTR_VALUE, line);
- if (fmt & LNKF_MAPPING && sep) {
+ if (strspn(line, "-0123456789") == strlen(line)) {
+ num = strtoll(line, NULL, 0);
+
+ NLA_PUT_S64(msg, LN_SCALAR_ATTR_INT_VALUE, num);
+ } else {
+ NLA_PUT_STRING(msg, LN_SCALAR_ATTR_VALUE, line);
+ }
+
+ if (fmt & LNKF_FLOW) {
+ memset(line, ' ', strlen(line) + 1);
+ goto nla_put_failure;
+ }
+
+ if (fmt & LNKF_MAPPING && sep && strlen(sep)) {
while (isspace(*sep))
++sep;
- if (strspn(sep, "0123456789") == strlen(sep)) {
- unsigned long num = strtoull(sep, NULL, 0);
-
+ if (strspn(sep, "-0123456789") == strlen(sep)) {
+ num = strtoll(sep, NULL, 0);
NLA_PUT_S64(msg, LN_SCALAR_ATTR_INT_VALUE, num);
} else {
NLA_PUT_STRING(msg, LN_SCALAR_ATTR_VALUE, sep);
char **entry, unsigned int *indent,
enum lnet_nl_key_format fmt)
{
+ bool nested = fmt & LNKF_SEQUENCE;
struct nlattr *list = NULL;
char *line;
int rc = 0;
- list = nla_nest_start(msg, LN_SCALAR_ATTR_LIST);
- if (!list) {
- yaml_emitter_set_writer_error(out->emitter,
- "Emmitter netlink list creation failed");
- rc = -EINVAL;
- goto nla_put_failure;
+ /* Not needed for FLOW only case */
+ if (nested) {
+ list = nla_nest_start(msg, LN_SCALAR_ATTR_LIST);
+ if (!list) {
+ yaml_emitter_set_writer_error(out->emitter,
+ "Emmitter netlink list creation failed");
+ rc = -EINVAL;
+ goto nla_put_failure;
+ }
+ }
+
+ if (fmt != LNKF_FLOW) {
+ rc = yaml_fill_scalar_data(msg, fmt, *hdr + *indent);
+ if (rc < 0)
+ goto nla_put_failure;
}
if (fmt & LNKF_FLOW) {
- char *tmp = NULL;
+ char *tmp = strchr(*hdr, '{');
+ bool format = false;
- tmp = strchr(*hdr, '{');
if (!tmp) {
tmp = strchr(*hdr, '[');
if (!tmp) {
rc = -EINVAL;
goto nla_put_failure;
}
- }
+ fmt |= LNKF_SEQUENCE;
+ } else
+ fmt |= LNKF_MAPPING;
*tmp = ' ';
- tmp = strchr(*hdr, '}');
- if (!tmp) {
- tmp = strchr(*hdr, ']');
- if (!tmp) {
- yaml_emitter_set_writer_error(out->emitter,
- "Emmitter flow format invalid");
- rc = -EINVAL;
- goto nla_put_failure;
- }
+ list = nla_nest_start(msg, LN_SCALAR_ATTR_LIST);
+ if (!list) {
+ yaml_emitter_set_writer_error(out->emitter,
+ "Emmitter netlink list creation failed");
+ rc = -EINVAL;
+ goto nla_put_failure;
}
- *tmp = '\0';
while ((line = strsep(hdr, ",")) != NULL) {
- if (isspace(line[0]))
+ while (!isalnum(line[0]))
line++;
+
+ /* Flow can be splt across lines by libyaml library.
+ * This is executed only once.
+ */
+ tmp = strchr(line, '}');
+ if (!tmp)
+ tmp = strchr(line, ']');
+ if (tmp) {
+ format = true;
+ *tmp = '\0';
+ }
+
rc = yaml_fill_scalar_data(msg, fmt, line);
if (rc < 0)
goto nla_put_failure;
+
+ /* Move to next YAML line */
+ tmp = strstr(*entry, line);
+ if (tmp && strcmp(tmp, line) == 0)
+ strsep(entry, "\n");
}
- nla_nest_end(msg, list);
- return 0;
- }
- rc = yaml_fill_scalar_data(msg, fmt, *hdr + *indent);
- if (rc < 0)
- goto nla_put_failure;
- do {
- line = strsep(entry, "\n");
+ if (!format) {
+ yaml_emitter_set_writer_error(out->emitter,
+ "Emmitter flow format invalid");
+ rc = -EINVAL;
+ goto nla_put_failure;
+ }
+
+ nla_nest_end(msg, list);
+ } else {
+ do {
+ line = strsep(entry, "\n");
have_next_line:
- if (!line || !strlen(line) || strcmp(line, "...") == 0)
- break;
+ if (!line || !strlen(line) || strcmp(line, "...") == 0)
+ break;
- fmt = yaml_format_type(out->emitter, line, indent, fmt);
- if (fmt == LNKF_END)
- break;
+ fmt = yaml_format_type(out->emitter, line, indent);
+ if (fmt == LNKF_END)
+ break;
- if (fmt & ~LNKF_MAPPING) { /* Filter out mappings */
- rc = yaml_create_nested_list(out, msg, &line, entry,
- indent, fmt);
- if (rc)
- goto nla_put_failure;
+ if (fmt & ~LNKF_MAPPING) { /* Filter out mappings */
+ rc = yaml_create_nested_list(out, msg, &line,
+ entry, indent,
+ fmt);
+ if (rc < 0)
+ goto nla_put_failure;
- goto have_next_line;
- } else {
- rc = yaml_fill_scalar_data(msg, fmt, line + *indent);
- if (rc)
- goto nla_put_failure;
- }
- } while (strcmp(line, ""));
+ if (line)
+ goto have_next_line;
+ } else {
+ rc = yaml_fill_scalar_data(msg, fmt,
+ line + *indent);
+ if (rc < 0)
+ goto nla_put_failure;
+ }
+ } while (strcmp(*entry, ""));
- nla_nest_end(msg, list);
- /* strsep in the above loop moves entry to a value pass the end of the
- * nested list. So to avoid losing this value we replace hdr with line
- */
- *hdr = line;
+ /* strsep in the above loop moves entry to a value pass the
+ * end of the nested list. So to avoid losing this value we
+ * replace hdr with line.
+ */
+ *hdr = line;
+ }
+
+ if (nested)
+ nla_nest_end(msg, list);
nla_put_failure:
return rc;
}
* libyaml prevents this from happing
*/
if (line[0] != ' ' && line[0] != '-') {
+ bool extra = false;
+
+ if (strchr(line, '{') || strchr(line, '['))
+ extra = true;
+
tmp = strchr(line, ':');
if (!tmp)
continue;
goto nla_put_failure;
}
nogroups = false;
-
/* Handle case first line contains more than a
* simple key
*/
- line = tmp + 2;
- if (strchr(line, '{') || strchr(line, '['))
- goto complicated;
+ if (extra) {
+ line = ++tmp;
+ indent = 1;
+ goto already_have_line;
+ }
} else {
-complicated:
if (!msg) {
void *usr_hdr;
}
}
- fmt = yaml_format_type(out->emitter, line, &indent,
- fmt);
+ fmt = yaml_format_type(out->emitter, line, &indent);
if (fmt & ~LNKF_MAPPING) {
rc = yaml_create_nested_list(out, msg, &line,
&entry, &indent,
nlmsg_free(msg);
goto nla_put_failure;
}
- /* yaml_created_nested_list set line to the next
+ /* yaml_create_nested_list set line to the next
* entry. We can just add it to the msg directly.
*/
if (line)
return out->emitter->error == YAML_NO_ERROR ? 1 : 0;
}
-/* This is the libnl callback for when an error has happened
- * kernel side. An error message is sent back to the user.
- */
-static int yaml_netlink_write_error(struct sockaddr_nl *who,
- struct nlmsgerr *errmsg, void *arg)
-{
- struct yaml_netlink_output *data = arg;
- struct nlmsghdr *nlh = &errmsg->msg;
-
- if ((nlh->nlmsg_type == NLMSG_ERROR ||
- nlh->nlmsg_flags & NLM_F_ACK_TLVS) && errmsg->error) {
- const char *errstr = nl_geterror(nl_syserr2nlerr(errmsg->error));
-
-#ifdef HAVE_USRSPC_NLMSGERR
- /* Newer kernels support NLM_F_ACK_TLVS in nlmsg_flags
- * which gives greater detail why we failed.
- */
- if (nlh->nlmsg_flags & NLM_F_ACK_TLVS &&
- !(nlh->nlmsg_flags & NLM_F_CAPPED)) {
- struct nlattr *head = ((void *)&errmsg->msg);
- struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
-
- if (nla_parse(tb, NLMSGERR_ATTR_MAX + 1, head,
- nlmsg_attrlen(nlh, 0), NULL) == 0) {
- if (tb[NLMSGERR_ATTR_MSG])
- errstr = nla_strdup(tb[NLMSGERR_ATTR_MSG]);
- }
- }
-#endif /* HAVE_USRSPC_NLMSGERR */
- yaml_emitter_set_writer_error(data->emitter, errstr);
- }
- return NL_STOP;
-}
-
/* This function is used by external utilities to use Netlink with
* libyaml so we can turn YAML documentations into Netlink message
* to send. This behavior mirrors yaml_emitter_set_output_file()
char *family, int version, int cmd, int flags)
{
struct yaml_netlink_output *out;
- int rc;
out = calloc(1, sizeof(*out));
if (!out) {
return false;
}
- rc = nl_socket_modify_err_cb(nl, NL_CB_CUSTOM,
- yaml_netlink_write_error, out);
- if (rc < 0) {
- yaml_emitter_set_writer_error(sender,
- "failed to register error handling");
- free(out);
- return false;
- }
-
- rc = nl_socket_enable_broadcast_error(nl);
- if (rc < 0) {
- yaml_emitter_set_writer_error(sender,
- "failed to enable broadcast errors");
- free(out);
- return false;
- }
-
- rc = nl_socket_set_ext_ack(nl, true);
- if (rc < 0) {
- yaml_emitter_set_writer_error(sender,
- "failed to enable ext ack");
- free(out);
- return false;
- }
-
out->emitter = sender;
out->nl = nl;
out->family = family;