All of lore.kernel.org
 help / color / mirror / Atom feed
From: Denis Kenzior <denkenz@gmail.com>
To: ell@lists.01.org
Subject: Re: [PATCH 4/4] dbus: Handle partial reads and writes
Date: Wed, 04 May 2016 11:38:50 -0500	[thread overview]
Message-ID: <572A259A.40104@gmail.com> (raw)
In-Reply-To: <1462359793-18789-4-git-send-email-andrew.zaborowski@intel.com>

[-- Attachment #1: Type: text/plain, Size: 7627 bytes --]

Hi Andrew,

On 05/04/2016 06:03 AM, Andrew Zaborowski wrote:
> Loop until we have received or sent all of the data requested.  We pass
> MSG_WAITALL to potentially reduce the number of iterations.  Make sure
> we free body and header in classic_recv_message when
> dbus_message_build() returns an error.
>
> Calling sendms and recvmsg multiple times makes it even more tricky to
> understand what will happen with the FDs passed as oob data when a
> partial read/write happens or when there are multiple messsages with FDs
> attached in the queue.  What this patch does should be equivalent to
> what systemd dbus code does.  It seems to assume that the FDs are
> attached to the last byte of the message so on a partial write, the FDs
> are not written and the full array can be passed to sendmsg() again.
> I'm not sure if this is documented anywhere but I've seen an explicit
> statement of this at ...

I think this is a bad assumption to make.  Browsing af_unix.c makes me 
think this is not the case.

> ---
>   ell/dbus.c | 179 +++++++++++++++++++++++++++++++++++++------------------------
>   1 file changed, 110 insertions(+), 69 deletions(-)
>
> diff --git a/ell/dbus.c b/ell/dbus.c
> index 35378b2..3ca7b87 100644
> --- a/ell/dbus.c
> +++ b/ell/dbus.c
> @@ -579,63 +579,126 @@ static void classic_free(struct l_dbus *dbus)
>   	l_free(classic);
>   }
>
> +static bool msg_op(int fd, bool send, struct iovec iov[],
> +			int **fds, uint32_t *num_fds)
> +{

Please don't do this, the code is so much easier to understand when the 
rx and tx paths are separate.

> +	int r;
> +	struct msghdr msg;
> +	struct cmsghdr *cmsg;
> +	uint8_t *fd_buf = NULL;
> +	int buf_fds;
> +	int iovlen = 2;
> +
> +	if (send)
> +		buf_fds = *num_fds;
> +	else {
> +		buf_fds = 16;
> +		*num_fds = 0;
> +	}
> +
> +	if (buf_fds) {
> +		fd_buf = alloca(CMSG_SPACE(buf_fds * sizeof(int)));
> +
> +		if (send) {
> +			msg.msg_control = fd_buf;
> +			msg.msg_controllen = CMSG_LEN(buf_fds * sizeof(int));
> +
> +			/* Set up fd_buf contents */
> +			cmsg = CMSG_FIRSTHDR(&msg);
> +			cmsg->cmsg_len = msg.msg_controllen;
> +			cmsg->cmsg_level = SOL_SOCKET;
> +			cmsg->cmsg_type = SCM_RIGHTS;
> +			memcpy(CMSG_DATA(cmsg), *fds, buf_fds * sizeof(int));
> +		}
> +	}
> +
> +	while (iovlen) {
> +		memset(&msg, 0, sizeof(msg));
> +		msg.msg_iov = iov;
> +		msg.msg_iovlen = iovlen;
> +		msg.msg_control = fd_buf;
> +		msg.msg_controllen = fd_buf ?
> +			CMSG_LEN(buf_fds * sizeof(int)) : 0;
> +
> +		if (send)
> +			r = sendmsg(fd, &msg, 0);
> +		else
> +			r = recvmsg(fd, &msg, MSG_CMSG_CLOEXEC | MSG_WAITALL);
> +
> +		if (r <= 0) {

Strictly speaking, you can't call sendmsg in a loop.  The socket is 
marked non-blocking, so EWOULDBLOCK or EAGAIN is a valid error return. 
This means that you need to exit the loop and re-enter once POLLOUT is 
received.

> +			if (r != -1 || errno != EINTR)
> +				return false;
> +
> +			continue;
> +		}
> +
> +		while (iovlen && r >= (int) iov[0].iov_len) {
> +			r -= iov[0].iov_len;
> +			iov++;
> +			iovlen--;
> +		}
> +
> +		if (iovlen) {
> +			iov[0].iov_base += r;
> +			iov[0].iov_len -= r;
> +		}
> +
> +		if (send)
> +			continue;
> +
> +		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
> +				cmsg = CMSG_NXTHDR(&msg, cmsg)) {
> +			int cnt;
> +
> +			if (cmsg->cmsg_level != SOL_SOCKET ||
> +						cmsg->cmsg_type != SCM_RIGHTS)
> +				continue;
> +
> +			cnt = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
> +
> +			*fds = l_realloc(*fds, (*num_fds + cnt) * sizeof(int));
> +
> +			memcpy(*fds + *num_fds, CMSG_DATA(cmsg),
> +					cnt * sizeof(int));
> +
> +			*num_fds += cnt;
> +                }
> +	}
> +
> +	return true;
> +}
> +
>   static bool classic_send_message(struct l_dbus *dbus,
>   					struct l_dbus_message *message)
>   {
>   	int fd = l_io_get_fd(dbus->io);
> -	struct msghdr msg;
>   	struct iovec iov[2];
> -	ssize_t len;
>   	int *fds;
>   	uint32_t num_fds = 0;
> -	struct cmsghdr *cmsg;
>
>   	iov[0].iov_base = _dbus_message_get_header(message, &iov[0].iov_len);
>   	iov[1].iov_base = _dbus_message_get_body(message, &iov[1].iov_len);
>
> -	memset(&msg, 0, sizeof(msg));
> -	msg.msg_iov = iov;
> -	msg.msg_iovlen = 2;
> -
>   	if (dbus->support_unix_fd)
>   		fds = _dbus_message_get_fds(message, &num_fds);
>
> -	if (num_fds) {
> -		msg.msg_control = alloca(CMSG_SPACE(num_fds * sizeof(int)));
> -		msg.msg_controllen = CMSG_LEN(num_fds * sizeof(int));
> -
> -		cmsg = CMSG_FIRSTHDR(&msg);
> -		cmsg->cmsg_len = msg.msg_controllen;
> -		cmsg->cmsg_level = SOL_SOCKET;
> -		cmsg->cmsg_type = SCM_RIGHTS;
> -		memcpy(CMSG_DATA(cmsg), fds, num_fds * sizeof(int));
> -	}
> -
> -	len = sendmsg(fd, &msg, 0);
> -	if (len < 0)
> -		return false;
> -
> -	return true;
> +	return msg_op(fd, true, iov, &fds, &num_fds);
>   }
>
>   static struct l_dbus_message *classic_recv_message(struct l_dbus *dbus)
>   {
>   	int fd = l_io_get_fd(dbus->io);
>   	struct dbus_header hdr;
> -	struct msghdr msg;
>   	struct iovec iov[2];
> -	struct cmsghdr *cmsg;
>   	ssize_t len;
>   	void *header, *body;
>   	size_t header_size, body_size;
> -	union {
> -		uint8_t bytes[CMSG_SPACE(16 * sizeof(int))];
> -		struct cmsghdr align;
> -	} fd_buf;
>   	int *fds = NULL;
>   	uint32_t num_fds = 0;
> +	struct l_dbus_message *msg;
> +	unsigned int i;
>
> -	len = recv(fd, &hdr, DBUS_HEADER_SIZE, MSG_PEEK);
> +	len = recv(fd, &hdr, DBUS_HEADER_SIZE, MSG_PEEK | MSG_WAITALL);
>   	if (len != DBUS_HEADER_SIZE)
>   		return NULL;
>
> @@ -650,59 +713,37 @@ static struct l_dbus_message *classic_recv_message(struct l_dbus *dbus)
>   	iov[1].iov_base = body;
>   	iov[1].iov_len  = body_size;
>
> -	memset(&msg, 0, sizeof(msg));
> -	msg.msg_iov = iov;
> -	msg.msg_iovlen = 2;
> -	msg.msg_control = &fd_buf;
> -	msg.msg_controllen = sizeof(fd_buf);
> -
> -	len = recvmsg(fd, &msg, MSG_CMSG_CLOEXEC);
> -	if (len < 0)
> -		goto cmsg_fail;
> -
> -	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
> -				cmsg = CMSG_NXTHDR(&msg, cmsg)) {
> -		unsigned int i;
> -
> -		if (cmsg->cmsg_level != SOL_SOCKET ||
> -					cmsg->cmsg_type != SCM_RIGHTS)
> -			continue;
> -
> -		num_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
> -		fds = (void *) CMSG_DATA(cmsg);
> -
> -		/* Set FD_CLOEXEC on all file descriptors */
> -		for (i = 0; i < num_fds; i++) {
> -			long flags;
> -
> -			flags = fcntl(fds[i], F_GETFD, NULL);
> -			if (flags < 0)
> -				continue;
> -
> -			if (!(flags & FD_CLOEXEC))
> -				fcntl(fds[i], F_SETFD, flags | FD_CLOEXEC);
> -                }
> -	}
> +	if (!msg_op(fd, false, iov, &fds, &num_fds))
> +		goto fail;
>
>   	if (hdr.endian != DBUS_NATIVE_ENDIAN) {
>   		l_util_debug(dbus->debug_handler,
>   				dbus->debug_data, "Endianness incorrect");
> -		goto bad_msg;
> +		goto fail;
>   	}
>
>   	if (hdr.version != 1) {
>   		l_util_debug(dbus->debug_handler,
>   				dbus->debug_data, "Protocol version incorrect");
> -		goto bad_msg;
> +		goto fail;
>   	}
>
> -	return dbus_message_build(header, header_size, body, body_size,
> +	msg = dbus_message_build(header, header_size, body, body_size,
>   					fds, num_fds);
> +	if (!msg)
> +		goto fail;
>
> -bad_msg:
> -cmsg_fail:
> -	l_free(header);
> +	l_free(fds);
> +
> +	return msg;
> +
> +fail:
> +	for (i = 0; i < num_fds; i++)
> +		close(fds[i]);
> +
> +	l_free(fds);
>   	l_free(body);
> +	l_free(header);
>
>   	return NULL;
>   }
>

Regards,
-Denis

  parent reply	other threads:[~2016-05-04 16:38 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-05-04 11:03 [PATCH 1/4] dbus: Close unused file descriptors when creating message Andrew Zaborowski
2016-05-04 11:03 ` [PATCH 2/4] unit: End to end FD passing test Andrew Zaborowski
2016-05-04 16:09   ` Denis Kenzior
2016-05-04 11:03 ` [PATCH 3/4] unit: Add UNIX_FDS header fields in FD test messages Andrew Zaborowski
2016-05-04 16:10   ` Denis Kenzior
2016-05-04 11:03 ` [PATCH 4/4] dbus: Handle partial reads and writes Andrew Zaborowski
2016-05-04 11:10   ` Andrzej Zaborowski
2016-05-04 16:38   ` Denis Kenzior [this message]
2016-05-04 16:05 ` [PATCH 1/4] dbus: Close unused file descriptors when creating message Denis Kenzior

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=572A259A.40104@gmail.com \
    --to=denkenz@gmail.com \
    --cc=ell@lists.01.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.