File: network_accept.c

package info (click to toggle)
spiped 1.6.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,328 kB
  • sloc: ansic: 11,951; sh: 1,081; makefile: 629; perl: 121
file content (104 lines) | stat: -rw-r--r-- 2,371 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#include <sys/socket.h>

#include <errno.h>
#include <stdlib.h>

#include "events.h"

#include "network.h"

struct accept_cookie {
	int (* callback)(void *, int);
	void * cookie;
	int fd;
};

/* Accept the connection and invoke the callback. */
static int
callback_accept(void * cookie)
{
	struct accept_cookie * C = cookie;
	int s;
	int rc;

	/* Attempt to accept a new connection. */
	if ((s = accept(C->fd, NULL, NULL)) == -1) {
		/* If a connection isn't available, reset the callback. */
		if ((errno == EAGAIN) ||
#if EAGAIN != EWOULDBLOCK
		    (errno == EWOULDBLOCK) ||
#endif
		    (errno == ECONNABORTED) ||
		    (errno == EINTR))
			goto tryagain;
	}

	/* Call the upstream callback. */
	rc = (C->callback)(C->cookie, s);

	/* Free the cookie. */
	free(C);

	/* Return status from upstream callback. */
	return (rc);

tryagain:
	/* Reset the callback. */
	return (events_network_register(callback_accept, C, C->fd,
	    EVENTS_NETWORK_OP_READ));
}

/**
 * network_accept(fd, callback, cookie):
 * Asynchronously accept a connection on the socket ${fd}, which must be
 * already marked as listening and non-blocking.  When a connection has been
 * accepted or an error occurs, invoke ${callback}(${cookie}, s) where s is
 * the accepted connection or -1 on error.  Return a cookie which can be
 * passed to network_accept_cancel() in order to cancel the accept.
 */
void *
network_accept(int fd, int (* callback)(void *, int), void * cookie)
{
	struct accept_cookie * C;

	/* Bake a cookie. */
	if ((C = malloc(sizeof(struct accept_cookie))) == NULL)
		goto err0;
	C->callback = callback;
	C->cookie = cookie;
	C->fd = fd;

	/*
	 * Register a network event.  A connection arriving on a listening
	 * socket is treated by select(2) as the socket becoming readable.
	 */
	if (events_network_register(callback_accept, C, C->fd,
	    EVENTS_NETWORK_OP_READ))
		goto err1;

	/* Success! */
	return (C);

err1:
	free(C);
err0:
	/* Failure! */
	return (NULL);
}

/**
 * network_accept_cancel(cookie):
 * Cancel the connection accept for which the cookie ${cookie} was returned
 * by network_accept().  Do not invoke the callback associated with the accept.
 */
void
network_accept_cancel(void * cookie)
{
	struct accept_cookie * C = cookie;

	/* Cancel the network event. */
	events_network_cancel(C->fd, EVENTS_NETWORK_OP_READ);

	/* Free the cookie. */
	free(C);
}