blob: 9643a2b0d6f9eab8757b1187ad48540376628641 [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;
Willy Tarreau2a429502006-10-15 14:52:29 +0200118 MY_FD_CLR(fd, StaticWriteEvent);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200119 }
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) {
Willy Tarreau2a429502006-10-15 14:52:29 +0200141 MY_FD_SET(fd, StaticReadEvent); /* prepare for reading reply */
142 MY_FD_CLR(fd, StaticWriteEvent); /* nothing more to write */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200143 return 0;
144 }
145 else {
146 s->result = -1;
Willy Tarreau2a429502006-10-15 14:52:29 +0200147 MY_FD_CLR(fd, StaticWriteEvent);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200148 }
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;
Willy Tarreauc6423482006-10-15 14:59:03 +0200179 if (!getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) && !skerr) {
Willy Tarreaubaaee002006-06-26 02:48:02 +0200180#ifndef MSG_NOSIGNAL
181 len = recv(fd, reply, sizeof(reply), 0);
182#else
183 /* Warning! Linux returns EAGAIN on SO_ERROR if data are still available
184 * but the connection was closed on the remote end. Fortunately, recv still
185 * works correctly and we don't need to do the getsockopt() on linux.
186 */
187 len = recv(fd, reply, sizeof(reply), MSG_NOSIGNAL);
188#endif
Willy Tarreauf3c69202006-07-09 16:42:34 +0200189 if (((s->proxy->options & PR_O_HTTP_CHK) &&
190 (len >= sizeof("HTTP/1.0 000")) &&
Willy Tarreaubaaee002006-06-26 02:48:02 +0200191 !memcmp(reply, "HTTP/1.", 7) &&
192 (reply[9] == '2' || reply[9] == '3')) /* 2xx or 3xx */
Willy Tarreauf3c69202006-07-09 16:42:34 +0200193 || ((s->proxy->options & PR_O_SSL3_CHK) && (len >= 5) &&
194 (reply[0] == 0x15 || reply[0] == 0x16))) /* alert or handshake */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200195 result = 1;
196 }
197
198 if (result == -1)
199 fdtab[fd].state = FD_STERROR;
200
201 if (s->result != -1)
202 s->result = result;
203
Willy Tarreau2a429502006-10-15 14:52:29 +0200204 MY_FD_CLR(fd, StaticReadEvent);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200205 task_wakeup(&rq, t);
206 return 0;
207}
208
209/*
210 * manages a server health-check. Returns
211 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
212 */
213int process_chk(struct task *t)
214{
215 struct server *s = t->context;
216 struct sockaddr_in sa;
217 int fd;
218
219 //fprintf(stderr, "process_chk: task=%p\n", t);
220
221 new_chk:
222 fd = s->curfd;
223 if (fd < 0) { /* no check currently running */
224 //fprintf(stderr, "process_chk: 2\n");
225 if (tv_cmp2_ms(&t->expire, &now) > 0) { /* not good time yet */
226 task_queue(t); /* restore t to its place in the task list */
227 return tv_remain2(&now, &t->expire);
228 }
229
230 /* we don't send any health-checks when the proxy is stopped or when
231 * the server should not be checked.
232 */
233 if (!(s->state & SRV_CHECKED) || s->proxy->state == PR_STSTOPPED) {
234 while (tv_cmp2_ms(&t->expire, &now) <= 0)
235 tv_delayfrom(&t->expire, &t->expire, s->inter);
236 task_queue(t); /* restore t to its place in the task list */
237 return tv_remain2(&now, &t->expire);
238 }
239
240 /* we'll initiate a new check */
241 s->result = 0; /* no result yet */
242 if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1) {
243 if ((fd < global.maxsock) &&
244 (fcntl(fd, F_SETFL, O_NONBLOCK) != -1) &&
245 (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) != -1)) {
246 //fprintf(stderr, "process_chk: 3\n");
247
248 /* we'll connect to the check port on the server */
249 sa = s->addr;
250 sa.sin_port = htons(s->check_port);
251
252 /* allow specific binding :
253 * - server-specific at first
254 * - proxy-specific next
255 */
256 if (s->state & SRV_BIND_SRC) {
257 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one));
258 if (bind(fd, (struct sockaddr *)&s->source_addr, sizeof(s->source_addr)) == -1) {
259 Alert("Cannot bind to source address before connect() for server %s/%s. Aborting.\n",
260 s->proxy->id, s->id);
261 s->result = -1;
262 }
263 }
264 else if (s->proxy->options & PR_O_BIND_SRC) {
265 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one));
266 if (bind(fd, (struct sockaddr *)&s->proxy->source_addr, sizeof(s->proxy->source_addr)) == -1) {
267 Alert("Cannot bind to source address before connect() for proxy %s. Aborting.\n",
268 s->proxy->id);
269 s->result = -1;
270 }
271 }
272
273 if (!s->result) {
274 if ((connect(fd, (struct sockaddr *)&sa, sizeof(sa)) != -1) || (errno == EINPROGRESS)) {
275 /* OK, connection in progress or established */
276
277 //fprintf(stderr, "process_chk: 4\n");
278
279 s->curfd = fd; /* that's how we know a test is in progress ;-) */
280 fdtab[fd].owner = t;
Willy Tarreau54469402006-07-29 16:59:06 +0200281 fdtab[fd].cb[DIR_RD].f = &event_srv_chk_r;
282 fdtab[fd].cb[DIR_RD].b = NULL;
283 fdtab[fd].cb[DIR_WR].f = &event_srv_chk_w;
284 fdtab[fd].cb[DIR_WR].b = NULL;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200285 fdtab[fd].state = FD_STCONN; /* connection in progress */
Willy Tarreau2a429502006-10-15 14:52:29 +0200286 MY_FD_SET(fd, StaticWriteEvent); /* for connect status */
Willy Tarreaubaaee002006-06-26 02:48:02 +0200287#ifdef DEBUG_FULL
Willy Tarreau2a429502006-10-15 14:52:29 +0200288 assert (!MY_FD_ISSET(fd, StaticReadEvent));
Willy Tarreaubaaee002006-06-26 02:48:02 +0200289#endif
290 fd_insert(fd);
291 /* FIXME: we allow up to <inter> for a connection to establish, but we should use another parameter */
292 tv_delayfrom(&t->expire, &now, s->inter);
293 task_queue(t); /* restore t to its place in the task list */
294 return tv_remain(&now, &t->expire);
295 }
296 else if (errno != EALREADY && errno != EISCONN && errno != EAGAIN) {
297 s->result = -1; /* a real error */
298 }
299 }
300 }
301 close(fd); /* socket creation error */
302 }
303
304 if (!s->result) { /* nothing done */
305 //fprintf(stderr, "process_chk: 6\n");
306 while (tv_cmp2_ms(&t->expire, &now) <= 0)
307 tv_delayfrom(&t->expire, &t->expire, s->inter);
308 goto new_chk; /* may be we should initialize a new check */
309 }
310
311 /* here, we have seen a failure */
312 if (s->health > s->rise) {
313 s->health--; /* still good */
314 s->failed_checks++;
315 }
316 else
317 set_server_down(s);
318
319 //fprintf(stderr, "process_chk: 7\n");
320 /* FIXME: we allow up to <inter> for a connection to establish, but we should use another parameter */
321 while (tv_cmp2_ms(&t->expire, &now) <= 0)
322 tv_delayfrom(&t->expire, &t->expire, s->inter);
323 goto new_chk;
324 }
325 else {
326 //fprintf(stderr, "process_chk: 8\n");
327 /* there was a test running */
328 if (s->result > 0) { /* good server detected */
329 //fprintf(stderr, "process_chk: 9\n");
330 s->health++; /* was bad, stays for a while */
331 if (s->health >= s->rise) {
332 s->state |= SRV_RUNNING;
333
334 if (s->health == s->rise) {
335 int xferred;
336
337 recount_servers(s->proxy);
338 recalc_server_map(s->proxy);
339
340 /* check if we can handle some connections queued at the proxy. We
341 * will take as many as we can handle.
342 */
343 for (xferred = 0; !s->maxconn || xferred < srv_dynamic_maxconn(s); xferred++) {
344 struct session *sess;
345 struct pendconn *p;
346
347 p = pendconn_from_px(s->proxy);
348 if (!p)
349 break;
350 p->sess->srv = s;
351 sess = p->sess;
352 pendconn_free(p);
353 task_wakeup(&rq, sess->task);
354 }
355
356 sprintf(trash,
357 "%sServer %s/%s is UP. %d active and %d backup servers online.%s"
358 " %d sessions requeued, %d total in queue.\n",
359 s->state & SRV_BACKUP ? "Backup " : "",
360 s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck,
361 (s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "",
362 xferred, s->nbpend);
363
364 Warning("%s", trash);
365 send_log(s->proxy, LOG_NOTICE, "%s", trash);
366 }
367
368 s->health = s->rise + s->fall - 1; /* OK now */
369 }
370 s->curfd = -1; /* no check running anymore */
Willy Tarreau2a429502006-10-15 14:52:29 +0200371 //MY_FD_CLR(fd, StaticWriteEvent);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200372 fd_delete(fd);
373 while (tv_cmp2_ms(&t->expire, &now) <= 0)
374 tv_delayfrom(&t->expire, &t->expire, s->inter);
375 goto new_chk;
376 }
377 else if (s->result < 0 || tv_cmp2_ms(&t->expire, &now) <= 0) {
378 //fprintf(stderr, "process_chk: 10\n");
379 /* failure or timeout detected */
380 if (s->health > s->rise) {
381 s->health--; /* still good */
382 s->failed_checks++;
383 }
384 else
385 set_server_down(s);
386 s->curfd = -1;
Willy Tarreau2a429502006-10-15 14:52:29 +0200387 //MY_FD_CLR(fd, StaticWriteEvent);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200388 fd_delete(fd);
389 while (tv_cmp2_ms(&t->expire, &now) <= 0)
390 tv_delayfrom(&t->expire, &t->expire, s->inter);
391 goto new_chk;
392 }
393 /* if result is 0 and there's no timeout, we have to wait again */
394 }
395 //fprintf(stderr, "process_chk: 11\n");
396 s->result = 0;
397 task_queue(t); /* restore t to its place in the task list */
398 return tv_remain2(&now, &t->expire);
399}
400
401
402/*
403 * Local variables:
404 * c-indent-level: 8
405 * c-basic-offset: 8
406 * End:
407 */