blob: 5fc9e285bfdf25a6e67a77e5a2894466c5f99963 [file] [log] [blame]
Willy Tarreaubaaee002006-06-26 02:48:02 +02001/*
2 * Health-checks functions.
3 *
4 * Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <errno.h>
14#include <fcntl.h>
15#include <stdio.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020016#include <string.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020017#include <unistd.h>
18#include <sys/socket.h>
19#include <netinet/in.h>
20#include <arpa/inet.h>
21
Willy Tarreau2dd0d472006-06-29 17:53:05 +020022#include <common/compat.h>
23#include <common/config.h>
24#include <common/mini-clist.h>
25#include <common/time.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020026
27#include <types/global.h>
28#include <types/polling.h>
29#include <types/proxy.h>
30#include <types/session.h>
31
32#include <proto/backend.h>
33#include <proto/fd.h>
34#include <proto/log.h>
35#include <proto/queue.h>
36#include <proto/server.h>
37#include <proto/task.h>
38
39
40/* Sets server <s> down, notifies by all available means, recounts the
41 * remaining servers on the proxy and transfers queued sessions whenever
42 * possible to other servers.
43 */
44void set_server_down(struct server *s)
45{
46 struct pendconn *pc, *pc_bck, *pc_end;
47 struct session *sess;
48 int xferred;
49
50 s->state &= ~SRV_RUNNING;
51
52 if (s->health == s->rise) {
53 recount_servers(s->proxy);
54 recalc_server_map(s->proxy);
55
56 /* we might have sessions queued on this server and waiting for
57 * a connection. Those which are redispatchable will be queued
58 * to another server or to the proxy itself.
59 */
60 xferred = 0;
61 FOREACH_ITEM_SAFE(pc, pc_bck, &s->pendconns, pc_end, struct pendconn *, list) {
62 sess = pc->sess;
63 if ((sess->proxy->options & PR_O_REDISP)) {
64 /* The REDISP option was specified. We will ignore
65 * cookie and force to balance or use the dispatcher.
66 */
67 sess->flags &= ~(SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET);
68 sess->srv = NULL; /* it's left to the dispatcher to choose a server */
69 if ((sess->flags & SN_CK_MASK) == SN_CK_VALID) {
70 sess->flags &= ~SN_CK_MASK;
71 sess->flags |= SN_CK_DOWN;
72 }
73 pendconn_free(pc);
74 task_wakeup(&rq, sess->task);
75 xferred++;
76 }
77 }
78
79 sprintf(trash, "%sServer %s/%s is DOWN. %d active and %d backup servers left.%s"
80 " %d sessions active, %d requeued, %d remaining in queue.\n",
81 s->state & SRV_BACKUP ? "Backup " : "",
82 s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck,
83 (s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "",
84 s->cur_sess, xferred, s->nbpend);
85
86 Warning("%s", trash);
87 send_log(s->proxy, LOG_ALERT, "%s", trash);
88
89 if (s->proxy->srv_bck == 0 && s->proxy->srv_act == 0) {
90 Alert("Proxy %s has no server available !\n", s->proxy->id);
91 send_log(s->proxy, LOG_EMERG, "Proxy %s has no server available !\n", s->proxy->id);
92 }
93 s->down_trans++;
94 }
95 s->health = 0; /* failure */
96}
97
98
99/*
100 * This function is used only for server health-checks. It handles
101 * the connection acknowledgement. If the proxy requires HTTP health-checks,
102 * it sends the request. In other cases, it returns 1 if the socket is OK,
103 * or -1 if an error occured.
104 */
105int event_srv_chk_w(int fd)
106{
107 struct task *t = fdtab[fd].owner;
108 struct server *s = t->context;
109 int skerr;
110 socklen_t lskerr = sizeof(skerr);
111
112 skerr = 1;
113 if ((getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == -1)
114 || (skerr != 0)) {
115 /* in case of TCP only, this tells us if the connection failed */
116 s->result = -1;
117 fdtab[fd].state = FD_STERROR;
118 FD_CLR(fd, StaticWriteEvent);
119 }
120 else if (s->result != -1) {
121 /* we don't want to mark 'UP' a server on which we detected an error earlier */
Willy Tarreauf3c69202006-07-09 16:42:34 +0200122 if ((s->proxy->options & PR_O_HTTP_CHK) ||
123 (s->proxy->options & PR_O_SSL3_CHK)) {
Willy Tarreaubaaee002006-06-26 02:48:02 +0200124 int ret;
Willy Tarreauf3c69202006-07-09 16:42:34 +0200125 /* we want to check if this host replies to HTTP or SSLv3 requests
Willy Tarreaubaaee002006-06-26 02:48:02 +0200126 * so we'll send the request, and won't wake the checker up now.
127 */
Willy Tarreauf3c69202006-07-09 16:42:34 +0200128
129 if (s->proxy->options & PR_O_SSL3_CHK) {
130 /* SSL requires that we put Unix time in the request */
131 int gmt_time = htonl(now.tv_sec);
132 memcpy(s->proxy->check_req + 11, &gmt_time, 4);
133 }
134
Willy Tarreaubaaee002006-06-26 02:48:02 +0200135#ifndef MSG_NOSIGNAL
136 ret = send(fd, s->proxy->check_req, s->proxy->check_len, MSG_DONTWAIT);
137#else
138 ret = send(fd, s->proxy->check_req, s->proxy->check_len, MSG_DONTWAIT | MSG_NOSIGNAL);
139#endif
140 if (ret == s->proxy->check_len) {
141 FD_SET(fd, StaticReadEvent); /* prepare for reading reply */
142 FD_CLR(fd, StaticWriteEvent); /* nothing more to write */
143 return 0;
144 }
145 else {
146 s->result = -1;
147 FD_CLR(fd, StaticWriteEvent);
148 }
149 }
150 else {
151 /* good TCP connection is enough */
152 s->result = 1;
153 }
154 }
155
156 task_wakeup(&rq, t);
157 return 0;
158}
159
160
161/*
Willy Tarreauf3c69202006-07-09 16:42:34 +0200162 * This function is used only for server health-checks. It handles the server's
163 * reply to an HTTP request or SSL HELLO. It returns 1 in s->result if the
164 * server replies HTTP 2xx or 3xx (valid responses), or if it returns at least
165 * 5 bytes in response to SSL HELLO. The principle is that this is enough to
166 * distinguish between an SSL server and a pure TCP relay. All other cases will
167 * return -1. The function returns 0.
Willy Tarreaubaaee002006-06-26 02:48:02 +0200168 */
169int event_srv_chk_r(int fd)
170{
171 char reply[64];
172 int len, result;
173 struct task *t = fdtab[fd].owner;
174 struct server *s = t->context;
175 int skerr;
176 socklen_t lskerr = sizeof(skerr);
177
178 result = len = -1;
179
180 getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
181 if (!skerr) {
182#ifndef MSG_NOSIGNAL
183 len = recv(fd, reply, sizeof(reply), 0);
184#else
185 /* Warning! Linux returns EAGAIN on SO_ERROR if data are still available
186 * but the connection was closed on the remote end. Fortunately, recv still
187 * works correctly and we don't need to do the getsockopt() on linux.
188 */
189 len = recv(fd, reply, sizeof(reply), MSG_NOSIGNAL);
190#endif
Willy Tarreauf3c69202006-07-09 16:42:34 +0200191 if (((s->proxy->options & PR_O_HTTP_CHK) &&
192 (len >= sizeof("HTTP/1.0 000")) &&
Willy Tarreaubaaee002006-06-26 02:48:02 +0200193 !memcmp(reply, "HTTP/1.", 7) &&
194 (reply[9] == '2' || reply[9] == '3')) /* 2xx or 3xx */
Willy Tarreauf3c69202006-07-09 16:42:34 +0200195 || ((s->proxy->options & PR_O_SSL3_CHK) && (len >= 5) &&
196 (reply[0] == 0x15 || reply[0] == 0x16))) /* alert or handshake */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200197 result = 1;
198 }
199
200 if (result == -1)
201 fdtab[fd].state = FD_STERROR;
202
203 if (s->result != -1)
204 s->result = result;
205
206 FD_CLR(fd, StaticReadEvent);
207 task_wakeup(&rq, t);
208 return 0;
209}
210
211/*
212 * manages a server health-check. Returns
213 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
214 */
215int process_chk(struct task *t)
216{
217 struct server *s = t->context;
218 struct sockaddr_in sa;
219 int fd;
220
221 //fprintf(stderr, "process_chk: task=%p\n", t);
222
223 new_chk:
224 fd = s->curfd;
225 if (fd < 0) { /* no check currently running */
226 //fprintf(stderr, "process_chk: 2\n");
227 if (tv_cmp2_ms(&t->expire, &now) > 0) { /* not good time yet */
228 task_queue(t); /* restore t to its place in the task list */
229 return tv_remain2(&now, &t->expire);
230 }
231
232 /* we don't send any health-checks when the proxy is stopped or when
233 * the server should not be checked.
234 */
235 if (!(s->state & SRV_CHECKED) || s->proxy->state == PR_STSTOPPED) {
236 while (tv_cmp2_ms(&t->expire, &now) <= 0)
237 tv_delayfrom(&t->expire, &t->expire, s->inter);
238 task_queue(t); /* restore t to its place in the task list */
239 return tv_remain2(&now, &t->expire);
240 }
241
242 /* we'll initiate a new check */
243 s->result = 0; /* no result yet */
244 if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1) {
245 if ((fd < global.maxsock) &&
246 (fcntl(fd, F_SETFL, O_NONBLOCK) != -1) &&
247 (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) != -1)) {
248 //fprintf(stderr, "process_chk: 3\n");
249
250 /* we'll connect to the check port on the server */
251 sa = s->addr;
252 sa.sin_port = htons(s->check_port);
253
254 /* allow specific binding :
255 * - server-specific at first
256 * - proxy-specific next
257 */
258 if (s->state & SRV_BIND_SRC) {
259 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one));
260 if (bind(fd, (struct sockaddr *)&s->source_addr, sizeof(s->source_addr)) == -1) {
261 Alert("Cannot bind to source address before connect() for server %s/%s. Aborting.\n",
262 s->proxy->id, s->id);
263 s->result = -1;
264 }
265 }
266 else if (s->proxy->options & PR_O_BIND_SRC) {
267 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one));
268 if (bind(fd, (struct sockaddr *)&s->proxy->source_addr, sizeof(s->proxy->source_addr)) == -1) {
269 Alert("Cannot bind to source address before connect() for proxy %s. Aborting.\n",
270 s->proxy->id);
271 s->result = -1;
272 }
273 }
274
275 if (!s->result) {
276 if ((connect(fd, (struct sockaddr *)&sa, sizeof(sa)) != -1) || (errno == EINPROGRESS)) {
277 /* OK, connection in progress or established */
278
279 //fprintf(stderr, "process_chk: 4\n");
280
281 s->curfd = fd; /* that's how we know a test is in progress ;-) */
282 fdtab[fd].owner = t;
Willy Tarreau54469402006-07-29 16:59:06 +0200283 fdtab[fd].cb[DIR_RD].f = &event_srv_chk_r;
284 fdtab[fd].cb[DIR_RD].b = NULL;
285 fdtab[fd].cb[DIR_WR].f = &event_srv_chk_w;
286 fdtab[fd].cb[DIR_WR].b = NULL;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200287 fdtab[fd].state = FD_STCONN; /* connection in progress */
288 FD_SET(fd, StaticWriteEvent); /* for connect status */
289#ifdef DEBUG_FULL
290 assert (!FD_ISSET(fd, StaticReadEvent));
291#endif
292 fd_insert(fd);
293 /* FIXME: we allow up to <inter> for a connection to establish, but we should use another parameter */
294 tv_delayfrom(&t->expire, &now, s->inter);
295 task_queue(t); /* restore t to its place in the task list */
296 return tv_remain(&now, &t->expire);
297 }
298 else if (errno != EALREADY && errno != EISCONN && errno != EAGAIN) {
299 s->result = -1; /* a real error */
300 }
301 }
302 }
303 close(fd); /* socket creation error */
304 }
305
306 if (!s->result) { /* nothing done */
307 //fprintf(stderr, "process_chk: 6\n");
308 while (tv_cmp2_ms(&t->expire, &now) <= 0)
309 tv_delayfrom(&t->expire, &t->expire, s->inter);
310 goto new_chk; /* may be we should initialize a new check */
311 }
312
313 /* here, we have seen a failure */
314 if (s->health > s->rise) {
315 s->health--; /* still good */
316 s->failed_checks++;
317 }
318 else
319 set_server_down(s);
320
321 //fprintf(stderr, "process_chk: 7\n");
322 /* FIXME: we allow up to <inter> for a connection to establish, but we should use another parameter */
323 while (tv_cmp2_ms(&t->expire, &now) <= 0)
324 tv_delayfrom(&t->expire, &t->expire, s->inter);
325 goto new_chk;
326 }
327 else {
328 //fprintf(stderr, "process_chk: 8\n");
329 /* there was a test running */
330 if (s->result > 0) { /* good server detected */
331 //fprintf(stderr, "process_chk: 9\n");
332 s->health++; /* was bad, stays for a while */
333 if (s->health >= s->rise) {
334 s->state |= SRV_RUNNING;
335
336 if (s->health == s->rise) {
337 int xferred;
338
339 recount_servers(s->proxy);
340 recalc_server_map(s->proxy);
341
342 /* check if we can handle some connections queued at the proxy. We
343 * will take as many as we can handle.
344 */
345 for (xferred = 0; !s->maxconn || xferred < srv_dynamic_maxconn(s); xferred++) {
346 struct session *sess;
347 struct pendconn *p;
348
349 p = pendconn_from_px(s->proxy);
350 if (!p)
351 break;
352 p->sess->srv = s;
353 sess = p->sess;
354 pendconn_free(p);
355 task_wakeup(&rq, sess->task);
356 }
357
358 sprintf(trash,
359 "%sServer %s/%s is UP. %d active and %d backup servers online.%s"
360 " %d sessions requeued, %d total in queue.\n",
361 s->state & SRV_BACKUP ? "Backup " : "",
362 s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck,
363 (s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "",
364 xferred, s->nbpend);
365
366 Warning("%s", trash);
367 send_log(s->proxy, LOG_NOTICE, "%s", trash);
368 }
369
370 s->health = s->rise + s->fall - 1; /* OK now */
371 }
372 s->curfd = -1; /* no check running anymore */
373 //FD_CLR(fd, StaticWriteEvent);
374 fd_delete(fd);
375 while (tv_cmp2_ms(&t->expire, &now) <= 0)
376 tv_delayfrom(&t->expire, &t->expire, s->inter);
377 goto new_chk;
378 }
379 else if (s->result < 0 || tv_cmp2_ms(&t->expire, &now) <= 0) {
380 //fprintf(stderr, "process_chk: 10\n");
381 /* failure or timeout detected */
382 if (s->health > s->rise) {
383 s->health--; /* still good */
384 s->failed_checks++;
385 }
386 else
387 set_server_down(s);
388 s->curfd = -1;
389 //FD_CLR(fd, StaticWriteEvent);
390 fd_delete(fd);
391 while (tv_cmp2_ms(&t->expire, &now) <= 0)
392 tv_delayfrom(&t->expire, &t->expire, s->inter);
393 goto new_chk;
394 }
395 /* if result is 0 and there's no timeout, we have to wait again */
396 }
397 //fprintf(stderr, "process_chk: 11\n");
398 s->result = 0;
399 task_queue(t); /* restore t to its place in the task list */
400 return tv_remain2(&now, &t->expire);
401}
402
403
404/*
405 * Local variables:
406 * c-indent-level: 8
407 * c-basic-offset: 8
408 * End:
409 */