Linux-man Archive on lore.kernel.org
 help / color / Atom feed
* [patch] accept.2: Added information about what can cause EAGAIN and EWOULDBLOCK errors
@ 2019-11-26 22:29 Jeremy Rifkin
  0 siblings, 0 replies; only message in thread
From: Jeremy Rifkin @ 2019-11-26 22:29 UTC (permalink / raw)
  To: mtk.manpages; +Cc: linux-man, netdev

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

Hello,
According to the accept.2 man page, EAGAIN and EWOULDBLOCK errors can occur when
"The socket is marked nonblocking and no connections are present to be
accepted". I have found that these errors can also occur when a receive timeout
has been set on the socket and the timeout expires before a connection can be
accepted. This appears to be the same behavior of the recv system call whose man
page states that EAGAIN and EWOULDBLOCK occur when "The socket is marked
nonblocking and the receive operation would block, or a receive timeout had been
set and the timeout expired before data was received."

I've included a test program to demonstrate that accept will fail with exit code
EAGAIN/EWOULDBLOCK when a receive timeout is set and the timeout expires.

This patch applies to the latest version man-pages, 5.04. I have amended
accept.2 to include this second reason why EAGAIN/EWOULDBLOCK can
occur. I have tried to use similar wording to that of the recv.2 man page.

======== Begin Diff ========

diff --git a/man2/accept.2 b/man2/accept.2
index a4bebd214..63e90a5e6 100644
--- a/man2/accept.2
+++ b/man2/accept.2
@@ -208,7 +208,9 @@ and
 .BR EAGAIN " or " EWOULDBLOCK
 .\" Actually EAGAIN on Linux
 The socket is marked nonblocking and no connections are
-present to be accepted.
+present to be accepted, or a receive timeout has been
+set and the timeout expired before a new connection
+was available to be accepted.
 POSIX.1-2001 and POSIX.1-2008
 allow either error to be returned for this case,
 and do not require these constants to have the same value,

========= End Diff =========

- Jeremy Rifkin

[-- Attachment #2: accept2test.c --]
[-- Type: text/plain, Size: 3012 bytes --]

// Jeremy M. Rifkin 11.25.19
// Demonstrate that EAGAIN/EWOULDBLOCK can be triggered on accept(2) by a recv timeout
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>

void fail(char* format) {
	fprintf(stderr, format, errno);
	fflush(stderr);
	exit(1);
}

int main (int argc, char *argv[]) {
	int err;
	int socketfd = socket(AF_INET, SOCK_STREAM, 0);
	if(socketfd < 0)
		fail("\x1b[1;35mError:\x1b[0m failed to create socket (errno: %d)... This shouldn't have "
			"happened.\n");

	// Configure socket
	struct sockaddr_in server;
	server.sin_family = AF_INET;
	server.sin_port = htons(0); // port 0 for random port
	server.sin_addr.s_addr = htonl(INADDR_ANY);

	// Set reuseaddr
	int opt = 1;
	err = setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
	if(err < 0)
		fail("\x1b[1;35mError:\x1b[0m failed to set socket reuseaddr (errno: %d)... This shouldn't "
			"have happened.\n");

	// Configure timeout
	struct timeval timeout;
	timeout.tv_sec = 4; // 4 second timeout
	timeout.tv_usec = 0;
	err = setsockopt(socketfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof timeout);
	if(err < 0)
		fail("\x1b[1;35mError:\x1b[0m failed to set socket recv timeout (errno: %d)... This "
			"shouldn't have happened.\n");

	// Bind the socket
	err = bind(socketfd, (struct sockaddr*) &server, sizeof(server));
	if(err < 0)
		fail("\x1b[1;35mError:\x1b[0m failed to bind socket (errno: %d)... This shouldn't have "
			"happened.\n");

	// Mark socket as passive.
	err = listen(socketfd, 128);
	if(err < 0)
		fail("\x1b[1;35mError:\x1b[0m failed to listen on socket (errno: %d)... This shouldn't "
			"have happened.\n");

	// Get port
	// Serves as both a check and also accommodates for "random" ports.
	struct sockaddr_in addr;
	socklen_t addr_len = sizeof(addr);
	err = getsockname(socketfd, (struct sockaddr*)&addr, &addr_len);
	if(err < 0)
		fail("\x1b[1;35mError:\x1b[0m failed to get socket address (errno: %d)... This shouldn't "
			"have happened.\n");
	printf("Socket listening on port %d\n", ntohs(addr.sin_port));
	
	// Demonstrate recv timeout failure
	printf("\nA 4 second recv timeout has been set on the socket.\n");
	printf("The program will now attempt to accept connections.\n");
	printf("It should fail with error EAGAIN/EWOULDBLOCK roughly\n");
	printf("every 4 seconds.\n\n");
	while(1) {
		struct sockaddr_in client;
		socklen_t client_len = sizeof(client);
		int clientfd = accept(socketfd, (struct sockaddr*)&client, &client_len);

		if(clientfd < 0) {
			if(errno == EAGAIN)
				fprintf(stderr, "\x1b[1;35mError:\x1b[0m Socket failed to accept with error EAGAIN."
					"\n");
			else if(errno == EWOULDBLOCK)
				fprintf(stderr, "\x1b[1;35mError:\x1b[0m Socket failed to accept with error "
					"EWOULDBLOCK.\n");
			else
				fprintf(stderr, "\x1b[1;35mError:\x1b[0m failed to establish new connection "
					"(errno: %d)... This shouldn't have happened.\n",
					errno);
			fflush(stderr);
		}
	}

	return 0;
}


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, back to index

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-11-26 22:29 [patch] accept.2: Added information about what can cause EAGAIN and EWOULDBLOCK errors Jeremy Rifkin

Linux-man Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-man/0 linux-man/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-man linux-man/ https://lore.kernel.org/linux-man \
		linux-man@vger.kernel.org
	public-inbox-index linux-man

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-man


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git