X-Git-Url: https://git.whamcloud.com/?p=fs%2Flustre-release.git;a=blobdiff_plain;f=lnet%2Futils%2Flnetconfig%2Fliblnetconfig_netlink.c;h=98e6ac4560fab9c7d55f915643b55cba499bebb1;hp=31864dbe7540b585cdbd29413b161b3843c476e8;hb=82835a1952dcb37e8aac8c9d0666016557ea58e7;hpb=3c39dac19aaf7f3f4fdee104ce6da92dd1962776 diff --git a/lnet/utils/lnetconfig/liblnetconfig_netlink.c b/lnet/utils/lnetconfig/liblnetconfig_netlink.c index 31864db..98e6ac4 100644 --- a/lnet/utils/lnetconfig/liblnetconfig_netlink.c +++ b/lnet/utils/lnetconfig/liblnetconfig_netlink.c @@ -89,6 +89,10 @@ int64_t nla_get_s64(const struct nlattr *nla) return tmp; } + +#define NLA_PUT_S64(msg, attrtype, value) \ + NLA_PUT_TYPE(msg, int64_t, attrtype, value) + #endif /* ! HAVE_NLA_GET_S64 */ /** @@ -307,6 +311,7 @@ struct yaml_netlink_input { const char *errmsg; struct nl_sock *nl; bool complete; + bool async; unsigned int indent; struct yaml_nl_node *cur; struct yaml_nl_node *root; @@ -347,22 +352,28 @@ yaml_parser_set_reader_error(yaml_parser_t *parser, const char *problem, * value will be. * * LN_SCALAR_ATTR_VALUE: - * The key's actually scalar value. + * The string represnting key's actually scalar value. + * + * LN_SCALAR_ATTR_INT_VALUE: + * For this case the key is an integer value. This shouldn't be + * sent for the receive case since we are going to just turn it + * into a string for YAML. Sending packets will make use of this. * * LN_SCALAR_ATTR_KEY_TYPE: * What YAML format is it? block or flow. Only useful for * LN_SCALAR_ATTR_NLA_TYPE of type NLA_NESTED or NLA_NUL_STRING * - * LN_SCALAR_ATTR_LIST + CFS_SCALAR_LIST_SIZE: + * LN_SCALAR_ATTR_LIST + LN_SCALAR_LIST_SIZE: * Defined the next collection which is a collection of nested * attributes of the above. */ -static struct nla_policy scalar_attr_policy[LN_SCALAR_CNT + 1] = { +static struct nla_policy scalar_attr_policy[LN_SCALAR_MAX + 1] = { [LN_SCALAR_ATTR_LIST] = { .type = NLA_NESTED }, [LN_SCALAR_ATTR_LIST_SIZE] = { .type = NLA_U16 }, [LN_SCALAR_ATTR_INDEX] = { .type = NLA_U16 }, [LN_SCALAR_ATTR_NLA_TYPE] = { .type = NLA_U16 }, [LN_SCALAR_ATTR_VALUE] = { .type = NLA_STRING }, + [LN_SCALAR_ATTR_INT_VALUE] = { .type = NLA_S64 }, [LN_SCALAR_ATTR_KEY_FORMAT] = { .type = NLA_U16 }, }; @@ -370,7 +381,7 @@ static int yaml_parse_key_list(struct yaml_netlink_input *data, struct yaml_nl_node *parent, struct nlattr *list) { - struct nlattr *tbl_info[LN_SCALAR_CNT + 1]; + struct nlattr *tbl_info[LN_SCALAR_MAX + 1]; struct yaml_nl_node *node = NULL; struct nlattr *attr; int rem; @@ -378,7 +389,7 @@ static int yaml_parse_key_list(struct yaml_netlink_input *data, nla_for_each_nested(attr, list, rem) { uint16_t index = 0; - if (nla_parse_nested(tbl_info, LN_SCALAR_CNT, attr, + if (nla_parse_nested(tbl_info, LN_SCALAR_MAX, attr, scalar_attr_policy)) break; @@ -434,12 +445,12 @@ static int yaml_parse_key_list(struct yaml_netlink_input *data, name = nla_strdup(tbl_info[LN_SCALAR_ATTR_VALUE]); if (!name) return NL_STOP; - node->keys.lkl_list[index].lkp_values = name; + node->keys.lkl_list[index].lkp_value = name; } if (tbl_info[LN_SCALAR_ATTR_LIST]) { - int rc = yaml_parse_key_list(data, node, - tbl_info[LN_SCALAR_ATTR_LIST]); + int rc = yaml_parse_key_list(data, node, + tbl_info[LN_SCALAR_ATTR_LIST]); if (rc != NL_OK) return rc; } @@ -514,7 +525,7 @@ static void yaml_parse_value_list(struct yaml_netlink_input *data, int *size, struct nlattr *attr; attr = attr_array[i]; - if (!attr && !keys[i].lkp_values) + if (!attr && !keys[i].lkp_value) continue; if (keys[i].lkp_data_type != NLA_NUL_STRING && @@ -538,7 +549,7 @@ static void yaml_parse_value_list(struct yaml_netlink_input *data, int *size, if (mapping & LNKF_MAPPING) { len = snprintf(data->buffer, *size, "%s: ", - keys[i].lkp_values); + keys[i].lkp_value); if (len < 0) goto unwind; data->buffer += len; @@ -583,7 +594,7 @@ static void yaml_parse_value_list(struct yaml_netlink_input *data, int *size, len = snprintf(data->buffer, *size, "%*s%s: %c ", data->indent, "", - keys[i].lkp_values, + keys[i].lkp_value, brace); } else { if (keys[i].lkp_key_format & @@ -596,7 +607,7 @@ static void yaml_parse_value_list(struct yaml_netlink_input *data, int *size, len = snprintf(data->buffer, *size, "%*s%s:\n", data->indent, "", - keys[i].lkp_values); + keys[i].lkp_value); } if (len < 0) goto unwind; @@ -634,10 +645,10 @@ static void yaml_parse_value_list(struct yaml_netlink_input *data, int *size, /* The top level is special so only print * once */ - if (strlen(keys[i].lkp_values)) { + if (strlen(keys[i].lkp_value)) { len = snprintf(data->buffer, *size, "%s:\n", - keys[i].lkp_values); + keys[i].lkp_value); if (len < 0) goto unwind; data->buffer += len; @@ -652,9 +663,9 @@ static void yaml_parse_value_list(struct yaml_netlink_input *data, int *size, data->indent += 2; } not_first: - if (attr && parent->lkp_values) { - free(parent->lkp_values); - parent->lkp_values = nla_strdup(attr); + if (attr && parent->lkp_value) { + free(parent->lkp_value); + parent->lkp_value = nla_strdup(attr); } } break; @@ -727,9 +738,9 @@ static int yaml_netlink_msg_parse(struct nl_msg *msg, void *arg) struct nlmsghdr *nlh = nlmsg_hdr(msg); if (nlh->nlmsg_flags & NLM_F_CREATE) { - struct nlattr *attrs[LN_SCALAR_CNT + 1]; + struct nlattr *attrs[LN_SCALAR_MAX + 1]; - if (genlmsg_parse(nlh, 0, attrs, LN_SCALAR_CNT + 1, + if (genlmsg_parse(nlh, 0, attrs, LN_SCALAR_MAX + 1, scalar_attr_policy)) return NL_SKIP; @@ -742,6 +753,18 @@ static int yaml_netlink_msg_parse(struct nl_msg *msg, void *arg) /* reset to root node */ data->cur = data->root; } + + /* For streaming insert '---' to define start of + * YAML document. This allows use to extract + * documents out of a multiplexed stream. + */ + if (data->async) { + char *start_doc = "---\n"; + size_t len = strlen(start_doc) + 1; + + strncpy(data->buffer, start_doc, len); + data->buffer += len - 1; + } } else { uint16_t maxtype = data->cur->keys.lkl_maxattr; struct nla_policy policy[maxtype]; @@ -761,10 +784,8 @@ static int yaml_netlink_msg_parse(struct nl_msg *msg, void *arg) &data->cur->keys.lkl_list[1]); } - if (nlh->nlmsg_flags & NLM_F_MULTI && nlh->nlmsg_type != NLMSG_DONE) - return NL_OK; - - return NL_STOP; + /* Let yaml_netlink_msg_complete end collecting data */ + return NL_OK; } static bool cleanup_children(struct yaml_nl_node *parent) @@ -776,8 +797,8 @@ static bool cleanup_children(struct yaml_nl_node *parent) int i; for (i = 1; i < parent->keys.lkl_maxattr; i++) - if (keys[i].lkp_values) - free(keys[i].lkp_values); + if (keys[i].lkp_value) + free(keys[i].lkp_value); nl_list_del(&parent->list); return true; } @@ -810,28 +831,42 @@ static int yaml_netlink_msg_complete(struct nl_msg *msg, void *arg) /* 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) { - struct nla_policy extack_policy[NLMSGERR_ATTR_MAX + 1] = { - [NLMSGERR_ATTR_MSG] = { .type = NLA_STRING }, - [NLMSGERR_ATTR_OFFS] = { .type = NLA_U32 }, - }; + 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 (nlmsg_parse(nlh, 0, tb, sizeof(extack_policy), - extack_policy) == 0) { + if (nla_parse(tb, NLMSGERR_ATTR_MAX + 1, head, + nlmsg_attrlen(nlh, 0), NULL) == 0) { if (tb[NLMSGERR_ATTR_MSG]) - data->errmsg = nla_get_string(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; + + return NL_STOP; } else { - cleanup_children(data->root); - free(data->root); + 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; + + strncpy(data->buffer, end_doc, len); + data->buffer += len - 1; + } else + data->complete = true; } - data->complete = true; - return NL_STOP; + return data->async ? NL_OK : NL_STOP; } /** @@ -919,6 +954,7 @@ yaml_parser_set_input_netlink(yaml_parser_t *reply, struct nl_sock *nl, } buf->nl = nl; + buf->async = stream; buf->parser = reply; yaml_parser_set_input(buf->parser, yaml_netlink_read_handler, buf); @@ -987,11 +1023,16 @@ static enum lnet_nl_key_format yaml_format_type(yaml_emitter_t *emitter, unsigned int *offset, enum lnet_nl_key_format prev) { + enum lnet_nl_key_format fmt = 0; unsigned int indent = *offset; unsigned int new_indent = 0; - if (strchr(line, '{') || strchr(line, '[')) - return LNKF_FLOW; + if (strchr(line, '{') || strchr(line, '[')) { + fmt = LNKF_FLOW; + if (strchr(line, '{')) + fmt |= LNKF_MAPPING; + return fmt; + } new_indent = indent_level(line); if (new_indent < indent) { @@ -1001,7 +1042,11 @@ static enum lnet_nl_key_format yaml_format_type(yaml_emitter_t *emitter, if (strncmp(line + new_indent, "- ", 2) == 0) { *offset = new_indent + emitter->best_indent; - return LNKF_SEQUENCE; + fmt = LNKF_SEQUENCE; + + if (strstr(line + new_indent, ": ")) + fmt |= LNKF_MAPPING; + return fmt; } if (indent != new_indent) { @@ -1010,7 +1055,34 @@ static enum lnet_nl_key_format yaml_format_type(yaml_emitter_t *emitter, return LNKF_MAPPING; } - return 0; + return fmt; +} + +static int yaml_fill_scalar_data(struct nl_msg *msg, + enum lnet_nl_key_format fmt, + char *line) +{ + char *sep = strchr(line, ':'); + int rc = 0; + + if (sep) + *sep++ = '\0'; + + NLA_PUT_STRING(msg, LN_SCALAR_ATTR_VALUE, line); + if (fmt & LNKF_MAPPING && sep) { + while (isspace(*sep)) + ++sep; + + if (strspn(sep, "0123456789") == strlen(sep)) { + unsigned long num = strtoull(sep, NULL, 0); + + NLA_PUT_S64(msg, LN_SCALAR_ATTR_INT_VALUE, num); + } else { + NLA_PUT_STRING(msg, LN_SCALAR_ATTR_VALUE, sep); + } + } +nla_put_failure: + return rc; } static int yaml_create_nested_list(struct yaml_netlink_output *out, @@ -1026,35 +1098,51 @@ static int yaml_create_nested_list(struct yaml_netlink_output *out, if (!list) { yaml_emitter_set_writer_error(out->emitter, "Emmitter netlink list creation failed"); - nlmsg_free(msg); rc = -EINVAL; goto nla_put_failure; } if (fmt & LNKF_FLOW) { - while ((line = strsep(hdr, ",")) != NULL) { - char *tmp = NULL; + char *tmp = NULL; - if (strchr(line, '{') || - strchr(line, '[') || - strchr(line, ' ')) - line++; + 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; + } + } + *tmp = ' '; - tmp = strchr(line, '}'); - if (!tmp) - tmp = strchr(line, ']'); - if (tmp) - *tmp = '\0'; + 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; + } + } + *tmp = '\0'; - NLA_PUT_STRING(msg, - LN_SCALAR_ATTR_VALUE, - line); + while ((line = strsep(hdr, ",")) != NULL) { + if (isspace(line[0])) + line++; + rc = yaml_fill_scalar_data(msg, fmt, line); + if (rc < 0) + goto nla_put_failure; } nla_nest_end(msg, list); return 0; } - NLA_PUT_STRING(msg, LN_SCALAR_ATTR_VALUE, *hdr + *indent); + rc = yaml_fill_scalar_data(msg, fmt, *hdr + *indent); + if (rc < 0) + goto nla_put_failure; do { line = strsep(entry, "\n"); have_next_line: @@ -1065,7 +1153,7 @@ have_next_line: if (fmt == LNKF_END) break; - if (fmt) { + if (fmt & ~LNKF_MAPPING) { /* Filter out mappings */ rc = yaml_create_nested_list(out, msg, &line, entry, indent, fmt); if (rc) @@ -1073,8 +1161,9 @@ have_next_line: goto have_next_line; } else { - NLA_PUT_STRING(msg, LN_SCALAR_ATTR_VALUE, - line + *indent); + rc = yaml_fill_scalar_data(msg, fmt, line + *indent); + if (rc) + goto nla_put_failure; } } while (strcmp(line, "")); @@ -1097,20 +1186,20 @@ static void yaml_quotation_handling(char *buf) { char *tmp = buf, *line; + line = strstr(tmp, "! \'"); + if (line) + line[0] = ' '; + while ((line = strchr(tmp, '\"')) != NULL) { - line[0] = '%'; - line[1] = ' '; - tmp = strchr(line, '\"') - 1; + line[0] = ' '; + tmp = strchr(line, '\"'); tmp[0] = ' '; - tmp[1] = '%'; } while ((line = strchr(tmp, '\'')) != NULL) { - line[0] = '%'; - line[1] = ' '; - tmp = strchr(line, '\'') - 1; + line[0] = ' '; + tmp = strchr(line, '\''); tmp[0] = ' '; - tmp[1] = '%'; } } @@ -1188,11 +1277,11 @@ complicated: fmt = yaml_format_type(out->emitter, line, &indent, fmt); - if (fmt) { + if (fmt & ~LNKF_MAPPING) { rc = yaml_create_nested_list(out, msg, &line, &entry, &indent, fmt); - if (rc) { + if (rc < 0) { yaml_emitter_set_writer_error(out->emitter, nl_geterror(rc)); nlmsg_free(msg); @@ -1204,8 +1293,14 @@ complicated: if (line) goto already_have_line; } else { - NLA_PUT_STRING(msg, LN_SCALAR_ATTR_VALUE, - line + indent); + rc = yaml_fill_scalar_data(msg, fmt, + line + indent); + if (rc < 0) { + yaml_emitter_set_writer_error(out->emitter, + nl_geterror(rc)); + nlmsg_free(msg); + goto nla_put_failure; + } } } } @@ -1232,6 +1327,40 @@ nla_put_failure: 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() @@ -1242,6 +1371,7 @@ yaml_emitter_set_output_netlink(yaml_emitter_t *sender, struct nl_sock *nl, char *family, int version, int cmd, int flags) { struct yaml_netlink_output *out; + int rc; out = calloc(1, sizeof(*out)); if (!out) { @@ -1257,6 +1387,32 @@ yaml_emitter_set_output_netlink(yaml_emitter_t *sender, struct nl_sock *nl, free(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; @@ -1322,14 +1478,12 @@ void yaml_parser_log_error(yaml_parser_t *parser, FILE *log, const char *errmsg) extra = parser->problem; if (parser->problem_value != -1) { - fprintf(log, - "Failed to %s: reader error '%s':#%X at %ld'\n", - errmsg, extra, parser->problem_value, + fprintf(log, "Reader error: '%s':#%X at %ld'\n", + extra, parser->problem_value, (long)parser->problem_offset); } else { - fprintf(log, - "Failed to %s: reader error '%s' at %ld\n", - errmsg, extra, (long)parser->problem_offset); + fprintf(log, "Reader error: '%s' at %ld\n", + extra, (long)parser->problem_offset); } /* fallthrough */ default: