diff mbox series

[RFC,iproute2] libnetlink: allow reading more than one message from extack

Message ID 0e68f6e4a4fc68fd3f5f164b8d50a95fde6a1f50.1521227930.git.mleitner@redhat.com
State RFC, archived
Delegated to: David Miller
Headers show
Series [RFC,iproute2] libnetlink: allow reading more than one message from extack | expand

Commit Message

Marcelo Ricardo Leitner March 16, 2018, 7:23 p.m. UTC
This patch introduces support for reading more than one message from
extack's and to adjust their level (warning/error) accordingly.

Yes, there is a FIXME tag in the callback call for now.

Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
---
 lib/libnetlink.c | 55 ++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 40 insertions(+), 15 deletions(-)

Comments

Stephen Hemminger March 19, 2018, 4:09 p.m. UTC | #1
On Fri, 16 Mar 2018 16:23:09 -0300
Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> wrote:

> This patch introduces support for reading more than one message from
> extack's and to adjust their level (warning/error) accordingly.
> 
> Yes, there is a FIXME tag in the callback call for now.
> 
> Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>

Make sense, can hold off until kernel supports warnings.

> ---
>  lib/libnetlink.c | 55 ++++++++++++++++++++++++++++++++++++++++---------------
>  1 file changed, 40 insertions(+), 15 deletions(-)
> 
> diff --git a/lib/libnetlink.c b/lib/libnetlink.c
> index 928de1dd16d84b7802c06d0a659f5d73e1bbcb4b..7c4c81e11e02ea857888190eb5e7a9e99d159bb3 100644
> --- a/lib/libnetlink.c
> +++ b/lib/libnetlink.c
> @@ -43,9 +43,16 @@ static const enum mnl_attr_data_type extack_policy[NLMSGERR_ATTR_MAX + 1] = {
>  	[NLMSGERR_ATTR_OFFS]	= MNL_TYPE_U32,
>  };
>  
> +#define NETLINK_MAX_EXTACK_MSGS 8

Would rather not have fixed maximums

> +struct extack_args {
> +	const struct nlattr *msg[NETLINK_MAX_EXTACK_MSGS];
> +	const struct nlattr *offs;
> +	int msg_count;
> +};

If you put msg[] last in structure, it could be variable length.

