diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2020-04-17 13:05:42 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2020-04-17 13:50:49 +0300 |
commit | 7a1985a0f05564fd7c381e828ff134836f3e3253 (patch) | |
tree | 737659273f526bcf9e31cf5efb257b718c6abfae | |
parent | 1047ca368eaf1c0f1cda21c9b0ab3c212a3d9379 (diff) | |
download | wydawca-7a1985a0f05564fd7c381e828ff134836f3e3253.tar.gz wydawca-7a1985a0f05564fd7c381e828ff134836f3e3253.tar.bz2 |
Limit number of simultaneous tcpmux connections. Impose idle connection timeouts.
* src/config.c: New statements: max-connections and idle-timeout
* src/net.c (max_connections,idle_timeout): New globals.
(handle_connection): Take single FILE* as argument.
(connection_start,connection_stop): New functions.
(wy_thr_tcpmux,wy_thr_connection_watcher): New functions.
(wy_thr_listen): Start wy_thr_connection_watcher thread if tcpmux
listener is enabled.
Start a separate thread to process each incoming connection.
* src/wydawca.h (max_connections,idle_timeout): New externs.
(wy_thr_tcpmux,wy_thr_connection_watcher): New protos.
-rw-r--r-- | src/config.c | 8 | ||||
-rw-r--r-- | src/net.c | 195 | ||||
-rw-r--r-- | src/triplet.c | 1 | ||||
-rw-r--r-- | src/wydawca.c | 1 | ||||
-rw-r--r-- | src/wydawca.h | 4 |
5 files changed, 185 insertions, 24 deletions
diff --git a/src/config.c b/src/config.c index 7d0e760..a7d4312 100644 --- a/src/config.c +++ b/src/config.c | |||
@@ -1489,7 +1489,13 @@ static struct grecs_keyword wydawca_kw[] = { | |||
1489 | { "listen", N_("socket"), | 1489 | { "listen", N_("socket"), |
1490 | N_("Listen on this address"), | 1490 | N_("Listen on this address"), |
1491 | grecs_type_sockaddr, GRECS_DFLT, &listen_sockaddr, }, | 1491 | grecs_type_sockaddr, GRECS_DFLT, &listen_sockaddr, }, |
1492 | 1492 | { "max-connections", NULL, | |
1493 | N_("maximum allowed number of simultaneous connections"), | ||
1494 | grecs_type_size, GRECS_DFLT, &max_connections }, | ||
1495 | { "idle-timeout", N_("interval"), | ||
1496 | N_("Idle timeout for a TXPMUX connection"), | ||
1497 | grecs_type_string, GRECS_CONST, &idle_timeout, 0, cb_interval }, | ||
1498 | |||
1493 | #ifdef WITH_LIBWRAP | 1499 | #ifdef WITH_LIBWRAP |
1494 | { "tcp-wrapper", NULL, | 1500 | { "tcp-wrapper", NULL, |
1495 | N_("Configure TCP wrappers"), | 1501 | N_("Configure TCP wrappers"), |
@@ -16,6 +16,10 @@ | |||
16 | 16 | ||
17 | #include "wydawca.h" | 17 | #include "wydawca.h" |
18 | 18 | ||
19 | struct grecs_sockaddr listen_sockaddr; | ||
20 | size_t max_connections = 16; | ||
21 | time_t idle_timeout = 5; | ||
22 | |||
19 | static int | 23 | static int |
20 | open_listener() | 24 | open_listener() |
21 | { | 25 | { |
@@ -80,39 +84,156 @@ trim_crlf(char *s) | |||
80 | s[--len] = 0; | 84 | s[--len] = 0; |
81 | } | 85 | } |
82 | } | 86 | } |
87 | |||
88 | struct wydawca_connection { | ||
89 | struct timespec ts; | ||
90 | pthread_t tid; | ||
91 | FILE *fp; | ||
92 | struct wydawca_connection *next, *prev; | ||
93 | }; | ||
83 | 94 | ||
84 | void | 95 | struct wydawca_connection_queue { |
85 | handle_connection(FILE * in, FILE * out) | 96 | struct wydawca_connection *head, *tail; |
97 | }; | ||
98 | |||
99 | static inline void | ||
100 | wydawca_connection_enqueue(struct wydawca_connection_queue *q, | ||
101 | struct wydawca_connection *conn) | ||
102 | { | ||
103 | conn->next = NULL; | ||
104 | conn->prev = q->tail; | ||
105 | if (q->tail) | ||
106 | q->tail->next = conn; | ||
107 | else | ||
108 | q->head = conn; | ||
109 | q->tail = conn; | ||
110 | } | ||
111 | |||
112 | static inline void | ||
113 | wydawca_connection_dequeue(struct wydawca_connection_queue *q, | ||
114 | struct wydawca_connection *conn) | ||
115 | { | ||
116 | if (conn->prev) | ||
117 | conn->prev->next = conn->next; | ||
118 | else | ||
119 | q->head = conn->next; | ||
120 | if (conn->next) | ||
121 | conn->next->prev = conn->prev; | ||
122 | else | ||
123 | q->tail = conn->prev; | ||
124 | conn->prev = conn->next = NULL; | ||
125 | } | ||
126 | |||
127 | static struct wydawca_connection *conn_table; | ||
128 | static struct wydawca_connection_queue conn_avail, conn_idle; | ||
129 | static pthread_mutex_t conn_mutex = PTHREAD_MUTEX_INITIALIZER; | ||
130 | static pthread_cond_t conn_cond = PTHREAD_COND_INITIALIZER; | ||
131 | |||
132 | static void | ||
133 | conn_table_init(void) | ||
134 | { | ||
135 | size_t i; | ||
136 | |||
137 | conn_table = grecs_calloc(max_connections, sizeof(conn_table[0])); | ||
138 | for (i = 1; i < max_connections; i++) { | ||
139 | wydawca_connection_enqueue(&conn_avail, &conn_table[i]); | ||
140 | } | ||
141 | } | ||
142 | |||
143 | static void | ||
144 | connection_start(int fd) | ||
145 | { | ||
146 | struct wydawca_connection *conn; | ||
147 | |||
148 | pthread_mutex_lock(&conn_mutex); | ||
149 | if (conn_avail.head) { | ||
150 | conn = conn_avail.head; | ||
151 | wydawca_connection_dequeue(&conn_avail, conn); | ||
152 | clock_gettime(CLOCK_REALTIME, &conn->ts); | ||
153 | conn->ts.tv_sec += idle_timeout; | ||
154 | conn->fp = fdopen(fd, "w+"); | ||
155 | pthread_create(&conn->tid, NULL, wy_thr_tcpmux, conn); | ||
156 | wydawca_connection_enqueue(&conn_idle, conn); | ||
157 | pthread_cond_broadcast(&conn_cond); | ||
158 | } else { | ||
159 | wy_log(LOG_ERR, "connection table is full"); | ||
160 | close(fd); | ||
161 | } | ||
162 | pthread_mutex_unlock(&conn_mutex); | ||
163 | } | ||
164 | |||
165 | static void | ||
166 | connection_unidle(struct wydawca_connection *conn) | ||
167 | { | ||
168 | pthread_mutex_lock(&conn_mutex); | ||
169 | fclose(conn->fp); | ||
170 | conn->fp = NULL; | ||
171 | wydawca_connection_dequeue(&conn_idle, conn); | ||
172 | pthread_cond_broadcast(&conn_cond); | ||
173 | pthread_mutex_unlock(&conn_mutex); | ||
174 | } | ||
175 | |||
176 | static void | ||
177 | connection_stop(struct wydawca_connection *conn) | ||
178 | { | ||
179 | pthread_mutex_lock(&conn_mutex); | ||
180 | if (conn->fp) { | ||
181 | fclose(conn->fp); | ||
182 | conn->fp = NULL; | ||
183 | wydawca_connection_dequeue(&conn_idle, conn); | ||
184 | pthread_cond_broadcast(&conn_cond); | ||
185 | } | ||
186 | wydawca_connection_enqueue(&conn_avail, conn); | ||
187 | pthread_mutex_unlock(&conn_mutex); | ||
188 | } | ||
189 | |||
190 | static inline int | ||
191 | timespec_cmp(struct timespec const *a, struct timespec const *b) | ||
192 | { | ||
193 | if (a->tv_sec > b->tv_sec) | ||
194 | return 1; | ||
195 | if (a->tv_sec < b->tv_sec) | ||
196 | return -1; | ||
197 | if (a->tv_nsec > b->tv_nsec) | ||
198 | return 1; | ||
199 | if (a->tv_nsec < b->tv_nsec) | ||
200 | return -1; | ||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static void | ||
205 | handle_connection(struct wydawca_connection *conn) | ||
86 | { | 206 | { |
87 | char *buf = NULL; | 207 | char *buf = NULL; |
88 | size_t buflen = 0; | 208 | size_t buflen = 0; |
89 | struct spool *spool; | 209 | struct spool *spool; |
90 | char *p; | 210 | char *p; |
91 | 211 | ||
92 | if (grecs_getline(&buf, &buflen, in) <= 0) | 212 | if (grecs_getline(&buf, &buflen, conn->fp) <= 0) |
93 | return; | 213 | return; |
94 | trim_crlf(buf); | 214 | trim_crlf(buf); |
95 | wy_debug(1, ("recv: %s", buf)); | 215 | wy_debug(1, ("recv: %s", buf)); |
96 | spool = wydawca_find_spool(buf); | 216 | spool = wydawca_find_spool(buf); |
97 | if (!spool) { | 217 | if (!spool) { |
98 | if (all_spool_aliases && grecs_list_locate(all_spool_aliases, buf)) | 218 | if (all_spool_aliases && grecs_list_locate(all_spool_aliases, buf)) |
99 | fprintf(out, "+ OK, all spools\r\n"); | 219 | fprintf(conn->fp, "+ OK, all spools\r\n"); |
100 | else { | 220 | else { |
101 | fprintf(out, "- Unknown service name\r\n"); | 221 | fprintf(conn->fp, "- Unknown service name\r\n"); |
102 | free(buf); | 222 | free(buf); |
103 | return; | 223 | return; |
104 | } | 224 | } |
105 | } else if (spool->url) | 225 | } else if (spool->url) |
106 | fprintf(out, "+ OK, URL %s\r\n", spool->url); | 226 | fprintf(conn->fp, "+ OK, URL %s\r\n", spool->url); |
107 | else | 227 | else |
108 | fprintf(out, "+ OK, spool %s\r\n", spool->tag); | 228 | fprintf(conn->fp, "+ OK, spool %s\r\n", spool->tag); |
109 | 229 | ||
110 | if (grecs_getline(&buf, &buflen, in) < 0) { | 230 | if (grecs_getline(&buf, &buflen, conn->fp) < 0) { |
111 | wy_log(LOG_ERR, "protocol error"); | 231 | wy_log(LOG_ERR, "protocol error"); |
112 | free(buf); | 232 | free(buf); |
113 | return; | 233 | return; |
114 | } | 234 | } |
115 | 235 | connection_unidle(conn); | |
236 | |||
116 | trim_crlf(buf); | 237 | trim_crlf(buf); |
117 | wy_debug(1, ("recv: %s", buf)); | 238 | wy_debug(1, ("recv: %s", buf)); |
118 | 239 | ||
@@ -135,6 +256,42 @@ handle_connection(FILE * in, FILE * out) | |||
135 | free(buf); | 256 | free(buf); |
136 | } | 257 | } |
137 | 258 | ||
259 | void * | ||
260 | wy_thr_tcpmux(void *ptr) | ||
261 | { | ||
262 | struct wydawca_connection *conn = ptr; | ||
263 | pthread_cleanup_push((void (*)(void*))connection_stop, conn); | ||
264 | setlinebuf(conn->fp); | ||
265 | handle_connection(conn); | ||
266 | pthread_cleanup_pop(1); | ||
267 | return NULL; | ||
268 | } | ||
269 | |||
270 | void * | ||
271 | wy_thr_connection_watcher(void *ptr) | ||
272 | { | ||
273 | pthread_mutex_lock(&conn_mutex); | ||
274 | while (1) { | ||
275 | if (conn_idle.head) { | ||
276 | struct timespec ts; | ||
277 | |||
278 | pthread_cond_timedwait(&conn_cond, &conn_mutex, | ||
279 | &conn_idle.head->ts); | ||