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
105
106
| | /*
* Copyright (C) 2012-2013, Eric Wong <normalperson@yhbt.net>
* License: GPLv3 or later (see COPYING for details)
*/
#include "cmogstored.h"
#include "compat_accept.h"
#define ENOSYS_msg \
MOG_ACCEPT_FN" missing, rebuild on the same platform this runs on"
/* don't spam syslog on accept flood */
static void do_expire(struct mog_accept *ac)
{
int err = errno;
time_t now;
static time_t last_expire;
static pthread_mutex_t err_lock = PTHREAD_MUTEX_INITIALIZER;
CHECK(int, 0, pthread_mutex_lock(&err_lock));
now = time(NULL);
if (last_expire == now)
err = 0;
else
last_expire = now;
CHECK(int, 0, pthread_mutex_unlock(&err_lock));
if (err) {
errno = err;
syslog(LOG_ERR, MOG_ACCEPT_FN" failed with: %m");
}
mog_fdmap_expire(ac->svc->idle_timeout);
}
MOG_NOINLINE static void accept_error_check(struct mog_accept *ac)
{
int fd;
switch (errno) {
case ECONNABORTED:
/* common error, nothing we can do about it */
case EINTR:
/* we'll hit mog_thr_test_quit when we restart the loop */
return;
case EBADF:
assert(0 && "BUG, called accept on bad FD");
case ENOTSOCK:
case EOPNOTSUPP:
return pthread_exit(NULL);
case_EAGAIN:
/*
* listen socket could've been inherited from another process,
* we'll support that in the near future (like nginx/unicorn)
*/
fd = mog_fd_of(ac)->fd;
if (mog_set_nonblocking(fd, false) != 0) {
assert(errno != EBADF && "unexpected EBADF");
syslog(LOG_ERR,
"failed to make fd=%d blocking: %m", fd);
}
syslog(LOG_DEBUG, "made fd=%d blocking", fd);
return;
case EMFILE:
case ENFILE:
case ENOBUFS:
case ENOMEM:
do_expire(ac);
return;
case ENOSYS:
syslog(LOG_CRIT, ENOSYS_msg);
die(ENOSYS_msg);
default:
syslog(LOG_ERR, MOG_ACCEPT_FN" failed with: %m");
}
}
/*
* passed as the start_routine argument to pthread_create.
* This function may run concurrently in multiple threads.
* The design of cmogstored assumes "wake-one" behavior for blocking
* accept()/accept4() callers. We will force accept_fd into blocking
* state if O_NONBLOCK is ever set (e.g. listen socket was inherited).
*/
void *mog_accept_loop(void *arg)
{
struct mog_accept *ac = arg;
int accept_fd = mog_fd_of(ac)->fd;
union mog_sockaddr msa;
for (;;) {
socklen_t salen = (socklen_t)sizeof(msa);
int client_fd;
mog_thr_test_quit();
client_fd = mog_accept_fn(accept_fd, &msa.sa, &salen);
if (client_fd >= 0)
ac->post_accept_fn(client_fd, ac, &msa, salen);
else
accept_error_check(ac);
}
return NULL;
}
|