>  static int err_attr_cb(const struct nlattr *attr, void *data)
>  {
> -	const struct nlattr **tb = data;
> +	struct extack_args *tb = data;
>  	uint16_t type;
>  
>  	if (mnl_attr_type_valid(attr, NLMSGERR_ATTR_MAX) < 0) {
> @@ -60,19 +67,23 @@ static int err_attr_cb(const struct nlattr *attr, void *data)
>  		return MNL_CB_ERROR;
>  	}
>  
> -	tb[type] = attr;
> +	if (type == NLMSGERR_ATTR_OFFS)
> +		tb->offs = attr;
> +	else if (tb->msg_count < NETLINK_MAX_EXTACK_MSGS)
> +		tb->msg[tb->msg_count++] = attr;
>  	return MNL_CB_OK;
>  }
>  
>  /* dump netlink extended ack error message */
>  int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
>  {
> -	struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
> +	struct extack_args tb = {};
>  	const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
>  	const struct nlmsghdr *err_nlh = NULL;
>  	unsigned int hlen = sizeof(*err);
> -	const char *msg = NULL;
> +	const char *msg[NETLINK_MAX_EXTACK_MSGS] = {};
>  	uint32_t off = 0;
> +	int ret, i;
>  
>  	/* no TLVs, nothing to do here */
>  	if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
> @@ -82,14 +93,14 @@ int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
>  	if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
>  		hlen += mnl_nlmsg_get_payload_len(&err->msg);
>  
> -	if (mnl_attr_parse(nlh, hlen, err_attr_cb, tb) != MNL_CB_OK)
> +	if (mnl_attr_parse(nlh, hlen, err_attr_cb, &tb) != MNL_CB_OK)
>  		return 0;
>  
> -	if (tb[NLMSGERR_ATTR_MSG])
> -		msg = mnl_attr_get_str(tb[NLMSGERR_ATTR_MSG]);
> +	for (i = 0; i < NETLINK_MAX_EXTACK_MSGS && tb.msg[i]; i++)
> +		msg[i] = mnl_attr_get_str(tb.msg[i]);
>  
> -	if (tb[NLMSGERR_ATTR_OFFS]) {
> -		off = mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]);
> +	if (tb.offs) {
> +		off = mnl_attr_get_u32(tb.offs);
>  
>  		if (off > nlh->nlmsg_len) {
>  			fprintf(stderr,
> @@ -100,21 +111,35 @@ int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
>  	}
>  
>  	if (errfn)
> -		return errfn(msg, off, err_nlh);
> +		return errfn(*msg, off, err_nlh); /* FIXME */
>  
> -	if (msg && *msg != '\0') {
> +	ret = 0;
> +	for (i = 0; i < NETLINK_MAX_EXTACK_MSGS && msg[i]; i++) {
>  		bool is_err = !!err->error;
> +		const char *_msg = msg[i];
> +
> +		/* Message tagging has precedence.
> +		 * KERN_WARNING = ASCII Start Of Header ('\001') + '4'
> +		 */
> +		if (!strncmp(_msg, "\0014", 2)) {
> +			is_err = false;
> +			_msg += 2;
> +		}

If you are going to have an API that has levels, it must be the same
as existing syslog kernel log format and maybe even get some code reuse.

> +		/* But we can't have Error if it didn't fail. */
> +		if (is_err && !err->error)
> +			is_err = 0;
>  
>  		fprintf(stderr, "%s: %s",
> -			is_err ? "Error" : "Warning", msg);
> -		if (msg[strlen(msg) - 1] != '.')
> +			is_err ? "Error" : "Warning", _msg);
> +		if (_msg[strlen(_msg) - 1] != '.')
>  			fprintf(stderr, ".");
>  		fprintf(stderr, "\n");
>  
> -		return is_err ? 1 : 0;
> +		if (is_err)
> +			ret = 1;
>  	}
>  
> -	return 0;
> +	return ret;
>  }
>  #else
>  #warning "libmnl required for error support"
Marcelo Ricardo Leitner March 19, 2018, 5:14 p.m. UTC | #2
On Mon, Mar 19, 2018 at 09:09:50AM -0700, Stephen Hemminger wrote:
> On Fri, 16 Mar 2018 16:23:09 -0300
> Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> wrote:
> 
> > This patch introduces support for reading more than one message from
> > extack's and to adjust their level (warning/error) accordingly.
> > 
> > Yes, there is a FIXME tag in the callback call for now.
> > 
> > Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
> 
> Make sense, can hold off until kernel supports warnings.

Okay.

> 
> > ---
> >  lib/libnetlink.c | 55 ++++++++++++++++++++++++++++++++++++++++---------------
> >  1 file changed, 40 insertions(+), 15 deletions(-)
> > 
> > diff --git a/lib/libnetlink.c b/lib/libnetlink.c
> > index 928de1dd16d84b7802c06d0a659f5d73e1bbcb4b..7c4c81e11e02ea857888190eb5e7a9e99d159bb3 100644
> > --- a/lib/libnetlink.c
> > +++ b/lib/libnetlink.c
> > @@ -43,9 +43,16 @@ static const enum mnl_attr_data_type extack_policy[NLMSGERR_ATTR_MAX + 1] = {
> >  	[NLMSGERR_ATTR_OFFS]	= MNL_TYPE_U32,
> >  };
> >  
> > +#define NETLINK_MAX_EXTACK_MSGS 8
> 
> Would rather not have fixed maximums

Makes sense. Then it is more forward-compatible in case we increase
that on kernel side later.

> 
> > +struct extack_args {
> > +	const struct nlattr *msg[NETLINK_MAX_EXTACK_MSGS];
> > +	const struct nlattr *offs;
> > +	int msg_count;
> > +};
> 
> If you put msg[] last in structure, it could be variable length.

It will also require a two-times parsing then because we don't have an
attribute with the number of messages in there and we have to allocate
it with a certain length.

> 
> >  static int err_attr_cb(const struct nlattr *attr, void *data)
> >  {
> > -	const struct nlattr **tb = data;
> > +	struct extack_args *tb = data;
> >  	uint16_t type;
> >  
> >  	if (mnl_attr_type_valid(attr, NLMSGERR_ATTR_MAX) < 0) {
> > @@ -60,19 +67,23 @@ static int err_attr_cb(const struct nlattr *attr, void *data)
> >  		return MNL_CB_ERROR;
> >  	}
> >  
> > -	tb[type] = attr;
> > +	if (type == NLMSGERR_ATTR_OFFS)
> > +		tb->offs = attr;
> > +	else if (tb->msg_count < NETLINK_MAX_EXTACK_MSGS)
> > +		tb->msg[tb->msg_count++] = attr;
> >  	return MNL_CB_OK;
> >  }
> >  
> >  /* dump netlink extended ack error message */
> >  int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
> >  {
> > -	struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
> > +	struct extack_args tb = {};
> >  	const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
> >  	const struct nlmsghdr *err_nlh = NULL;
> >  	unsigned int hlen = sizeof(*err);
> > -	const char *msg = NULL;
> > +	const char *msg[NETLINK_MAX_EXTACK_MSGS] = {};
> >  	uint32_t off = 0;
> > +	int ret, i;
> >  
> >  	/* no TLVs, nothing to do here */
> >  	if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
> > @@ -82,14 +93,14 @@ int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
> >  	if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
> >  		hlen += mnl_nlmsg_get_payload_len(&err->msg);
> >  
> > -	if (mnl_attr_parse(nlh, hlen, err_attr_cb, tb) != MNL_CB_OK)
> > +	if (mnl_attr_parse(nlh, hlen, err_attr_cb, &tb) != MNL_CB_OK)
> >  		return 0;
> >  
> > -	if (tb[NLMSGERR_ATTR_MSG])
> > -		msg = mnl_attr_get_str(tb[NLMSGERR_ATTR_MSG]);
> > +	for (i = 0; i < NETLINK_MAX_EXTACK_MSGS && tb.msg[i]; i++)
> > +		msg[i] = mnl_attr_get_str(tb.msg[i]);
> >  
> > -	if (tb[NLMSGERR_ATTR_OFFS]) {
> > -		off = mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]);
> > +	if (tb.offs) {
> > +		off = mnl_attr_get_u32(tb.offs);
> >  
> >  		if (off > nlh->nlmsg_len) {
> >  			fprintf(stderr,
> > @@ -100,21 +111,35 @@ int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
> >  	}
> >  
> >  	if (errfn)
> > -		return errfn(msg, off, err_nlh);
> > +		return errfn(*msg, off, err_nlh); /* FIXME */
> >  
> > -	if (msg && *msg != '\0') {
> > +	ret = 0;
> > +	for (i = 0; i < NETLINK_MAX_EXTACK_MSGS && msg[i]; i++) {
> >  		bool is_err = !!err->error;
> > +		const char *_msg = msg[i];
> > +
> > +		/* Message tagging has precedence.
> > +		 * KERN_WARNING = ASCII Start Of Header ('\001') + '4'
> > +		 */
> > +		if (!strncmp(_msg, "\0014", 2)) {
> > +			is_err = false;
> > +			_msg += 2;
> > +		}
> 
> If you are going to have an API that has levels, it must be the same
> as existing syslog kernel log format and maybe even get some code reuse.

It is the same representation already. The magic "\0014" in there is
the same as KERN_WARNING. I'll check how other userspace applications
are dealing with this, as I don't see KERN_WARNING being exported to
userspace.

Thanks,
  Marcelo

> 
> > +		/* But we can't have Error if it didn't fail. */
> > +		if (is_err && !err->error)
> > +			is_err = 0;
> >  
> >  		fprintf(stderr, "%s: %s",
> > -			is_err ? "Error" : "Warning", msg);
> > -		if (msg[strlen(msg) - 1] != '.')
> > +			is_err ? "Error" : "Warning", _msg);
> > +		if (_msg[strlen(_msg) - 1] != '.')
> >  			fprintf(stderr, ".");
> >  		fprintf(stderr, "\n");
> >  
> > -		return is_err ? 1 : 0;
> > +		if (is_err)
> > +			ret = 1;
> >  	}
> >  
> > -	return 0;
> > +	return ret;
> >  }
> >  #else
> >  #warning "libmnl required for error support"
>
diff mbox series

Patch

diff --git a/lib/libnetlink.c b/lib/libnetlink.c
index 928de1dd16d84b7802c06d0a659f5d73e1bbcb4b..7c4c81e11e02ea857888190eb5e7a9e99d159bb3 100644
--- a/lib/libnetlink.c
+++ b/lib/libnetlink.c
@@ -43,9 +43,16 @@  static const enum mnl_attr_data_type extack_policy[NLMSGERR_ATTR_MAX + 1] = {
 	[NLMSGERR_ATTR_OFFS]	= MNL_TYPE_U32,
 };
 
+#define NETLINK_MAX_EXTACK_MSGS 8
+struct extack_args {
+	const struct nlattr *msg[NETLINK_MAX_EXTACK_MSGS];
+	const struct nlattr *offs;
+	int msg_count;
+};
+
 static int err_attr_cb(const struct nlattr *attr, void *data)
 {
-	const struct nlattr **tb = data;
+	struct extack_args *tb = data;
 	uint16_t type;
 
 	if (mnl_attr_type_valid(attr, NLMSGERR_ATTR_MAX) < 0) {
@@ -60,19 +67,23 @@  static int err_attr_cb(const struct nlattr *attr, void *data)
 		return MNL_CB_ERROR;
 	}
 
-	tb[type] = attr;
+	if (type == NLMSGERR_ATTR_OFFS)
+		tb->offs = attr;
+	else if (tb->msg_count < NETLINK_MAX_EXTACK_MSGS)
+		tb->msg[tb->msg_count++] = attr;
 	return MNL_CB_OK;
 }
 
 /* dump netlink extended ack error message */
 int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
 {
-	struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {};
+	struct extack_args tb = {};
 	const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);
 	const struct nlmsghdr *err_nlh = NULL;
 	unsigned int hlen = sizeof(*err);
-	const char *msg = NULL;
+	const char *msg[NETLINK_MAX_EXTACK_MSGS] = {};
 	uint32_t off = 0;
+	int ret, i;
 
 	/* no TLVs, nothing to do here */
 	if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
@@ -82,14 +93,14 @@  int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
 	if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
 		hlen += mnl_nlmsg_get_payload_len(&err->msg);
 
-	if (mnl_attr_parse(nlh, hlen, err_attr_cb, tb) != MNL_CB_OK)
+	if (mnl_attr_parse(nlh, hlen, err_attr_cb, &tb) != MNL_CB_OK)
 		return 0;
 
-	if (tb[NLMSGERR_ATTR_MSG])
-		msg = mnl_attr_get_str(tb[NLMSGERR_ATTR_MSG]);
+	for (i = 0; i < NETLINK_MAX_EXTACK_MSGS && tb.msg[i]; i++)
+		msg[i] = mnl_attr_get_str(tb.msg[i]);
 
-	if (tb[NLMSGERR_ATTR_OFFS]) {
-		off = mnl_attr_get_u32(tb[NLMSGERR_ATTR_OFFS]);
+	if (tb.offs) {
+		off = mnl_attr_get_u32(tb.offs);
 
 		if (off > nlh->nlmsg_len) {
 			fprintf(stderr,
@@ -100,21 +111,35 @@  int nl_dump_ext_ack(const struct nlmsghdr *nlh, nl_ext_ack_fn_t errfn)
 	}
 
 	if (errfn)
-		return errfn(msg, off, err_nlh);
+		return errfn(*msg, off, err_nlh); /* FIXME */
 
-	if (msg && *msg != '\0') {
+	ret = 0;
+	for (i = 0; i < NETLINK_MAX_EXTACK_MSGS && msg[i]; i++) {
 		bool is_err = !!err->error;
+		const char *_msg = msg[i];
+
+		/* Message tagging has precedence.
+		 * KERN_WARNING = ASCII Start Of Header ('\001') + '4'
+		 */
+		if (!strncmp(_msg, "\0014", 2)) {
+			is_err = false;
+			_msg += 2;
+		}
+		/* But we can't have Error if it didn't fail. */
+		if (is_err && !err->error)
+			is_err = 0;
 
 		fprintf(stderr, "%s: %s",
-			is_err ? "Error" : "Warning", msg);
-		if (msg[strlen(msg) - 1] != '.')
+			is_err ? "Error" : "Warning", _msg);
+		if (_msg[strlen(_msg) - 1] != '.')
 			fprintf(stderr, ".");
 		fprintf(stderr, "\n");
 
-		return is_err ? 1 : 0;
+		if (is_err)
+			ret = 1;
 	}
 
-	return 0;
+	return ret;
 }
 #else
 #warning "libmnl required for error support"