blob: e621918a8d556dd6367eb5f0f1fc56b2d5b93232 [file] [log] [blame]
Willy Tarreaubcc67332020-06-05 15:31:31 +02001/*
2 * External health-checks functions.
3 *
4 * Copyright 2000-2009,2020 Willy Tarreau <w@1wt.eu>
5 * Copyright 2014 Horms Solutions Ltd, Simon Horman <horms@verge.net.au>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 *
12 */
13
14#include <sys/resource.h>
15#include <sys/socket.h>
16#include <sys/types.h>
17#include <sys/wait.h>
18#include <assert.h>
19#include <ctype.h>
20#include <errno.h>
21#include <fcntl.h>
22#include <signal.h>
23#include <stdarg.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27#include <time.h>
28#include <unistd.h>
29
30#include <haproxy/api.h>
31#include <haproxy/cfgparse.h>
32#include <haproxy/check.h>
Willy Tarreau36979d92020-06-05 17:27:29 +020033#include <haproxy/errors.h>
Willy Tarreaubcc67332020-06-05 15:31:31 +020034#include <haproxy/global.h>
35#include <haproxy/list.h>
Willy Tarreaubcc67332020-06-05 15:31:31 +020036#include <haproxy/proxy.h>
37#include <haproxy/server.h>
38#include <haproxy/signal.h>
Willy Tarreau397ad412021-10-06 09:11:54 +020039#include <haproxy/stream-t.h>
Willy Tarreaubcc67332020-06-05 15:31:31 +020040#include <haproxy/task.h>
41#include <haproxy/thread.h>
42#include <haproxy/time.h>
43#include <haproxy/tools.h>
44
45
46static struct list pid_list = LIST_HEAD_INIT(pid_list);
Willy Tarreauff882702021-04-10 17:23:00 +020047static struct pool_head *pool_head_pid_list __read_mostly;
Willy Tarreaubcc67332020-06-05 15:31:31 +020048__decl_spinlock(pid_list_lock);
49
50struct extcheck_env {
51 char *name; /* environment variable name */
52 int vmaxlen; /* value maximum length, used to determine the required memory allocation */
53};
54
55/* environment variables memory requirement for different types of data */
56#define EXTCHK_SIZE_EVAL_INIT 0 /* size determined during the init phase,
57 * such environment variables are not updatable. */
58#define EXTCHK_SIZE_ULONG 20 /* max string length for an unsigned long value */
59#define EXTCHK_SIZE_UINT 11 /* max string length for an unsigned int value */
60#define EXTCHK_SIZE_ADDR INET6_ADDRSTRLEN+1 /* max string length for an address */
61
62/* external checks environment variables */
63enum {
64 EXTCHK_PATH = 0,
65
66 /* Proxy specific environment variables */
67 EXTCHK_HAPROXY_PROXY_NAME, /* the backend name */
68 EXTCHK_HAPROXY_PROXY_ID, /* the backend id */
69 EXTCHK_HAPROXY_PROXY_ADDR, /* the first bind address if available (or empty) */
70 EXTCHK_HAPROXY_PROXY_PORT, /* the first bind port if available (or empty) */
71
72 /* Server specific environment variables */
73 EXTCHK_HAPROXY_SERVER_NAME, /* the server name */
74 EXTCHK_HAPROXY_SERVER_ID, /* the server id */
75 EXTCHK_HAPROXY_SERVER_ADDR, /* the server address */
76 EXTCHK_HAPROXY_SERVER_PORT, /* the server port if available (or empty) */
77 EXTCHK_HAPROXY_SERVER_MAXCONN, /* the server max connections */
78 EXTCHK_HAPROXY_SERVER_CURCONN, /* the current number of connections on the server */
79
80 EXTCHK_SIZE
81};
82
83const struct extcheck_env extcheck_envs[EXTCHK_SIZE] = {
84 [EXTCHK_PATH] = { "PATH", EXTCHK_SIZE_EVAL_INIT },
85 [EXTCHK_HAPROXY_PROXY_NAME] = { "HAPROXY_PROXY_NAME", EXTCHK_SIZE_EVAL_INIT },
86 [EXTCHK_HAPROXY_PROXY_ID] = { "HAPROXY_PROXY_ID", EXTCHK_SIZE_EVAL_INIT },
87 [EXTCHK_HAPROXY_PROXY_ADDR] = { "HAPROXY_PROXY_ADDR", EXTCHK_SIZE_EVAL_INIT },
88 [EXTCHK_HAPROXY_PROXY_PORT] = { "HAPROXY_PROXY_PORT", EXTCHK_SIZE_EVAL_INIT },
89 [EXTCHK_HAPROXY_SERVER_NAME] = { "HAPROXY_SERVER_NAME", EXTCHK_SIZE_EVAL_INIT },
90 [EXTCHK_HAPROXY_SERVER_ID] = { "HAPROXY_SERVER_ID", EXTCHK_SIZE_EVAL_INIT },
91 [EXTCHK_HAPROXY_SERVER_ADDR] = { "HAPROXY_SERVER_ADDR", EXTCHK_SIZE_ADDR },
92 [EXTCHK_HAPROXY_SERVER_PORT] = { "HAPROXY_SERVER_PORT", EXTCHK_SIZE_UINT },
93 [EXTCHK_HAPROXY_SERVER_MAXCONN] = { "HAPROXY_SERVER_MAXCONN", EXTCHK_SIZE_EVAL_INIT },
94 [EXTCHK_HAPROXY_SERVER_CURCONN] = { "HAPROXY_SERVER_CURCONN", EXTCHK_SIZE_ULONG },
95};
96
97void block_sigchld(void)
98{
99 sigset_t set;
100 sigemptyset(&set);
101 sigaddset(&set, SIGCHLD);
102 assert(ha_sigmask(SIG_BLOCK, &set, NULL) == 0);
103}
104
105void unblock_sigchld(void)
106{
107 sigset_t set;
108 sigemptyset(&set);
109 sigaddset(&set, SIGCHLD);
110 assert(ha_sigmask(SIG_UNBLOCK, &set, NULL) == 0);
111}
112
113static struct pid_list *pid_list_add(pid_t pid, struct task *t)
114{
115 struct pid_list *elem;
116 struct check *check = t->context;
117
118 elem = pool_alloc(pool_head_pid_list);
119 if (!elem)
120 return NULL;
121 elem->pid = pid;
122 elem->t = t;
123 elem->exited = 0;
124 check->curpid = elem;
125 LIST_INIT(&elem->list);
126
127 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
Willy Tarreau2b718102021-04-21 07:32:39 +0200128 LIST_INSERT(&pid_list, &elem->list);
Willy Tarreaubcc67332020-06-05 15:31:31 +0200129 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
130
131 return elem;
132}
133
134static void pid_list_del(struct pid_list *elem)
135{
136 struct check *check;
137
138 if (!elem)
139 return;
140
141 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
Willy Tarreau2b718102021-04-21 07:32:39 +0200142 LIST_DELETE(&elem->list);
Willy Tarreaubcc67332020-06-05 15:31:31 +0200143 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
144
145 if (!elem->exited)
146 kill(elem->pid, SIGTERM);
147
148 check = elem->t->context;
149 check->curpid = NULL;
150 pool_free(pool_head_pid_list, elem);
151}
152
153/* Called from inside SIGCHLD handler, SIGCHLD is blocked */
154static void pid_list_expire(pid_t pid, int status)
155{
156 struct pid_list *elem;
157
158 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
159 list_for_each_entry(elem, &pid_list, list) {
160 if (elem->pid == pid) {
161 elem->t->expire = now_ms;
162 elem->status = status;
163 elem->exited = 1;
164 task_wakeup(elem->t, TASK_WOKEN_IO);
165 break;
166 }
167 }
168 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
169}
170
171static void sigchld_handler(struct sig_handler *sh)
172{
173 pid_t pid;
174 int status;
175
176 while ((pid = waitpid(0, &status, WNOHANG)) > 0)
177 pid_list_expire(pid, status);
178}
179
180int init_pid_list(void)
181{
182 if (pool_head_pid_list != NULL)
183 /* Nothing to do */
184 return 0;
185
186 if (!signal_register_fct(SIGCHLD, sigchld_handler, SIGCHLD)) {
187 ha_alert("Failed to set signal handler for external health checks: %s. Aborting.\n",
188 strerror(errno));
189 return 1;
190 }
191
192 pool_head_pid_list = create_pool("pid_list", sizeof(struct pid_list), MEM_F_SHARED);
193 if (pool_head_pid_list == NULL) {
194 ha_alert("Failed to allocate memory pool for external health checks: %s. Aborting.\n",
195 strerror(errno));
196 return 1;
197 }
198
199 return 0;
200}
201
202/* helper macro to set an environment variable and jump to a specific label on failure. */
203#define EXTCHK_SETENV(check, envidx, value, fail) { if (extchk_setenv(check, envidx, value)) goto fail; }
204
205/*
206 * helper function to allocate enough memory to store an environment variable.
207 * It will also check that the environment variable is updatable, and silently
208 * fail if not.
209 */
210static int extchk_setenv(struct check *check, int idx, const char *value)
211{
212 int len, ret;
213 char *envname;
214 int vmaxlen;
215
216 if (idx < 0 || idx >= EXTCHK_SIZE) {
217 ha_alert("Illegal environment variable index %d. Aborting.\n", idx);
218 return 1;
219 }
220
221 envname = extcheck_envs[idx].name;
222 vmaxlen = extcheck_envs[idx].vmaxlen;
223
224 /* Check if the environment variable is already set, and silently reject
225 * the update if this one is not updatable. */
226 if ((vmaxlen == EXTCHK_SIZE_EVAL_INIT) && (check->envp[idx]))
227 return 0;
228
229 /* Instead of sending NOT_USED, sending an empty value is preferable */
230 if (strcmp(value, "NOT_USED") == 0) {
231 value = "";
232 }
233
234 len = strlen(envname) + 1;
235 if (vmaxlen == EXTCHK_SIZE_EVAL_INIT)
236 len += strlen(value);
237 else
238 len += vmaxlen;
239
240 if (!check->envp[idx])
241 check->envp[idx] = malloc(len + 1);
242
243 if (!check->envp[idx]) {
244 ha_alert("Failed to allocate memory for the environment variable '%s'. Aborting.\n", envname);
245 return 1;
246 }
247 ret = snprintf(check->envp[idx], len + 1, "%s=%s", envname, value);
248 if (ret < 0) {
249 ha_alert("Failed to store the environment variable '%s'. Reason : %s. Aborting.\n", envname, strerror(errno));
250 return 1;
251 }
252 else if (ret > len) {
253 ha_alert("Environment variable '%s' was truncated. Aborting.\n", envname);
254 return 1;
255 }
256 return 0;
257}
258
259int prepare_external_check(struct check *check)
260{
261 struct server *s = check->server;
262 struct proxy *px = s->proxy;
263 struct listener *listener = NULL, *l;
264 int i;
265 const char *path = px->check_path ? px->check_path : DEF_CHECK_PATH;
266 char buf[256];
267
268 list_for_each_entry(l, &px->conf.listeners, by_fe)
269 /* Use the first INET, INET6 or UNIX listener */
Willy Tarreau37159062020-08-27 07:48:42 +0200270 if (l->rx.addr.ss_family == AF_INET ||
271 l->rx.addr.ss_family == AF_INET6 ||
272 l->rx.addr.ss_family == AF_UNIX) {
Willy Tarreaubcc67332020-06-05 15:31:31 +0200273 listener = l;
274 break;
275 }
276
277 check->curpid = NULL;
Tim Duesterhuse52b6e52020-09-12 20:26:43 +0200278 check->envp = calloc((EXTCHK_SIZE + 1), sizeof(*check->envp));
Willy Tarreaubcc67332020-06-05 15:31:31 +0200279 if (!check->envp) {
280 ha_alert("Failed to allocate memory for environment variables. Aborting\n");
281 goto err;
282 }
283
Tim Duesterhuse52b6e52020-09-12 20:26:43 +0200284 check->argv = calloc(6, sizeof(*check->argv));
Willy Tarreaubcc67332020-06-05 15:31:31 +0200285 if (!check->argv) {
286 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
287 goto err;
288 }
289
290 check->argv[0] = px->check_command;
291
292 if (!listener) {
293 check->argv[1] = strdup("NOT_USED");
294 check->argv[2] = strdup("NOT_USED");
295 }
Willy Tarreau37159062020-08-27 07:48:42 +0200296 else if (listener->rx.addr.ss_family == AF_INET ||
297 listener->rx.addr.ss_family == AF_INET6) {
298 addr_to_str(&listener->rx.addr, buf, sizeof(buf));
Willy Tarreaubcc67332020-06-05 15:31:31 +0200299 check->argv[1] = strdup(buf);
Willy Tarreau37159062020-08-27 07:48:42 +0200300 port_to_str(&listener->rx.addr, buf, sizeof(buf));
Willy Tarreaubcc67332020-06-05 15:31:31 +0200301 check->argv[2] = strdup(buf);
302 }
Willy Tarreau37159062020-08-27 07:48:42 +0200303 else if (listener->rx.addr.ss_family == AF_UNIX) {
Willy Tarreaubcc67332020-06-05 15:31:31 +0200304 const struct sockaddr_un *un;
305
Willy Tarreau37159062020-08-27 07:48:42 +0200306 un = (struct sockaddr_un *)&listener->rx.addr;
Willy Tarreaubcc67332020-06-05 15:31:31 +0200307 check->argv[1] = strdup(un->sun_path);
308 check->argv[2] = strdup("NOT_USED");
309 }
310 else {
311 ha_alert("Starting [%s:%s] check: unsupported address family.\n", px->id, s->id);
312 goto err;
313 }
314
315 if (!check->argv[1] || !check->argv[2]) {
316 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
317 goto err;
318 }
319
320 check->argv[3] = calloc(EXTCHK_SIZE_ADDR, sizeof(*check->argv[3]));
321 check->argv[4] = calloc(EXTCHK_SIZE_UINT, sizeof(*check->argv[4]));
322 if (!check->argv[3] || !check->argv[4]) {
323 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
324 goto err;
325 }
326
327 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
328 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
329 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
330
331 for (i = 0; i < 5; i++) {
332 if (!check->argv[i]) {
333 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
334 goto err;
335 }
336 }
337
338 EXTCHK_SETENV(check, EXTCHK_PATH, path, err);
339 /* Add proxy environment variables */
340 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_NAME, px->id, err);
341 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ID, ultoa_r(px->uuid, buf, sizeof(buf)), err);
342 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ADDR, check->argv[1], err);
343 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_PORT, check->argv[2], err);
344 /* Add server environment variables */
345 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_NAME, s->id, err);
346 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ID, ultoa_r(s->puid, buf, sizeof(buf)), err);
347 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3], err);
348 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4], err);
349 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_MAXCONN, ultoa_r(s->maxconn, buf, sizeof(buf)), err);
350 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)), err);
351
352 /* Ensure that we don't leave any hole in check->envp */
353 for (i = 0; i < EXTCHK_SIZE; i++)
354 if (!check->envp[i])
355 EXTCHK_SETENV(check, i, "", err);
356
357 return 1;
358err:
359 if (check->envp) {
360 for (i = 0; i < EXTCHK_SIZE; i++)
361 free(check->envp[i]);
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100362 ha_free(&check->envp);
Willy Tarreaubcc67332020-06-05 15:31:31 +0200363 }
364
365 if (check->argv) {
366 for (i = 1; i < 5; i++)
367 free(check->argv[i]);
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100368 ha_free(&check->argv);
Willy Tarreaubcc67332020-06-05 15:31:31 +0200369 }
370 return 0;
371}
372
373/*
374 * establish a server health-check that makes use of a process.
375 *
376 * It can return one of :
377 * - SF_ERR_NONE if everything's OK
378 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
379 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
380 *
381 * Blocks and then unblocks SIGCHLD
382 */
383static int connect_proc_chk(struct task *t)
384{
385 char buf[256];
386 struct check *check = t->context;
387 struct server *s = check->server;
388 struct proxy *px = s->proxy;
389 int status;
390 pid_t pid;
391
392 status = SF_ERR_RESOURCE;
393
394 block_sigchld();
395
396 pid = fork();
397 if (pid < 0) {
398 ha_alert("Failed to fork process for external health check%s: %s. Aborting.\n",
399 (global.tune.options & GTUNE_INSECURE_FORK) ?
400 "" : " (likely caused by missing 'insecure-fork-wanted')",
401 strerror(errno));
402 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
403 goto out;
404 }
405 if (pid == 0) {
406 /* Child */
407 extern char **environ;
408 struct rlimit limit;
409 int fd;
410
411 /* close all FDs. Keep stdin/stdout/stderr in verbose mode */
412 fd = (global.mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_QUIET ? 0 : 3;
413
414 my_closefrom(fd);
415
416 /* restore the initial FD limits */
417 limit.rlim_cur = rlim_fd_cur_at_boot;
418 limit.rlim_max = rlim_fd_max_at_boot;
419 if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
420 getrlimit(RLIMIT_NOFILE, &limit);
421 ha_warning("External check: failed to restore initial FD limits (cur=%u max=%u), using cur=%u max=%u\n",
422 rlim_fd_cur_at_boot, rlim_fd_max_at_boot,
423 (unsigned int)limit.rlim_cur, (unsigned int)limit.rlim_max);
424 }
425
426 environ = check->envp;
427
428 /* Update some environment variables and command args: curconn, server addr and server port */
Willy Tarreaub3250a22020-10-24 13:07:39 +0200429 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)), fail);
Willy Tarreaubcc67332020-06-05 15:31:31 +0200430
431 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
Willy Tarreaub3250a22020-10-24 13:07:39 +0200432 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3], fail);
Willy Tarreaubcc67332020-06-05 15:31:31 +0200433
434 *check->argv[4] = 0;
435 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
436 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
Willy Tarreaub3250a22020-10-24 13:07:39 +0200437 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4], fail);
Willy Tarreaubcc67332020-06-05 15:31:31 +0200438
439 haproxy_unblock_signals();
440 execvp(px->check_command, check->argv);
441 ha_alert("Failed to exec process for external health check: %s. Aborting.\n",
442 strerror(errno));
Willy Tarreaub3250a22020-10-24 13:07:39 +0200443 fail:
Willy Tarreaubcc67332020-06-05 15:31:31 +0200444 exit(-1);
445 }
446
447 /* Parent */
448 if (check->result == CHK_RES_UNKNOWN) {
449 if (pid_list_add(pid, t) != NULL) {
450 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
451
452 if (px->timeout.check && px->timeout.connect) {
453 int t_con = tick_add(now_ms, px->timeout.connect);
454 t->expire = tick_first(t->expire, t_con);
455 }
456 status = SF_ERR_NONE;
457 goto out;
458 }
459 else {
460 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
461 }
462 kill(pid, SIGTERM); /* process creation error */
463 }
464 else
465 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
466
467out:
468 unblock_sigchld();
469 return status;
470}
471
472/*
473 * manages a server health-check that uses an external process. Returns
474 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
475 *
476 * Please do NOT place any return statement in this function and only leave
477 * via the out_unlock label.
478 */
Willy Tarreau144f84a2021-03-02 16:09:26 +0100479struct task *process_chk_proc(struct task *t, void *context, unsigned int state)
Willy Tarreaubcc67332020-06-05 15:31:31 +0200480{
481 struct check *check = context;
482 struct server *s = check->server;
483 int rv;
484 int ret;
485 int expired = tick_is_expired(t->expire, now_ms);
486
487 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
488 if (!(check->state & CHK_ST_INPROGRESS)) {
489 /* no check currently running */
490 if (!expired) /* woke up too early */
491 goto out_unlock;
492
493 /* we don't send any health-checks when the proxy is
494 * stopped, the server should not be checked or the check
495 * is disabled.
496 */
497 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
Willy Tarreauc3914d42020-09-24 08:39:22 +0200498 s->proxy->disabled)
Willy Tarreaubcc67332020-06-05 15:31:31 +0200499 goto reschedule;
500
501 /* we'll initiate a new check */
502 set_server_check_status(check, HCHK_STATUS_START, NULL);
503
504 check->state |= CHK_ST_INPROGRESS;
505
506 ret = connect_proc_chk(t);
507 if (ret == SF_ERR_NONE) {
508 /* the process was forked, we allow up to min(inter,
509 * timeout.connect) for it to report its status, but
510 * only when timeout.check is set as it may be to short
511 * for a full check otherwise.
512 */
513 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
514
515 if (s->proxy->timeout.check && s->proxy->timeout.connect) {
516 int t_con = tick_add(now_ms, s->proxy->timeout.connect);
517 t->expire = tick_first(t->expire, t_con);
518 }
519 task_set_affinity(t, tid_bit);
520 goto reschedule;
521 }
522
523 /* here, we failed to start the check */
524
525 check->state &= ~CHK_ST_INPROGRESS;
526 check_notify_failure(check);
527
528 /* we allow up to min(inter, timeout.connect) for a connection
529 * to establish but only when timeout.check is set
530 * as it may be to short for a full check otherwise
531 */
532 while (tick_is_expired(t->expire, now_ms)) {
533 int t_con;
534
535 t_con = tick_add(t->expire, s->proxy->timeout.connect);
536 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
537
538 if (s->proxy->timeout.check)
539 t->expire = tick_first(t->expire, t_con);
540 }
541 }
542 else {
543 /* there was a test running.
544 * First, let's check whether there was an uncaught error,
545 * which can happen on connect timeout or error.
546 */
547 if (check->result == CHK_RES_UNKNOWN) {
548 /* good connection is enough for pure TCP check */
549 struct pid_list *elem = check->curpid;
550 int status = HCHK_STATUS_UNKNOWN;
551
552 if (elem->exited) {
553 status = elem->status; /* Save in case the process exits between use below */
554 if (!WIFEXITED(status))
555 check->code = -1;
556 else
557 check->code = WEXITSTATUS(status);
558 if (!WIFEXITED(status) || WEXITSTATUS(status))
559 status = HCHK_STATUS_PROCERR;
560 else
561 status = HCHK_STATUS_PROCOK;
562 } else if (expired) {
563 status = HCHK_STATUS_PROCTOUT;
564 ha_warning("kill %d\n", (int)elem->pid);
565 kill(elem->pid, SIGTERM);
566 }
567 set_server_check_status(check, status, NULL);
568 }
569
570 if (check->result == CHK_RES_FAILED) {
571 /* a failure or timeout detected */
572 check_notify_failure(check);
573 }
574 else if (check->result == CHK_RES_CONDPASS) {
575 /* check is OK but asks for stopping mode */
576 check_notify_stopping(check);
577 }
578 else if (check->result == CHK_RES_PASSED) {
579 /* a success was detected */
580 check_notify_success(check);
581 }
582 task_set_affinity(t, 1);
583 check->state &= ~CHK_ST_INPROGRESS;
584
585 pid_list_del(check->curpid);
586
587 rv = 0;
588 if (global.spread_checks > 0) {
589 rv = srv_getinter(check) * global.spread_checks / 100;
590 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
591 }
592 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
593 }
594
595 reschedule:
596 while (tick_is_expired(t->expire, now_ms))
597 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
598
599 out_unlock:
600 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
601 return t;
602}
603
604/* Parses the "external-check" proxy keyword */
605int proxy_parse_extcheck(char **args, int section, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +0100606 const struct proxy *defpx, const char *file, int line,
Willy Tarreaubcc67332020-06-05 15:31:31 +0200607 char **errmsg)
608{
609 int cur_arg, ret = 0;
610
611 cur_arg = 1;
612 if (!*(args[cur_arg])) {
613 memprintf(errmsg, "missing argument after '%s'.\n", args[0]);
614 goto error;
615 }
616
617 if (strcmp(args[cur_arg], "command") == 0) {
618 if (too_many_args(2, args, errmsg, NULL))
619 goto error;
620 if (!*(args[cur_arg+1])) {
621 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
622 goto error;
623 }
624 free(curpx->check_command);
625 curpx->check_command = strdup(args[cur_arg+1]);
626 }
627 else if (strcmp(args[cur_arg], "path") == 0) {
628 if (too_many_args(2, args, errmsg, NULL))
629 goto error;
630 if (!*(args[cur_arg+1])) {
631 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
632 goto error;
633 }
634 free(curpx->check_path);
635 curpx->check_path = strdup(args[cur_arg+1]);
636 }
637 else {
638 memprintf(errmsg, "'%s' only supports 'command' and 'path'. but got '%s'.",
639 args[0], args[1]);
640 goto error;
641 }
642
643 ret = (*errmsg != NULL); /* Handle warning */
644 return ret;
645
646error:
647 return -1;
648}
649
Willy Tarreau220fd702021-02-12 12:07:38 +0100650int proxy_parse_external_check_opt(char **args, int cur_arg, struct proxy *curpx, const struct proxy *defpx,
Willy Tarreaubcc67332020-06-05 15:31:31 +0200651 const char *file, int line)
652{
653 int err_code = 0;
654
655 curpx->options2 &= ~PR_O2_CHK_ANY;
656 curpx->options2 |= PR_O2_EXT_CHK;
657 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
658 goto out;
659
660 out:
661 return err_code;
662}
663
664static struct cfg_kw_list cfg_kws = {ILH, {
665 { CFG_LISTEN, "external-check", proxy_parse_extcheck },
666 { 0, NULL, NULL },
667}};
668
669INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);