blob: 5b8daa144a4fdf0f0ddb55e02517cec9dfd6c580 [file] [log] [blame]
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001/*
2 * Stream processing offload engine management.
3 *
4 * Copyright 2016 HAProxy Technologies, Christopher Faulet <cfaulet@haproxy.com>
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#include <ctype.h>
13#include <errno.h>
14
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020015#include <haproxy/api.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020016#include <common/cfgparse.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020017#include <common/debug.h>
Willy Tarreau0108d902018-11-25 19:14:37 +010018#include <common/hathreads.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020019#include <common/memory.h>
20#include <common/time.h>
21
22#include <types/arg.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020023#include <types/global.h>
Christopher Faulet1f40b912017-02-17 09:32:19 +010024#include <types/spoe.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020025
Christopher Faulet57583e42017-09-04 15:41:09 +020026#include <proto/acl.h>
Christopher Faulet76c09ef2017-09-21 11:03:52 +020027#include <proto/action.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020028#include <proto/arg.h>
29#include <proto/backend.h>
30#include <proto/filters.h>
Christopher Faulet48026722016-11-16 15:01:12 +010031#include <proto/freq_ctr.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020032#include <proto/frontend.h>
Willy Tarreau61c112a2018-10-02 16:43:32 +020033#include <proto/http_rules.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020034#include <proto/log.h>
Christopher Fauletfc9cfe42019-07-16 14:54:53 +020035#include <proto/http_ana.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020036#include <proto/proxy.h>
37#include <proto/sample.h>
38#include <proto/session.h>
39#include <proto/signal.h>
Christopher Faulet4ff3e572017-02-24 14:31:11 +010040#include <proto/spoe.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020041#include <proto/stream.h>
42#include <proto/stream_interface.h>
43#include <proto/task.h>
Christopher Faulet76c09ef2017-09-21 11:03:52 +020044#include <proto/tcp_rules.h>
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020045#include <proto/vars.h>
46
47#if defined(DEBUG_SPOE) || defined(DEBUG_FULL)
48#define SPOE_PRINTF(x...) fprintf(x)
Christopher Fauletb077cdc2018-01-24 16:37:57 +010049#define SPOE_DEBUG_STMT(statement) statement
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020050#else
51#define SPOE_PRINTF(x...)
Christopher Fauletb077cdc2018-01-24 16:37:57 +010052#define SPOE_DEBUG_STMT(statement)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020053#endif
54
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +010055/* Reserved 4 bytes to the frame size. So a frame and its size can be written
56 * together in a buffer */
57#define MAX_FRAME_SIZE global.tune.bufsize - 4
58
59/* The minimum size for a frame */
60#define MIN_FRAME_SIZE 256
61
Christopher Fauletf51f5fa2017-01-19 10:01:12 +010062/* Reserved for the metadata and the frame type.
63 * So <MAX_FRAME_SIZE> - <FRAME_HDR_SIZE> is the maximum payload size */
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +010064#define FRAME_HDR_SIZE 32
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020065
Christopher Fauletf51f5fa2017-01-19 10:01:12 +010066/* Helper to get SPOE ctx inside an appctx */
Christopher Faulet42bfa462017-01-04 14:14:19 +010067#define SPOE_APPCTX(appctx) ((struct spoe_appctx *)((appctx)->ctx.spoe.ptr))
68
Christopher Faulet3b386a32017-02-23 10:17:15 +010069/* SPOE filter id. Used to identify SPOE filters */
70const char *spoe_filter_id = "SPOE filter";
71
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020072/* Set if the handle on SIGUSR1 is registered */
73static int sighandler_registered = 0;
74
75/* proxy used during the parsing */
76struct proxy *curproxy = NULL;
77
78/* The name of the SPOE engine, used during the parsing */
79char *curengine = NULL;
80
81/* SPOE agent used during the parsing */
Christopher Faulet11610f32017-09-21 10:23:10 +020082/* SPOE agent/group/message used during the parsing */
83struct spoe_agent *curagent = NULL;
84struct spoe_group *curgrp = NULL;
85struct spoe_message *curmsg = NULL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020086
87/* list of SPOE messages and placeholders used during the parsing */
88struct list curmsgs;
Christopher Faulet11610f32017-09-21 10:23:10 +020089struct list curgrps;
90struct list curmphs;
91struct list curgphs;
Christopher Faulet336d3ef2017-12-22 10:00:55 +010092struct list curvars;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +020093
Christopher Faulet7250b8f2018-03-26 17:19:01 +020094/* list of log servers used during the parsing */
95struct list curlogsrvs;
96
Christopher Faulet0e0f0852018-03-26 17:20:36 +020097/* agent's proxy flags (PR_O_* and PR_O2_*) used during parsing */
98int curpxopts;
99int curpxopts2;
100
Christopher Faulet42bfa462017-01-04 14:14:19 +0100101/* Pools used to allocate SPOE structs */
Willy Tarreau8ceae722018-11-26 11:58:30 +0100102DECLARE_STATIC_POOL(pool_head_spoe_ctx, "spoe_ctx", sizeof(struct spoe_context));
103DECLARE_STATIC_POOL(pool_head_spoe_appctx, "spoe_appctx", sizeof(struct spoe_appctx));
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200104
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200105struct flt_ops spoe_ops;
106
Christopher Faulet8ef75252017-02-20 22:56:03 +0100107static int spoe_queue_context(struct spoe_context *ctx);
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200108static int spoe_acquire_buffer(struct buffer *buf, struct buffer_wait *buffer_wait);
109static void spoe_release_buffer(struct buffer *buf, struct buffer_wait *buffer_wait);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200110
111/********************************************************************
112 * helper functions/globals
113 ********************************************************************/
114static void
Christopher Faulet11610f32017-09-21 10:23:10 +0200115spoe_release_placeholder(struct spoe_placeholder *ph)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200116{
Christopher Faulet11610f32017-09-21 10:23:10 +0200117 if (!ph)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200118 return;
Christopher Faulet11610f32017-09-21 10:23:10 +0200119 free(ph->id);
120 free(ph);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200121}
122
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200123static void
Christopher Faulet8ef75252017-02-20 22:56:03 +0100124spoe_release_message(struct spoe_message *msg)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200125{
Christopher Faulet57583e42017-09-04 15:41:09 +0200126 struct spoe_arg *arg, *argback;
127 struct acl *acl, *aclback;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200128
129 if (!msg)
130 return;
131 free(msg->id);
132 free(msg->conf.file);
Christopher Faulet57583e42017-09-04 15:41:09 +0200133 list_for_each_entry_safe(arg, argback, &msg->args, list) {
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200134 release_sample_expr(arg->expr);
135 free(arg->name);
136 LIST_DEL(&arg->list);
137 free(arg);
138 }
Christopher Faulet57583e42017-09-04 15:41:09 +0200139 list_for_each_entry_safe(acl, aclback, &msg->acls, list) {
140 LIST_DEL(&acl->list);
141 prune_acl(acl);
142 free(acl);
143 }
144 if (msg->cond) {
145 prune_acl_cond(msg->cond);
146 free(msg->cond);
147 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200148 free(msg);
149}
150
151static void
Christopher Faulet11610f32017-09-21 10:23:10 +0200152spoe_release_group(struct spoe_group *grp)
153{
154 if (!grp)
155 return;
156 free(grp->id);
157 free(grp->conf.file);
158 free(grp);
159}
160
161static void
Christopher Faulet8ef75252017-02-20 22:56:03 +0100162spoe_release_agent(struct spoe_agent *agent)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200163{
Christopher Faulet11610f32017-09-21 10:23:10 +0200164 struct spoe_message *msg, *msgback;
165 struct spoe_group *grp, *grpback;
Christopher Faulet24289f22017-09-25 14:48:02 +0200166 int i;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200167
168 if (!agent)
169 return;
170 free(agent->id);
171 free(agent->conf.file);
172 free(agent->var_pfx);
Christopher Faulet985532d2016-11-16 15:36:19 +0100173 free(agent->var_on_error);
Christopher Faulet36bda1c2018-03-22 09:08:20 +0100174 free(agent->var_t_process);
175 free(agent->var_t_total);
Christopher Faulet11610f32017-09-21 10:23:10 +0200176 list_for_each_entry_safe(msg, msgback, &agent->messages, list) {
177 LIST_DEL(&msg->list);
178 spoe_release_message(msg);
179 }
180 list_for_each_entry_safe(grp, grpback, &agent->groups, list) {
181 LIST_DEL(&grp->list);
182 spoe_release_group(grp);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200183 }
Willy Tarreau3ddcf762019-02-07 14:22:52 +0100184 if (agent->rt) {
Christopher Fauletb1bb1af2019-09-17 11:55:52 +0200185 for (i = 0; i < global.nbthread; ++i) {
186 free(agent->rt[i].engine_id);
Willy Tarreau3ddcf762019-02-07 14:22:52 +0100187 HA_SPIN_DESTROY(&agent->rt[i].lock);
Christopher Fauletb1bb1af2019-09-17 11:55:52 +0200188 }
Willy Tarreau3ddcf762019-02-07 14:22:52 +0100189 }
Christopher Faulet24289f22017-09-25 14:48:02 +0200190 free(agent->rt);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200191 free(agent);
192}
193
194static const char *spoe_frm_err_reasons[SPOE_FRM_ERRS] = {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100195 [SPOE_FRM_ERR_NONE] = "normal",
196 [SPOE_FRM_ERR_IO] = "I/O error",
197 [SPOE_FRM_ERR_TOUT] = "a timeout occurred",
198 [SPOE_FRM_ERR_TOO_BIG] = "frame is too big",
199 [SPOE_FRM_ERR_INVALID] = "invalid frame received",
200 [SPOE_FRM_ERR_NO_VSN] = "version value not found",
201 [SPOE_FRM_ERR_NO_FRAME_SIZE] = "max-frame-size value not found",
202 [SPOE_FRM_ERR_NO_CAP] = "capabilities value not found",
203 [SPOE_FRM_ERR_BAD_VSN] = "unsupported version",
204 [SPOE_FRM_ERR_BAD_FRAME_SIZE] = "max-frame-size too big or too small",
205 [SPOE_FRM_ERR_FRAG_NOT_SUPPORTED] = "fragmentation not supported",
206 [SPOE_FRM_ERR_INTERLACED_FRAMES] = "invalid interlaced frames",
Christopher Faulet8eda93f2017-02-09 09:44:33 +0100207 [SPOE_FRM_ERR_FRAMEID_NOTFOUND] = "frame-id not found",
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100208 [SPOE_FRM_ERR_RES] = "resource allocation error",
209 [SPOE_FRM_ERR_UNKNOWN] = "an unknown error occurred",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200210};
211
212static const char *spoe_event_str[SPOE_EV_EVENTS] = {
213 [SPOE_EV_ON_CLIENT_SESS] = "on-client-session",
214 [SPOE_EV_ON_TCP_REQ_FE] = "on-frontend-tcp-request",
215 [SPOE_EV_ON_TCP_REQ_BE] = "on-backend-tcp-request",
216 [SPOE_EV_ON_HTTP_REQ_FE] = "on-frontend-http-request",
217 [SPOE_EV_ON_HTTP_REQ_BE] = "on-backend-http-request",
218
219 [SPOE_EV_ON_SERVER_SESS] = "on-server-session",
220 [SPOE_EV_ON_TCP_RSP] = "on-tcp-response",
221 [SPOE_EV_ON_HTTP_RSP] = "on-http-response",
222};
223
224
225#if defined(DEBUG_SPOE) || defined(DEBUG_FULL)
226
227static const char *spoe_ctx_state_str[SPOE_CTX_ST_ERROR+1] = {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100228 [SPOE_CTX_ST_NONE] = "NONE",
229 [SPOE_CTX_ST_READY] = "READY",
230 [SPOE_CTX_ST_ENCODING_MSGS] = "ENCODING_MSGS",
231 [SPOE_CTX_ST_SENDING_MSGS] = "SENDING_MSGS",
232 [SPOE_CTX_ST_WAITING_ACK] = "WAITING_ACK",
233 [SPOE_CTX_ST_DONE] = "DONE",
234 [SPOE_CTX_ST_ERROR] = "ERROR",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200235};
236
237static const char *spoe_appctx_state_str[SPOE_APPCTX_ST_END+1] = {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100238 [SPOE_APPCTX_ST_CONNECT] = "CONNECT",
239 [SPOE_APPCTX_ST_CONNECTING] = "CONNECTING",
240 [SPOE_APPCTX_ST_IDLE] = "IDLE",
241 [SPOE_APPCTX_ST_PROCESSING] = "PROCESSING",
242 [SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY] = "SENDING_FRAG_NOTIFY",
243 [SPOE_APPCTX_ST_WAITING_SYNC_ACK] = "WAITING_SYNC_ACK",
244 [SPOE_APPCTX_ST_DISCONNECT] = "DISCONNECT",
245 [SPOE_APPCTX_ST_DISCONNECTING] = "DISCONNECTING",
246 [SPOE_APPCTX_ST_EXIT] = "EXIT",
247 [SPOE_APPCTX_ST_END] = "END",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200248};
249
250#endif
Christopher Fauleta1cda022016-12-21 08:58:06 +0100251
Christopher Faulet8ef75252017-02-20 22:56:03 +0100252/* Used to generates a unique id for an engine. On success, it returns a
Ilya Shipitsin6fb0f212020-04-02 15:25:26 +0500253 * allocated string. So it is the caller's responsibility to release it. If the
Christopher Faulet8ef75252017-02-20 22:56:03 +0100254 * allocation failed, it returns NULL. */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100255static char *
256generate_pseudo_uuid()
257{
Willy Tarreauee3bcdd2020-03-08 17:48:17 +0100258 ha_generate_uuid(&trash);
Kevin Zhu079f8082020-03-13 10:39:51 +0800259 return my_strndup(trash.area, trash.data);
Christopher Fauleta1cda022016-12-21 08:58:06 +0100260}
261
Christopher Fauletb2dd1e02018-03-22 09:07:41 +0100262
263static inline void
264spoe_update_stat_time(struct timeval *tv, long *t)
265{
266 if (*t == -1)
267 *t = tv_ms_elapsed(tv, &now);
268 else
269 *t += tv_ms_elapsed(tv, &now);
270 tv_zero(tv);
271}
272
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200273/********************************************************************
274 * Functions that encode/decode SPOE frames
275 ********************************************************************/
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200276/* Helper to get static string length, excluding the terminating null byte */
277#define SLEN(str) (sizeof(str)-1)
278
279/* Predefined key used in HELLO/DISCONNECT frames */
280#define SUPPORTED_VERSIONS_KEY "supported-versions"
281#define VERSION_KEY "version"
282#define MAX_FRAME_SIZE_KEY "max-frame-size"
283#define CAPABILITIES_KEY "capabilities"
Christopher Fauleta1cda022016-12-21 08:58:06 +0100284#define ENGINE_ID_KEY "engine-id"
Christopher Fauletba7bc162016-11-07 21:07:38 +0100285#define HEALTHCHECK_KEY "healthcheck"
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200286#define STATUS_CODE_KEY "status-code"
287#define MSG_KEY "message"
288
289struct spoe_version {
290 char *str;
291 int min;
292 int max;
293};
294
295/* All supported versions */
296static struct spoe_version supported_versions[] = {
Christopher Faulet63816502018-05-31 14:56:42 +0200297 /* 1.0 is now unsupported because of a bug about frame's flags*/
298 {"2.0", 2000, 2000},
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200299 {NULL, 0, 0}
300};
301
302/* Comma-separated list of supported versions */
Christopher Faulet63816502018-05-31 14:56:42 +0200303#define SUPPORTED_VERSIONS_VAL "2.0"
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200304
Christopher Faulet8ef75252017-02-20 22:56:03 +0100305/* Convert a string to a SPOE version value. The string must follow the format
306 * "MAJOR.MINOR". It will be concerted into the integer (1000 * MAJOR + MINOR).
307 * If an error occurred, -1 is returned. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200308static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100309spoe_str_to_vsn(const char *str, size_t len)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200310{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100311 const char *p, *end;
312 int maj, min, vsn;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200313
Christopher Faulet8ef75252017-02-20 22:56:03 +0100314 p = str;
315 end = str+len;
316 maj = min = 0;
317 vsn = -1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200318
Christopher Faulet8ef75252017-02-20 22:56:03 +0100319 /* skip leading spaces */
Willy Tarreau90807112020-02-25 08:16:33 +0100320 while (p < end && isspace((unsigned char)*p))
Christopher Faulet8ef75252017-02-20 22:56:03 +0100321 p++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200322
Christopher Faulet8ef75252017-02-20 22:56:03 +0100323 /* parse Major number, until the '.' */
324 while (*p != '.') {
325 if (p >= end || *p < '0' || *p > '9')
326 goto out;
327 maj *= 10;
328 maj += (*p - '0');
329 p++;
330 }
331
332 /* check Major version */
333 if (!maj)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200334 goto out;
335
Christopher Faulet8ef75252017-02-20 22:56:03 +0100336 p++; /* skip the '.' */
337 if (p >= end || *p < '0' || *p > '9') /* Minor number is missing */
338 goto out;
339
340 /* Parse Minor number */
341 while (p < end) {
342 if (*p < '0' || *p > '9')
343 break;
344 min *= 10;
345 min += (*p - '0');
346 p++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200347 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100348
349 /* check Minor number */
350 if (min > 999)
351 goto out;
352
353 /* skip trailing spaces */
Willy Tarreau90807112020-02-25 08:16:33 +0100354 while (p < end && isspace((unsigned char)*p))
Christopher Faulet8ef75252017-02-20 22:56:03 +0100355 p++;
356 if (p != end)
357 goto out;
358
359 vsn = maj * 1000 + min;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200360 out:
361 return vsn;
362}
363
Christopher Faulet8ef75252017-02-20 22:56:03 +0100364/* Encode the HELLO frame sent by HAProxy to an agent. It returns the number of
Joseph Herlantf7f60312018-11-15 13:46:49 -0800365 * encoded bytes in the frame on success, 0 if an encoding error occurred and -1
Christopher Faulet8ef75252017-02-20 22:56:03 +0100366 * if a fatal error occurred. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200367static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100368spoe_prepare_hahello_frame(struct appctx *appctx, char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200369{
Willy Tarreau83061a82018-07-13 11:56:34 +0200370 struct buffer *chk;
Christopher Faulet42bfa462017-01-04 14:14:19 +0100371 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100372 char *p, *end;
373 unsigned int flags = SPOE_FRM_FL_FIN;
374 size_t sz;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200375
Christopher Faulet8ef75252017-02-20 22:56:03 +0100376 p = frame;
377 end = frame+size;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200378
Christopher Faulet8ef75252017-02-20 22:56:03 +0100379 /* Set Frame type */
380 *p++ = SPOE_FRM_T_HAPROXY_HELLO;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200381
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100382 /* Set flags */
Thierry FOURNIERc4dcaff2018-05-18 12:25:39 +0200383 flags = htonl(flags);
Christopher Faulet8ef75252017-02-20 22:56:03 +0100384 memcpy(p, (char *)&flags, 4);
385 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200386
387 /* No stream-id and frame-id for HELLO frames */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100388 *p++ = 0; *p++ = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200389
390 /* There are 3 mandatory items: "supported-versions", "max-frame-size"
391 * and "capabilities" */
392
393 /* "supported-versions" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100394 sz = SLEN(SUPPORTED_VERSIONS_KEY);
395 if (spoe_encode_buffer(SUPPORTED_VERSIONS_KEY, sz, &p, end) == -1)
396 goto too_big;
397
398 *p++ = SPOE_DATA_T_STR;
399 sz = SLEN(SUPPORTED_VERSIONS_VAL);
400 if (spoe_encode_buffer(SUPPORTED_VERSIONS_VAL, sz, &p, end) == -1)
401 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200402
403 /* "max-fram-size" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100404 sz = SLEN(MAX_FRAME_SIZE_KEY);
405 if (spoe_encode_buffer(MAX_FRAME_SIZE_KEY, sz, &p, end) == -1)
406 goto too_big;
407
408 *p++ = SPOE_DATA_T_UINT32;
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200409 if (encode_varint(SPOE_APPCTX(appctx)->max_frame_size, &p, end) == -1)
Christopher Faulet8ef75252017-02-20 22:56:03 +0100410 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200411
412 /* "capabilities" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100413 sz = SLEN(CAPABILITIES_KEY);
414 if (spoe_encode_buffer(CAPABILITIES_KEY, sz, &p, end) == -1)
415 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200416
Christopher Faulet8ef75252017-02-20 22:56:03 +0100417 *p++ = SPOE_DATA_T_STR;
Christopher Faulet305c6072017-02-23 16:17:53 +0100418 chk = get_trash_chunk();
419 if (agent != NULL && (agent->flags & SPOE_FL_PIPELINING)) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200420 memcpy(chk->area, "pipelining", 10);
421 chk->data += 10;
Christopher Faulet305c6072017-02-23 16:17:53 +0100422 }
423 if (agent != NULL && (agent->flags & SPOE_FL_ASYNC)) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200424 if (chk->data) chk->area[chk->data++] = ',';
425 memcpy(chk->area+chk->data, "async", 5);
426 chk->data += 5;
Christopher Faulet305c6072017-02-23 16:17:53 +0100427 }
Christopher Fauletcecd8522017-02-24 22:11:21 +0100428 if (agent != NULL && (agent->flags & SPOE_FL_RCV_FRAGMENTATION)) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200429 if (chk->data) chk->area[chk->data++] = ',';
430 memcpy(chk->area+chk->data, "fragmentation", 13);
Miroslav Zagorac6b3690b2019-01-13 16:55:01 +0100431 chk->data += 13;
Christopher Fauletcecd8522017-02-24 22:11:21 +0100432 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200433 if (spoe_encode_buffer(chk->area, chk->data, &p, end) == -1)
Christopher Faulet8ef75252017-02-20 22:56:03 +0100434 goto too_big;
435
Ilya Shipitsin6fb0f212020-04-02 15:25:26 +0500436 /* (optional) "engine-id" K/V item, if present */
Christopher Fauletb1bb1af2019-09-17 11:55:52 +0200437 if (agent != NULL && agent->rt[tid].engine_id != NULL) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100438 sz = SLEN(ENGINE_ID_KEY);
439 if (spoe_encode_buffer(ENGINE_ID_KEY, sz, &p, end) == -1)
440 goto too_big;
441
442 *p++ = SPOE_DATA_T_STR;
Christopher Fauletb1bb1af2019-09-17 11:55:52 +0200443 sz = strlen(agent->rt[tid].engine_id);
444 if (spoe_encode_buffer(agent->rt[tid].engine_id, sz, &p, end) == -1)
Christopher Faulet8ef75252017-02-20 22:56:03 +0100445 goto too_big;
Christopher Fauleta1cda022016-12-21 08:58:06 +0100446 }
447
Christopher Faulet8ef75252017-02-20 22:56:03 +0100448 return (p - frame);
449
450 too_big:
451 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOO_BIG;
452 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200453}
454
Christopher Faulet8ef75252017-02-20 22:56:03 +0100455/* Encode DISCONNECT frame sent by HAProxy to an agent. It returns the number of
456 * encoded bytes in the frame on success, 0 if an encoding error occurred and -1
457 * if a fatal error occurred. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200458static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100459spoe_prepare_hadiscon_frame(struct appctx *appctx, char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200460{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100461 const char *reason;
462 char *p, *end;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100463 unsigned int flags = SPOE_FRM_FL_FIN;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100464 size_t sz;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200465
Christopher Faulet8ef75252017-02-20 22:56:03 +0100466 p = frame;
467 end = frame+size;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200468
Christopher Faulet8ef75252017-02-20 22:56:03 +0100469 /* Set Frame type */
470 *p++ = SPOE_FRM_T_HAPROXY_DISCON;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200471
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100472 /* Set flags */
Thierry FOURNIERc4dcaff2018-05-18 12:25:39 +0200473 flags = htonl(flags);
Christopher Faulet8ef75252017-02-20 22:56:03 +0100474 memcpy(p, (char *)&flags, 4);
475 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200476
477 /* No stream-id and frame-id for DISCONNECT frames */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100478 *p++ = 0; *p++ = 0;
479
480 if (SPOE_APPCTX(appctx)->status_code >= SPOE_FRM_ERRS)
481 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_UNKNOWN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200482
483 /* There are 2 mandatory items: "status-code" and "message" */
484
485 /* "status-code" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100486 sz = SLEN(STATUS_CODE_KEY);
487 if (spoe_encode_buffer(STATUS_CODE_KEY, sz, &p, end) == -1)
488 goto too_big;
489
490 *p++ = SPOE_DATA_T_UINT32;
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200491 if (encode_varint(SPOE_APPCTX(appctx)->status_code, &p, end) == -1)
Christopher Faulet8ef75252017-02-20 22:56:03 +0100492 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200493
494 /* "message" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100495 sz = SLEN(MSG_KEY);
496 if (spoe_encode_buffer(MSG_KEY, sz, &p, end) == -1)
497 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200498
Christopher Faulet8ef75252017-02-20 22:56:03 +0100499 /*Get the message corresponding to the status code */
500 reason = spoe_frm_err_reasons[SPOE_APPCTX(appctx)->status_code];
501
502 *p++ = SPOE_DATA_T_STR;
503 sz = strlen(reason);
504 if (spoe_encode_buffer(reason, sz, &p, end) == -1)
505 goto too_big;
506
507 return (p - frame);
508
509 too_big:
510 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOO_BIG;
511 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200512}
513
Christopher Faulet8ef75252017-02-20 22:56:03 +0100514/* Encode the NOTIFY frame sent by HAProxy to an agent. It returns the number of
515 * encoded bytes in the frame on success, 0 if an encoding error occurred and -1
516 * if a fatal error occurred. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200517static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100518spoe_prepare_hanotify_frame(struct appctx *appctx, struct spoe_context *ctx,
Christopher Fauleta1cda022016-12-21 08:58:06 +0100519 char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200520{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100521 char *p, *end;
522 unsigned int stream_id, frame_id;
523 unsigned int flags = SPOE_FRM_FL_FIN;
524 size_t sz;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200525
Christopher Faulet8ef75252017-02-20 22:56:03 +0100526 p = frame;
527 end = frame+size;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200528
Christopher Fauletf032c3e2017-02-17 15:18:35 +0100529 stream_id = ctx->stream_id;
530 frame_id = ctx->frame_id;
531
532 if (ctx->flags & SPOE_CTX_FL_FRAGMENTED) {
533 /* The fragmentation is not supported by the applet */
534 if (!(SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_FRAGMENTATION)) {
535 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
536 return -1;
537 }
538 flags = ctx->frag_ctx.flags;
539 }
540
541 /* Set Frame type */
542 *p++ = SPOE_FRM_T_HAPROXY_NOTIFY;
543
544 /* Set flags */
Thierry FOURNIERc4dcaff2018-05-18 12:25:39 +0200545 flags = htonl(flags);
Christopher Fauletf032c3e2017-02-17 15:18:35 +0100546 memcpy(p, (char *)&flags, 4);
547 p += 4;
548
549 /* Set stream-id and frame-id */
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200550 if (encode_varint(stream_id, &p, end) == -1)
Christopher Fauletf032c3e2017-02-17 15:18:35 +0100551 goto too_big;
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200552 if (encode_varint(frame_id, &p, end) == -1)
Christopher Fauletf032c3e2017-02-17 15:18:35 +0100553 goto too_big;
554
555 /* Copy encoded messages, if possible */
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200556 sz = b_data(&ctx->buffer);
Christopher Fauletf032c3e2017-02-17 15:18:35 +0100557 if (p + sz >= end)
558 goto too_big;
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200559 memcpy(p, b_head(&ctx->buffer), sz);
Christopher Fauletf032c3e2017-02-17 15:18:35 +0100560 p += sz;
561
562 return (p - frame);
563
564 too_big:
565 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOO_BIG;
566 return 0;
567}
568
569/* Encode next part of a fragmented frame sent by HAProxy to an agent. It
570 * returns the number of encoded bytes in the frame on success, 0 if an encoding
571 * error occurred and -1 if a fatal error occurred. */
572static int
573spoe_prepare_hafrag_frame(struct appctx *appctx, struct spoe_context *ctx,
574 char *frame, size_t size)
575{
576 char *p, *end;
577 unsigned int stream_id, frame_id;
578 unsigned int flags;
579 size_t sz;
580
581 p = frame;
582 end = frame+size;
583
Christopher Faulet8ef75252017-02-20 22:56:03 +0100584 /* <ctx> is null when the stream has aborted the processing of a
585 * fragmented frame. In this case, we must notify the corresponding
586 * agent using ids stored in <frag_ctx>. */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100587 if (ctx == NULL) {
Christopher Fauletf032c3e2017-02-17 15:18:35 +0100588 flags = (SPOE_FRM_FL_FIN|SPOE_FRM_FL_ABRT);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100589 stream_id = SPOE_APPCTX(appctx)->frag_ctx.cursid;
590 frame_id = SPOE_APPCTX(appctx)->frag_ctx.curfid;
591 }
592 else {
Christopher Fauletf032c3e2017-02-17 15:18:35 +0100593 flags = ctx->frag_ctx.flags;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100594 stream_id = ctx->stream_id;
595 frame_id = ctx->frame_id;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100596 }
597
Christopher Faulet8ef75252017-02-20 22:56:03 +0100598 /* Set Frame type */
Christopher Fauletf032c3e2017-02-17 15:18:35 +0100599 *p++ = SPOE_FRM_T_UNSET;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100600
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100601 /* Set flags */
Thierry FOURNIERc4dcaff2018-05-18 12:25:39 +0200602 flags = htonl(flags);
Christopher Faulet8ef75252017-02-20 22:56:03 +0100603 memcpy(p, (char *)&flags, 4);
604 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200605
606 /* Set stream-id and frame-id */
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200607 if (encode_varint(stream_id, &p, end) == -1)
Christopher Faulet8ef75252017-02-20 22:56:03 +0100608 goto too_big;
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200609 if (encode_varint(frame_id, &p, end) == -1)
Christopher Faulet8ef75252017-02-20 22:56:03 +0100610 goto too_big;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200611
Christopher Fauletf032c3e2017-02-17 15:18:35 +0100612 if (ctx == NULL)
613 goto end;
614
Christopher Faulet8ef75252017-02-20 22:56:03 +0100615 /* Copy encoded messages, if possible */
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200616 sz = b_data(&ctx->buffer);
Christopher Faulet8ef75252017-02-20 22:56:03 +0100617 if (p + sz >= end)
618 goto too_big;
Willy Tarreauc9fa0482018-07-10 17:43:27 +0200619 memcpy(p, b_head(&ctx->buffer), sz);
Christopher Faulet8ef75252017-02-20 22:56:03 +0100620 p += sz;
Christopher Fauleta1cda022016-12-21 08:58:06 +0100621
Christopher Fauletf032c3e2017-02-17 15:18:35 +0100622 end:
Christopher Faulet8ef75252017-02-20 22:56:03 +0100623 return (p - frame);
624
625 too_big:
626 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOO_BIG;
627 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200628}
629
Christopher Faulet8ef75252017-02-20 22:56:03 +0100630/* Decode and process the HELLO frame sent by an agent. It returns the number of
631 * read bytes on success, 0 if a decoding error occurred, and -1 if a fatal
632 * error occurred. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200633static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100634spoe_handle_agenthello_frame(struct appctx *appctx, char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200635{
Christopher Faulet305c6072017-02-23 16:17:53 +0100636 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
637 char *p, *end;
638 int vsn, max_frame_size;
639 unsigned int flags;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100640
641 p = frame;
642 end = frame + size;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200643
644 /* Check frame type */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100645 if (*p++ != SPOE_FRM_T_AGENT_HELLO) {
646 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200647 return 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100648 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200649
Christopher Faulet8ef75252017-02-20 22:56:03 +0100650 if (size < 7 /* TYPE + METADATA */) {
651 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
652 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200653 }
654
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100655 /* Retrieve flags */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100656 memcpy((char *)&flags, p, 4);
Thierry FOURNIERc4dcaff2018-05-18 12:25:39 +0200657 flags = ntohl(flags);
Christopher Faulet8ef75252017-02-20 22:56:03 +0100658 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200659
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100660 /* Fragmentation is not supported for HELLO frame */
661 if (!(flags & SPOE_FRM_FL_FIN)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100662 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100663 return -1;
664 }
665
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200666 /* stream-id and frame-id must be cleared */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100667 if (*p != 0 || *(p+1) != 0) {
668 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
669 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200670 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100671 p += 2;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200672
673 /* There are 3 mandatory items: "version", "max-frame-size" and
674 * "capabilities" */
675
676 /* Loop on K/V items */
Christopher Fauleta1cda022016-12-21 08:58:06 +0100677 vsn = max_frame_size = flags = 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100678 while (p < end) {
679 char *str;
Frédéric Lécaille6ca71a92017-08-22 10:33:14 +0200680 uint64_t sz;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100681 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200682
683 /* Decode the item key */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100684 ret = spoe_decode_buffer(&p, end, &str, &sz);
685 if (ret == -1 || !sz) {
686 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
687 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200688 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100689
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200690 /* Check "version" K/V item */
691 if (!memcmp(str, VERSION_KEY, sz)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100692 int i, type = *p++;
693
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200694 /* The value must be a string */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100695 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR) {
696 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
697 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200698 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100699 if (spoe_decode_buffer(&p, end, &str, &sz) == -1) {
700 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
701 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200702 }
703
Christopher Faulet8ef75252017-02-20 22:56:03 +0100704 vsn = spoe_str_to_vsn(str, sz);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200705 if (vsn == -1) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100706 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_BAD_VSN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200707 return -1;
708 }
709 for (i = 0; supported_versions[i].str != NULL; ++i) {
710 if (vsn >= supported_versions[i].min &&
711 vsn <= supported_versions[i].max)
712 break;
713 }
714 if (supported_versions[i].str == NULL) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100715 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_BAD_VSN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200716 return -1;
717 }
718 }
719 /* Check "max-frame-size" K/V item */
720 else if (!memcmp(str, MAX_FRAME_SIZE_KEY, sz)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100721 int type = *p++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200722
723 /* The value must be integer */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200724 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT32 &&
725 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT64 &&
726 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT32 &&
727 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT64) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100728 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
729 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200730 }
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200731 if (decode_varint(&p, end, &sz) == -1) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100732 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
733 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200734 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100735 if (sz < MIN_FRAME_SIZE ||
736 sz > SPOE_APPCTX(appctx)->max_frame_size) {
737 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_BAD_FRAME_SIZE;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200738 return -1;
739 }
740 max_frame_size = sz;
741 }
Christopher Fauleta1cda022016-12-21 08:58:06 +0100742 /* Check "capabilities" K/V item */
743 else if (!memcmp(str, CAPABILITIES_KEY, sz)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100744 int type = *p++;
Christopher Fauleta1cda022016-12-21 08:58:06 +0100745
746 /* The value must be a string */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100747 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR) {
748 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
749 return 0;
Christopher Fauleta1cda022016-12-21 08:58:06 +0100750 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100751 if (spoe_decode_buffer(&p, end, &str, &sz) == -1) {
752 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
753 return 0;
754 }
Christopher Fauleta1cda022016-12-21 08:58:06 +0100755
Christopher Faulet8ef75252017-02-20 22:56:03 +0100756 while (sz) {
Christopher Fauleta1cda022016-12-21 08:58:06 +0100757 char *delim;
758
759 /* Skip leading spaces */
Willy Tarreau90807112020-02-25 08:16:33 +0100760 for (; isspace((unsigned char)*str) && sz; str++, sz--);
Christopher Fauleta1cda022016-12-21 08:58:06 +0100761
Christopher Faulet8ef75252017-02-20 22:56:03 +0100762 if (sz >= 10 && !strncmp(str, "pipelining", 10)) {
763 str += 10; sz -= 10;
Willy Tarreau90807112020-02-25 08:16:33 +0100764 if (!sz || isspace((unsigned char)*str) || *str == ',')
Christopher Fauleta1cda022016-12-21 08:58:06 +0100765 flags |= SPOE_APPCTX_FL_PIPELINING;
766 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100767 else if (sz >= 5 && !strncmp(str, "async", 5)) {
768 str += 5; sz -= 5;
Willy Tarreau90807112020-02-25 08:16:33 +0100769 if (!sz || isspace((unsigned char)*str) || *str == ',')
Christopher Fauleta1cda022016-12-21 08:58:06 +0100770 flags |= SPOE_APPCTX_FL_ASYNC;
771 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100772 else if (sz >= 13 && !strncmp(str, "fragmentation", 13)) {
773 str += 13; sz -= 13;
Willy Tarreau90807112020-02-25 08:16:33 +0100774 if (!sz || isspace((unsigned char)*str) || *str == ',')
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100775 flags |= SPOE_APPCTX_FL_FRAGMENTATION;
776 }
Christopher Fauleta1cda022016-12-21 08:58:06 +0100777
Christopher Faulet8ef75252017-02-20 22:56:03 +0100778 /* Get the next comma or break */
779 if (!sz || (delim = memchr(str, ',', sz)) == NULL)
Christopher Fauleta1cda022016-12-21 08:58:06 +0100780 break;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100781 delim++;
782 sz -= (delim - str);
783 str = delim;
Christopher Fauleta1cda022016-12-21 08:58:06 +0100784 }
785 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200786 else {
787 /* Silently ignore unknown item */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100788 if (spoe_skip_data(&p, end) == -1) {
789 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
790 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200791 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200792 }
793 }
794
795 /* Final checks */
796 if (!vsn) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100797 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_NO_VSN;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200798 return -1;
799 }
800 if (!max_frame_size) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100801 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_NO_FRAME_SIZE;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200802 return -1;
803 }
Christopher Faulet11389012019-02-07 16:13:26 +0100804 if (!agent)
805 flags &= ~(SPOE_APPCTX_FL_PIPELINING|SPOE_APPCTX_FL_ASYNC);
806 else {
807 if ((flags & SPOE_APPCTX_FL_PIPELINING) && !(agent->flags & SPOE_FL_PIPELINING))
808 flags &= ~SPOE_APPCTX_FL_PIPELINING;
809 if ((flags & SPOE_APPCTX_FL_ASYNC) && !(agent->flags & SPOE_FL_ASYNC))
810 flags &= ~SPOE_APPCTX_FL_ASYNC;
811 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200812
Christopher Faulet42bfa462017-01-04 14:14:19 +0100813 SPOE_APPCTX(appctx)->version = (unsigned int)vsn;
814 SPOE_APPCTX(appctx)->max_frame_size = (unsigned int)max_frame_size;
815 SPOE_APPCTX(appctx)->flags |= flags;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100816
817 return (p - frame);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200818}
819
820/* Decode DISCONNECT frame sent by an agent. It returns the number of by read
821 * bytes on success, 0 if the frame can be ignored and -1 if an error
822 * occurred. */
823static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100824spoe_handle_agentdiscon_frame(struct appctx *appctx, char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200825{
Christopher Faulet8ef75252017-02-20 22:56:03 +0100826 char *p, *end;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100827 unsigned int flags;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100828
829 p = frame;
830 end = frame + size;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200831
832 /* Check frame type */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100833 if (*p++ != SPOE_FRM_T_AGENT_DISCON) {
834 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200835 return 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100836 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200837
Christopher Faulet8ef75252017-02-20 22:56:03 +0100838 if (size < 7 /* TYPE + METADATA */) {
839 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
840 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200841 }
842
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100843 /* Retrieve flags */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100844 memcpy((char *)&flags, p, 4);
Thierry FOURNIERc4dcaff2018-05-18 12:25:39 +0200845 flags = ntohl(flags);
Christopher Faulet8ef75252017-02-20 22:56:03 +0100846 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200847
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100848 /* Fragmentation is not supported for DISCONNECT frame */
849 if (!(flags & SPOE_FRM_FL_FIN)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100850 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100851 return -1;
852 }
853
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200854 /* stream-id and frame-id must be cleared */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100855 if (*p != 0 || *(p+1) != 0) {
856 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
857 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200858 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100859 p += 2;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200860
861 /* There are 2 mandatory items: "status-code" and "message" */
862
863 /* Loop on K/V items */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100864 while (p < end) {
865 char *str;
Frédéric Lécaille6ca71a92017-08-22 10:33:14 +0200866 uint64_t sz;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100867 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200868
869 /* Decode the item key */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100870 ret = spoe_decode_buffer(&p, end, &str, &sz);
871 if (ret == -1 || !sz) {
872 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
873 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200874 }
875
876 /* Check "status-code" K/V item */
877 if (!memcmp(str, STATUS_CODE_KEY, sz)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100878 int type = *p++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200879
880 /* The value must be an integer */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200881 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT32 &&
882 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_INT64 &&
883 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT32 &&
884 (type & SPOE_DATA_T_MASK) != SPOE_DATA_T_UINT64) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100885 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
886 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200887 }
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200888 if (decode_varint(&p, end, &sz) == -1) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100889 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
890 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200891 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100892 SPOE_APPCTX(appctx)->status_code = sz;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200893 }
894
895 /* Check "message" K/V item */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100896 else if (!memcmp(str, MSG_KEY, sz)) {
897 int type = *p++;
898
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200899 /* The value must be a string */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100900 if ((type & SPOE_DATA_T_MASK) != SPOE_DATA_T_STR) {
901 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
902 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200903 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100904 ret = spoe_decode_buffer(&p, end, &str, &sz);
905 if (ret == -1 || sz > 255) {
906 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
907 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200908 }
Christopher Faulet8ef75252017-02-20 22:56:03 +0100909#if defined(DEBUG_SPOE) || defined(DEBUG_FULL)
910 SPOE_APPCTX(appctx)->reason = str;
911 SPOE_APPCTX(appctx)->rlen = sz;
912#endif
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200913 }
914 else {
915 /* Silently ignore unknown item */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100916 if (spoe_skip_data(&p, end) == -1) {
917 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
918 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200919 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200920 }
921 }
922
Christopher Faulet8ef75252017-02-20 22:56:03 +0100923 return (p - frame);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200924}
925
926
Christopher Fauleta1cda022016-12-21 08:58:06 +0100927/* Decode ACK frame sent by an agent. It returns the number of read bytes on
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200928 * success, 0 if the frame can be ignored and -1 if an error occurred. */
929static int
Christopher Faulet8ef75252017-02-20 22:56:03 +0100930spoe_handle_agentack_frame(struct appctx *appctx, struct spoe_context **ctx,
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100931 char *frame, size_t size)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200932{
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100933 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100934 char *p, *end;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100935 uint64_t stream_id, frame_id;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100936 int len;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100937 unsigned int flags;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100938
939 p = frame;
940 end = frame + size;
941 *ctx = NULL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200942
943 /* Check frame type */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100944 if (*p++ != SPOE_FRM_T_AGENT_ACK) {
945 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200946 return 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100947 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200948
Christopher Faulet8ef75252017-02-20 22:56:03 +0100949 if (size < 7 /* TYPE + METADATA */) {
950 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
951 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200952 }
953
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100954 /* Retrieve flags */
Christopher Faulet8ef75252017-02-20 22:56:03 +0100955 memcpy((char *)&flags, p, 4);
Thierry FOURNIERc4dcaff2018-05-18 12:25:39 +0200956 flags = ntohl(flags);
Christopher Faulet8ef75252017-02-20 22:56:03 +0100957 p += 4;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200958
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100959 /* Fragmentation is not supported for now */
960 if (!(flags & SPOE_FRM_FL_FIN)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100961 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_FRAG_NOT_SUPPORTED;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100962 return -1;
963 }
964
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200965 /* Get the stream-id and the frame-id */
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200966 if (decode_varint(&p, end, &stream_id) == -1) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100967 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
Christopher Fauleta1cda022016-12-21 08:58:06 +0100968 return 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100969 }
Thierry FOURNIER6ab2bae2017-04-19 11:49:44 +0200970 if (decode_varint(&p, end, &frame_id) == -1) {
Christopher Faulet8ef75252017-02-20 22:56:03 +0100971 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +0200972 return 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +0100973 }
Christopher Fauleta1cda022016-12-21 08:58:06 +0100974
Christopher Faulet8ef75252017-02-20 22:56:03 +0100975 /* Try to find the corresponding SPOE context */
Christopher Faulet42bfa462017-01-04 14:14:19 +0100976 if (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_ASYNC) {
Christopher Faulet24289f22017-09-25 14:48:02 +0200977 list_for_each_entry((*ctx), &agent->rt[tid].waiting_queue, list) {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100978 if ((*ctx)->stream_id == (unsigned int)stream_id &&
979 (*ctx)->frame_id == (unsigned int)frame_id)
Christopher Fauleta1cda022016-12-21 08:58:06 +0100980 goto found;
981 }
982 }
983 else {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +0100984 list_for_each_entry((*ctx), &SPOE_APPCTX(appctx)->waiting_queue, list) {
985 if ((*ctx)->stream_id == (unsigned int)stream_id &&
Christopher Faulet8ef75252017-02-20 22:56:03 +0100986 (*ctx)->frame_id == (unsigned int)frame_id)
Christopher Fauleta1cda022016-12-21 08:58:06 +0100987 goto found;
988 }
989 }
990
Christopher Faulet8eda93f2017-02-09 09:44:33 +0100991 if (SPOE_APPCTX(appctx)->frag_ctx.ctx &&
992 SPOE_APPCTX(appctx)->frag_ctx.cursid == (unsigned int)stream_id &&
993 SPOE_APPCTX(appctx)->frag_ctx.curfid == (unsigned int)frame_id) {
994
995 /* ABRT bit is set for an unfinished fragmented frame */
996 if (flags & SPOE_FRM_FL_ABRT) {
997 *ctx = SPOE_APPCTX(appctx)->frag_ctx.ctx;
Christopher Faulet8eda93f2017-02-09 09:44:33 +0100998 (*ctx)->state = SPOE_CTX_ST_ERROR;
999 (*ctx)->status_code = SPOE_CTX_ERR_FRAG_FRAME_ABRT;
1000 /* Ignore the payload */
1001 goto end;
1002 }
1003 /* TODO: Handle more flags for fragmented frames: RESUME, FINISH... */
1004 /* For now, we ignore the ack */
1005 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_INVALID;
1006 return 0;
1007 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001008
Christopher Fauleta1cda022016-12-21 08:58:06 +01001009 /* No Stream found, ignore the frame */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001010 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1011 " - Ignore ACK frame"
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001012 " - stream-id=%u - frame-id=%u\n",
1013 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1014 __FUNCTION__, appctx,
1015 (unsigned int)stream_id, (unsigned int)frame_id);
1016
Christopher Faulet8eda93f2017-02-09 09:44:33 +01001017 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_FRAMEID_NOTFOUND;
Christopher Faulet3a47e5e2018-05-25 10:42:37 +02001018 if (appctx->st0 == SPOE_APPCTX_ST_WAITING_SYNC_ACK)
1019 return -1;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001020 return 0;
1021
1022 found:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001023 if (!spoe_acquire_buffer(&SPOE_APPCTX(appctx)->buffer,
1024 &SPOE_APPCTX(appctx)->buffer_wait)) {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001025 *ctx = NULL;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001026 return 1; /* Retry later */
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001027 }
Christopher Faulet4596fb72017-01-11 14:05:19 +01001028
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001029 /* Copy encoded actions */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001030 len = (end - p);
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001031 memcpy(b_head(&SPOE_APPCTX(appctx)->buffer), p, len);
1032 b_set_data(&SPOE_APPCTX(appctx)->buffer, len);
Christopher Faulet8ef75252017-02-20 22:56:03 +01001033 p += len;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001034
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001035 /* Transfer the buffer ownership to the SPOE context */
1036 (*ctx)->buffer = SPOE_APPCTX(appctx)->buffer;
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001037 SPOE_APPCTX(appctx)->buffer = BUF_NULL;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001038
Christopher Faulet8ef75252017-02-20 22:56:03 +01001039 (*ctx)->state = SPOE_CTX_ST_DONE;
1040
Christopher Faulet8eda93f2017-02-09 09:44:33 +01001041 end:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001042 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
Christopher Faulet8ef75252017-02-20 22:56:03 +01001043 " - ACK frame received"
1044 " - ctx=%p - stream-id=%u - frame-id=%u - flags=0x%08x\n",
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001045 (int)now.tv_sec, (int)now.tv_usec, agent->id,
Christopher Faulet8ef75252017-02-20 22:56:03 +01001046 __FUNCTION__, appctx, *ctx, (*ctx)->stream_id,
1047 (*ctx)->frame_id, flags);
1048 return (p - frame);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001049}
1050
Christopher Fauletba7bc162016-11-07 21:07:38 +01001051/* This function is used in cfgparse.c and declared in proto/checks.h. It
1052 * prepare the request to send to agents during a healthcheck. It returns 0 on
1053 * success and -1 if an error occurred. */
1054int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001055spoe_prepare_healthcheck_request(char **req, int *len)
Christopher Fauletba7bc162016-11-07 21:07:38 +01001056{
Christopher Faulet8ef75252017-02-20 22:56:03 +01001057 struct appctx appctx;
1058 struct spoe_appctx spoe_appctx;
1059 char *frame, *end, buf[MAX_FRAME_SIZE+4];
1060 size_t sz;
1061 int ret;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001062
Christopher Faulet42bfa462017-01-04 14:14:19 +01001063 memset(&appctx, 0, sizeof(appctx));
1064 memset(&spoe_appctx, 0, sizeof(spoe_appctx));
Christopher Fauletba7bc162016-11-07 21:07:38 +01001065 memset(buf, 0, sizeof(buf));
Christopher Faulet42bfa462017-01-04 14:14:19 +01001066
1067 appctx.ctx.spoe.ptr = &spoe_appctx;
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001068 SPOE_APPCTX(&appctx)->max_frame_size = MAX_FRAME_SIZE;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001069
Christopher Faulet8ef75252017-02-20 22:56:03 +01001070 frame = buf+4; /* Reserved the 4 first bytes for the frame size */
1071 end = frame + MAX_FRAME_SIZE;
1072
1073 ret = spoe_prepare_hahello_frame(&appctx, frame, MAX_FRAME_SIZE);
1074 if (ret <= 0)
Christopher Fauletba7bc162016-11-07 21:07:38 +01001075 return -1;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001076 frame += ret;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001077
Christopher Faulet8ef75252017-02-20 22:56:03 +01001078 /* Add "healthcheck" K/V item */
1079 sz = SLEN(HEALTHCHECK_KEY);
1080 if (spoe_encode_buffer(HEALTHCHECK_KEY, sz, &frame, end) == -1)
1081 return -1;
1082 *frame++ = (SPOE_DATA_T_BOOL | SPOE_DATA_FL_TRUE);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001083
Christopher Faulet8ef75252017-02-20 22:56:03 +01001084 *len = frame - buf;
1085 sz = htonl(*len - 4);
1086 memcpy(buf, (char *)&sz, 4);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001087
Christopher Faulet8ef75252017-02-20 22:56:03 +01001088 if ((*req = malloc(*len)) == NULL)
Christopher Fauletba7bc162016-11-07 21:07:38 +01001089 return -1;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001090 memcpy(*req, buf, *len);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001091 return 0;
1092}
1093
1094/* This function is used in checks.c and declared in proto/checks.h. It decode
1095 * the response received from an agent during a healthcheck. It returns 0 on
1096 * success and -1 if an error occurred. */
1097int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001098spoe_handle_healthcheck_response(char *frame, size_t size, char *err, int errlen)
Christopher Fauletba7bc162016-11-07 21:07:38 +01001099{
Christopher Faulet42bfa462017-01-04 14:14:19 +01001100 struct appctx appctx;
1101 struct spoe_appctx spoe_appctx;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001102
Christopher Faulet42bfa462017-01-04 14:14:19 +01001103 memset(&appctx, 0, sizeof(appctx));
1104 memset(&spoe_appctx, 0, sizeof(spoe_appctx));
Christopher Fauletba7bc162016-11-07 21:07:38 +01001105
Christopher Faulet42bfa462017-01-04 14:14:19 +01001106 appctx.ctx.spoe.ptr = &spoe_appctx;
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001107 SPOE_APPCTX(&appctx)->max_frame_size = MAX_FRAME_SIZE;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001108
Christopher Faulet8ef75252017-02-20 22:56:03 +01001109 if (*frame == SPOE_FRM_T_AGENT_DISCON) {
1110 spoe_handle_agentdiscon_frame(&appctx, frame, size);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001111 goto error;
1112 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001113 if (spoe_handle_agenthello_frame(&appctx, frame, size) <= 0)
1114 goto error;
Christopher Fauletba7bc162016-11-07 21:07:38 +01001115
1116 return 0;
1117
1118 error:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001119 if (SPOE_APPCTX(&appctx)->status_code >= SPOE_FRM_ERRS)
1120 SPOE_APPCTX(&appctx)->status_code = SPOE_FRM_ERR_UNKNOWN;
1121 strncpy(err, spoe_frm_err_reasons[SPOE_APPCTX(&appctx)->status_code], errlen);
Christopher Fauletba7bc162016-11-07 21:07:38 +01001122 return -1;
1123}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001124
Christopher Fauleta1cda022016-12-21 08:58:06 +01001125/* Send a SPOE frame to an agent. It returns -1 when an error occurred, 0 when
Ilya Shipitsin6fb0f212020-04-02 15:25:26 +05001126 * the frame can be ignored, 1 to retry later, and the frame length on
Christopher Fauleta1cda022016-12-21 08:58:06 +01001127 * success. */
1128static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001129spoe_send_frame(struct appctx *appctx, char *buf, size_t framesz)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001130{
1131 struct stream_interface *si = appctx->owner;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001132 int ret;
1133 uint32_t netint;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001134
Christopher Faulet8ef75252017-02-20 22:56:03 +01001135 /* 4 bytes are reserved at the beginning of <buf> to store the frame
1136 * length. */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001137 netint = htonl(framesz);
1138 memcpy(buf, (char *)&netint, 4);
Willy Tarreau06d80a92017-10-19 14:32:15 +02001139 ret = ci_putblk(si_ic(si), buf, framesz+4);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001140 if (ret <= 0) {
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001141 if ((ret == -3 && b_is_null(&si_ic(si)->buf)) || ret == -1) {
Willy Tarreaudb398432018-11-15 11:08:52 +01001142 si_rx_room_blk(si);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001143 return 1; /* retry */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001144 }
1145 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001146 return -1; /* error */
1147 }
1148 return framesz;
1149}
1150
1151/* Receive a SPOE frame from an agent. It return -1 when an error occurred, 0
1152 * when the frame can be ignored, 1 to retry later and the frame length on
1153 * success. */
1154static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001155spoe_recv_frame(struct appctx *appctx, char *buf, size_t framesz)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001156{
1157 struct stream_interface *si = appctx->owner;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001158 int ret;
1159 uint32_t netint;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001160
Willy Tarreau06d80a92017-10-19 14:32:15 +02001161 ret = co_getblk(si_oc(si), (char *)&netint, 4, 0);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001162 if (ret > 0) {
1163 framesz = ntohl(netint);
Christopher Faulet42bfa462017-01-04 14:14:19 +01001164 if (framesz > SPOE_APPCTX(appctx)->max_frame_size) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001165 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOO_BIG;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001166 return -1;
1167 }
Willy Tarreau06d80a92017-10-19 14:32:15 +02001168 ret = co_getblk(si_oc(si), buf, framesz, 4);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001169 }
1170 if (ret <= 0) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001171 if (ret == 0) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001172 return 1; /* retry */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001173 }
1174 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001175 return -1; /* error */
1176 }
1177 return framesz;
1178}
1179
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001180/********************************************************************
1181 * Functions that manage the SPOE applet
1182 ********************************************************************/
Christopher Faulet4596fb72017-01-11 14:05:19 +01001183static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001184spoe_wakeup_appctx(struct appctx *appctx)
Christopher Faulet4596fb72017-01-11 14:05:19 +01001185{
Willy Tarreau0cd3bd62018-11-06 18:46:37 +01001186 si_want_get(appctx->owner);
Willy Tarreau8bb2ffb2018-11-14 17:54:13 +01001187 si_rx_endp_more(appctx->owner);
Christopher Faulet4596fb72017-01-11 14:05:19 +01001188 appctx_wakeup(appctx);
1189 return 1;
1190}
1191
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001192/* Callback function that catches applet timeouts. If a timeout occurred, we set
1193 * <appctx->st1> flag and the SPOE applet is woken up. */
1194static struct task *
Olivier Houchard9f6af332018-05-25 14:04:04 +02001195spoe_process_appctx(struct task * task, void *context, unsigned short state)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001196{
Olivier Houchard9f6af332018-05-25 14:04:04 +02001197 struct appctx *appctx = context;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001198
1199 appctx->st1 = SPOE_APPCTX_ERR_NONE;
1200 if (tick_is_expired(task->expire, now_ms)) {
1201 task->expire = TICK_ETERNITY;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001202 appctx->st1 = SPOE_APPCTX_ERR_TOUT;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001203 }
Christopher Faulet8ef75252017-02-20 22:56:03 +01001204 spoe_wakeup_appctx(appctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001205 return task;
1206}
1207
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001208/* Callback function that releases a SPOE applet. This happens when the
1209 * connection with the agent is closed. */
1210static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01001211spoe_release_appctx(struct appctx *appctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001212{
Christopher Faulet8ef75252017-02-20 22:56:03 +01001213 struct stream_interface *si = appctx->owner;
1214 struct spoe_appctx *spoe_appctx = SPOE_APPCTX(appctx);
1215 struct spoe_agent *agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001216 struct spoe_context *ctx, *back;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001217
1218 if (spoe_appctx == NULL)
1219 return;
1220
1221 appctx->ctx.spoe.ptr = NULL;
1222 agent = spoe_appctx->agent;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001223
1224 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p\n",
1225 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1226 __FUNCTION__, appctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001227
Christopher Faulet8ef75252017-02-20 22:56:03 +01001228 /* Remove applet from the list of running applets */
Olivier Houchard9e7ae282019-03-08 18:50:42 +01001229 _HA_ATOMIC_SUB(&agent->counters.applets, 1);
Christopher Fauletb077cdc2018-01-24 16:37:57 +01001230 HA_SPIN_LOCK(SPOE_APPLET_LOCK, &agent->rt[tid].lock);
Christopher Faulet8ef75252017-02-20 22:56:03 +01001231 if (!LIST_ISEMPTY(&spoe_appctx->list)) {
1232 LIST_DEL(&spoe_appctx->list);
1233 LIST_INIT(&spoe_appctx->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001234 }
Christopher Fauletb077cdc2018-01-24 16:37:57 +01001235 HA_SPIN_UNLOCK(SPOE_APPLET_LOCK, &agent->rt[tid].lock);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001236
Christopher Faulet8ef75252017-02-20 22:56:03 +01001237 /* Shutdown the server connection, if needed */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001238 if (appctx->st0 != SPOE_APPCTX_ST_END) {
Christopher Fauletb077cdc2018-01-24 16:37:57 +01001239 if (appctx->st0 == SPOE_APPCTX_ST_IDLE) {
1240 eb32_delete(&spoe_appctx->node);
Olivier Houchard9e7ae282019-03-08 18:50:42 +01001241 _HA_ATOMIC_SUB(&agent->counters.idles, 1);
Christopher Fauletb077cdc2018-01-24 16:37:57 +01001242 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001243
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001244 appctx->st0 = SPOE_APPCTX_ST_END;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001245 if (spoe_appctx->status_code == SPOE_FRM_ERR_NONE)
1246 spoe_appctx->status_code = SPOE_FRM_ERR_IO;
Christopher Fauletf032c3e2017-02-17 15:18:35 +01001247
1248 si_shutw(si);
1249 si_shutr(si);
1250 si_ic(si)->flags |= CF_READ_NULL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001251 }
1252
Christopher Faulet8ef75252017-02-20 22:56:03 +01001253 /* Destroy the task attached to this applet */
Willy Tarreauf6562792019-05-07 19:05:35 +02001254 task_destroy(spoe_appctx->task);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001255
Christopher Faulet8ef75252017-02-20 22:56:03 +01001256 /* Notify all waiting streams */
1257 list_for_each_entry_safe(ctx, back, &spoe_appctx->waiting_queue, list) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001258 LIST_DEL(&ctx->list);
1259 LIST_INIT(&ctx->list);
Olivier Houchard9e7ae282019-03-08 18:50:42 +01001260 _HA_ATOMIC_SUB(&agent->counters.nb_waiting, 1);
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01001261 spoe_update_stat_time(&ctx->stats.tv_wait, &ctx->stats.t_waiting);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001262 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001263 ctx->status_code = (spoe_appctx->status_code + 0x100);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001264 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001265 }
1266
Christopher Faulet8ef75252017-02-20 22:56:03 +01001267 /* If the applet was processing a fragmented frame, notify the
1268 * corresponding stream. */
1269 if (spoe_appctx->frag_ctx.ctx) {
1270 ctx = spoe_appctx->frag_ctx.ctx;
Christopher Fauletfce747b2018-01-24 15:59:32 +01001271 ctx->spoe_appctx = NULL;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001272 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001273 ctx->status_code = (spoe_appctx->status_code + 0x100);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001274 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1275 }
1276
Christopher Faulet24289f22017-09-25 14:48:02 +02001277 if (!LIST_ISEMPTY(&agent->rt[tid].applets))
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001278 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001279
Christopher Faulet8ef75252017-02-20 22:56:03 +01001280 /* If this was the last running applet, notify all waiting streams */
Christopher Faulet24289f22017-09-25 14:48:02 +02001281 list_for_each_entry_safe(ctx, back, &agent->rt[tid].sending_queue, list) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001282 LIST_DEL(&ctx->list);
1283 LIST_INIT(&ctx->list);
Olivier Houchard9e7ae282019-03-08 18:50:42 +01001284 _HA_ATOMIC_SUB(&agent->counters.nb_sending, 1);
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01001285 spoe_update_stat_time(&ctx->stats.tv_queue, &ctx->stats.t_queue);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001286 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001287 ctx->status_code = (spoe_appctx->status_code + 0x100);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001288 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001289 }
Christopher Faulet24289f22017-09-25 14:48:02 +02001290 list_for_each_entry_safe(ctx, back, &agent->rt[tid].waiting_queue, list) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01001291 LIST_DEL(&ctx->list);
1292 LIST_INIT(&ctx->list);
Olivier Houchard9e7ae282019-03-08 18:50:42 +01001293 _HA_ATOMIC_SUB(&agent->counters.nb_waiting, 1);
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01001294 spoe_update_stat_time(&ctx->stats.tv_wait, &ctx->stats.t_waiting);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001295 ctx->state = SPOE_CTX_ST_ERROR;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001296 ctx->status_code = (spoe_appctx->status_code + 0x100);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001297 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1298 }
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001299
1300 end:
Christopher Faulet55ae8a62019-05-23 22:47:48 +02001301 /* Release allocated memory */
1302 spoe_release_buffer(&spoe_appctx->buffer,
1303 &spoe_appctx->buffer_wait);
1304 pool_free(pool_head_spoe_appctx, spoe_appctx);
1305
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001306 /* Update runtinme agent info */
Christopher Faulet24289f22017-09-25 14:48:02 +02001307 agent->rt[tid].frame_size = agent->max_frame_size;
1308 list_for_each_entry(spoe_appctx, &agent->rt[tid].applets, list)
1309 HA_ATOMIC_UPDATE_MIN(&agent->rt[tid].frame_size, spoe_appctx->max_frame_size);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001310}
1311
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001312static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001313spoe_handle_connect_appctx(struct appctx *appctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001314{
Christopher Fauleta1cda022016-12-21 08:58:06 +01001315 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001316 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001317 char *frame, *buf;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001318 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001319
Willy Tarreau7ab22adb2019-06-05 14:53:22 +02001320 if (si_state_in(si->state, SI_SB_CER|SI_SB_DIS|SI_SB_CLO)) {
1321 /* closed */
1322 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
1323 goto exit;
1324 }
1325
Willy Tarreau4f283fa2019-06-05 14:34:03 +02001326 if (!si_state_in(si->state, SI_SB_RDY|SI_SB_EST)) {
Willy Tarreau7ab22adb2019-06-05 14:53:22 +02001327 /* not connected yet */
Willy Tarreau8bb2ffb2018-11-14 17:54:13 +01001328 si_rx_endp_more(si);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001329 task_wakeup(si_strm(si)->task, TASK_WOKEN_MSG);
1330 goto stop;
1331 }
Christopher Fauleta73e59b2016-12-09 17:30:18 +01001332
Christopher Fauleta1cda022016-12-21 08:58:06 +01001333 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001334 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1335 " - Connection timed out\n",
1336 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1337 __FUNCTION__, appctx);
1338 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOUT;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001339 goto exit;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001340 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001341
Christopher Faulet42bfa462017-01-04 14:14:19 +01001342 if (SPOE_APPCTX(appctx)->task->expire == TICK_ETERNITY)
Christopher Faulet8ef75252017-02-20 22:56:03 +01001343 SPOE_APPCTX(appctx)->task->expire =
1344 tick_add_ifset(now_ms, agent->timeout.hello);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001345
Christopher Faulet8ef75252017-02-20 22:56:03 +01001346 /* 4 bytes are reserved at the beginning of <buf> to store the frame
1347 * length. */
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001348 buf = trash.area; frame = buf+4;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001349 ret = spoe_prepare_hahello_frame(appctx, frame,
1350 SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001351 if (ret > 1)
Christopher Faulet8ef75252017-02-20 22:56:03 +01001352 ret = spoe_send_frame(appctx, buf, ret);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001353
1354 switch (ret) {
1355 case -1: /* error */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001356 case 0: /* ignore => an error, cannot be ignored */
1357 goto exit;
1358
1359 case 1: /* retry later */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001360 goto stop;
1361
Christopher Faulet8ef75252017-02-20 22:56:03 +01001362 default:
1363 /* HELLO frame successfully sent, now wait for the
1364 * reply. */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001365 appctx->st0 = SPOE_APPCTX_ST_CONNECTING;
1366 goto next;
1367 }
1368
1369 next:
1370 return 0;
1371 stop:
1372 return 1;
1373 exit:
1374 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1375 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001376}
1377
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001378static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001379spoe_handle_connecting_appctx(struct appctx *appctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001380{
Christopher Fauleta1cda022016-12-21 08:58:06 +01001381 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001382 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001383 char *frame;
1384 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001385
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001386
Christopher Fauletb067b062017-01-04 16:39:11 +01001387 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001388 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001389 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001390 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001391
Christopher Fauleta1cda022016-12-21 08:58:06 +01001392 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001393 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1394 " - Connection timed out\n",
1395 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1396 __FUNCTION__, appctx);
1397 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOUT;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001398 goto exit;
1399 }
1400
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001401 frame = trash.area; trash.data = 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001402 ret = spoe_recv_frame(appctx, frame,
1403 SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001404 if (ret > 1) {
1405 if (*frame == SPOE_FRM_T_AGENT_DISCON) {
1406 appctx->st0 = SPOE_APPCTX_ST_DISCONNECTING;
1407 goto next;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001408 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001409 trash.data = ret + 4;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001410 ret = spoe_handle_agenthello_frame(appctx, frame, ret);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001411 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001412
Christopher Fauleta1cda022016-12-21 08:58:06 +01001413 switch (ret) {
1414 case -1: /* error */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001415 case 0: /* ignore => an error, cannot be ignored */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001416 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1417 goto next;
1418
1419 case 1: /* retry later */
1420 goto stop;
1421
1422 default:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001423 /* HELLO handshake is finished, set the idle timeout and
1424 * add the applet in the list of running applets. */
Olivier Houchard9e7ae282019-03-08 18:50:42 +01001425 _HA_ATOMIC_ADD(&agent->counters.idles, 1);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001426 appctx->st0 = SPOE_APPCTX_ST_IDLE;
Christopher Fauletb077cdc2018-01-24 16:37:57 +01001427 SPOE_APPCTX(appctx)->node.key = 0;
1428 eb32_insert(&agent->rt[tid].idle_applets, &SPOE_APPCTX(appctx)->node);
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01001429
1430 /* Update runtinme agent info */
Christopher Faulet24289f22017-09-25 14:48:02 +02001431 HA_ATOMIC_UPDATE_MIN(&agent->rt[tid].frame_size, SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001432 goto next;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001433 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001434
Christopher Fauleta1cda022016-12-21 08:58:06 +01001435 next:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001436 /* Do not forget to remove processed frame from the output buffer */
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001437 if (trash.data)
1438 co_skip(si_oc(si), trash.data);
Christopher Faulet8ef75252017-02-20 22:56:03 +01001439
1440 SPOE_APPCTX(appctx)->task->expire =
1441 tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001442 return 0;
1443 stop:
1444 return 1;
1445 exit:
1446 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1447 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001448}
1449
Christopher Fauletf032c3e2017-02-17 15:18:35 +01001450
Christopher Fauleta1cda022016-12-21 08:58:06 +01001451static int
Christopher Fauletf032c3e2017-02-17 15:18:35 +01001452spoe_handle_sending_frame_appctx(struct appctx *appctx, int *skip)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001453{
Christopher Fauletf032c3e2017-02-17 15:18:35 +01001454 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
1455 struct spoe_context *ctx = NULL;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001456 char *frame, *buf;
1457 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001458
Christopher Faulet8ef75252017-02-20 22:56:03 +01001459 /* 4 bytes are reserved at the beginning of <buf> to store the frame
1460 * length. */
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001461 buf = trash.area; frame = buf+4;
Christopher Fauletf032c3e2017-02-17 15:18:35 +01001462
1463 if (appctx->st0 == SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY) {
1464 ctx = SPOE_APPCTX(appctx)->frag_ctx.ctx;
1465 ret = spoe_prepare_hafrag_frame(appctx, ctx, frame,
1466 SPOE_APPCTX(appctx)->max_frame_size);
1467 }
Christopher Faulet24289f22017-09-25 14:48:02 +02001468 else if (LIST_ISEMPTY(&agent->rt[tid].sending_queue)) {
Christopher Fauletf032c3e2017-02-17 15:18:35 +01001469 *skip = 1;
1470 ret = 1;
1471 goto end;
1472 }
1473 else {
Christopher Faulet24289f22017-09-25 14:48:02 +02001474 ctx = LIST_NEXT(&agent->rt[tid].sending_queue, typeof(ctx), list);
Christopher Fauletf032c3e2017-02-17 15:18:35 +01001475 ret = spoe_prepare_hanotify_frame(appctx, ctx, frame,
1476 SPOE_APPCTX(appctx)->max_frame_size);
1477
1478 }
1479
Christopher Faulet8ef75252017-02-20 22:56:03 +01001480 if (ret > 1)
1481 ret = spoe_send_frame(appctx, buf, ret);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001482
Christopher Faulet8ef75252017-02-20 22:56:03 +01001483 switch (ret) {
1484 case -1: /* error */
1485 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1486 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001487
Christopher Faulet8ef75252017-02-20 22:56:03 +01001488 case 0: /* ignore */
1489 if (ctx == NULL)
1490 goto abort_frag_frame;
1491
Christopher Fauletf032c3e2017-02-17 15:18:35 +01001492 spoe_release_buffer(&ctx->buffer, &ctx->buffer_wait);
Christopher Faulet8ef75252017-02-20 22:56:03 +01001493 LIST_DEL(&ctx->list);
1494 LIST_INIT(&ctx->list);
Olivier Houchard9e7ae282019-03-08 18:50:42 +01001495 _HA_ATOMIC_SUB(&agent->counters.nb_sending, 1);
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01001496 spoe_update_stat_time(&ctx->stats.tv_queue, &ctx->stats.t_queue);
Christopher Fauletfce747b2018-01-24 15:59:32 +01001497 ctx->spoe_appctx = NULL;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001498 ctx->state = SPOE_CTX_ST_ERROR;
1499 ctx->status_code = (SPOE_APPCTX(appctx)->status_code + 0x100);
1500 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
Christopher Fauletb077cdc2018-01-24 16:37:57 +01001501 *skip = 1;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001502 break;
1503
1504 case 1: /* retry */
1505 *skip = 1;
1506 break;
1507
1508 default:
1509 if (ctx == NULL)
1510 goto abort_frag_frame;
1511
Christopher Fauletf032c3e2017-02-17 15:18:35 +01001512 spoe_release_buffer(&ctx->buffer, &ctx->buffer_wait);
Christopher Faulet8ef75252017-02-20 22:56:03 +01001513 LIST_DEL(&ctx->list);
1514 LIST_INIT(&ctx->list);
Olivier Houchard9e7ae282019-03-08 18:50:42 +01001515 _HA_ATOMIC_SUB(&agent->counters.nb_sending, 1);
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01001516 spoe_update_stat_time(&ctx->stats.tv_queue, &ctx->stats.t_queue);
Christopher Fauletfce747b2018-01-24 15:59:32 +01001517 ctx->spoe_appctx = SPOE_APPCTX(appctx);
Christopher Faulet8ef75252017-02-20 22:56:03 +01001518 if (!(ctx->flags & SPOE_CTX_FL_FRAGMENTED) ||
1519 (ctx->frag_ctx.flags & SPOE_FRM_FL_FIN))
1520 goto no_frag_frame_sent;
Christopher Fauletb077cdc2018-01-24 16:37:57 +01001521 else
Christopher Faulet8ef75252017-02-20 22:56:03 +01001522 goto frag_frame_sent;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001523 }
1524 goto end;
1525
1526 frag_frame_sent:
1527 appctx->st0 = SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY;
Christopher Fauletb077cdc2018-01-24 16:37:57 +01001528 *skip = 1;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001529 SPOE_APPCTX(appctx)->frag_ctx.ctx = ctx;
1530 SPOE_APPCTX(appctx)->frag_ctx.cursid = ctx->stream_id;
1531 SPOE_APPCTX(appctx)->frag_ctx.curfid = ctx->frame_id;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001532 ctx->state = SPOE_CTX_ST_ENCODING_MSGS;
1533 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1534 goto end;
1535
1536 no_frag_frame_sent:
1537 if (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_ASYNC) {
1538 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
Christopher Faulet24289f22017-09-25 14:48:02 +02001539 LIST_ADDQ(&agent->rt[tid].waiting_queue, &ctx->list);
Christopher Faulet8ef75252017-02-20 22:56:03 +01001540 }
1541 else if (SPOE_APPCTX(appctx)->flags & SPOE_APPCTX_FL_PIPELINING) {
1542 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1543 LIST_ADDQ(&SPOE_APPCTX(appctx)->waiting_queue, &ctx->list);
1544 }
1545 else {
1546 appctx->st0 = SPOE_APPCTX_ST_WAITING_SYNC_ACK;
Christopher Fauletb077cdc2018-01-24 16:37:57 +01001547 *skip = 1;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001548 LIST_ADDQ(&SPOE_APPCTX(appctx)->waiting_queue, &ctx->list);
1549 }
Olivier Houchard9e7ae282019-03-08 18:50:42 +01001550 _HA_ATOMIC_ADD(&agent->counters.nb_waiting, 1);
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01001551 ctx->stats.tv_wait = now;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001552 SPOE_APPCTX(appctx)->frag_ctx.ctx = NULL;
1553 SPOE_APPCTX(appctx)->frag_ctx.cursid = 0;
1554 SPOE_APPCTX(appctx)->frag_ctx.curfid = 0;
Christopher Faulet8f82b202018-01-24 16:23:03 +01001555 SPOE_APPCTX(appctx)->cur_fpa++;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001556
Christopher Faulet8ef75252017-02-20 22:56:03 +01001557 ctx->state = SPOE_CTX_ST_WAITING_ACK;
1558 goto end;
1559
1560 abort_frag_frame:
1561 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1562 SPOE_APPCTX(appctx)->frag_ctx.ctx = NULL;
1563 SPOE_APPCTX(appctx)->frag_ctx.cursid = 0;
1564 SPOE_APPCTX(appctx)->frag_ctx.curfid = 0;
1565 goto end;
1566
1567 end:
1568 return ret;
1569}
1570
1571static int
1572spoe_handle_receiving_frame_appctx(struct appctx *appctx, int *skip)
1573{
Christopher Fauletcaf2fec2018-04-04 10:25:50 +02001574 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001575 struct spoe_context *ctx = NULL;
1576 char *frame;
1577 int ret;
1578
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001579 frame = trash.area; trash.data = 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001580 ret = spoe_recv_frame(appctx, frame,
1581 SPOE_APPCTX(appctx)->max_frame_size);
1582 if (ret > 1) {
1583 if (*frame == SPOE_FRM_T_AGENT_DISCON) {
1584 appctx->st0 = SPOE_APPCTX_ST_DISCONNECTING;
Christopher Fauletb077cdc2018-01-24 16:37:57 +01001585 ret = -1;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001586 goto end;
1587 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001588 trash.data = ret + 4;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001589 ret = spoe_handle_agentack_frame(appctx, &ctx, frame, ret);
1590 }
1591 switch (ret) {
1592 case -1: /* error */
1593 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1594 break;
1595
1596 case 0: /* ignore */
1597 break;
1598
1599 case 1: /* retry */
1600 *skip = 1;
1601 break;
1602
1603 default:
1604 LIST_DEL(&ctx->list);
1605 LIST_INIT(&ctx->list);
Olivier Houchard9e7ae282019-03-08 18:50:42 +01001606 _HA_ATOMIC_SUB(&agent->counters.nb_waiting, 1);
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01001607 spoe_update_stat_time(&ctx->stats.tv_wait, &ctx->stats.t_waiting);
1608 ctx->stats.tv_response = now;
Christopher Faulet8f82b202018-01-24 16:23:03 +01001609 if (ctx->spoe_appctx) {
1610 ctx->spoe_appctx->cur_fpa--;
Christopher Fauletfce747b2018-01-24 15:59:32 +01001611 ctx->spoe_appctx = NULL;
Christopher Faulet8f82b202018-01-24 16:23:03 +01001612 }
Christopher Faulet8eda93f2017-02-09 09:44:33 +01001613 if (appctx->st0 == SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY &&
1614 ctx == SPOE_APPCTX(appctx)->frag_ctx.ctx) {
1615 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1616 SPOE_APPCTX(appctx)->frag_ctx.ctx = NULL;
1617 SPOE_APPCTX(appctx)->frag_ctx.cursid = 0;
1618 SPOE_APPCTX(appctx)->frag_ctx.curfid = 0;
1619 }
1620 else if (appctx->st0 == SPOE_APPCTX_ST_WAITING_SYNC_ACK)
1621 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001622 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
1623 break;
1624 }
1625
1626 /* Do not forget to remove processed frame from the output buffer */
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001627 if (trash.data)
1628 co_skip(si_oc(appctx->owner), trash.data);
Christopher Faulet8ef75252017-02-20 22:56:03 +01001629 end:
1630 return ret;
1631}
1632
1633static int
1634spoe_handle_processing_appctx(struct appctx *appctx)
1635{
1636 struct stream_interface *si = appctx->owner;
1637 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8f82b202018-01-24 16:23:03 +01001638 int ret, skip_sending = 0, skip_receiving = 0, active_s = 0, active_r = 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001639
1640 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
1641 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
1642 goto exit;
1643 }
1644
1645 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
1646 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOUT;
1647 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
1648 appctx->st1 = SPOE_APPCTX_ERR_NONE;
1649 goto next;
1650 }
1651
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001652 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
Christopher Fauletb077cdc2018-01-24 16:37:57 +01001653 " - process: fpa=%u/%u - appctx-state=%s - weight=%u - flags=0x%08x\n",
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001654 (int)now.tv_sec, (int)now.tv_usec, agent->id,
Christopher Faulet8f82b202018-01-24 16:23:03 +01001655 __FUNCTION__, appctx, SPOE_APPCTX(appctx)->cur_fpa,
1656 agent->max_fpa, spoe_appctx_state_str[appctx->st0],
Christopher Fauletb077cdc2018-01-24 16:37:57 +01001657 SPOE_APPCTX(appctx)->node.key, SPOE_APPCTX(appctx)->flags);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001658
Christopher Faulet8f82b202018-01-24 16:23:03 +01001659 if (appctx->st0 == SPOE_APPCTX_ST_WAITING_SYNC_ACK)
1660 skip_sending = 1;
Christopher Faulet4596fb72017-01-11 14:05:19 +01001661
Christopher Faulet8f82b202018-01-24 16:23:03 +01001662 /* receiving_frame loop */
1663 while (!skip_receiving) {
1664 ret = spoe_handle_receiving_frame_appctx(appctx, &skip_receiving);
1665 switch (ret) {
1666 case -1: /* error */
1667 goto next;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001668
Christopher Faulet8f82b202018-01-24 16:23:03 +01001669 case 0: /* ignore */
1670 active_r = 1;
1671 break;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001672
Christopher Faulet8f82b202018-01-24 16:23:03 +01001673 case 1: /* retry */
1674 break;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001675
Christopher Faulet8f82b202018-01-24 16:23:03 +01001676 default:
1677 active_r = 1;
1678 break;
1679 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001680 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001681
Christopher Faulet8f82b202018-01-24 16:23:03 +01001682 /* send_frame loop */
1683 while (!skip_sending && SPOE_APPCTX(appctx)->cur_fpa < agent->max_fpa) {
1684 ret = spoe_handle_sending_frame_appctx(appctx, &skip_sending);
1685 switch (ret) {
1686 case -1: /* error */
1687 goto next;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001688
Christopher Faulet8f82b202018-01-24 16:23:03 +01001689 case 0: /* ignore */
Christopher Fauletb077cdc2018-01-24 16:37:57 +01001690 if (SPOE_APPCTX(appctx)->node.key)
1691 SPOE_APPCTX(appctx)->node.key--;
Christopher Faulet8f82b202018-01-24 16:23:03 +01001692 active_s++;
1693 break;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001694
Christopher Faulet8f82b202018-01-24 16:23:03 +01001695 case 1: /* retry */
1696 break;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001697
Christopher Faulet8f82b202018-01-24 16:23:03 +01001698 default:
Christopher Fauletb077cdc2018-01-24 16:37:57 +01001699 if (SPOE_APPCTX(appctx)->node.key)
1700 SPOE_APPCTX(appctx)->node.key--;
Christopher Faulet8f82b202018-01-24 16:23:03 +01001701 active_s++;
1702 break;
1703 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001704 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001705
Christopher Faulet8f82b202018-01-24 16:23:03 +01001706 if (active_s || active_r) {
Christopher Faulet8f82b202018-01-24 16:23:03 +01001707 update_freq_ctr(&agent->rt[tid].processing_per_sec, active_s);
1708 SPOE_APPCTX(appctx)->task->expire = tick_add_ifset(now_ms, agent->timeout.idle);
1709 }
Christopher Fauletb077cdc2018-01-24 16:37:57 +01001710
Christopher Faulet8f82b202018-01-24 16:23:03 +01001711 if (appctx->st0 == SPOE_APPCTX_ST_PROCESSING && SPOE_APPCTX(appctx)->cur_fpa < agent->max_fpa) {
Olivier Houchard9e7ae282019-03-08 18:50:42 +01001712 _HA_ATOMIC_ADD(&agent->counters.idles, 1);
Christopher Faulet8f82b202018-01-24 16:23:03 +01001713 appctx->st0 = SPOE_APPCTX_ST_IDLE;
Christopher Fauletb077cdc2018-01-24 16:37:57 +01001714 eb32_insert(&agent->rt[tid].idle_applets, &SPOE_APPCTX(appctx)->node);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001715 }
1716 return 1;
1717
Christopher Faulet8f82b202018-01-24 16:23:03 +01001718 next:
1719 SPOE_APPCTX(appctx)->task->expire = tick_add_ifset(now_ms, agent->timeout.idle);
1720 return 0;
1721
Christopher Fauleta1cda022016-12-21 08:58:06 +01001722 exit:
1723 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1724 return 0;
1725}
1726
1727static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001728spoe_handle_disconnect_appctx(struct appctx *appctx)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001729{
1730 struct stream_interface *si = appctx->owner;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001731 struct spoe_agent *agent = SPOE_APPCTX(appctx)->agent;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001732 char *frame, *buf;
1733 int ret;
Christopher Fauletb067b062017-01-04 16:39:11 +01001734
Christopher Fauleta1cda022016-12-21 08:58:06 +01001735 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO)
1736 goto exit;
1737
1738 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT)
1739 goto exit;
1740
Christopher Faulet8ef75252017-02-20 22:56:03 +01001741 /* 4 bytes are reserved at the beginning of <buf> to store the frame
1742 * length. */
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001743 buf = trash.area; frame = buf+4;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001744 ret = spoe_prepare_hadiscon_frame(appctx, frame,
1745 SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001746 if (ret > 1)
Christopher Faulet8ef75252017-02-20 22:56:03 +01001747 ret = spoe_send_frame(appctx, buf, ret);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001748
1749 switch (ret) {
1750 case -1: /* error */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001751 case 0: /* ignore => an error, cannot be ignored */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001752 goto exit;
1753
1754 case 1: /* retry */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001755 goto stop;
1756
1757 default:
1758 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1759 " - disconnected by HAProxy (%d): %s\n",
1760 (int)now.tv_sec, (int)now.tv_usec, agent->id,
Christopher Faulet8ef75252017-02-20 22:56:03 +01001761 __FUNCTION__, appctx,
1762 SPOE_APPCTX(appctx)->status_code,
1763 spoe_frm_err_reasons[SPOE_APPCTX(appctx)->status_code]);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001764
1765 appctx->st0 = SPOE_APPCTX_ST_DISCONNECTING;
1766 goto next;
1767 }
1768
1769 next:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001770 SPOE_APPCTX(appctx)->task->expire =
1771 tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001772 return 0;
1773 stop:
1774 return 1;
1775 exit:
1776 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1777 return 0;
1778}
1779
1780static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01001781spoe_handle_disconnecting_appctx(struct appctx *appctx)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001782{
1783 struct stream_interface *si = appctx->owner;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001784 char *frame;
1785 int ret;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001786
Christopher Fauletb067b062017-01-04 16:39:11 +01001787 if (si->state == SI_ST_CLO || si_opposite(si)->state == SI_ST_CLO) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001788 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_IO;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001789 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001790 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001791
Christopher Fauletb067b062017-01-04 16:39:11 +01001792 if (appctx->st1 == SPOE_APPCTX_ERR_TOUT) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001793 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_TOUT;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001794 goto exit;
Christopher Fauletb067b062017-01-04 16:39:11 +01001795 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001796
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001797 frame = trash.area; trash.data = 0;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001798 ret = spoe_recv_frame(appctx, frame,
1799 SPOE_APPCTX(appctx)->max_frame_size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001800 if (ret > 1) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001801 trash.data = ret + 4;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001802 ret = spoe_handle_agentdiscon_frame(appctx, frame, ret);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001803 }
1804
1805 switch (ret) {
1806 case -1: /* error */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001807 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1808 " - error on frame (%s)\n",
1809 (int)now.tv_sec, (int)now.tv_usec,
Christopher Faulet42bfa462017-01-04 14:14:19 +01001810 ((struct spoe_agent *)SPOE_APPCTX(appctx)->agent)->id,
Christopher Fauleta1cda022016-12-21 08:58:06 +01001811 __FUNCTION__, appctx,
Christopher Faulet8ef75252017-02-20 22:56:03 +01001812 spoe_frm_err_reasons[SPOE_APPCTX(appctx)->status_code]);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001813 goto exit;
1814
1815 case 0: /* ignore */
Christopher Fauleta1cda022016-12-21 08:58:06 +01001816 goto next;
1817
1818 case 1: /* retry */
1819 goto stop;
1820
1821 default:
Christopher Fauleta1cda022016-12-21 08:58:06 +01001822 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
Christopher Faulet8ef75252017-02-20 22:56:03 +01001823 " - disconnected by peer (%d): %.*s\n",
Christopher Fauleta1cda022016-12-21 08:58:06 +01001824 (int)now.tv_sec, (int)now.tv_usec,
Christopher Faulet42bfa462017-01-04 14:14:19 +01001825 ((struct spoe_agent *)SPOE_APPCTX(appctx)->agent)->id,
Christopher Faulet8ef75252017-02-20 22:56:03 +01001826 __FUNCTION__, appctx, SPOE_APPCTX(appctx)->status_code,
1827 SPOE_APPCTX(appctx)->rlen, SPOE_APPCTX(appctx)->reason);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001828 goto exit;
1829 }
1830
1831 next:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001832 /* Do not forget to remove processed frame from the output buffer */
Willy Tarreau843b7cb2018-07-13 10:54:26 +02001833 if (trash.data)
1834 co_skip(si_oc(appctx->owner), trash.data);
Christopher Faulet8ef75252017-02-20 22:56:03 +01001835
Christopher Fauleta1cda022016-12-21 08:58:06 +01001836 return 0;
1837 stop:
1838 return 1;
1839 exit:
1840 appctx->st0 = SPOE_APPCTX_ST_EXIT;
1841 return 0;
1842}
1843
1844/* I/O Handler processing messages exchanged with the agent */
1845static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01001846spoe_handle_appctx(struct appctx *appctx)
Christopher Fauleta1cda022016-12-21 08:58:06 +01001847{
Christopher Faulet8ef75252017-02-20 22:56:03 +01001848 struct stream_interface *si = appctx->owner;
1849 struct spoe_agent *agent;
1850
1851 if (SPOE_APPCTX(appctx) == NULL)
1852 return;
Christopher Fauleta1cda022016-12-21 08:58:06 +01001853
Christopher Faulet8ef75252017-02-20 22:56:03 +01001854 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_NONE;
1855 agent = SPOE_APPCTX(appctx)->agent;
Christopher Fauletb067b062017-01-04 16:39:11 +01001856
Christopher Fauleta1cda022016-12-21 08:58:06 +01001857 switchstate:
1858 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: appctx=%p"
1859 " - appctx-state=%s\n",
1860 (int)now.tv_sec, (int)now.tv_usec, agent->id,
1861 __FUNCTION__, appctx, spoe_appctx_state_str[appctx->st0]);
1862
1863 switch (appctx->st0) {
1864 case SPOE_APPCTX_ST_CONNECT:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001865 if (spoe_handle_connect_appctx(appctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01001866 goto out;
1867 goto switchstate;
1868
1869 case SPOE_APPCTX_ST_CONNECTING:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001870 if (spoe_handle_connecting_appctx(appctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01001871 goto out;
1872 goto switchstate;
1873
1874 case SPOE_APPCTX_ST_IDLE:
Olivier Houchard9e7ae282019-03-08 18:50:42 +01001875 _HA_ATOMIC_SUB(&agent->counters.idles, 1);
Christopher Faulet7d9f1ba2018-02-28 13:33:26 +01001876 eb32_delete(&SPOE_APPCTX(appctx)->node);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001877 if (stopping &&
Christopher Faulet24289f22017-09-25 14:48:02 +02001878 LIST_ISEMPTY(&agent->rt[tid].sending_queue) &&
Christopher Faulet42bfa462017-01-04 14:14:19 +01001879 LIST_ISEMPTY(&SPOE_APPCTX(appctx)->waiting_queue)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01001880 SPOE_APPCTX(appctx)->task->expire =
1881 tick_add_ifset(now_ms, agent->timeout.idle);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001882 appctx->st0 = SPOE_APPCTX_ST_DISCONNECT;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001883 goto switchstate;
1884 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001885 appctx->st0 = SPOE_APPCTX_ST_PROCESSING;
1886 /* fall through */
1887
1888 case SPOE_APPCTX_ST_PROCESSING:
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01001889 case SPOE_APPCTX_ST_SENDING_FRAG_NOTIFY:
1890 case SPOE_APPCTX_ST_WAITING_SYNC_ACK:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001891 if (spoe_handle_processing_appctx(appctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01001892 goto out;
1893 goto switchstate;
1894
1895 case SPOE_APPCTX_ST_DISCONNECT:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001896 if (spoe_handle_disconnect_appctx(appctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01001897 goto out;
1898 goto switchstate;
1899
1900 case SPOE_APPCTX_ST_DISCONNECTING:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001901 if (spoe_handle_disconnecting_appctx(appctx))
Christopher Fauleta1cda022016-12-21 08:58:06 +01001902 goto out;
1903 goto switchstate;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001904
1905 case SPOE_APPCTX_ST_EXIT:
Christopher Faulet8ef75252017-02-20 22:56:03 +01001906 appctx->st0 = SPOE_APPCTX_ST_END;
1907 SPOE_APPCTX(appctx)->task->expire = TICK_ETERNITY;
1908
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001909 si_shutw(si);
1910 si_shutr(si);
1911 si_ic(si)->flags |= CF_READ_NULL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001912 /* fall through */
1913
1914 case SPOE_APPCTX_ST_END:
Christopher Fauleta73e59b2016-12-09 17:30:18 +01001915 return;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001916 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01001917 out:
Christopher Faulet24289f22017-09-25 14:48:02 +02001918 if (stopping)
1919 spoe_wakeup_appctx(appctx);
1920
Christopher Faulet42bfa462017-01-04 14:14:19 +01001921 if (SPOE_APPCTX(appctx)->task->expire != TICK_ETERNITY)
1922 task_queue(SPOE_APPCTX(appctx)->task);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001923}
1924
1925struct applet spoe_applet = {
1926 .obj_type = OBJ_TYPE_APPLET,
1927 .name = "<SPOE>", /* used for logging */
Christopher Faulet8ef75252017-02-20 22:56:03 +01001928 .fct = spoe_handle_appctx,
1929 .release = spoe_release_appctx,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001930};
1931
1932/* Create a SPOE applet. On success, the created applet is returned, else
1933 * NULL. */
1934static struct appctx *
Christopher Faulet8ef75252017-02-20 22:56:03 +01001935spoe_create_appctx(struct spoe_config *conf)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001936{
1937 struct appctx *appctx;
1938 struct session *sess;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001939 struct stream *strm;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001940
Emeric Brun1138fd02017-06-19 12:38:55 +02001941 if ((appctx = appctx_new(&spoe_applet, tid_bit)) == NULL)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001942 goto out_error;
1943
Willy Tarreaubafbe012017-11-24 17:34:44 +01001944 appctx->ctx.spoe.ptr = pool_alloc_dirty(pool_head_spoe_appctx);
Christopher Faulet42bfa462017-01-04 14:14:19 +01001945 if (SPOE_APPCTX(appctx) == NULL)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001946 goto out_free_appctx;
Willy Tarreaubafbe012017-11-24 17:34:44 +01001947 memset(appctx->ctx.spoe.ptr, 0, pool_head_spoe_appctx->size);
Christopher Fauleta1cda022016-12-21 08:58:06 +01001948
Christopher Faulet42bfa462017-01-04 14:14:19 +01001949 appctx->st0 = SPOE_APPCTX_ST_CONNECT;
Willy Tarreau5f4a47b2017-10-31 15:59:32 +01001950 if ((SPOE_APPCTX(appctx)->task = task_new(tid_bit)) == NULL)
Christopher Faulet42bfa462017-01-04 14:14:19 +01001951 goto out_free_spoe_appctx;
1952
1953 SPOE_APPCTX(appctx)->owner = appctx;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001954 SPOE_APPCTX(appctx)->task->process = spoe_process_appctx;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001955 SPOE_APPCTX(appctx)->task->context = appctx;
1956 SPOE_APPCTX(appctx)->agent = conf->agent;
1957 SPOE_APPCTX(appctx)->version = 0;
1958 SPOE_APPCTX(appctx)->max_frame_size = conf->agent->max_frame_size;
1959 SPOE_APPCTX(appctx)->flags = 0;
Christopher Fauletb067b062017-01-04 16:39:11 +01001960 SPOE_APPCTX(appctx)->status_code = SPOE_FRM_ERR_NONE;
Willy Tarreauc9fa0482018-07-10 17:43:27 +02001961 SPOE_APPCTX(appctx)->buffer = BUF_NULL;
Christopher Faulet8f82b202018-01-24 16:23:03 +01001962 SPOE_APPCTX(appctx)->cur_fpa = 0;
Christopher Faulet4596fb72017-01-11 14:05:19 +01001963
Willy Tarreau21046592020-02-26 10:39:36 +01001964 MT_LIST_INIT(&SPOE_APPCTX(appctx)->buffer_wait.list);
Christopher Faulet4596fb72017-01-11 14:05:19 +01001965 SPOE_APPCTX(appctx)->buffer_wait.target = appctx;
Christopher Faulet8ef75252017-02-20 22:56:03 +01001966 SPOE_APPCTX(appctx)->buffer_wait.wakeup_cb = (int (*)(void *))spoe_wakeup_appctx;
Christopher Faulet42bfa462017-01-04 14:14:19 +01001967
1968 LIST_INIT(&SPOE_APPCTX(appctx)->list);
1969 LIST_INIT(&SPOE_APPCTX(appctx)->waiting_queue);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001970
Willy Tarreau5820a362016-12-22 15:59:02 +01001971 sess = session_new(&conf->agent_fe, NULL, &appctx->obj_type);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001972 if (!sess)
1973 goto out_free_spoe;
1974
Willy Tarreau87787ac2017-08-28 16:22:54 +02001975 if ((strm = stream_new(sess, &appctx->obj_type)) == NULL)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001976 goto out_free_sess;
1977
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001978 stream_set_backend(strm, conf->agent->b.be);
1979
1980 /* applet is waiting for data */
Willy Tarreau0cd3bd62018-11-06 18:46:37 +01001981 si_cant_get(&strm->si[0]);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001982 appctx_wakeup(appctx);
1983
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001984 strm->do_log = NULL;
1985 strm->res.flags |= CF_READ_DONTWAIT;
1986
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001987 HA_SPIN_LOCK(SPOE_APPLET_LOCK, &conf->agent->rt[tid].lock);
Christopher Faulet24289f22017-09-25 14:48:02 +02001988 LIST_ADDQ(&conf->agent->rt[tid].applets, &SPOE_APPCTX(appctx)->list);
Christopher Faulet2a944ee2017-11-07 10:42:54 +01001989 HA_SPIN_UNLOCK(SPOE_APPLET_LOCK, &conf->agent->rt[tid].lock);
Olivier Houchard9e7ae282019-03-08 18:50:42 +01001990 _HA_ATOMIC_ADD(&conf->agent->counters.applets, 1);
Emeric Brun5f77fef2017-05-29 15:26:51 +02001991
Emeric Brunc60def82017-09-27 14:59:38 +02001992 task_wakeup(SPOE_APPCTX(appctx)->task, TASK_WOKEN_INIT);
Willy Tarreau87787ac2017-08-28 16:22:54 +02001993 task_wakeup(strm->task, TASK_WOKEN_INIT);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001994 return appctx;
1995
1996 /* Error unrolling */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02001997 out_free_sess:
1998 session_free(sess);
1999 out_free_spoe:
Olivier Houchard3f795f72019-04-17 22:51:06 +02002000 task_destroy(SPOE_APPCTX(appctx)->task);
Christopher Faulet42bfa462017-01-04 14:14:19 +01002001 out_free_spoe_appctx:
Willy Tarreaubafbe012017-11-24 17:34:44 +01002002 pool_free(pool_head_spoe_appctx, SPOE_APPCTX(appctx));
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002003 out_free_appctx:
2004 appctx_free(appctx);
2005 out_error:
2006 return NULL;
2007}
2008
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002009static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01002010spoe_queue_context(struct spoe_context *ctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002011{
2012 struct spoe_config *conf = FLT_CONF(ctx->filter);
2013 struct spoe_agent *agent = conf->agent;
2014 struct appctx *appctx;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002015 struct spoe_appctx *spoe_appctx;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002016
Christopher Fauleta1cda022016-12-21 08:58:06 +01002017 /* Check if we need to create a new SPOE applet or not. */
Christopher Fauletb077cdc2018-01-24 16:37:57 +01002018 if (!eb_is_empty(&agent->rt[tid].idle_applets) &&
Christopher Faulet6f9ea4f2018-01-24 16:13:48 +01002019 agent->rt[tid].processing < read_freq_ctr(&agent->rt[tid].processing_per_sec))
Christopher Fauleta1cda022016-12-21 08:58:06 +01002020 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002021
2022 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Fauleta1cda022016-12-21 08:58:06 +01002023 " - try to create new SPOE appctx\n",
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002024 (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__,
2025 ctx->strm);
2026
Christopher Fauleta1cda022016-12-21 08:58:06 +01002027 /* Do not try to create a new applet if there is no server up for the
2028 * agent's backend. */
2029 if (!agent->b.be->srv_act && !agent->b.be->srv_bck) {
2030 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2031 " - cannot create SPOE appctx: no server up\n",
2032 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2033 __FUNCTION__, ctx->strm);
2034 goto end;
2035 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002036
Christopher Fauleta1cda022016-12-21 08:58:06 +01002037 /* Do not try to create a new applet if we have reached the maximum of
2038 * connection per seconds */
Christopher Faulet48026722016-11-16 15:01:12 +01002039 if (agent->cps_max > 0) {
Christopher Faulet24289f22017-09-25 14:48:02 +02002040 if (!freq_ctr_remain(&agent->rt[tid].conn_per_sec, agent->cps_max, 0)) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01002041 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2042 " - cannot create SPOE appctx: max CPS reached\n",
2043 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2044 __FUNCTION__, ctx->strm);
2045 goto end;
2046 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002047 }
2048
Christopher Faulet8ef75252017-02-20 22:56:03 +01002049 appctx = spoe_create_appctx(conf);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002050 if (appctx == NULL) {
2051 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2052 " - failed to create SPOE appctx\n",
2053 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2054 __FUNCTION__, ctx->strm);
Christopher Faulet3b8e3492018-03-26 17:20:58 +02002055 send_log(&conf->agent_fe, LOG_EMERG,
Christopher Faulet72bcc472017-01-04 16:39:41 +01002056 "SPOE: [%s] failed to create SPOE applet\n",
2057 agent->id);
2058
Christopher Fauleta1cda022016-12-21 08:58:06 +01002059 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002060 }
2061
Christopher Fauleta1cda022016-12-21 08:58:06 +01002062 /* Increase the per-process number of cumulated connections */
2063 if (agent->cps_max > 0)
Christopher Faulet24289f22017-09-25 14:48:02 +02002064 update_freq_ctr(&agent->rt[tid].conn_per_sec, 1);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002065
Christopher Fauleta1cda022016-12-21 08:58:06 +01002066 end:
2067 /* The only reason to return an error is when there is no applet */
Christopher Faulet24289f22017-09-25 14:48:02 +02002068 if (LIST_ISEMPTY(&agent->rt[tid].applets)) {
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002069 ctx->status_code = SPOE_CTX_ERR_RES;
2070 return -1;
2071 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002072
Christopher Faulet3e86cec2019-04-10 14:02:12 +02002073 /* Add the SPOE context in the sending queue if the stream has no applet
2074 * already assigned and wakeup all idle applets. Otherwise, don't queue
2075 * it. */
Olivier Houchard9e7ae282019-03-08 18:50:42 +01002076 _HA_ATOMIC_ADD(&agent->counters.nb_sending, 1);
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01002077 spoe_update_stat_time(&ctx->stats.tv_request, &ctx->stats.t_request);
2078 ctx->stats.tv_queue = now;
Christopher Faulet3e86cec2019-04-10 14:02:12 +02002079 if (ctx->spoe_appctx)
2080 return 1;
2081 LIST_ADDQ(&agent->rt[tid].sending_queue, &ctx->list);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002082
2083 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Faulet8ef75252017-02-20 22:56:03 +01002084 " - Add stream in sending queue"
Christopher Faulet68db0232018-04-06 11:34:12 +02002085 " - applets=%u - idles=%u - processing=%u\n",
Christopher Fauleta1cda022016-12-21 08:58:06 +01002086 (int)now.tv_sec, (int)now.tv_usec, agent->id, __FUNCTION__,
Christopher Faulet68db0232018-04-06 11:34:12 +02002087 ctx->strm, agent->counters.applets, agent->counters.idles,
Christopher Faulet6f9ea4f2018-01-24 16:13:48 +01002088 agent->rt[tid].processing);
Christopher Fauletf7a30922016-11-10 15:04:51 +01002089
Christopher Fauletb077cdc2018-01-24 16:37:57 +01002090 /* Finally try to wakeup an IDLE applet. */
2091 if (!eb_is_empty(&agent->rt[tid].idle_applets)) {
2092 struct eb32_node *node;
2093
2094 node = eb32_first(&agent->rt[tid].idle_applets);
2095 spoe_appctx = eb32_entry(node, struct spoe_appctx, node);
2096 if (node && spoe_appctx) {
2097 eb32_delete(&spoe_appctx->node);
2098 spoe_appctx->node.key++;
2099 eb32_insert(&agent->rt[tid].idle_applets, &spoe_appctx->node);
2100 spoe_wakeup_appctx(spoe_appctx->owner);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002101 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002102 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002103 return 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002104}
2105
2106/***************************************************************************
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002107 * Functions that encode SPOE messages
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002108 **************************************************************************/
Christopher Faulet10e37672017-09-21 16:38:22 +02002109/* Encode a SPOE message. Info in <ctx->frag_ctx>, if any, are used to handle
2110 * fragmented_content. If the next message can be processed, it returns 0. If
2111 * the message is too big, it returns -1.*/
2112static int
2113spoe_encode_message(struct stream *s, struct spoe_context *ctx,
2114 struct spoe_message *msg, int dir,
2115 char **buf, char *end)
2116{
2117 struct sample *smp;
2118 struct spoe_arg *arg;
2119 int ret;
2120
2121 if (msg->cond) {
2122 ret = acl_exec_cond(msg->cond, s->be, s->sess, s, dir|SMP_OPT_FINAL);
2123 ret = acl_pass(ret);
2124 if (msg->cond->pol == ACL_COND_UNLESS)
2125 ret = !ret;
2126
2127 /* the rule does not match */
2128 if (!ret)
2129 goto next;
2130 }
2131
2132 /* Resume encoding of a SPOE argument */
2133 if (ctx->frag_ctx.curarg != NULL) {
2134 arg = ctx->frag_ctx.curarg;
2135 goto encode_argument;
2136 }
2137
2138 if (ctx->frag_ctx.curoff != UINT_MAX)
2139 goto encode_msg_payload;
2140
2141 /* Check if there is enough space for the message name and the
2142 * number of arguments. It implies <msg->id_len> is encoded on 2
2143 * bytes, at most (< 2288). */
2144 if (*buf + 2 + msg->id_len + 1 > end)
2145 goto too_big;
2146
2147 /* Encode the message name */
2148 if (spoe_encode_buffer(msg->id, msg->id_len, buf, end) == -1)
2149 goto too_big;
2150
2151 /* Set the number of arguments for this message */
2152 **buf = msg->nargs;
2153 (*buf)++;
2154
2155 ctx->frag_ctx.curoff = 0;
2156 encode_msg_payload:
2157
2158 /* Loop on arguments */
2159 list_for_each_entry(arg, &msg->args, list) {
2160 ctx->frag_ctx.curarg = arg;
2161 ctx->frag_ctx.curoff = UINT_MAX;
Kevin Zhuf7f54282019-04-26 14:00:01 +08002162 ctx->frag_ctx.curlen = 0;
Christopher Faulet10e37672017-09-21 16:38:22 +02002163
2164 encode_argument:
2165 if (ctx->frag_ctx.curoff != UINT_MAX)
2166 goto encode_arg_value;
2167
Joseph Herlantf7f60312018-11-15 13:46:49 -08002168 /* Encode the argument name as a string. It can by NULL */
Christopher Faulet10e37672017-09-21 16:38:22 +02002169 if (spoe_encode_buffer(arg->name, arg->name_len, buf, end) == -1)
2170 goto too_big;
2171
2172 ctx->frag_ctx.curoff = 0;
2173 encode_arg_value:
2174
Joseph Herlantf7f60312018-11-15 13:46:49 -08002175 /* Fetch the argument value */
Christopher Faulet10e37672017-09-21 16:38:22 +02002176 smp = sample_process(s->be, s->sess, s, dir|SMP_OPT_FINAL, arg->expr, NULL);
Christopher Faulet3b1d0042019-05-06 09:53:10 +02002177 if (smp) {
2178 smp->ctx.a[0] = &ctx->frag_ctx.curlen;
2179 smp->ctx.a[1] = &ctx->frag_ctx.curoff;
2180 }
Christopher Faulet85db3212019-04-26 14:30:15 +02002181 ret = spoe_encode_data(smp, buf, end);
Christopher Faulet10e37672017-09-21 16:38:22 +02002182 if (ret == -1 || ctx->frag_ctx.curoff)
2183 goto too_big;
2184 }
2185
2186 next:
2187 return 0;
2188
2189 too_big:
2190 return -1;
2191}
2192
Christopher Fauletc718b822017-09-21 16:50:56 +02002193/* Encode list of SPOE messages. Info in <ctx->frag_ctx>, if any, are used to
2194 * handle fragmented content. On success it returns 1. If an error occurred, -1
2195 * is returned. If nothing has been encoded, it returns 0 (this is only possible
2196 * for unfragmented payload). */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002197static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01002198spoe_encode_messages(struct stream *s, struct spoe_context *ctx,
Christopher Fauletc718b822017-09-21 16:50:56 +02002199 struct list *messages, int dir, int type)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002200{
Christopher Fauleta1cda022016-12-21 08:58:06 +01002201 struct spoe_config *conf = FLT_CONF(ctx->filter);
2202 struct spoe_agent *agent = conf->agent;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002203 struct spoe_message *msg;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002204 char *p, *end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002205
Willy Tarreauc9fa0482018-07-10 17:43:27 +02002206 p = b_head(&ctx->buffer);
Christopher Faulet24289f22017-09-25 14:48:02 +02002207 end = p + agent->rt[tid].frame_size - FRAME_HDR_SIZE;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002208
Christopher Fauletc718b822017-09-21 16:50:56 +02002209 if (type == SPOE_MSGS_BY_EVENT) { /* Loop on messages by event */
2210 /* Resume encoding of a SPOE message */
2211 if (ctx->frag_ctx.curmsg != NULL) {
2212 msg = ctx->frag_ctx.curmsg;
2213 goto encode_evt_message;
2214 }
2215
2216 list_for_each_entry(msg, messages, by_evt) {
2217 ctx->frag_ctx.curmsg = msg;
2218 ctx->frag_ctx.curarg = NULL;
2219 ctx->frag_ctx.curoff = UINT_MAX;
2220
2221 encode_evt_message:
2222 if (spoe_encode_message(s, ctx, msg, dir, &p, end) == -1)
2223 goto too_big;
2224 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002225 }
Christopher Fauletc718b822017-09-21 16:50:56 +02002226 else if (type == SPOE_MSGS_BY_GROUP) { /* Loop on messages by group */
2227 /* Resume encoding of a SPOE message */
2228 if (ctx->frag_ctx.curmsg != NULL) {
2229 msg = ctx->frag_ctx.curmsg;
2230 goto encode_grp_message;
2231 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002232
Christopher Fauletc718b822017-09-21 16:50:56 +02002233 list_for_each_entry(msg, messages, by_grp) {
2234 ctx->frag_ctx.curmsg = msg;
2235 ctx->frag_ctx.curarg = NULL;
2236 ctx->frag_ctx.curoff = UINT_MAX;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002237
Christopher Fauletc718b822017-09-21 16:50:56 +02002238 encode_grp_message:
2239 if (spoe_encode_message(s, ctx, msg, dir, &p, end) == -1)
2240 goto too_big;
2241 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002242 }
Christopher Fauletc718b822017-09-21 16:50:56 +02002243 else
2244 goto skip;
2245
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002246
Christopher Faulet57583e42017-09-04 15:41:09 +02002247 /* nothing has been encoded for an unfragmented payload */
Willy Tarreauc9fa0482018-07-10 17:43:27 +02002248 if (!(ctx->flags & SPOE_CTX_FL_FRAGMENTED) && p == b_head(&ctx->buffer))
Christopher Faulet57583e42017-09-04 15:41:09 +02002249 goto skip;
2250
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002251 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Faulet8ef75252017-02-20 22:56:03 +01002252 " - encode %s messages - spoe_appctx=%p"
2253 "- max_size=%u - encoded=%ld\n",
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002254 (int)now.tv_sec, (int)now.tv_usec,
2255 agent->id, __FUNCTION__, s,
2256 ((ctx->flags & SPOE_CTX_FL_FRAGMENTED) ? "last fragment of" : "unfragmented"),
Christopher Fauletfce747b2018-01-24 15:59:32 +01002257 ctx->spoe_appctx, (agent->rt[tid].frame_size - FRAME_HDR_SIZE),
Christopher Faulet45073512018-07-20 10:16:29 +02002258 p - b_head(&ctx->buffer));
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002259
Willy Tarreauc9fa0482018-07-10 17:43:27 +02002260 b_set_data(&ctx->buffer, p - b_head(&ctx->buffer));
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002261 ctx->frag_ctx.curmsg = NULL;
2262 ctx->frag_ctx.curarg = NULL;
2263 ctx->frag_ctx.curoff = 0;
2264 ctx->frag_ctx.flags = SPOE_FRM_FL_FIN;
Christopher Faulet57583e42017-09-04 15:41:09 +02002265
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002266 return 1;
2267
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002268 too_big:
Christopher Fauleta715ea82019-04-10 14:21:51 +02002269 /* Return an error if fragmentation is unsupported or if nothing has
2270 * been encoded because its too big and not splittable. */
2271 if (!(agent->flags & SPOE_FL_SND_FRAGMENTATION) || p == b_head(&ctx->buffer)) {
Christopher Fauletcecd8522017-02-24 22:11:21 +01002272 ctx->status_code = SPOE_CTX_ERR_TOO_BIG;
2273 return -1;
2274 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002275
2276 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Faulet8ef75252017-02-20 22:56:03 +01002277 " - encode fragmented messages - spoe_appctx=%p"
2278 " - curmsg=%p - curarg=%p - curoff=%u"
2279 " - max_size=%u - encoded=%ld\n",
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002280 (int)now.tv_sec, (int)now.tv_usec,
Christopher Fauletfce747b2018-01-24 15:59:32 +01002281 agent->id, __FUNCTION__, s, ctx->spoe_appctx,
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002282 ctx->frag_ctx.curmsg, ctx->frag_ctx.curarg, ctx->frag_ctx.curoff,
Willy Tarreauc9fa0482018-07-10 17:43:27 +02002283 (agent->rt[tid].frame_size - FRAME_HDR_SIZE), p - b_head(&ctx->buffer));
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002284
Willy Tarreauc9fa0482018-07-10 17:43:27 +02002285 b_set_data(&ctx->buffer, p - b_head(&ctx->buffer));
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002286 ctx->flags |= SPOE_CTX_FL_FRAGMENTED;
2287 ctx->frag_ctx.flags &= ~SPOE_FRM_FL_FIN;
2288 return 1;
Christopher Faulet57583e42017-09-04 15:41:09 +02002289
2290 skip:
2291 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2292 " - skip the frame because nothing has been encoded\n",
2293 (int)now.tv_sec, (int)now.tv_usec,
2294 agent->id, __FUNCTION__, s);
2295 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002296}
2297
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002298
2299/***************************************************************************
2300 * Functions that handle SPOE actions
2301 **************************************************************************/
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002302/* Helper function to set a variable */
2303static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01002304spoe_set_var(struct spoe_context *ctx, char *scope, char *name, int len,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002305 struct sample *smp)
2306{
2307 struct spoe_config *conf = FLT_CONF(ctx->filter);
2308 struct spoe_agent *agent = conf->agent;
2309 char varname[64];
2310
2311 memset(varname, 0, sizeof(varname));
2312 len = snprintf(varname, sizeof(varname), "%s.%s.%.*s",
2313 scope, agent->var_pfx, len, name);
Etienne Carriereaec89892017-12-14 09:36:40 +00002314 if (agent->flags & SPOE_FL_FORCE_SET_VAR)
2315 vars_set_by_name(varname, len, smp);
2316 else
2317 vars_set_by_name_ifexist(varname, len, smp);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002318}
2319
2320/* Helper function to unset a variable */
2321static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01002322spoe_unset_var(struct spoe_context *ctx, char *scope, char *name, int len,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002323 struct sample *smp)
2324{
2325 struct spoe_config *conf = FLT_CONF(ctx->filter);
2326 struct spoe_agent *agent = conf->agent;
2327 char varname[64];
2328
2329 memset(varname, 0, sizeof(varname));
2330 len = snprintf(varname, sizeof(varname), "%s.%s.%.*s",
2331 scope, agent->var_pfx, len, name);
2332 vars_unset_by_name_ifexist(varname, len, smp);
2333}
2334
2335
Christopher Faulet8ef75252017-02-20 22:56:03 +01002336static inline int
2337spoe_decode_action_set_var(struct stream *s, struct spoe_context *ctx,
2338 char **buf, char *end, int dir)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002339{
Christopher Faulet8ef75252017-02-20 22:56:03 +01002340 char *str, *scope, *p = *buf;
2341 struct sample smp;
2342 uint64_t sz;
2343 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002344
Christopher Faulet8ef75252017-02-20 22:56:03 +01002345 if (p + 2 >= end)
2346 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002347
Christopher Faulet8ef75252017-02-20 22:56:03 +01002348 /* SET-VAR requires 3 arguments */
2349 if (*p++ != 3)
2350 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002351
Christopher Faulet8ef75252017-02-20 22:56:03 +01002352 switch (*p++) {
2353 case SPOE_SCOPE_PROC: scope = "proc"; break;
2354 case SPOE_SCOPE_SESS: scope = "sess"; break;
2355 case SPOE_SCOPE_TXN : scope = "txn"; break;
2356 case SPOE_SCOPE_REQ : scope = "req"; break;
2357 case SPOE_SCOPE_RES : scope = "res"; break;
2358 default: goto skip;
2359 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002360
Christopher Faulet8ef75252017-02-20 22:56:03 +01002361 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
2362 goto skip;
2363 memset(&smp, 0, sizeof(smp));
2364 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002365
Christopher Faulet8ef75252017-02-20 22:56:03 +01002366 if (spoe_decode_data(&p, end, &smp) == -1)
2367 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002368
Christopher Faulet8ef75252017-02-20 22:56:03 +01002369 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2370 " - set-var '%s.%s.%.*s'\n",
2371 (int)now.tv_sec, (int)now.tv_usec,
2372 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->id,
2373 __FUNCTION__, s, scope,
2374 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->var_pfx,
2375 (int)sz, str);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002376
Christopher Faulet8ef75252017-02-20 22:56:03 +01002377 spoe_set_var(ctx, scope, str, sz, &smp);
Christopher Fauletb5cff602016-11-24 14:53:22 +01002378
Christopher Faulet8ef75252017-02-20 22:56:03 +01002379 ret = (p - *buf);
2380 *buf = p;
2381 return ret;
2382 skip:
2383 return 0;
2384}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002385
Christopher Faulet8ef75252017-02-20 22:56:03 +01002386static inline int
2387spoe_decode_action_unset_var(struct stream *s, struct spoe_context *ctx,
2388 char **buf, char *end, int dir)
2389{
2390 char *str, *scope, *p = *buf;
2391 struct sample smp;
2392 uint64_t sz;
2393 int ret;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002394
Christopher Faulet8ef75252017-02-20 22:56:03 +01002395 if (p + 2 >= end)
2396 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002397
Christopher Faulet8ef75252017-02-20 22:56:03 +01002398 /* UNSET-VAR requires 2 arguments */
2399 if (*p++ != 2)
2400 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002401
Christopher Faulet8ef75252017-02-20 22:56:03 +01002402 switch (*p++) {
2403 case SPOE_SCOPE_PROC: scope = "proc"; break;
2404 case SPOE_SCOPE_SESS: scope = "sess"; break;
2405 case SPOE_SCOPE_TXN : scope = "txn"; break;
2406 case SPOE_SCOPE_REQ : scope = "req"; break;
2407 case SPOE_SCOPE_RES : scope = "res"; break;
2408 default: goto skip;
2409 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002410
Christopher Faulet8ef75252017-02-20 22:56:03 +01002411 if (spoe_decode_buffer(&p, end, &str, &sz) == -1)
2412 goto skip;
2413 memset(&smp, 0, sizeof(smp));
2414 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002415
Christopher Faulet8ef75252017-02-20 22:56:03 +01002416 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2417 " - unset-var '%s.%s.%.*s'\n",
2418 (int)now.tv_sec, (int)now.tv_usec,
2419 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->id,
2420 __FUNCTION__, s, scope,
2421 ((struct spoe_config *)FLT_CONF(ctx->filter))->agent->var_pfx,
2422 (int)sz, str);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002423
Christopher Faulet8ef75252017-02-20 22:56:03 +01002424 spoe_unset_var(ctx, scope, str, sz, &smp);
2425
2426 ret = (p - *buf);
2427 *buf = p;
2428 return ret;
2429 skip:
2430 return 0;
2431}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002432
Christopher Faulet8ef75252017-02-20 22:56:03 +01002433/* Process SPOE actions for a specific event. It returns 1 on success. If an
2434 * error occurred, 0 is returned. */
2435static int
Christopher Faulet58d03682017-09-21 16:57:24 +02002436spoe_process_actions(struct stream *s, struct spoe_context *ctx, int dir)
Christopher Faulet8ef75252017-02-20 22:56:03 +01002437{
2438 char *p, *end;
2439 int ret;
2440
Willy Tarreauc9fa0482018-07-10 17:43:27 +02002441 p = b_head(&ctx->buffer);
2442 end = p + b_data(&ctx->buffer);
Christopher Faulet8ef75252017-02-20 22:56:03 +01002443
2444 while (p < end) {
2445 enum spoe_action_type type;
2446
2447 type = *p++;
2448 switch (type) {
2449 case SPOE_ACT_T_SET_VAR:
2450 ret = spoe_decode_action_set_var(s, ctx, &p, end, dir);
2451 if (!ret)
2452 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002453 break;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002454
Christopher Faulet8ef75252017-02-20 22:56:03 +01002455 case SPOE_ACT_T_UNSET_VAR:
2456 ret = spoe_decode_action_unset_var(s, ctx, &p, end, dir);
2457 if (!ret)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002458 goto skip;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002459 break;
2460
2461 default:
2462 goto skip;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002463 }
2464 }
2465
2466 return 1;
2467 skip:
2468 return 0;
2469}
2470
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002471/***************************************************************************
2472 * Functions that process SPOE events
2473 **************************************************************************/
Christopher Fauletcaf2fec2018-04-04 10:25:50 +02002474static void
2475spoe_update_stats(struct stream *s, struct spoe_agent *agent,
2476 struct spoe_context *ctx, int dir)
2477{
2478 if (!tv_iszero(&ctx->stats.tv_start)) {
2479 spoe_update_stat_time(&ctx->stats.tv_start, &ctx->stats.t_process);
2480 ctx->stats.t_total += ctx->stats.t_process;
2481 tv_zero(&ctx->stats.tv_request);
2482 tv_zero(&ctx->stats.tv_queue);
2483 tv_zero(&ctx->stats.tv_wait);
2484 tv_zero(&ctx->stats.tv_response);
2485 }
2486
2487 if (agent->var_t_process) {
2488 struct sample smp;
2489
2490 memset(&smp, 0, sizeof(smp));
2491 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
2492 smp.data.u.sint = ctx->stats.t_process;
2493 smp.data.type = SMP_T_SINT;
2494
2495 spoe_set_var(ctx, "txn", agent->var_t_process,
2496 strlen(agent->var_t_process), &smp);
2497 }
2498
2499 if (agent->var_t_total) {
2500 struct sample smp;
2501
2502 memset(&smp, 0, sizeof(smp));
2503 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
2504 smp.data.u.sint = ctx->stats.t_total;
2505 smp.data.type = SMP_T_SINT;
2506
2507 spoe_set_var(ctx, "txn", agent->var_t_total,
2508 strlen(agent->var_t_total), &smp);
2509 }
2510}
2511
2512static void
2513spoe_handle_processing_error(struct stream *s, struct spoe_agent *agent,
2514 struct spoe_context *ctx, int dir)
2515{
2516 if (agent->eps_max > 0)
2517 update_freq_ctr(&agent->rt[tid].err_per_sec, 1);
2518
2519 if (agent->var_on_error) {
2520 struct sample smp;
2521
2522 memset(&smp, 0, sizeof(smp));
2523 smp_set_owner(&smp, s->be, s->sess, s, dir|SMP_OPT_FINAL);
2524 smp.data.u.sint = ctx->status_code;
2525 smp.data.type = SMP_T_BOOL;
2526
2527 spoe_set_var(ctx, "txn", agent->var_on_error,
2528 strlen(agent->var_on_error), &smp);
2529 }
2530
2531 ctx->state = ((agent->flags & SPOE_FL_CONT_ON_ERR)
2532 ? SPOE_CTX_ST_READY
2533 : SPOE_CTX_ST_NONE);
2534}
2535
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002536static inline int
Christopher Faulet6f9ea4f2018-01-24 16:13:48 +01002537spoe_start_processing(struct spoe_agent *agent, struct spoe_context *ctx, int dir)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002538{
Christopher Fauleta1cda022016-12-21 08:58:06 +01002539 /* If a process is already started for this SPOE context, retry
2540 * later. */
2541 if (ctx->flags & SPOE_CTX_FL_PROCESS)
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002542 return 0;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002543
Christopher Faulet6f9ea4f2018-01-24 16:13:48 +01002544 agent->rt[tid].processing++;
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01002545 ctx->stats.tv_start = now;
2546 ctx->stats.tv_request = now;
2547 ctx->stats.t_request = -1;
2548 ctx->stats.t_queue = -1;
2549 ctx->stats.t_waiting = -1;
2550 ctx->stats.t_response = -1;
2551 ctx->stats.t_process = -1;
2552
2553 ctx->status_code = 0;
Christopher Faulet6f9ea4f2018-01-24 16:13:48 +01002554
Christopher Fauleta1cda022016-12-21 08:58:06 +01002555 /* Set the right flag to prevent request and response processing
2556 * in same time. */
2557 ctx->flags |= ((dir == SMP_OPT_DIR_REQ)
2558 ? SPOE_CTX_FL_REQ_PROCESS
2559 : SPOE_CTX_FL_RSP_PROCESS);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002560 return 1;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002561}
2562
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002563static inline void
Christopher Faulet6f9ea4f2018-01-24 16:13:48 +01002564spoe_stop_processing(struct spoe_agent *agent, struct spoe_context *ctx)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002565{
Christopher Fauletfce747b2018-01-24 15:59:32 +01002566 struct spoe_appctx *sa = ctx->spoe_appctx;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002567
Christopher Faulet6f9ea4f2018-01-24 16:13:48 +01002568 if (!(ctx->flags & SPOE_CTX_FL_PROCESS))
2569 return;
Olivier Houchard9e7ae282019-03-08 18:50:42 +01002570 _HA_ATOMIC_ADD(&agent->counters.nb_processed, 1);
Christopher Faulet879dca92018-03-23 11:53:24 +01002571 if (sa) {
2572 if (sa->frag_ctx.ctx == ctx) {
2573 sa->frag_ctx.ctx = NULL;
2574 spoe_wakeup_appctx(sa->owner);
2575 }
2576 else
2577 sa->cur_fpa--;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002578 }
2579
Christopher Fauleta1cda022016-12-21 08:58:06 +01002580 /* Reset the flag to allow next processing */
Christopher Faulet6f9ea4f2018-01-24 16:13:48 +01002581 agent->rt[tid].processing--;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002582 ctx->flags &= ~(SPOE_CTX_FL_PROCESS|SPOE_CTX_FL_FRAGMENTED);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002583
2584 /* Reset processing timer */
2585 ctx->process_exp = TICK_ETERNITY;
2586
Christopher Faulet8ef75252017-02-20 22:56:03 +01002587 spoe_release_buffer(&ctx->buffer, &ctx->buffer_wait);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002588
Christopher Fauletfce747b2018-01-24 15:59:32 +01002589 ctx->spoe_appctx = NULL;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002590 ctx->frag_ctx.curmsg = NULL;
2591 ctx->frag_ctx.curarg = NULL;
2592 ctx->frag_ctx.curoff = 0;
2593 ctx->frag_ctx.flags = 0;
2594
Christopher Fauleta1cda022016-12-21 08:58:06 +01002595 if (!LIST_ISEMPTY(&ctx->list)) {
Christopher Fauletcaf2fec2018-04-04 10:25:50 +02002596 if (ctx->state == SPOE_CTX_ST_SENDING_MSGS)
Olivier Houchard9e7ae282019-03-08 18:50:42 +01002597 _HA_ATOMIC_SUB(&agent->counters.nb_sending, 1);
Christopher Fauletcaf2fec2018-04-04 10:25:50 +02002598 else
Olivier Houchard9e7ae282019-03-08 18:50:42 +01002599 _HA_ATOMIC_SUB(&agent->counters.nb_waiting, 1);
Christopher Fauletcaf2fec2018-04-04 10:25:50 +02002600
Christopher Fauleta1cda022016-12-21 08:58:06 +01002601 LIST_DEL(&ctx->list);
2602 LIST_INIT(&ctx->list);
2603 }
Christopher Faulet344c4ab2017-09-22 10:20:13 +02002604}
2605
Christopher Faulet58d03682017-09-21 16:57:24 +02002606/* Process a list of SPOE messages. First, this functions will process messages
2607 * and send them to an agent in a NOTIFY frame. Then, it will wait a ACK frame
2608 * to process corresponding actions. During all the processing, it returns 0
2609 * and it returns 1 when the processing is finished. If an error occurred, -1
2610 * is returned. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002611static int
Christopher Faulet58d03682017-09-21 16:57:24 +02002612spoe_process_messages(struct stream *s, struct spoe_context *ctx,
2613 struct list *messages, int dir, int type)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002614{
Christopher Fauletf7a30922016-11-10 15:04:51 +01002615 struct spoe_config *conf = FLT_CONF(ctx->filter);
2616 struct spoe_agent *agent = conf->agent;
Christopher Faulet58d03682017-09-21 16:57:24 +02002617 int ret = 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002618
2619 if (ctx->state == SPOE_CTX_ST_ERROR)
Christopher Fauletcaf2fec2018-04-04 10:25:50 +02002620 goto end;
Christopher Fauletf7a30922016-11-10 15:04:51 +01002621
2622 if (tick_is_expired(ctx->process_exp, now_ms) && ctx->state != SPOE_CTX_ST_DONE) {
2623 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Faulet58d03682017-09-21 16:57:24 +02002624 " - failed to process messages: timeout\n",
Christopher Fauletf7a30922016-11-10 15:04:51 +01002625 (int)now.tv_sec, (int)now.tv_usec,
Christopher Faulet58d03682017-09-21 16:57:24 +02002626 agent->id, __FUNCTION__, s);
Christopher Fauletb067b062017-01-04 16:39:11 +01002627 ctx->status_code = SPOE_CTX_ERR_TOUT;
Christopher Fauletcaf2fec2018-04-04 10:25:50 +02002628 goto end;
Christopher Fauletf7a30922016-11-10 15:04:51 +01002629 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002630
2631 if (ctx->state == SPOE_CTX_ST_READY) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01002632 if (agent->eps_max > 0) {
Christopher Faulet24289f22017-09-25 14:48:02 +02002633 if (!freq_ctr_remain(&agent->rt[tid].err_per_sec, agent->eps_max, 0)) {
Christopher Fauleta1cda022016-12-21 08:58:06 +01002634 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Faulet58d03682017-09-21 16:57:24 +02002635 " - skip processing of messages: max EPS reached\n",
Christopher Fauleta1cda022016-12-21 08:58:06 +01002636 (int)now.tv_sec, (int)now.tv_usec,
Christopher Faulet58d03682017-09-21 16:57:24 +02002637 agent->id, __FUNCTION__, s);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002638 goto skip;
2639 }
2640 }
2641
Christopher Fauletf7a30922016-11-10 15:04:51 +01002642 if (!tick_isset(ctx->process_exp)) {
2643 ctx->process_exp = tick_add_ifset(now_ms, agent->timeout.processing);
2644 s->task->expire = tick_first((tick_is_expired(s->task->expire, now_ms) ? 0 : s->task->expire),
2645 ctx->process_exp);
2646 }
Christopher Faulet6f9ea4f2018-01-24 16:13:48 +01002647 ret = spoe_start_processing(agent, ctx, dir);
Christopher Fauletb067b062017-01-04 16:39:11 +01002648 if (!ret)
2649 goto out;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002650
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002651 ctx->state = SPOE_CTX_ST_ENCODING_MSGS;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002652 /* fall through */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002653 }
2654
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002655 if (ctx->state == SPOE_CTX_ST_ENCODING_MSGS) {
Christopher Faulet63263e52019-04-10 14:47:18 +02002656 if (tv_iszero(&ctx->stats.tv_request))
2657 ctx->stats.tv_request = now;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002658 if (!spoe_acquire_buffer(&ctx->buffer, &ctx->buffer_wait))
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002659 goto out;
Christopher Faulet58d03682017-09-21 16:57:24 +02002660 ret = spoe_encode_messages(s, ctx, messages, dir, type);
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002661 if (ret < 0)
Christopher Fauletcaf2fec2018-04-04 10:25:50 +02002662 goto end;
Christopher Faulet57583e42017-09-04 15:41:09 +02002663 if (!ret)
2664 goto skip;
Christopher Faulet333694d2018-01-12 10:45:47 +01002665 if (spoe_queue_context(ctx) < 0)
Christopher Fauletcaf2fec2018-04-04 10:25:50 +02002666 goto end;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002667 ctx->state = SPOE_CTX_ST_SENDING_MSGS;
2668 }
2669
2670 if (ctx->state == SPOE_CTX_ST_SENDING_MSGS) {
Christopher Fauletfce747b2018-01-24 15:59:32 +01002671 if (ctx->spoe_appctx)
2672 spoe_wakeup_appctx(ctx->spoe_appctx->owner);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002673 ret = 0;
2674 goto out;
2675 }
2676
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01002677 if (ctx->state == SPOE_CTX_ST_WAITING_ACK) {
2678 ret = 0;
2679 goto out;
2680 }
2681
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002682 if (ctx->state == SPOE_CTX_ST_DONE) {
Christopher Faulet58d03682017-09-21 16:57:24 +02002683 spoe_process_actions(s, ctx, dir);
Christopher Faulet8ef75252017-02-20 22:56:03 +01002684 ret = 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002685 ctx->frame_id++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002686 ctx->state = SPOE_CTX_ST_READY;
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01002687 spoe_update_stat_time(&ctx->stats.tv_response, &ctx->stats.t_response);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002688 goto end;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002689 }
2690
2691 out:
2692 return ret;
2693
Christopher Fauleta1cda022016-12-21 08:58:06 +01002694 skip:
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01002695 tv_zero(&ctx->stats.tv_start);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002696 ctx->state = SPOE_CTX_ST_READY;
Christopher Fauletcaf2fec2018-04-04 10:25:50 +02002697 spoe_stop_processing(agent, ctx);
2698 return 1;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002699
Christopher Fauleta1cda022016-12-21 08:58:06 +01002700 end:
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01002701 spoe_update_stats(s, agent, ctx, dir);
Christopher Faulet6f9ea4f2018-01-24 16:13:48 +01002702 spoe_stop_processing(agent, ctx);
Christopher Fauletcaf2fec2018-04-04 10:25:50 +02002703 if (ctx->status_code) {
Olivier Houchard9e7ae282019-03-08 18:50:42 +01002704 _HA_ATOMIC_ADD(&agent->counters.nb_errors, 1);
Christopher Fauletcaf2fec2018-04-04 10:25:50 +02002705 spoe_handle_processing_error(s, agent, ctx, dir);
2706 ret = 1;
2707 }
Christopher Faulet58d03682017-09-21 16:57:24 +02002708 return ret;
2709}
2710
Christopher Faulet344c4ab2017-09-22 10:20:13 +02002711/* Process a SPOE group, ie the list of messages attached to the group <grp>.
2712 * See spoe_process_message for details. */
2713static int
2714spoe_process_group(struct stream *s, struct spoe_context *ctx,
2715 struct spoe_group *group, int dir)
2716{
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01002717 struct spoe_config *conf = FLT_CONF(ctx->filter);
2718 struct spoe_agent *agent = conf->agent;
Christopher Faulet344c4ab2017-09-22 10:20:13 +02002719 int ret;
2720
2721 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
2722 " - ctx-state=%s - Process messages for group=%s\n",
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01002723 (int)now.tv_sec, (int)now.tv_usec, agent->id,
Christopher Faulet344c4ab2017-09-22 10:20:13 +02002724 __FUNCTION__, s, spoe_ctx_state_str[ctx->state],
2725 group->id);
2726
2727 if (LIST_ISEMPTY(&group->messages))
2728 return 1;
2729
2730 ret = spoe_process_messages(s, ctx, &group->messages, dir, SPOE_MSGS_BY_GROUP);
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01002731 if (ret && ctx->stats.t_process != -1) {
2732 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Fauletcaf2fec2018-04-04 10:25:50 +02002733 " - <GROUP:%s> sid=%u st=%u %ld/%ld/%ld/%ld/%ld %u/%u %u/%u %llu/%llu %u/%u\n",
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01002734 (int)now.tv_sec, (int)now.tv_usec, agent->id,
Christopher Fauletcaf2fec2018-04-04 10:25:50 +02002735 __FUNCTION__, s, group->id, s->uniq_id, ctx->status_code,
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01002736 ctx->stats.t_request, ctx->stats.t_queue, ctx->stats.t_waiting,
Christopher Fauletcaf2fec2018-04-04 10:25:50 +02002737 ctx->stats.t_response, ctx->stats.t_process,
2738 agent->counters.idles, agent->counters.applets,
2739 agent->counters.nb_sending, agent->counters.nb_waiting,
2740 agent->counters.nb_errors, agent->counters.nb_processed,
2741 agent->rt[tid].processing, read_freq_ctr(&agent->rt[tid].processing_per_sec));
2742 if (ctx->status_code || !(conf->agent_fe.options2 & PR_O2_NOLOGNORM))
2743 send_log(&conf->agent_fe, (!ctx->status_code ? LOG_NOTICE : LOG_WARNING),
2744 "SPOE: [%s] <GROUP:%s> sid=%u st=%u %ld/%ld/%ld/%ld/%ld %u/%u %u/%u %llu/%llu\n",
2745 agent->id, group->id, s->uniq_id, ctx->status_code,
2746 ctx->stats.t_request, ctx->stats.t_queue, ctx->stats.t_waiting,
2747 ctx->stats.t_response, ctx->stats.t_process,
2748 agent->counters.idles, agent->counters.applets,
2749 agent->counters.nb_sending, agent->counters.nb_waiting,
2750 agent->counters.nb_errors, agent->counters.nb_processed);
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01002751 }
Christopher Faulet344c4ab2017-09-22 10:20:13 +02002752 return ret;
2753}
2754
Christopher Faulet58d03682017-09-21 16:57:24 +02002755/* Process a SPOE event, ie the list of messages attached to the event <ev>.
2756 * See spoe_process_message for details. */
2757static int
2758spoe_process_event(struct stream *s, struct spoe_context *ctx,
2759 enum spoe_event ev)
2760{
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01002761 struct spoe_config *conf = FLT_CONF(ctx->filter);
2762 struct spoe_agent *agent = conf->agent;
Christopher Faulet58d03682017-09-21 16:57:24 +02002763 int dir, ret;
2764
2765 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Faulet344c4ab2017-09-22 10:20:13 +02002766 " - ctx-state=%s - Process messages for event=%s\n",
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01002767 (int)now.tv_sec, (int)now.tv_usec, agent->id,
Christopher Faulet58d03682017-09-21 16:57:24 +02002768 __FUNCTION__, s, spoe_ctx_state_str[ctx->state],
2769 spoe_event_str[ev]);
2770
2771 dir = ((ev < SPOE_EV_ON_SERVER_SESS) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES);
2772
2773 if (LIST_ISEMPTY(&(ctx->events[ev])))
2774 return 1;
2775
2776 ret = spoe_process_messages(s, ctx, &(ctx->events[ev]), dir, SPOE_MSGS_BY_EVENT);
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01002777 if (ret && ctx->stats.t_process != -1) {
2778 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
Christopher Fauletcaf2fec2018-04-04 10:25:50 +02002779 " - <EVENT:%s> sid=%u st=%u %ld/%ld/%ld/%ld/%ld %u/%u %u/%u %llu/%llu %u/%u\n",
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01002780 (int)now.tv_sec, (int)now.tv_usec, agent->id,
2781 __FUNCTION__, s, spoe_event_str[ev], s->uniq_id, ctx->status_code,
2782 ctx->stats.t_request, ctx->stats.t_queue, ctx->stats.t_waiting,
Christopher Fauletcaf2fec2018-04-04 10:25:50 +02002783 ctx->stats.t_response, ctx->stats.t_process,
2784 agent->counters.idles, agent->counters.applets,
2785 agent->counters.nb_sending, agent->counters.nb_waiting,
2786 agent->counters.nb_errors, agent->counters.nb_processed,
2787 agent->rt[tid].processing, read_freq_ctr(&agent->rt[tid].processing_per_sec));
2788 if (ctx->status_code || !(conf->agent_fe.options2 & PR_O2_NOLOGNORM))
2789 send_log(&conf->agent_fe, (!ctx->status_code ? LOG_NOTICE : LOG_WARNING),
2790 "SPOE: [%s] <EVENT:%s> sid=%u st=%u %ld/%ld/%ld/%ld/%ld %u/%u %u/%u %llu/%llu\n",
2791 agent->id, spoe_event_str[ev], s->uniq_id, ctx->status_code,
2792 ctx->stats.t_request, ctx->stats.t_queue, ctx->stats.t_waiting,
2793 ctx->stats.t_response, ctx->stats.t_process,
2794 agent->counters.idles, agent->counters.applets,
2795 agent->counters.nb_sending, agent->counters.nb_waiting,
2796 agent->counters.nb_errors, agent->counters.nb_processed);
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01002797 }
Christopher Fauleta1cda022016-12-21 08:58:06 +01002798 return ret;
2799}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002800
2801/***************************************************************************
2802 * Functions that create/destroy SPOE contexts
2803 **************************************************************************/
Christopher Fauleta1cda022016-12-21 08:58:06 +01002804static int
Willy Tarreauc9fa0482018-07-10 17:43:27 +02002805spoe_acquire_buffer(struct buffer *buf, struct buffer_wait *buffer_wait)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002806{
Willy Tarreauc9fa0482018-07-10 17:43:27 +02002807 if (buf->size)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002808 return 1;
2809
Willy Tarreau21046592020-02-26 10:39:36 +01002810 if (MT_LIST_ADDED(&buffer_wait->list))
2811 MT_LIST_DEL(&buffer_wait->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002812
Christopher Faulet4596fb72017-01-11 14:05:19 +01002813 if (b_alloc_margin(buf, global.tune.reserved_bufs))
Christopher Fauleta1cda022016-12-21 08:58:06 +01002814 return 1;
2815
Willy Tarreau21046592020-02-26 10:39:36 +01002816 MT_LIST_ADDQ(&buffer_wq, &buffer_wait->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002817 return 0;
2818}
2819
2820static void
Willy Tarreauc9fa0482018-07-10 17:43:27 +02002821spoe_release_buffer(struct buffer *buf, struct buffer_wait *buffer_wait)
Christopher Fauleta1cda022016-12-21 08:58:06 +01002822{
Willy Tarreau21046592020-02-26 10:39:36 +01002823 if (MT_LIST_ADDED(&buffer_wait->list))
2824 MT_LIST_DEL(&buffer_wait->list);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002825
2826 /* Release the buffer if needed */
Willy Tarreauc9fa0482018-07-10 17:43:27 +02002827 if (buf->size) {
Christopher Faulet4596fb72017-01-11 14:05:19 +01002828 b_free(buf);
Olivier Houchard673867c2018-05-25 16:58:52 +02002829 offer_buffers(buffer_wait->target, tasks_run_queue);
Christopher Fauleta1cda022016-12-21 08:58:06 +01002830 }
2831}
2832
Christopher Faulet4596fb72017-01-11 14:05:19 +01002833static int
Christopher Faulet8ef75252017-02-20 22:56:03 +01002834spoe_wakeup_context(struct spoe_context *ctx)
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002835{
2836 task_wakeup(ctx->strm->task, TASK_WOKEN_MSG);
2837 return 1;
2838}
2839
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002840static struct spoe_context *
Christopher Faulet6f9ea4f2018-01-24 16:13:48 +01002841spoe_create_context(struct stream *s, struct filter *filter)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002842{
2843 struct spoe_config *conf = FLT_CONF(filter);
2844 struct spoe_context *ctx;
2845
Willy Tarreaubafbe012017-11-24 17:34:44 +01002846 ctx = pool_alloc_dirty(pool_head_spoe_ctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002847 if (ctx == NULL) {
2848 return NULL;
2849 }
2850 memset(ctx, 0, sizeof(*ctx));
Christopher Fauletb067b062017-01-04 16:39:11 +01002851 ctx->filter = filter;
2852 ctx->state = SPOE_CTX_ST_NONE;
2853 ctx->status_code = SPOE_CTX_ERR_NONE;
2854 ctx->flags = 0;
Christopher Faulet11610f32017-09-21 10:23:10 +02002855 ctx->events = conf->agent->events;
Christopher Faulet76c09ef2017-09-21 11:03:52 +02002856 ctx->groups = &conf->agent->groups;
Willy Tarreauc9fa0482018-07-10 17:43:27 +02002857 ctx->buffer = BUF_NULL;
Willy Tarreau21046592020-02-26 10:39:36 +01002858 MT_LIST_INIT(&ctx->buffer_wait.list);
Christopher Fauleta73e59b2016-12-09 17:30:18 +01002859 ctx->buffer_wait.target = ctx;
Christopher Faulet8ef75252017-02-20 22:56:03 +01002860 ctx->buffer_wait.wakeup_cb = (int (*)(void *))spoe_wakeup_context;
Christopher Fauleta1cda022016-12-21 08:58:06 +01002861 LIST_INIT(&ctx->list);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002862
Christopher Fauletf7a30922016-11-10 15:04:51 +01002863 ctx->stream_id = 0;
2864 ctx->frame_id = 1;
2865 ctx->process_exp = TICK_ETERNITY;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002866
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01002867 tv_zero(&ctx->stats.tv_start);
2868 tv_zero(&ctx->stats.tv_request);
2869 tv_zero(&ctx->stats.tv_queue);
2870 tv_zero(&ctx->stats.tv_wait);
2871 tv_zero(&ctx->stats.tv_response);
2872 ctx->stats.t_request = -1;
2873 ctx->stats.t_queue = -1;
2874 ctx->stats.t_waiting = -1;
2875 ctx->stats.t_response = -1;
2876 ctx->stats.t_process = -1;
2877 ctx->stats.t_total = 0;
2878
Christopher Faulet6f9ea4f2018-01-24 16:13:48 +01002879 ctx->strm = s;
2880 ctx->state = SPOE_CTX_ST_READY;
2881 filter->ctx = ctx;
2882
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002883 return ctx;
2884}
2885
2886static void
Christopher Faulet6f9ea4f2018-01-24 16:13:48 +01002887spoe_destroy_context(struct filter *filter)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002888{
Christopher Faulet6f9ea4f2018-01-24 16:13:48 +01002889 struct spoe_config *conf = FLT_CONF(filter);
2890 struct spoe_context *ctx = filter->ctx;
2891
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002892 if (!ctx)
2893 return;
2894
Christopher Faulet6f9ea4f2018-01-24 16:13:48 +01002895 spoe_stop_processing(conf->agent, ctx);
Willy Tarreaubafbe012017-11-24 17:34:44 +01002896 pool_free(pool_head_spoe_ctx, ctx);
Christopher Faulet6f9ea4f2018-01-24 16:13:48 +01002897 filter->ctx = NULL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002898}
2899
2900static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01002901spoe_reset_context(struct spoe_context *ctx)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002902{
2903 ctx->state = SPOE_CTX_ST_READY;
Christopher Fauletf032c3e2017-02-17 15:18:35 +01002904 ctx->flags &= ~(SPOE_CTX_FL_PROCESS|SPOE_CTX_FL_FRAGMENTED);
Christopher Fauletb2dd1e02018-03-22 09:07:41 +01002905
2906 tv_zero(&ctx->stats.tv_start);
2907 tv_zero(&ctx->stats.tv_request);
2908 tv_zero(&ctx->stats.tv_queue);
2909 tv_zero(&ctx->stats.tv_wait);
2910 tv_zero(&ctx->stats.tv_response);
2911 ctx->stats.t_request = -1;
2912 ctx->stats.t_queue = -1;
2913 ctx->stats.t_waiting = -1;
2914 ctx->stats.t_response = -1;
2915 ctx->stats.t_process = -1;
2916 ctx->stats.t_total = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002917}
2918
2919
2920/***************************************************************************
2921 * Hooks that manage the filter lifecycle (init/check/deinit)
2922 **************************************************************************/
2923/* Signal handler: Do a soft stop, wakeup SPOE applet */
2924static void
Christopher Faulet8ef75252017-02-20 22:56:03 +01002925spoe_sig_stop(struct sig_handler *sh)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002926{
2927 struct proxy *p;
2928
Olivier Houchardfbc74e82017-11-24 16:54:05 +01002929 p = proxies_list;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002930 while (p) {
2931 struct flt_conf *fconf;
2932
2933 list_for_each_entry(fconf, &p->filter_configs, list) {
Christopher Faulet3b386a32017-02-23 10:17:15 +01002934 struct spoe_config *conf;
2935 struct spoe_agent *agent;
Christopher Faulet42bfa462017-01-04 14:14:19 +01002936 struct spoe_appctx *spoe_appctx;
Christopher Faulet24289f22017-09-25 14:48:02 +02002937 int i;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002938
Christopher Faulet3b386a32017-02-23 10:17:15 +01002939 if (fconf->id != spoe_filter_id)
2940 continue;
2941
2942 conf = fconf->conf;
2943 agent = conf->agent;
2944
Christopher Faulet24289f22017-09-25 14:48:02 +02002945 for (i = 0; i < global.nbthread; ++i) {
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002946 HA_SPIN_LOCK(SPOE_APPLET_LOCK, &agent->rt[i].lock);
Christopher Faulet24289f22017-09-25 14:48:02 +02002947 list_for_each_entry(spoe_appctx, &agent->rt[i].applets, list)
2948 spoe_wakeup_appctx(spoe_appctx->owner);
Christopher Faulet2a944ee2017-11-07 10:42:54 +01002949 HA_SPIN_UNLOCK(SPOE_APPLET_LOCK, &agent->rt[i].lock);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002950 }
2951 }
2952 p = p->next;
2953 }
2954}
2955
2956
2957/* Initialize the SPOE filter. Returns -1 on error, else 0. */
2958static int
2959spoe_init(struct proxy *px, struct flt_conf *fconf)
2960{
2961 struct spoe_config *conf = fconf->conf;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002962
Christopher Faulet7250b8f2018-03-26 17:19:01 +02002963 /* conf->agent_fe was already initialized during the config
2964 * parsing. Finish initialization. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002965 conf->agent_fe.last_change = now.tv_sec;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002966 conf->agent_fe.cap = PR_CAP_FE;
2967 conf->agent_fe.mode = PR_MODE_TCP;
2968 conf->agent_fe.maxconn = 0;
2969 conf->agent_fe.options2 |= PR_O2_INDEPSTR;
2970 conf->agent_fe.conn_retries = CONN_RETRIES;
2971 conf->agent_fe.accept = frontend_accept;
2972 conf->agent_fe.srv = NULL;
2973 conf->agent_fe.timeout.client = TICK_ETERNITY;
2974 conf->agent_fe.default_target = &spoe_applet.obj_type;
2975 conf->agent_fe.fe_req_ana = AN_REQ_SWITCHING_RULES;
2976
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002977 if (!sighandler_registered) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01002978 signal_register_fct(0, spoe_sig_stop, 0);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002979 sighandler_registered = 1;
2980 }
2981
Christopher Faulet00292352019-01-09 15:05:10 +01002982 fconf->flags |= FLT_CFG_FL_HTX;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002983 return 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002984}
2985
Ilya Shipitsin6fb0f212020-04-02 15:25:26 +05002986/* Free resources allocated by the SPOE filter. */
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002987static void
2988spoe_deinit(struct proxy *px, struct flt_conf *fconf)
2989{
2990 struct spoe_config *conf = fconf->conf;
2991
2992 if (conf) {
2993 struct spoe_agent *agent = conf->agent;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002994
Christopher Faulet8ef75252017-02-20 22:56:03 +01002995 spoe_release_agent(agent);
Christopher Faulet7ee86672017-09-19 11:08:28 +02002996 free(conf->id);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02002997 free(conf);
2998 }
2999 fconf->conf = NULL;
3000}
3001
3002/* Check configuration of a SPOE filter for a specified proxy.
3003 * Return 1 on error, else 0. */
3004static int
3005spoe_check(struct proxy *px, struct flt_conf *fconf)
3006{
Christopher Faulet7ee86672017-09-19 11:08:28 +02003007 struct flt_conf *f;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003008 struct spoe_config *conf = fconf->conf;
3009 struct proxy *target;
Willy Tarreaub0769b22019-02-07 13:40:33 +01003010 int i;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003011
Christopher Faulet7ee86672017-09-19 11:08:28 +02003012 /* Check all SPOE filters for proxy <px> to be sure all SPOE agent names
3013 * are uniq */
3014 list_for_each_entry(f, &px->filter_configs, list) {
3015 struct spoe_config *c = f->conf;
3016
3017 /* This is not an SPOE filter */
3018 if (f->id != spoe_filter_id)
3019 continue;
3020 /* This is the current SPOE filter */
3021 if (f == fconf)
3022 continue;
3023
3024 /* Check engine Id. It should be uniq */
3025 if (!strcmp(conf->id, c->id)) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003026 ha_alert("Proxy %s : duplicated name for SPOE engine '%s'.\n",
3027 px->id, conf->id);
Christopher Faulet7ee86672017-09-19 11:08:28 +02003028 return 1;
3029 }
3030 }
3031
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003032 target = proxy_be_by_name(conf->agent->b.name);
3033 if (target == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003034 ha_alert("Proxy %s : unknown backend '%s' used by SPOE agent '%s'"
3035 " declared at %s:%d.\n",
3036 px->id, conf->agent->b.name, conf->agent->id,
3037 conf->agent->conf.file, conf->agent->conf.line);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003038 return 1;
3039 }
3040 if (target->mode != PR_MODE_TCP) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003041 ha_alert("Proxy %s : backend '%s' used by SPOE agent '%s' declared"
3042 " at %s:%d does not support HTTP mode.\n",
3043 px->id, target->id, conf->agent->id,
3044 conf->agent->conf.file, conf->agent->conf.line);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003045 return 1;
3046 }
3047
Willy Tarreau2bdcfde2019-02-05 13:37:19 +01003048 if (px->bind_proc & ~target->bind_proc) {
3049 ha_alert("Proxy %s : backend '%s' used by SPOE agent '%s' declared"
3050 " at %s:%d does not cover all of its processes.\n",
3051 px->id, target->id, conf->agent->id,
3052 conf->agent->conf.file, conf->agent->conf.line);
3053 return 1;
3054 }
3055
Christopher Fauletfe261552019-03-18 13:57:42 +01003056 if ((conf->agent->rt = calloc(global.nbthread, sizeof(*conf->agent->rt))) == NULL) {
Willy Tarreaub0769b22019-02-07 13:40:33 +01003057 ha_alert("Proxy %s : out of memory initializing SPOE agent '%s' declared at %s:%d.\n",
3058 px->id, conf->agent->id, conf->agent->conf.file, conf->agent->conf.line);
3059 return 1;
3060 }
3061 for (i = 0; i < global.nbthread; ++i) {
Christopher Fauletb1bb1af2019-09-17 11:55:52 +02003062 conf->agent->rt[i].engine_id = NULL;
Christopher Fauletfe261552019-03-18 13:57:42 +01003063 conf->agent->rt[i].frame_size = conf->agent->max_frame_size;
3064 conf->agent->rt[i].processing = 0;
3065 LIST_INIT(&conf->agent->rt[i].applets);
3066 LIST_INIT(&conf->agent->rt[i].sending_queue);
3067 LIST_INIT(&conf->agent->rt[i].waiting_queue);
3068 HA_SPIN_INIT(&conf->agent->rt[i].lock);
Willy Tarreaub0769b22019-02-07 13:40:33 +01003069 }
3070
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003071 free(conf->agent->b.name);
3072 conf->agent->b.name = NULL;
3073 conf->agent->b.be = target;
3074 return 0;
3075}
3076
Kevin Zhud87b1a52019-09-17 15:05:45 +02003077/* Initializes the SPOE filter for a proxy for a specific thread.
3078 * Returns a negative value if an error occurs. */
3079static int
3080spoe_init_per_thread(struct proxy *p, struct flt_conf *fconf)
3081{
3082 struct spoe_config *conf = fconf->conf;
3083 struct spoe_agent *agent = conf->agent;
3084
Christopher Fauletb1bb1af2019-09-17 11:55:52 +02003085 agent->rt[tid].engine_id = generate_pseudo_uuid();
3086 if (agent->rt[tid].engine_id == NULL)
3087 return -1;
Kevin Zhud87b1a52019-09-17 15:05:45 +02003088 return 0;
3089}
3090
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003091/**************************************************************************
3092 * Hooks attached to a stream
3093 *************************************************************************/
3094/* Called when a filter instance is created and attach to a stream. It creates
3095 * the context that will be used to process this stream. */
3096static int
3097spoe_start(struct stream *s, struct filter *filter)
3098{
Christopher Faulet72bcc472017-01-04 16:39:41 +01003099 struct spoe_config *conf = FLT_CONF(filter);
3100 struct spoe_agent *agent = conf->agent;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003101 struct spoe_context *ctx;
3102
3103 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p\n",
Christopher Faulet72bcc472017-01-04 16:39:41 +01003104 (int)now.tv_sec, (int)now.tv_usec, agent->id,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003105 __FUNCTION__, s);
3106
Christopher Faulet6f9ea4f2018-01-24 16:13:48 +01003107 if ((ctx = spoe_create_context(s, filter)) == NULL) {
Christopher Faulet72bcc472017-01-04 16:39:41 +01003108 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
3109 " - failed to create SPOE context\n",
3110 (int)now.tv_sec, (int)now.tv_usec, agent->id,
Christopher Fauletccbc3fd2017-09-15 11:51:18 +02003111 __FUNCTION__, s);
Christopher Faulet3b8e3492018-03-26 17:20:58 +02003112 send_log(&conf->agent_fe, LOG_EMERG,
Christopher Faulet72bcc472017-01-04 16:39:41 +01003113 "SPOE: [%s] failed to create SPOE context\n",
3114 agent->id);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003115 return 0;
3116 }
3117
Christopher Faulet11610f32017-09-21 10:23:10 +02003118 if (!LIST_ISEMPTY(&ctx->events[SPOE_EV_ON_TCP_REQ_FE]))
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003119 filter->pre_analyzers |= AN_REQ_INSPECT_FE;
3120
Christopher Faulet11610f32017-09-21 10:23:10 +02003121 if (!LIST_ISEMPTY(&ctx->events[SPOE_EV_ON_TCP_REQ_BE]))
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003122 filter->pre_analyzers |= AN_REQ_INSPECT_BE;
3123
Christopher Faulet11610f32017-09-21 10:23:10 +02003124 if (!LIST_ISEMPTY(&ctx->events[SPOE_EV_ON_TCP_RSP]))
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003125 filter->pre_analyzers |= AN_RES_INSPECT;
3126
Christopher Faulet11610f32017-09-21 10:23:10 +02003127 if (!LIST_ISEMPTY(&ctx->events[SPOE_EV_ON_HTTP_REQ_FE]))
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003128 filter->pre_analyzers |= AN_REQ_HTTP_PROCESS_FE;
3129
Christopher Faulet11610f32017-09-21 10:23:10 +02003130 if (!LIST_ISEMPTY(&ctx->events[SPOE_EV_ON_HTTP_REQ_BE]))
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003131 filter->pre_analyzers |= AN_REQ_HTTP_PROCESS_BE;
3132
Christopher Faulet11610f32017-09-21 10:23:10 +02003133 if (!LIST_ISEMPTY(&ctx->events[SPOE_EV_ON_HTTP_RSP]))
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003134 filter->pre_analyzers |= AN_RES_HTTP_PROCESS_FE;
3135
3136 return 1;
3137}
3138
3139/* Called when a filter instance is detached from a stream. It release the
3140 * attached SPOE context. */
3141static void
3142spoe_stop(struct stream *s, struct filter *filter)
3143{
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003144 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p\n",
3145 (int)now.tv_sec, (int)now.tv_usec,
3146 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
3147 __FUNCTION__, s);
Christopher Faulet6f9ea4f2018-01-24 16:13:48 +01003148 spoe_destroy_context(filter);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003149}
3150
Christopher Fauletf7a30922016-11-10 15:04:51 +01003151
3152/*
3153 * Called when the stream is woken up because of expired timer.
3154 */
3155static void
3156spoe_check_timeouts(struct stream *s, struct filter *filter)
3157{
3158 struct spoe_context *ctx = filter->ctx;
3159
Christopher Fauletac580602018-03-20 16:09:20 +01003160 if (tick_is_expired(ctx->process_exp, now_ms))
Christopher Fauleta73e59b2016-12-09 17:30:18 +01003161 s->pending_events |= TASK_WOKEN_MSG;
Christopher Fauletf7a30922016-11-10 15:04:51 +01003162}
3163
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003164/* Called when we are ready to filter data on a channel */
3165static int
3166spoe_start_analyze(struct stream *s, struct filter *filter, struct channel *chn)
3167{
3168 struct spoe_context *ctx = filter->ctx;
3169 int ret = 1;
3170
3171 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p - ctx-state=%s"
3172 " - ctx-flags=0x%08x\n",
3173 (int)now.tv_sec, (int)now.tv_usec,
3174 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
3175 __FUNCTION__, s, spoe_ctx_state_str[ctx->state], ctx->flags);
3176
Christopher Fauletb067b062017-01-04 16:39:11 +01003177 if (ctx->state == SPOE_CTX_ST_NONE)
3178 goto out;
3179
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003180 if (!(chn->flags & CF_ISRESP)) {
3181 if (filter->pre_analyzers & AN_REQ_INSPECT_FE)
3182 chn->analysers |= AN_REQ_INSPECT_FE;
3183 if (filter->pre_analyzers & AN_REQ_INSPECT_BE)
3184 chn->analysers |= AN_REQ_INSPECT_BE;
3185
3186 if (ctx->flags & SPOE_CTX_FL_CLI_CONNECTED)
3187 goto out;
3188
3189 ctx->stream_id = s->uniq_id;
Christopher Faulet8ef75252017-02-20 22:56:03 +01003190 ret = spoe_process_event(s, ctx, SPOE_EV_ON_CLIENT_SESS);
Christopher Fauletb067b062017-01-04 16:39:11 +01003191 if (!ret)
3192 goto out;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003193 ctx->flags |= SPOE_CTX_FL_CLI_CONNECTED;
3194 }
3195 else {
3196 if (filter->pre_analyzers & SPOE_EV_ON_TCP_RSP)
3197 chn->analysers |= AN_RES_INSPECT;
3198
3199 if (ctx->flags & SPOE_CTX_FL_SRV_CONNECTED)
3200 goto out;
3201
Christopher Faulet8ef75252017-02-20 22:56:03 +01003202 ret = spoe_process_event(s, ctx, SPOE_EV_ON_SERVER_SESS);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003203 if (!ret) {
3204 channel_dont_read(chn);
3205 channel_dont_close(chn);
Christopher Fauletb067b062017-01-04 16:39:11 +01003206 goto out;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003207 }
Christopher Fauletb067b062017-01-04 16:39:11 +01003208 ctx->flags |= SPOE_CTX_FL_SRV_CONNECTED;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003209 }
3210
3211 out:
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003212 return ret;
3213}
3214
3215/* Called before a processing happens on a given channel */
3216static int
3217spoe_chn_pre_analyze(struct stream *s, struct filter *filter,
3218 struct channel *chn, unsigned an_bit)
3219{
3220 struct spoe_context *ctx = filter->ctx;
3221 int ret = 1;
3222
3223 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p - ctx-state=%s"
3224 " - ctx-flags=0x%08x - ana=0x%08x\n",
3225 (int)now.tv_sec, (int)now.tv_usec,
3226 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
3227 __FUNCTION__, s, spoe_ctx_state_str[ctx->state],
3228 ctx->flags, an_bit);
3229
Christopher Fauletb067b062017-01-04 16:39:11 +01003230 if (ctx->state == SPOE_CTX_ST_NONE)
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003231 goto out;
3232
3233 switch (an_bit) {
3234 case AN_REQ_INSPECT_FE:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003235 ret = spoe_process_event(s, ctx, SPOE_EV_ON_TCP_REQ_FE);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003236 break;
3237 case AN_REQ_INSPECT_BE:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003238 ret = spoe_process_event(s, ctx, SPOE_EV_ON_TCP_REQ_BE);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003239 break;
3240 case AN_RES_INSPECT:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003241 ret = spoe_process_event(s, ctx, SPOE_EV_ON_TCP_RSP);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003242 break;
3243 case AN_REQ_HTTP_PROCESS_FE:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003244 ret = spoe_process_event(s, ctx, SPOE_EV_ON_HTTP_REQ_FE);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003245 break;
3246 case AN_REQ_HTTP_PROCESS_BE:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003247 ret = spoe_process_event(s, ctx, SPOE_EV_ON_HTTP_REQ_BE);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003248 break;
3249 case AN_RES_HTTP_PROCESS_FE:
Christopher Faulet8ef75252017-02-20 22:56:03 +01003250 ret = spoe_process_event(s, ctx, SPOE_EV_ON_HTTP_RSP);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003251 break;
3252 }
3253
3254 out:
Christopher Faulet9cdca972018-02-01 08:45:45 +01003255 if (!ret && (chn->flags & CF_ISRESP)) {
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003256 channel_dont_read(chn);
3257 channel_dont_close(chn);
3258 }
3259 return ret;
3260}
3261
3262/* Called when the filtering on the channel ends. */
3263static int
3264spoe_end_analyze(struct stream *s, struct filter *filter, struct channel *chn)
3265{
3266 struct spoe_context *ctx = filter->ctx;
3267
3268 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p - ctx-state=%s"
3269 " - ctx-flags=0x%08x\n",
3270 (int)now.tv_sec, (int)now.tv_usec,
3271 ((struct spoe_config *)FLT_CONF(filter))->agent->id,
3272 __FUNCTION__, s, spoe_ctx_state_str[ctx->state], ctx->flags);
3273
3274 if (!(ctx->flags & SPOE_CTX_FL_PROCESS)) {
Christopher Faulet8ef75252017-02-20 22:56:03 +01003275 spoe_reset_context(ctx);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003276 }
3277
3278 return 1;
3279}
3280
3281/********************************************************************
3282 * Functions that manage the filter initialization
3283 ********************************************************************/
3284struct flt_ops spoe_ops = {
3285 /* Manage SPOE filter, called for each filter declaration */
3286 .init = spoe_init,
3287 .deinit = spoe_deinit,
3288 .check = spoe_check,
Kevin Zhud87b1a52019-09-17 15:05:45 +02003289 .init_per_thread = spoe_init_per_thread,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003290
3291 /* Handle start/stop of SPOE */
Christopher Fauletf7a30922016-11-10 15:04:51 +01003292 .attach = spoe_start,
3293 .detach = spoe_stop,
3294 .check_timeouts = spoe_check_timeouts,
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003295
3296 /* Handle channels activity */
3297 .channel_start_analyze = spoe_start_analyze,
3298 .channel_pre_analyze = spoe_chn_pre_analyze,
3299 .channel_end_analyze = spoe_end_analyze,
3300};
3301
3302
3303static int
3304cfg_parse_spoe_agent(const char *file, int linenum, char **args, int kwm)
3305{
3306 const char *err;
3307 int i, err_code = 0;
3308
3309 if ((cfg_scope == NULL && curengine != NULL) ||
3310 (cfg_scope != NULL && curengine == NULL) ||
Christopher Faulete1405e52017-09-19 10:35:35 +02003311 (curengine != NULL && cfg_scope != NULL && strcmp(curengine, cfg_scope)))
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003312 goto out;
3313
3314 if (!strcmp(args[0], "spoe-agent")) { /* new spoe-agent section */
3315 if (!*args[1]) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003316 ha_alert("parsing [%s:%d] : missing name for spoe-agent section.\n",
3317 file, linenum);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003318 err_code |= ERR_ALERT | ERR_ABORT;
3319 goto out;
3320 }
Christopher Fauletecc537a2017-02-23 22:52:39 +01003321 if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
3322 err_code |= ERR_ABORT;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003323 goto out;
3324 }
3325
3326 err = invalid_char(args[1]);
3327 if (err) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003328 ha_alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
3329 file, linenum, *err, args[0], args[1]);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003330 err_code |= ERR_ALERT | ERR_ABORT;
3331 goto out;
3332 }
3333
3334 if (curagent != NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003335 ha_alert("parsing [%s:%d] : another spoe-agent section previously defined.\n",
3336 file, linenum);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003337 err_code |= ERR_ALERT | ERR_ABORT;
3338 goto out;
3339 }
3340 if ((curagent = calloc(1, sizeof(*curagent))) == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003341 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003342 err_code |= ERR_ALERT | ERR_ABORT;
3343 goto out;
3344 }
3345
3346 curagent->id = strdup(args[1]);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003347
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003348 curagent->conf.file = strdup(file);
3349 curagent->conf.line = linenum;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003350
3351 curagent->timeout.hello = TICK_ETERNITY;
3352 curagent->timeout.idle = TICK_ETERNITY;
Christopher Fauletf7a30922016-11-10 15:04:51 +01003353 curagent->timeout.processing = TICK_ETERNITY;
Christopher Fauleta1cda022016-12-21 08:58:06 +01003354
Christopher Fauleta1cda022016-12-21 08:58:06 +01003355 curagent->var_pfx = NULL;
3356 curagent->var_on_error = NULL;
Christopher Faulet36bda1c2018-03-22 09:08:20 +01003357 curagent->var_t_process = NULL;
3358 curagent->var_t_total = NULL;
Christopher Fauletb1bb1af2019-09-17 11:55:52 +02003359 curagent->flags = (SPOE_FL_ASYNC | SPOE_FL_PIPELINING | SPOE_FL_SND_FRAGMENTATION);
Christopher Fauleta1cda022016-12-21 08:58:06 +01003360 curagent->cps_max = 0;
3361 curagent->eps_max = 0;
Christopher Faulet7aa0b2b2017-01-13 11:30:50 +01003362 curagent->max_frame_size = MAX_FRAME_SIZE;
Christopher Fauletb077cdc2018-01-24 16:37:57 +01003363 curagent->max_fpa = 20;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003364
3365 for (i = 0; i < SPOE_EV_EVENTS; ++i)
Christopher Faulet11610f32017-09-21 10:23:10 +02003366 LIST_INIT(&curagent->events[i]);
3367 LIST_INIT(&curagent->groups);
3368 LIST_INIT(&curagent->messages);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003369 }
3370 else if (!strcmp(args[0], "use-backend")) {
3371 if (!*args[1]) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003372 ha_alert("parsing [%s:%d] : '%s' expects a backend name.\n",
3373 file, linenum, args[0]);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003374 err_code |= ERR_ALERT | ERR_FATAL;
3375 goto out;
3376 }
Christopher Fauletecc537a2017-02-23 22:52:39 +01003377 if (alertif_too_many_args(1, file, linenum, args, &err_code))
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003378 goto out;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003379 free(curagent->b.name);
3380 curagent->b.name = strdup(args[1]);
3381 }
3382 else if (!strcmp(args[0], "messages")) {
3383 int cur_arg = 1;
3384 while (*args[cur_arg]) {
Christopher Faulet11610f32017-09-21 10:23:10 +02003385 struct spoe_placeholder *ph = NULL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003386
Christopher Faulet11610f32017-09-21 10:23:10 +02003387 list_for_each_entry(ph, &curmphs, list) {
3388 if (!strcmp(ph->id, args[cur_arg])) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003389 ha_alert("parsing [%s:%d]: spoe-message '%s' already used.\n",
3390 file, linenum, args[cur_arg]);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003391 err_code |= ERR_ALERT | ERR_FATAL;
3392 goto out;
3393 }
3394 }
3395
Christopher Faulet11610f32017-09-21 10:23:10 +02003396 if ((ph = calloc(1, sizeof(*ph))) == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003397 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003398 err_code |= ERR_ALERT | ERR_ABORT;
3399 goto out;
3400 }
Christopher Faulet11610f32017-09-21 10:23:10 +02003401 ph->id = strdup(args[cur_arg]);
3402 LIST_ADDQ(&curmphs, &ph->list);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003403 cur_arg++;
3404 }
3405 }
Christopher Faulet11610f32017-09-21 10:23:10 +02003406 else if (!strcmp(args[0], "groups")) {
3407 int cur_arg = 1;
3408 while (*args[cur_arg]) {
3409 struct spoe_placeholder *ph = NULL;
3410
3411 list_for_each_entry(ph, &curgphs, list) {
3412 if (!strcmp(ph->id, args[cur_arg])) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003413 ha_alert("parsing [%s:%d]: spoe-group '%s' already used.\n",
3414 file, linenum, args[cur_arg]);
Christopher Faulet11610f32017-09-21 10:23:10 +02003415 err_code |= ERR_ALERT | ERR_FATAL;
3416 goto out;
3417 }
3418 }
3419
3420 if ((ph = calloc(1, sizeof(*ph))) == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003421 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
Christopher Faulet11610f32017-09-21 10:23:10 +02003422 err_code |= ERR_ALERT | ERR_ABORT;
3423 goto out;
3424 }
3425 ph->id = strdup(args[cur_arg]);
3426 LIST_ADDQ(&curgphs, &ph->list);
3427 cur_arg++;
3428 }
3429 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003430 else if (!strcmp(args[0], "timeout")) {
3431 unsigned int *tv = NULL;
3432 const char *res;
3433 unsigned timeout;
3434
3435 if (!*args[1]) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003436 ha_alert("parsing [%s:%d] : 'timeout' expects 'hello', 'idle' and 'processing'.\n",
3437 file, linenum);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003438 err_code |= ERR_ALERT | ERR_FATAL;
3439 goto out;
3440 }
Christopher Fauletecc537a2017-02-23 22:52:39 +01003441 if (alertif_too_many_args(2, file, linenum, args, &err_code))
3442 goto out;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003443 if (!strcmp(args[1], "hello"))
3444 tv = &curagent->timeout.hello;
3445 else if (!strcmp(args[1], "idle"))
3446 tv = &curagent->timeout.idle;
Christopher Fauletf7a30922016-11-10 15:04:51 +01003447 else if (!strcmp(args[1], "processing"))
3448 tv = &curagent->timeout.processing;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003449 else {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003450 ha_alert("parsing [%s:%d] : 'timeout' supports 'hello', 'idle' or 'processing' (got %s).\n",
3451 file, linenum, args[1]);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003452 err_code |= ERR_ALERT | ERR_FATAL;
3453 goto out;
3454 }
3455 if (!*args[2]) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003456 ha_alert("parsing [%s:%d] : 'timeout %s' expects an integer value (in milliseconds).\n",
3457 file, linenum, args[1]);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003458 err_code |= ERR_ALERT | ERR_FATAL;
3459 goto out;
3460 }
3461 res = parse_time_err(args[2], &timeout, TIME_UNIT_MS);
Willy Tarreau9faebe32019-06-07 19:00:37 +02003462 if (res == PARSE_TIME_OVER) {
3463 ha_alert("parsing [%s:%d]: timer overflow in argument <%s> to <%s %s>, maximum value is 2147483647 ms (~24.8 days).\n",
3464 file, linenum, args[2], args[0], args[1]);
3465 err_code |= ERR_ALERT | ERR_FATAL;
3466 goto out;
3467 }
3468 else if (res == PARSE_TIME_UNDER) {
3469 ha_alert("parsing [%s:%d]: timer underflow in argument <%s> to <%s %s>, minimum non-null value is 1 ms.\n",
3470 file, linenum, args[2], args[0], args[1]);
3471 err_code |= ERR_ALERT | ERR_FATAL;
3472 goto out;
3473 }
3474 else if (res) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003475 ha_alert("parsing [%s:%d] : unexpected character '%c' in 'timeout %s'.\n",
3476 file, linenum, *res, args[1]);
Christopher Fauletecc537a2017-02-23 22:52:39 +01003477 err_code |= ERR_ALERT | ERR_FATAL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003478 goto out;
3479 }
3480 *tv = MS_TO_TICKS(timeout);
3481 }
3482 else if (!strcmp(args[0], "option")) {
3483 if (!*args[1]) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003484 ha_alert("parsing [%s:%d]: '%s' expects an option name.\n",
3485 file, linenum, args[0]);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003486 err_code |= ERR_ALERT | ERR_FATAL;
3487 goto out;
3488 }
Christopher Faulet6a2940c2017-02-23 15:06:26 +01003489
Christopher Faulet305c6072017-02-23 16:17:53 +01003490 if (!strcmp(args[1], "pipelining")) {
Christopher Fauletecc537a2017-02-23 22:52:39 +01003491 if (alertif_too_many_args(1, file, linenum, args, &err_code))
Christopher Faulet305c6072017-02-23 16:17:53 +01003492 goto out;
Christopher Faulet305c6072017-02-23 16:17:53 +01003493 if (kwm == 1)
3494 curagent->flags &= ~SPOE_FL_PIPELINING;
3495 else
3496 curagent->flags |= SPOE_FL_PIPELINING;
3497 goto out;
3498 }
3499 else if (!strcmp(args[1], "async")) {
Christopher Fauletecc537a2017-02-23 22:52:39 +01003500 if (alertif_too_many_args(1, file, linenum, args, &err_code))
Christopher Faulet305c6072017-02-23 16:17:53 +01003501 goto out;
Christopher Faulet305c6072017-02-23 16:17:53 +01003502 if (kwm == 1)
3503 curagent->flags &= ~SPOE_FL_ASYNC;
Christopher Fauletb1bb1af2019-09-17 11:55:52 +02003504 else
3505 curagent->flags |= SPOE_FL_ASYNC;
Christopher Faulet305c6072017-02-23 16:17:53 +01003506 goto out;
3507 }
Christopher Fauletcecd8522017-02-24 22:11:21 +01003508 else if (!strcmp(args[1], "send-frag-payload")) {
3509 if (alertif_too_many_args(1, file, linenum, args, &err_code))
3510 goto out;
3511 if (kwm == 1)
3512 curagent->flags &= ~SPOE_FL_SND_FRAGMENTATION;
3513 else
3514 curagent->flags |= SPOE_FL_SND_FRAGMENTATION;
3515 goto out;
3516 }
Christopher Faulet0e0f0852018-03-26 17:20:36 +02003517 else if (!strcmp(args[1], "dontlog-normal")) {
3518 if (alertif_too_many_args(1, file, linenum, args, &err_code))
3519 goto out;
3520 if (kwm == 1)
3521 curpxopts2 &= ~PR_O2_NOLOGNORM;
3522 else
3523 curpxopts2 |= PR_O2_NOLOGNORM;
Christopher Faulet799f5182018-04-26 11:36:34 +02003524 goto out;
Christopher Faulet0e0f0852018-03-26 17:20:36 +02003525 }
Christopher Faulet305c6072017-02-23 16:17:53 +01003526
Christopher Faulet6a2940c2017-02-23 15:06:26 +01003527 /* Following options does not support negation */
3528 if (kwm == 1) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003529 ha_alert("parsing [%s:%d]: negation is not supported for option '%s'.\n",
3530 file, linenum, args[1]);
Christopher Faulet6a2940c2017-02-23 15:06:26 +01003531 err_code |= ERR_ALERT | ERR_FATAL;
3532 goto out;
3533 }
3534
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003535 if (!strcmp(args[1], "var-prefix")) {
3536 char *tmp;
3537
3538 if (!*args[2]) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003539 ha_alert("parsing [%s:%d]: '%s %s' expects a value.\n",
3540 file, linenum, args[0],
3541 args[1]);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003542 err_code |= ERR_ALERT | ERR_FATAL;
3543 goto out;
3544 }
Christopher Fauletecc537a2017-02-23 22:52:39 +01003545 if (alertif_too_many_args(2, file, linenum, args, &err_code))
3546 goto out;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003547 tmp = args[2];
3548 while (*tmp) {
Willy Tarreau90807112020-02-25 08:16:33 +01003549 if (!isalnum((unsigned char)*tmp) && *tmp != '_' && *tmp != '.') {
Thierry FOURNIER01a3f202018-05-10 16:41:26 +02003550 ha_alert("parsing [%s:%d]: '%s %s' only supports [a-zA-Z0-9_.] chars.\n",
Christopher Faulet767a84b2017-11-24 16:50:31 +01003551 file, linenum, args[0], args[1]);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003552 err_code |= ERR_ALERT | ERR_FATAL;
3553 goto out;
3554 }
3555 tmp++;
3556 }
3557 curagent->var_pfx = strdup(args[2]);
3558 }
Etienne Carriereaec89892017-12-14 09:36:40 +00003559 else if (!strcmp(args[1], "force-set-var")) {
3560 if (alertif_too_many_args(1, file, linenum, args, &err_code))
3561 goto out;
3562 curagent->flags |= SPOE_FL_FORCE_SET_VAR;
3563 }
Christopher Fauletea62c2a2016-11-14 10:54:21 +01003564 else if (!strcmp(args[1], "continue-on-error")) {
Christopher Fauletecc537a2017-02-23 22:52:39 +01003565 if (alertif_too_many_args(1, file, linenum, args, &err_code))
Christopher Fauletea62c2a2016-11-14 10:54:21 +01003566 goto out;
Christopher Fauletea62c2a2016-11-14 10:54:21 +01003567 curagent->flags |= SPOE_FL_CONT_ON_ERR;
3568 }
Christopher Faulet985532d2016-11-16 15:36:19 +01003569 else if (!strcmp(args[1], "set-on-error")) {
3570 char *tmp;
3571
3572 if (!*args[2]) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003573 ha_alert("parsing [%s:%d]: '%s %s' expects a value.\n",
3574 file, linenum, args[0],
3575 args[1]);
Christopher Faulet985532d2016-11-16 15:36:19 +01003576 err_code |= ERR_ALERT | ERR_FATAL;
3577 goto out;
3578 }
Christopher Fauletecc537a2017-02-23 22:52:39 +01003579 if (alertif_too_many_args(2, file, linenum, args, &err_code))
3580 goto out;
Christopher Faulet985532d2016-11-16 15:36:19 +01003581 tmp = args[2];
3582 while (*tmp) {
Willy Tarreau90807112020-02-25 08:16:33 +01003583 if (!isalnum((unsigned char)*tmp) && *tmp != '_' && *tmp != '.') {
Thierry FOURNIER01a3f202018-05-10 16:41:26 +02003584 ha_alert("parsing [%s:%d]: '%s %s' only supports [a-zA-Z0-9_.] chars.\n",
Christopher Faulet767a84b2017-11-24 16:50:31 +01003585 file, linenum, args[0], args[1]);
Christopher Faulet985532d2016-11-16 15:36:19 +01003586 err_code |= ERR_ALERT | ERR_FATAL;
3587 goto out;
3588 }
3589 tmp++;
3590 }
3591 curagent->var_on_error = strdup(args[2]);
3592 }
Christopher Faulet36bda1c2018-03-22 09:08:20 +01003593 else if (!strcmp(args[1], "set-process-time")) {
3594 char *tmp;
3595
3596 if (!*args[2]) {
3597 ha_alert("parsing [%s:%d]: '%s %s' expects a value.\n",
3598 file, linenum, args[0],
3599 args[1]);
3600 err_code |= ERR_ALERT | ERR_FATAL;
3601 goto out;
3602 }
3603 if (alertif_too_many_args(2, file, linenum, args, &err_code))
3604 goto out;
3605 tmp = args[2];
3606 while (*tmp) {
Willy Tarreau90807112020-02-25 08:16:33 +01003607 if (!isalnum((unsigned char)*tmp) && *tmp != '_' && *tmp != '.') {
Thierry FOURNIER01a3f202018-05-10 16:41:26 +02003608 ha_alert("parsing [%s:%d]: '%s %s' only supports [a-zA-Z0-9_.] chars.\n",
Christopher Faulet36bda1c2018-03-22 09:08:20 +01003609 file, linenum, args[0], args[1]);
3610 err_code |= ERR_ALERT | ERR_FATAL;
3611 goto out;
3612 }
3613 tmp++;
3614 }
3615 curagent->var_t_process = strdup(args[2]);
3616 }
3617 else if (!strcmp(args[1], "set-total-time")) {
3618 char *tmp;
3619
3620 if (!*args[2]) {
3621 ha_alert("parsing [%s:%d]: '%s %s' expects a value.\n",
3622 file, linenum, args[0],
3623 args[1]);
3624 err_code |= ERR_ALERT | ERR_FATAL;
3625 goto out;
3626 }
3627 if (alertif_too_many_args(2, file, linenum, args, &err_code))
3628 goto out;
3629 tmp = args[2];
3630 while (*tmp) {
Willy Tarreau90807112020-02-25 08:16:33 +01003631 if (!isalnum((unsigned char)*tmp) && *tmp != '_' && *tmp != '.') {
Thierry FOURNIER01a3f202018-05-10 16:41:26 +02003632 ha_alert("parsing [%s:%d]: '%s %s' only supports [a-zA-Z0-9_.] chars.\n",
Christopher Faulet36bda1c2018-03-22 09:08:20 +01003633 file, linenum, args[0], args[1]);
3634 err_code |= ERR_ALERT | ERR_FATAL;
3635 goto out;
3636 }
3637 tmp++;
3638 }
3639 curagent->var_t_total = strdup(args[2]);
3640 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003641 else {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003642 ha_alert("parsing [%s:%d]: option '%s' is not supported.\n",
3643 file, linenum, args[1]);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003644 err_code |= ERR_ALERT | ERR_FATAL;
3645 goto out;
3646 }
Christopher Faulet48026722016-11-16 15:01:12 +01003647 }
3648 else if (!strcmp(args[0], "maxconnrate")) {
3649 if (!*args[1]) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003650 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n",
3651 file, linenum, args[0]);
Christopher Faulet48026722016-11-16 15:01:12 +01003652 err_code |= ERR_ALERT | ERR_FATAL;
3653 goto out;
3654 }
Christopher Fauletecc537a2017-02-23 22:52:39 +01003655 if (alertif_too_many_args(1, file, linenum, args, &err_code))
Christopher Faulet48026722016-11-16 15:01:12 +01003656 goto out;
Christopher Faulet48026722016-11-16 15:01:12 +01003657 curagent->cps_max = atol(args[1]);
3658 }
3659 else if (!strcmp(args[0], "maxerrrate")) {
3660 if (!*args[1]) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003661 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n",
3662 file, linenum, args[0]);
Christopher Faulet48026722016-11-16 15:01:12 +01003663 err_code |= ERR_ALERT | ERR_FATAL;
3664 goto out;
3665 }
Christopher Fauletecc537a2017-02-23 22:52:39 +01003666 if (alertif_too_many_args(1, file, linenum, args, &err_code))
Christopher Faulet48026722016-11-16 15:01:12 +01003667 goto out;
Christopher Faulet48026722016-11-16 15:01:12 +01003668 curagent->eps_max = atol(args[1]);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003669 }
Christopher Faulet2eca6b52017-02-27 09:40:34 +01003670 else if (!strcmp(args[0], "max-frame-size")) {
3671 if (!*args[1]) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003672 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n",
3673 file, linenum, args[0]);
Christopher Faulet2eca6b52017-02-27 09:40:34 +01003674 err_code |= ERR_ALERT | ERR_FATAL;
3675 goto out;
3676 }
3677 if (alertif_too_many_args(1, file, linenum, args, &err_code))
3678 goto out;
3679 curagent->max_frame_size = atol(args[1]);
3680 if (curagent->max_frame_size < MIN_FRAME_SIZE ||
3681 curagent->max_frame_size > MAX_FRAME_SIZE) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003682 ha_alert("parsing [%s:%d] : '%s' expects a positive integer argument in the range [%d, %d].\n",
3683 file, linenum, args[0], MIN_FRAME_SIZE, MAX_FRAME_SIZE);
Christopher Faulet2eca6b52017-02-27 09:40:34 +01003684 err_code |= ERR_ALERT | ERR_FATAL;
3685 goto out;
3686 }
3687 }
Christopher Faulete8ade382018-01-25 15:32:22 +01003688 else if (!strcmp(args[0], "max-waiting-frames")) {
3689 if (!*args[1]) {
3690 ha_alert("parsing [%s:%d] : '%s' expects an integer argument.\n",
3691 file, linenum, args[0]);
3692 err_code |= ERR_ALERT | ERR_FATAL;
3693 goto out;
3694 }
3695 if (alertif_too_many_args(1, file, linenum, args, &err_code))
3696 goto out;
3697 curagent->max_fpa = atol(args[1]);
3698 if (curagent->max_fpa < 1) {
3699 ha_alert("parsing [%s:%d] : '%s' expects a positive integer argument.\n",
3700 file, linenum, args[0]);
3701 err_code |= ERR_ALERT | ERR_FATAL;
3702 goto out;
3703 }
3704 }
Christopher Faulet336d3ef2017-12-22 10:00:55 +01003705 else if (!strcmp(args[0], "register-var-names")) {
3706 int cur_arg;
3707
3708 if (!*args[1]) {
3709 ha_alert("parsing [%s:%d] : '%s' expects one or more variable names.\n",
3710 file, linenum, args[0]);
3711 err_code |= ERR_ALERT | ERR_FATAL;
3712 goto out;
3713 }
3714 cur_arg = 1;
3715 while (*args[cur_arg]) {
3716 struct spoe_var_placeholder *vph;
3717
3718 if ((vph = calloc(1, sizeof(*vph))) == NULL) {
3719 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3720 err_code |= ERR_ALERT | ERR_ABORT;
3721 goto out;
3722 }
3723 if ((vph->name = strdup(args[cur_arg])) == NULL) {
Tim Duesterhusb2986132019-06-23 22:10:13 +02003724 free(vph);
Christopher Faulet336d3ef2017-12-22 10:00:55 +01003725 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
3726 err_code |= ERR_ALERT | ERR_ABORT;
3727 goto out;
3728 }
3729 LIST_ADDQ(&curvars, &vph->list);
3730 cur_arg++;
3731 }
3732 }
Christopher Faulet7250b8f2018-03-26 17:19:01 +02003733 else if (!strcmp(args[0], "log")) {
3734 char *errmsg = NULL;
3735
3736 if (!parse_logsrv(args, &curlogsrvs, (kwm == 1), &errmsg)) {
3737 ha_alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg);
3738 err_code |= ERR_ALERT | ERR_FATAL;
3739 goto out;
3740 }
3741 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003742 else if (*args[0]) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003743 ha_alert("parsing [%s:%d] : unknown keyword '%s' in spoe-agent section.\n",
3744 file, linenum, args[0]);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003745 err_code |= ERR_ALERT | ERR_FATAL;
3746 goto out;
3747 }
3748 out:
3749 return err_code;
3750}
Christopher Faulet11610f32017-09-21 10:23:10 +02003751static int
3752cfg_parse_spoe_group(const char *file, int linenum, char **args, int kwm)
3753{
3754 struct spoe_group *grp;
3755 const char *err;
3756 int err_code = 0;
3757
3758 if ((cfg_scope == NULL && curengine != NULL) ||
3759 (cfg_scope != NULL && curengine == NULL) ||
3760 (curengine != NULL && cfg_scope != NULL && strcmp(curengine, cfg_scope)))
3761 goto out;
3762
3763 if (!strcmp(args[0], "spoe-group")) { /* new spoe-group section */
3764 if (!*args[1]) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003765 ha_alert("parsing [%s:%d] : missing name for spoe-group section.\n",
3766 file, linenum);
Christopher Faulet11610f32017-09-21 10:23:10 +02003767 err_code |= ERR_ALERT | ERR_ABORT;
3768 goto out;
3769 }
3770 if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
3771 err_code |= ERR_ABORT;
3772 goto out;
3773 }
3774
3775 err = invalid_char(args[1]);
3776 if (err) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003777 ha_alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
3778 file, linenum, *err, args[0], args[1]);
Christopher Faulet11610f32017-09-21 10:23:10 +02003779 err_code |= ERR_ALERT | ERR_ABORT;
3780 goto out;
3781 }
3782
3783 list_for_each_entry(grp, &curgrps, list) {
3784 if (!strcmp(grp->id, args[1])) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003785 ha_alert("parsing [%s:%d]: spoe-group section '%s' has the same"
3786 " name as another one declared at %s:%d.\n",
3787 file, linenum, args[1], grp->conf.file, grp->conf.line);
Christopher Faulet11610f32017-09-21 10:23:10 +02003788 err_code |= ERR_ALERT | ERR_FATAL;
3789 goto out;
3790 }
3791 }
3792
3793 if ((curgrp = calloc(1, sizeof(*curgrp))) == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003794 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
Christopher Faulet11610f32017-09-21 10:23:10 +02003795 err_code |= ERR_ALERT | ERR_ABORT;
3796 goto out;
3797 }
3798
3799 curgrp->id = strdup(args[1]);
3800 curgrp->conf.file = strdup(file);
3801 curgrp->conf.line = linenum;
3802 LIST_INIT(&curgrp->phs);
3803 LIST_INIT(&curgrp->messages);
3804 LIST_ADDQ(&curgrps, &curgrp->list);
3805 }
3806 else if (!strcmp(args[0], "messages")) {
3807 int cur_arg = 1;
3808 while (*args[cur_arg]) {
3809 struct spoe_placeholder *ph = NULL;
3810
3811 list_for_each_entry(ph, &curgrp->phs, list) {
3812 if (!strcmp(ph->id, args[cur_arg])) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003813 ha_alert("parsing [%s:%d]: spoe-message '%s' already used.\n",
3814 file, linenum, args[cur_arg]);
Christopher Faulet11610f32017-09-21 10:23:10 +02003815 err_code |= ERR_ALERT | ERR_FATAL;
3816 goto out;
3817 }
3818 }
3819
3820 if ((ph = calloc(1, sizeof(*ph))) == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003821 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
Christopher Faulet11610f32017-09-21 10:23:10 +02003822 err_code |= ERR_ALERT | ERR_ABORT;
3823 goto out;
3824 }
3825 ph->id = strdup(args[cur_arg]);
3826 LIST_ADDQ(&curgrp->phs, &ph->list);
3827 cur_arg++;
3828 }
3829 }
3830 else if (*args[0]) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003831 ha_alert("parsing [%s:%d] : unknown keyword '%s' in spoe-group section.\n",
3832 file, linenum, args[0]);
Christopher Faulet11610f32017-09-21 10:23:10 +02003833 err_code |= ERR_ALERT | ERR_FATAL;
3834 goto out;
3835 }
3836 out:
3837 return err_code;
3838}
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003839
3840static int
3841cfg_parse_spoe_message(const char *file, int linenum, char **args, int kwm)
3842{
3843 struct spoe_message *msg;
3844 struct spoe_arg *arg;
3845 const char *err;
3846 char *errmsg = NULL;
3847 int err_code = 0;
3848
3849 if ((cfg_scope == NULL && curengine != NULL) ||
3850 (cfg_scope != NULL && curengine == NULL) ||
Christopher Faulete1405e52017-09-19 10:35:35 +02003851 (curengine != NULL && cfg_scope != NULL && strcmp(curengine, cfg_scope)))
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003852 goto out;
3853
3854 if (!strcmp(args[0], "spoe-message")) { /* new spoe-message section */
3855 if (!*args[1]) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003856 ha_alert("parsing [%s:%d] : missing name for spoe-message section.\n",
3857 file, linenum);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003858 err_code |= ERR_ALERT | ERR_ABORT;
3859 goto out;
3860 }
Christopher Fauletecc537a2017-02-23 22:52:39 +01003861 if (alertif_too_many_args(1, file, linenum, args, &err_code)) {
3862 err_code |= ERR_ABORT;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003863 goto out;
3864 }
3865
3866 err = invalid_char(args[1]);
3867 if (err) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003868 ha_alert("parsing [%s:%d] : character '%c' is not permitted in '%s' name '%s'.\n",
3869 file, linenum, *err, args[0], args[1]);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003870 err_code |= ERR_ALERT | ERR_ABORT;
3871 goto out;
3872 }
3873
3874 list_for_each_entry(msg, &curmsgs, list) {
3875 if (!strcmp(msg->id, args[1])) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003876 ha_alert("parsing [%s:%d]: spoe-message section '%s' has the same"
3877 " name as another one declared at %s:%d.\n",
3878 file, linenum, args[1], msg->conf.file, msg->conf.line);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003879 err_code |= ERR_ALERT | ERR_FATAL;
3880 goto out;
3881 }
3882 }
3883
3884 if ((curmsg = calloc(1, sizeof(*curmsg))) == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003885 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003886 err_code |= ERR_ALERT | ERR_ABORT;
3887 goto out;
3888 }
3889
3890 curmsg->id = strdup(args[1]);
3891 curmsg->id_len = strlen(curmsg->id);
3892 curmsg->event = SPOE_EV_NONE;
3893 curmsg->conf.file = strdup(file);
3894 curmsg->conf.line = linenum;
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01003895 curmsg->nargs = 0;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003896 LIST_INIT(&curmsg->args);
Christopher Faulet57583e42017-09-04 15:41:09 +02003897 LIST_INIT(&curmsg->acls);
Christopher Faulet11610f32017-09-21 10:23:10 +02003898 LIST_INIT(&curmsg->by_evt);
3899 LIST_INIT(&curmsg->by_grp);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003900 LIST_ADDQ(&curmsgs, &curmsg->list);
3901 }
3902 else if (!strcmp(args[0], "args")) {
3903 int cur_arg = 1;
3904
3905 curproxy->conf.args.ctx = ARGC_SPOE;
3906 curproxy->conf.args.file = file;
3907 curproxy->conf.args.line = linenum;
3908 while (*args[cur_arg]) {
3909 char *delim = strchr(args[cur_arg], '=');
3910 int idx = 0;
3911
3912 if ((arg = calloc(1, sizeof(*arg))) == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003913 ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003914 err_code |= ERR_ALERT | ERR_ABORT;
3915 goto out;
3916 }
3917
3918 if (!delim) {
3919 arg->name = NULL;
3920 arg->name_len = 0;
3921 delim = args[cur_arg];
3922 }
3923 else {
3924 arg->name = my_strndup(args[cur_arg], delim - args[cur_arg]);
3925 arg->name_len = delim - args[cur_arg];
3926 delim++;
3927 }
Christopher Fauletb0b42382017-02-23 22:41:09 +01003928 arg->expr = sample_parse_expr((char*[]){delim, NULL},
3929 &idx, file, linenum, &errmsg,
Willy Tarreaue3b57bf2020-02-14 16:50:14 +01003930 &curproxy->conf.args, NULL);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003931 if (arg->expr == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003932 ha_alert("parsing [%s:%d] : '%s': %s.\n", file, linenum, args[0], errmsg);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003933 err_code |= ERR_ALERT | ERR_FATAL;
3934 free(arg->name);
3935 free(arg);
3936 goto out;
3937 }
Christopher Fauletf51f5fa2017-01-19 10:01:12 +01003938 curmsg->nargs++;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003939 LIST_ADDQ(&curmsg->args, &arg->list);
3940 cur_arg++;
3941 }
3942 curproxy->conf.args.file = NULL;
3943 curproxy->conf.args.line = 0;
3944 }
Christopher Faulet57583e42017-09-04 15:41:09 +02003945 else if (!strcmp(args[0], "acl")) {
3946 err = invalid_char(args[1]);
3947 if (err) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003948 ha_alert("parsing [%s:%d] : character '%c' is not permitted in acl name '%s'.\n",
3949 file, linenum, *err, args[1]);
Christopher Faulet57583e42017-09-04 15:41:09 +02003950 err_code |= ERR_ALERT | ERR_FATAL;
3951 goto out;
3952 }
Tim Duesterhus0cf811a2020-02-05 21:00:50 +01003953 if (strcasecmp(args[1], "or") == 0) {
Tim Duesterhusf1bc24c2020-02-06 22:04:03 +01003954 ha_alert("parsing [%s:%d] : acl name '%s' will never match. 'or' is used to express a "
Tim Duesterhus0cf811a2020-02-05 21:00:50 +01003955 "logical disjunction within a condition.\n",
3956 file, linenum, args[1]);
3957 err_code |= ERR_ALERT | ERR_FATAL;
3958 goto out;
3959 }
Christopher Faulet57583e42017-09-04 15:41:09 +02003960 if (parse_acl((const char **)args + 1, &curmsg->acls, &errmsg, &curproxy->conf.args, file, linenum) == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003961 ha_alert("parsing [%s:%d] : error detected while parsing ACL '%s' : %s.\n",
3962 file, linenum, args[1], errmsg);
Christopher Faulet57583e42017-09-04 15:41:09 +02003963 err_code |= ERR_ALERT | ERR_FATAL;
3964 goto out;
3965 }
3966 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003967 else if (!strcmp(args[0], "event")) {
3968 if (!*args[1]) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01003969 ha_alert("parsing [%s:%d] : missing event name.\n", file, linenum);
Christopher Fauletecc537a2017-02-23 22:52:39 +01003970 err_code |= ERR_ALERT | ERR_FATAL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003971 goto out;
3972 }
Christopher Faulet57583e42017-09-04 15:41:09 +02003973 /* if (alertif_too_many_args(1, file, linenum, args, &err_code)) */
3974 /* goto out; */
Christopher Fauletecc537a2017-02-23 22:52:39 +01003975
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003976 if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_CLIENT_SESS]))
3977 curmsg->event = SPOE_EV_ON_CLIENT_SESS;
3978 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_SERVER_SESS]))
3979 curmsg->event = SPOE_EV_ON_SERVER_SESS;
3980
3981 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_REQ_FE]))
3982 curmsg->event = SPOE_EV_ON_TCP_REQ_FE;
3983 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_REQ_BE]))
3984 curmsg->event = SPOE_EV_ON_TCP_REQ_BE;
3985 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_TCP_RSP]))
3986 curmsg->event = SPOE_EV_ON_TCP_RSP;
3987
3988 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_REQ_FE]))
3989 curmsg->event = SPOE_EV_ON_HTTP_REQ_FE;
3990 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_REQ_BE]))
3991 curmsg->event = SPOE_EV_ON_HTTP_REQ_BE;
3992 else if (!strcmp(args[1], spoe_event_str[SPOE_EV_ON_HTTP_RSP]))
3993 curmsg->event = SPOE_EV_ON_HTTP_RSP;
3994 else {
Joseph Herlantf1da69d2018-11-15 13:49:02 -08003995 ha_alert("parsing [%s:%d] : unknown event '%s'.\n",
Christopher Faulet767a84b2017-11-24 16:50:31 +01003996 file, linenum, args[1]);
Christopher Fauletecc537a2017-02-23 22:52:39 +01003997 err_code |= ERR_ALERT | ERR_FATAL;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02003998 goto out;
3999 }
Christopher Faulet57583e42017-09-04 15:41:09 +02004000
4001 if (strcmp(args[2], "if") == 0 || strcmp(args[2], "unless") == 0) {
4002 struct acl_cond *cond;
4003
4004 cond = build_acl_cond(file, linenum, &curmsg->acls,
4005 curproxy, (const char **)args+2,
4006 &errmsg);
4007 if (cond == NULL) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01004008 ha_alert("parsing [%s:%d] : error detected while "
4009 "parsing an 'event %s' condition : %s.\n",
4010 file, linenum, args[1], errmsg);
Christopher Faulet57583e42017-09-04 15:41:09 +02004011 err_code |= ERR_ALERT | ERR_FATAL;
4012 goto out;
4013 }
4014 curmsg->cond = cond;
4015 }
4016 else if (*args[2]) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01004017 ha_alert("parsing [%s:%d]: 'event %s' expects either 'if' "
4018 "or 'unless' followed by a condition but found '%s'.\n",
4019 file, linenum, args[1], args[2]);
Christopher Faulet57583e42017-09-04 15:41:09 +02004020 err_code |= ERR_ALERT | ERR_FATAL;
4021 goto out;
4022 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004023 }
4024 else if (!*args[0]) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01004025 ha_alert("parsing [%s:%d] : unknown keyword '%s' in spoe-message section.\n",
4026 file, linenum, args[0]);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004027 err_code |= ERR_ALERT | ERR_FATAL;
4028 goto out;
4029 }
4030 out:
4031 free(errmsg);
4032 return err_code;
4033}
4034
4035/* Return -1 on error, else 0 */
4036static int
4037parse_spoe_flt(char **args, int *cur_arg, struct proxy *px,
4038 struct flt_conf *fconf, char **err, void *private)
4039{
4040 struct list backup_sections;
4041 struct spoe_config *conf;
4042 struct spoe_message *msg, *msgback;
Christopher Faulet11610f32017-09-21 10:23:10 +02004043 struct spoe_group *grp, *grpback;
4044 struct spoe_placeholder *ph, *phback;
Christopher Faulet336d3ef2017-12-22 10:00:55 +01004045 struct spoe_var_placeholder *vph, *vphback;
Christopher Faulet7250b8f2018-03-26 17:19:01 +02004046 struct logsrv *logsrv, *logsrvback;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004047 char *file = NULL, *engine = NULL;
4048 int ret, pos = *cur_arg + 1;
4049
Christopher Faulet84c844e2018-03-23 14:37:14 +01004050 LIST_INIT(&curmsgs);
4051 LIST_INIT(&curgrps);
4052 LIST_INIT(&curmphs);
4053 LIST_INIT(&curgphs);
4054 LIST_INIT(&curvars);
Christopher Faulet7250b8f2018-03-26 17:19:01 +02004055 LIST_INIT(&curlogsrvs);
Christopher Faulet0e0f0852018-03-26 17:20:36 +02004056 curpxopts = 0;
4057 curpxopts2 = 0;
Christopher Faulet84c844e2018-03-23 14:37:14 +01004058
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004059 conf = calloc(1, sizeof(*conf));
4060 if (conf == NULL) {
4061 memprintf(err, "%s: out of memory", args[*cur_arg]);
4062 goto error;
4063 }
4064 conf->proxy = px;
4065
4066 while (*args[pos]) {
4067 if (!strcmp(args[pos], "config")) {
4068 if (!*args[pos+1]) {
4069 memprintf(err, "'%s' : '%s' option without value",
4070 args[*cur_arg], args[pos]);
4071 goto error;
4072 }
4073 file = args[pos+1];
4074 pos += 2;
4075 }
4076 else if (!strcmp(args[pos], "engine")) {
4077 if (!*args[pos+1]) {
4078 memprintf(err, "'%s' : '%s' option without value",
4079 args[*cur_arg], args[pos]);
4080 goto error;
4081 }
4082 engine = args[pos+1];
4083 pos += 2;
4084 }
4085 else {
4086 memprintf(err, "unknown keyword '%s'", args[pos]);
4087 goto error;
4088 }
4089 }
4090 if (file == NULL) {
4091 memprintf(err, "'%s' : missing config file", args[*cur_arg]);
4092 goto error;
4093 }
4094
4095 /* backup sections and register SPOE sections */
4096 LIST_INIT(&backup_sections);
4097 cfg_backup_sections(&backup_sections);
Christopher Faulet11610f32017-09-21 10:23:10 +02004098 cfg_register_section("spoe-agent", cfg_parse_spoe_agent, NULL);
4099 cfg_register_section("spoe-group", cfg_parse_spoe_group, NULL);
William Lallemandd2ff56d2017-10-16 11:06:50 +02004100 cfg_register_section("spoe-message", cfg_parse_spoe_message, NULL);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004101
4102 /* Parse SPOE filter configuration file */
4103 curengine = engine;
4104 curproxy = px;
4105 curagent = NULL;
4106 curmsg = NULL;
4107 ret = readcfgfile(file);
4108 curproxy = NULL;
4109
4110 /* unregister SPOE sections and restore previous sections */
4111 cfg_unregister_sections();
4112 cfg_restore_sections(&backup_sections);
4113
4114 if (ret == -1) {
4115 memprintf(err, "Could not open configuration file %s : %s",
4116 file, strerror(errno));
4117 goto error;
4118 }
4119 if (ret & (ERR_ABORT|ERR_FATAL)) {
4120 memprintf(err, "Error(s) found in configuration file %s", file);
4121 goto error;
4122 }
4123
4124 /* Check SPOE agent */
4125 if (curagent == NULL) {
4126 memprintf(err, "No SPOE agent found in file %s", file);
4127 goto error;
4128 }
4129 if (curagent->b.name == NULL) {
4130 memprintf(err, "No backend declared for SPOE agent '%s' declared at %s:%d",
4131 curagent->id, curagent->conf.file, curagent->conf.line);
4132 goto error;
4133 }
Christopher Fauletf7a30922016-11-10 15:04:51 +01004134 if (curagent->timeout.hello == TICK_ETERNITY ||
4135 curagent->timeout.idle == TICK_ETERNITY ||
Christopher Fauletf7a30922016-11-10 15:04:51 +01004136 curagent->timeout.processing == TICK_ETERNITY) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01004137 ha_warning("Proxy '%s': missing timeouts for SPOE agent '%s' declare at %s:%d.\n"
4138 " | While not properly invalid, you will certainly encounter various problems\n"
4139 " | with such a configuration. To fix this, please ensure that all following\n"
4140 " | timeouts are set to a non-zero value: 'hello', 'idle', 'processing'.\n",
4141 px->id, curagent->id, curagent->conf.file, curagent->conf.line);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004142 }
4143 if (curagent->var_pfx == NULL) {
4144 char *tmp = curagent->id;
4145
4146 while (*tmp) {
Willy Tarreau90807112020-02-25 08:16:33 +01004147 if (!isalnum((unsigned char)*tmp) && *tmp != '_' && *tmp != '.') {
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004148 memprintf(err, "Invalid variable prefix '%s' for SPOE agent '%s' declared at %s:%d. "
4149 "Use 'option var-prefix' to set it. Only [a-zA-Z0-9_.] chars are supported.\n",
4150 curagent->id, curagent->id, curagent->conf.file, curagent->conf.line);
4151 goto error;
4152 }
4153 tmp++;
4154 }
4155 curagent->var_pfx = strdup(curagent->id);
4156 }
4157
Christopher Fauletb7426d12018-03-21 14:12:17 +01004158 if (curagent->var_on_error) {
4159 struct arg arg;
4160
Willy Tarreau843b7cb2018-07-13 10:54:26 +02004161 trash.data = snprintf(trash.area, trash.size, "txn.%s.%s",
Christopher Fauletb7426d12018-03-21 14:12:17 +01004162 curagent->var_pfx, curagent->var_on_error);
4163
4164 arg.type = ARGT_STR;
Willy Tarreau843b7cb2018-07-13 10:54:26 +02004165 arg.data.str.area = trash.area;
4166 arg.data.str.data = trash.data;
Christopher Fauletbf9bcb02019-05-13 10:39:36 +02004167 arg.data.str.size = 0; /* Set it to 0 to not release it in vars_check_args() */
Christopher Fauletb7426d12018-03-21 14:12:17 +01004168 if (!vars_check_arg(&arg, err)) {
4169 memprintf(err, "SPOE agent '%s': failed to register variable %s.%s (%s)",
4170 curagent->id, curagent->var_pfx, curagent->var_on_error, *err);
4171 goto error;
4172 }
4173 }
4174
Christopher Faulet36bda1c2018-03-22 09:08:20 +01004175 if (curagent->var_t_process) {
4176 struct arg arg;
4177
Willy Tarreau843b7cb2018-07-13 10:54:26 +02004178 trash.data = snprintf(trash.area, trash.size, "txn.%s.%s",
Christopher Faulet36bda1c2018-03-22 09:08:20 +01004179 curagent->var_pfx, curagent->var_t_process);
4180
4181 arg.type = ARGT_STR;
Willy Tarreau843b7cb2018-07-13 10:54:26 +02004182 arg.data.str.area = trash.area;
4183 arg.data.str.data = trash.data;
Christopher Fauletbf9bcb02019-05-13 10:39:36 +02004184 arg.data.str.size = 0; /* Set it to 0 to not release it in vars_check_args() */
Christopher Faulet36bda1c2018-03-22 09:08:20 +01004185 if (!vars_check_arg(&arg, err)) {
4186 memprintf(err, "SPOE agent '%s': failed to register variable %s.%s (%s)",
4187 curagent->id, curagent->var_pfx, curagent->var_t_process, *err);
4188 goto error;
4189 }
4190 }
4191
4192 if (curagent->var_t_total) {
4193 struct arg arg;
4194
Willy Tarreau843b7cb2018-07-13 10:54:26 +02004195 trash.data = snprintf(trash.area, trash.size, "txn.%s.%s",
Christopher Faulet36bda1c2018-03-22 09:08:20 +01004196 curagent->var_pfx, curagent->var_t_total);
4197
4198 arg.type = ARGT_STR;
Willy Tarreau843b7cb2018-07-13 10:54:26 +02004199 arg.data.str.area = trash.area;
4200 arg.data.str.data = trash.data;
Christopher Fauletbf9bcb02019-05-13 10:39:36 +02004201 arg.data.str.size = 0; /* Set it to 0 to not release it in vars_check_args() */
Christopher Faulet36bda1c2018-03-22 09:08:20 +01004202 if (!vars_check_arg(&arg, err)) {
4203 memprintf(err, "SPOE agent '%s': failed to register variable %s.%s (%s)",
4204 curagent->id, curagent->var_pfx, curagent->var_t_process, *err);
4205 goto error;
4206 }
4207 }
4208
Christopher Faulet11610f32017-09-21 10:23:10 +02004209 if (LIST_ISEMPTY(&curmphs) && LIST_ISEMPTY(&curgphs)) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01004210 ha_warning("Proxy '%s': No message/group used by SPOE agent '%s' declared at %s:%d.\n",
4211 px->id, curagent->id, curagent->conf.file, curagent->conf.line);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004212 goto finish;
4213 }
4214
Christopher Faulet11610f32017-09-21 10:23:10 +02004215 /* Replace placeholders by the corresponding messages for the SPOE
4216 * agent */
4217 list_for_each_entry(ph, &curmphs, list) {
4218 list_for_each_entry(msg, &curmsgs, list) {
Christopher Fauleta21b0642017-01-09 16:56:23 +01004219 struct spoe_arg *arg;
4220 unsigned int where;
4221
Christopher Faulet11610f32017-09-21 10:23:10 +02004222 if (!strcmp(msg->id, ph->id)) {
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004223 if ((px->cap & (PR_CAP_FE|PR_CAP_BE)) == (PR_CAP_FE|PR_CAP_BE)) {
4224 if (msg->event == SPOE_EV_ON_TCP_REQ_BE)
4225 msg->event = SPOE_EV_ON_TCP_REQ_FE;
4226 if (msg->event == SPOE_EV_ON_HTTP_REQ_BE)
4227 msg->event = SPOE_EV_ON_HTTP_REQ_FE;
4228 }
4229 if (!(px->cap & PR_CAP_FE) && (msg->event == SPOE_EV_ON_CLIENT_SESS ||
4230 msg->event == SPOE_EV_ON_TCP_REQ_FE ||
4231 msg->event == SPOE_EV_ON_HTTP_REQ_FE)) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01004232 ha_warning("Proxy '%s': frontend event used on a backend proxy at %s:%d.\n",
4233 px->id, msg->conf.file, msg->conf.line);
Christopher Faulet11610f32017-09-21 10:23:10 +02004234 goto next_mph;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004235 }
4236 if (msg->event == SPOE_EV_NONE) {
Christopher Faulet767a84b2017-11-24 16:50:31 +01004237 ha_warning("Proxy '%s': Ignore SPOE message '%s' without event at %s:%d.\n",
4238 px->id, msg->id, msg->conf.file, msg->conf.line);
Christopher Faulet11610f32017-09-21 10:23:10 +02004239 goto next_mph;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004240 }
Christopher Fauleta21b0642017-01-09 16:56:23 +01004241
4242 where = 0;
4243 switch (msg->event) {
4244 case SPOE_EV_ON_CLIENT_SESS:
4245 where |= SMP_VAL_FE_CON_ACC;
4246 break;
4247
4248 case SPOE_EV_ON_TCP_REQ_FE:
4249 where |= SMP_VAL_FE_REQ_CNT;
4250 break;
4251
4252 case SPOE_EV_ON_HTTP_REQ_FE:
4253 where |= SMP_VAL_FE_HRQ_HDR;
4254 break;
4255
4256 case SPOE_EV_ON_TCP_REQ_BE:
4257 if (px->cap & PR_CAP_FE)
4258 where |= SMP_VAL_FE_REQ_CNT;
4259 if (px->cap & PR_CAP_BE)
4260 where |= SMP_VAL_BE_REQ_CNT;
4261 break;
4262
4263 case SPOE_EV_ON_HTTP_REQ_BE:
4264 if (px->cap & PR_CAP_FE)
4265 where |= SMP_VAL_FE_HRQ_HDR;
4266 if (px->cap & PR_CAP_BE)
4267 where |= SMP_VAL_BE_HRQ_HDR;
4268 break;
4269
4270 case SPOE_EV_ON_SERVER_SESS:
4271 where |= SMP_VAL_BE_SRV_CON;
4272 break;
4273
4274 case SPOE_EV_ON_TCP_RSP:
4275 if (px->cap & PR_CAP_FE)
4276 where |= SMP_VAL_FE_RES_CNT;
4277 if (px->cap & PR_CAP_BE)
4278 where |= SMP_VAL_BE_RES_CNT;
4279 break;
4280
4281 case SPOE_EV_ON_HTTP_RSP:
4282 if (px->cap & PR_CAP_FE)
4283 where |= SMP_VAL_FE_HRS_HDR;
4284 if (px->cap & PR_CAP_BE)
4285 where |= SMP_VAL_BE_HRS_HDR;
4286 break;
4287
4288 default:
4289 break;
4290 }
4291
4292 list_for_each_entry(arg, &msg->args, list) {
4293 if (!(arg->expr->fetch->val & where)) {
Christopher Faulet76c09ef2017-09-21 11:03:52 +02004294 memprintf(err, "Ignore SPOE message '%s' at %s:%d: "
Christopher Fauleta21b0642017-01-09 16:56:23 +01004295 "some args extract information from '%s', "
Christopher Faulet76c09ef2017-09-21 11:03:52 +02004296 "none of which is available here ('%s')",
4297 msg->id, msg->conf.file, msg->conf.line,
Christopher Fauleta21b0642017-01-09 16:56:23 +01004298 sample_ckp_names(arg->expr->fetch->use),
4299 sample_ckp_names(where));
Christopher Faulet76c09ef2017-09-21 11:03:52 +02004300 goto error;
Christopher Fauleta21b0642017-01-09 16:56:23 +01004301 }
4302 }
4303
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004304 msg->agent = curagent;
Christopher Faulet11610f32017-09-21 10:23:10 +02004305 LIST_ADDQ(&curagent->events[msg->event], &msg->by_evt);
4306 goto next_mph;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004307 }
4308 }
4309 memprintf(err, "SPOE agent '%s' try to use undefined SPOE message '%s' at %s:%d",
Christopher Faulet11610f32017-09-21 10:23:10 +02004310 curagent->id, ph->id, curagent->conf.file, curagent->conf.line);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004311 goto error;
Christopher Faulet11610f32017-09-21 10:23:10 +02004312 next_mph:
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004313 continue;
4314 }
4315
Christopher Faulet11610f32017-09-21 10:23:10 +02004316 /* Replace placeholders by the corresponding groups for the SPOE
4317 * agent */
4318 list_for_each_entry(ph, &curgphs, list) {
4319 list_for_each_entry_safe(grp, grpback, &curgrps, list) {
4320 if (!strcmp(grp->id, ph->id)) {
4321 grp->agent = curagent;
4322 LIST_DEL(&grp->list);
4323 LIST_ADDQ(&curagent->groups, &grp->list);
4324 goto next_aph;
4325 }
4326 }
4327 memprintf(err, "SPOE agent '%s' try to use undefined SPOE group '%s' at %s:%d",
4328 curagent->id, ph->id, curagent->conf.file, curagent->conf.line);
4329 goto error;
4330 next_aph:
4331 continue;
4332 }
4333
4334 /* Replace placeholders by the corresponding message for each SPOE
4335 * group of the SPOE agent */
4336 list_for_each_entry(grp, &curagent->groups, list) {
4337 list_for_each_entry_safe(ph, phback, &grp->phs, list) {
4338 list_for_each_entry(msg, &curmsgs, list) {
4339 if (!strcmp(msg->id, ph->id)) {
4340 if (msg->group != NULL) {
4341 memprintf(err, "SPOE message '%s' already belongs to "
4342 "the SPOE group '%s' declare at %s:%d",
4343 msg->id, msg->group->id,
4344 msg->group->conf.file,
4345 msg->group->conf.line);
4346 goto error;
4347 }
4348
4349 /* Scope for arguments are not checked for now. We will check
4350 * them only if a rule use the corresponding SPOE group. */
4351 msg->agent = curagent;
4352 msg->group = grp;
4353 LIST_DEL(&ph->list);
4354 LIST_ADDQ(&grp->messages, &msg->by_grp);
4355 goto next_mph_grp;
4356 }
4357 }
4358 memprintf(err, "SPOE group '%s' try to use undefined SPOE message '%s' at %s:%d",
4359 grp->id, ph->id, curagent->conf.file, curagent->conf.line);
4360 goto error;
4361 next_mph_grp:
4362 continue;
4363 }
4364 }
4365
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004366 finish:
Christopher Faulet11610f32017-09-21 10:23:10 +02004367 /* move curmsgs to the agent message list */
4368 curmsgs.n->p = &curagent->messages;
4369 curmsgs.p->n = &curagent->messages;
4370 curagent->messages = curmsgs;
4371 LIST_INIT(&curmsgs);
4372
Christopher Faulet7ee86672017-09-19 11:08:28 +02004373 conf->id = strdup(engine ? engine : curagent->id);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004374 conf->agent = curagent;
Christopher Faulet7250b8f2018-03-26 17:19:01 +02004375
4376 /* Start agent's proxy initialization here. It will be finished during
4377 * the filter init. */
4378 memset(&conf->agent_fe, 0, sizeof(conf->agent_fe));
4379 init_new_proxy(&conf->agent_fe);
4380 conf->agent_fe.id = conf->agent->id;
4381 conf->agent_fe.parent = conf->agent;
Christopher Faulet0e0f0852018-03-26 17:20:36 +02004382 conf->agent_fe.options |= curpxopts;
4383 conf->agent_fe.options2 |= curpxopts2;
Christopher Faulet7250b8f2018-03-26 17:19:01 +02004384
4385 list_for_each_entry_safe(logsrv, logsrvback, &curlogsrvs, list) {
4386 LIST_DEL(&logsrv->list);
4387 LIST_ADDQ(&conf->agent_fe.logsrvs, &logsrv->list);
4388 }
4389
Christopher Faulet11610f32017-09-21 10:23:10 +02004390 list_for_each_entry_safe(ph, phback, &curmphs, list) {
4391 LIST_DEL(&ph->list);
4392 spoe_release_placeholder(ph);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004393 }
Christopher Faulet11610f32017-09-21 10:23:10 +02004394 list_for_each_entry_safe(ph, phback, &curgphs, list) {
4395 LIST_DEL(&ph->list);
4396 spoe_release_placeholder(ph);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004397 }
Christopher Faulet336d3ef2017-12-22 10:00:55 +01004398 list_for_each_entry_safe(vph, vphback, &curvars, list) {
4399 struct arg arg;
4400
Willy Tarreau843b7cb2018-07-13 10:54:26 +02004401 trash.data = snprintf(trash.area, trash.size, "proc.%s.%s",
Christopher Faulet336d3ef2017-12-22 10:00:55 +01004402 curagent->var_pfx, vph->name);
4403
4404 arg.type = ARGT_STR;
Willy Tarreau843b7cb2018-07-13 10:54:26 +02004405 arg.data.str.area = trash.area;
4406 arg.data.str.data = trash.data;
Christopher Fauletbf9bcb02019-05-13 10:39:36 +02004407 arg.data.str.size = 0; /* Set it to 0 to not release it in vars_check_args() */
Christopher Faulet336d3ef2017-12-22 10:00:55 +01004408 if (!vars_check_arg(&arg, err)) {
4409 memprintf(err, "SPOE agent '%s': failed to register variable %s.%s (%s)",
4410 curagent->id, curagent->var_pfx, vph->name, *err);
4411 goto error;
4412 }
4413
4414 LIST_DEL(&vph->list);
4415 free(vph->name);
4416 free(vph);
4417 }
Christopher Faulet11610f32017-09-21 10:23:10 +02004418 list_for_each_entry_safe(grp, grpback, &curgrps, list) {
4419 LIST_DEL(&grp->list);
4420 spoe_release_group(grp);
4421 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004422 *cur_arg = pos;
Christopher Faulet3b386a32017-02-23 10:17:15 +01004423 fconf->id = spoe_filter_id;
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004424 fconf->ops = &spoe_ops;
4425 fconf->conf = conf;
4426 return 0;
4427
4428 error:
Christopher Faulet8ef75252017-02-20 22:56:03 +01004429 spoe_release_agent(curagent);
Christopher Faulet11610f32017-09-21 10:23:10 +02004430 list_for_each_entry_safe(ph, phback, &curmphs, list) {
4431 LIST_DEL(&ph->list);
4432 spoe_release_placeholder(ph);
4433 }
4434 list_for_each_entry_safe(ph, phback, &curgphs, list) {
4435 LIST_DEL(&ph->list);
4436 spoe_release_placeholder(ph);
4437 }
Christopher Faulet336d3ef2017-12-22 10:00:55 +01004438 list_for_each_entry_safe(vph, vphback, &curvars, list) {
4439 LIST_DEL(&vph->list);
4440 free(vph->name);
4441 free(vph);
4442 }
Christopher Faulet11610f32017-09-21 10:23:10 +02004443 list_for_each_entry_safe(grp, grpback, &curgrps, list) {
4444 LIST_DEL(&grp->list);
4445 spoe_release_group(grp);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004446 }
4447 list_for_each_entry_safe(msg, msgback, &curmsgs, list) {
4448 LIST_DEL(&msg->list);
Christopher Faulet8ef75252017-02-20 22:56:03 +01004449 spoe_release_message(msg);
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004450 }
Christopher Faulet7250b8f2018-03-26 17:19:01 +02004451 list_for_each_entry_safe(logsrv, logsrvback, &curlogsrvs, list) {
4452 LIST_DEL(&logsrv->list);
4453 free(logsrv);
4454 }
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004455 free(conf);
4456 return -1;
4457}
4458
Christopher Faulet344c4ab2017-09-22 10:20:13 +02004459/* Send message of a SPOE group. This is the action_ptr callback of a rule
4460 * associated to a "send-spoe-group" action.
4461 *
Christopher Faulet13403762019-12-13 09:01:57 +01004462 * It returns ACT_RET_CONT if processing is finished (with error or not), it returns
4463 * ACT_RET_YIELD if the action is in progress. */
Christopher Faulet76c09ef2017-09-21 11:03:52 +02004464static enum act_return
4465spoe_send_group(struct act_rule *rule, struct proxy *px,
4466 struct session *sess, struct stream *s, int flags)
4467{
4468 struct filter *filter;
4469 struct spoe_agent *agent = NULL;
4470 struct spoe_group *group = NULL;
4471 struct spoe_context *ctx = NULL;
4472 int ret, dir;
4473
4474 list_for_each_entry(filter, &s->strm_flt.filters, list) {
4475 if (filter->config == rule->arg.act.p[0]) {
4476 agent = rule->arg.act.p[2];
4477 group = rule->arg.act.p[3];
4478 ctx = filter->ctx;
4479 break;
4480 }
4481 }
4482 if (agent == NULL || group == NULL || ctx == NULL)
Christopher Faulet13403762019-12-13 09:01:57 +01004483 return ACT_RET_CONT;
Christopher Faulet344c4ab2017-09-22 10:20:13 +02004484 if (ctx->state == SPOE_CTX_ST_NONE)
4485 return ACT_RET_CONT;
Christopher Faulet76c09ef2017-09-21 11:03:52 +02004486
Christopher Faulet344c4ab2017-09-22 10:20:13 +02004487 switch (rule->from) {
4488 case ACT_F_TCP_REQ_SES: dir = SMP_OPT_DIR_REQ; break;
4489 case ACT_F_TCP_REQ_CNT: dir = SMP_OPT_DIR_REQ; break;
4490 case ACT_F_TCP_RES_CNT: dir = SMP_OPT_DIR_RES; break;
4491 case ACT_F_HTTP_REQ: dir = SMP_OPT_DIR_REQ; break;
4492 case ACT_F_HTTP_RES: dir = SMP_OPT_DIR_RES; break;
4493 default:
4494 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
4495 " - internal error while execute spoe-send-group\n",
4496 (int)now.tv_sec, (int)now.tv_usec, agent->id,
4497 __FUNCTION__, s);
4498 send_log(px, LOG_ERR, "SPOE: [%s] internal error while execute spoe-send-group\n",
4499 agent->id);
4500 return ACT_RET_CONT;
4501 }
4502
4503 ret = spoe_process_group(s, ctx, group, dir);
4504 if (ret == 1)
4505 return ACT_RET_CONT;
4506 else if (ret == 0) {
Christopher Faulet105ba6c2019-12-18 14:41:51 +01004507 if (flags & ACT_OPT_FINAL) {
Christopher Faulet344c4ab2017-09-22 10:20:13 +02004508 SPOE_PRINTF(stderr, "%d.%06d [SPOE/%-15s] %s: stream=%p"
4509 " - failed to process group '%s': interrupted by caller\n",
4510 (int)now.tv_sec, (int)now.tv_usec,
4511 agent->id, __FUNCTION__, s, group->id);
4512 ctx->status_code = SPOE_CTX_ERR_INTERRUPT;
Christopher Faulet6f9ea4f2018-01-24 16:13:48 +01004513 spoe_stop_processing(agent, ctx);
Christopher Fauletcaf2fec2018-04-04 10:25:50 +02004514 spoe_handle_processing_error(s, agent, ctx, dir);
Christopher Faulet344c4ab2017-09-22 10:20:13 +02004515 return ACT_RET_CONT;
4516 }
4517 return ACT_RET_YIELD;
4518 }
4519 else
Christopher Faulet13403762019-12-13 09:01:57 +01004520 return ACT_RET_CONT;
Christopher Faulet76c09ef2017-09-21 11:03:52 +02004521}
4522
4523/* Check an "send-spoe-group" action. Here, we'll try to find the real SPOE
4524 * group associated to <rule>. The format of an rule using 'send-spoe-group'
4525 * action should be:
4526 *
4527 * (http|tcp)-(request|response) send-spoe-group <engine-id> <group-id>
4528 *
4529 * So, we'll loop on each configured SPOE filter for the proxy <px> to find the
4530 * SPOE engine matching <engine-id>. And then, we'll try to find the good group
4531 * matching <group-id>. Finally, we'll check all messages referenced by the SPOE
4532 * group.
4533 *
4534 * The function returns 1 in success case, otherwise, it returns 0 and err is
4535 * filled.
4536 */
4537static int
4538check_send_spoe_group(struct act_rule *rule, struct proxy *px, char **err)
4539{
4540 struct flt_conf *fconf;
4541 struct spoe_config *conf;
4542 struct spoe_agent *agent = NULL;
4543 struct spoe_group *group;
4544 struct spoe_message *msg;
4545 char *engine_id = rule->arg.act.p[0];
4546 char *group_id = rule->arg.act.p[1];
4547 unsigned int where = 0;
4548
4549 switch (rule->from) {
4550 case ACT_F_TCP_REQ_SES: where = SMP_VAL_FE_SES_ACC; break;
4551 case ACT_F_TCP_REQ_CNT: where = SMP_VAL_FE_REQ_CNT; break;
4552 case ACT_F_TCP_RES_CNT: where = SMP_VAL_BE_RES_CNT; break;
4553 case ACT_F_HTTP_REQ: where = SMP_VAL_FE_HRQ_HDR; break;
4554 case ACT_F_HTTP_RES: where = SMP_VAL_BE_HRS_HDR; break;
4555 default:
4556 memprintf(err,
4557 "internal error, unexpected rule->from=%d, please report this bug!",
4558 rule->from);
4559 goto error;
4560 }
4561
4562 /* Try to find the SPOE engine by checking all SPOE filters for proxy
4563 * <px> */
4564 list_for_each_entry(fconf, &px->filter_configs, list) {
4565 conf = fconf->conf;
4566
4567 /* This is not an SPOE filter */
4568 if (fconf->id != spoe_filter_id)
4569 continue;
4570
4571 /* This is the good engine */
4572 if (!strcmp(conf->id, engine_id)) {
4573 agent = conf->agent;
4574 break;
4575 }
4576 }
4577 if (agent == NULL) {
4578 memprintf(err, "unable to find SPOE engine '%s' used by the send-spoe-group '%s'",
4579 engine_id, group_id);
4580 goto error;
4581 }
4582
4583 /* Try to find the right group */
4584 list_for_each_entry(group, &agent->groups, list) {
4585 /* This is the good group */
4586 if (!strcmp(group->id, group_id))
4587 break;
4588 }
4589 if (&group->list == &agent->groups) {
4590 memprintf(err, "unable to find SPOE group '%s' into SPOE engine '%s' configuration",
4591 group_id, engine_id);
4592 goto error;
4593 }
4594
4595 /* Ok, we found the group, we need to check messages and their
4596 * arguments */
4597 list_for_each_entry(msg, &group->messages, by_grp) {
4598 struct spoe_arg *arg;
4599
4600 list_for_each_entry(arg, &msg->args, list) {
4601 if (!(arg->expr->fetch->val & where)) {
4602 memprintf(err, "Invalid SPOE message '%s' used by SPOE group '%s' at %s:%d: "
4603 "some args extract information from '%s',"
4604 "none of which is available here ('%s')",
4605 msg->id, group->id, msg->conf.file, msg->conf.line,
4606 sample_ckp_names(arg->expr->fetch->use),
4607 sample_ckp_names(where));
4608 goto error;
4609 }
4610 }
4611 }
4612
4613 free(engine_id);
4614 free(group_id);
4615 rule->arg.act.p[0] = fconf; /* Associate filter config with the rule */
4616 rule->arg.act.p[1] = conf; /* Associate SPOE config with the rule */
4617 rule->arg.act.p[2] = agent; /* Associate SPOE agent with the rule */
4618 rule->arg.act.p[3] = group; /* Associate SPOE group with the rule */
4619 return 1;
4620
4621 error:
4622 free(engine_id);
4623 free(group_id);
4624 return 0;
4625}
4626
4627/* Parse 'send-spoe-group' action following the format:
4628 *
4629 * ... send-spoe-group <engine-id> <group-id>
4630 *
4631 * It returns ACT_RET_PRS_ERR if fails and <err> is filled with an error
4632 * message. Otherwise, it returns ACT_RET_PRS_OK and parsing engine and group
4633 * ids are saved and used later, when the rule will be checked.
4634 */
4635static enum act_parse_ret
4636parse_send_spoe_group(const char **args, int *orig_arg, struct proxy *px,
4637 struct act_rule *rule, char **err)
4638{
4639 if (!*args[*orig_arg] || !*args[*orig_arg+1] ||
4640 (*args[*orig_arg+2] && strcmp(args[*orig_arg+2], "if") != 0 && strcmp(args[*orig_arg+2], "unless") != 0)) {
4641 memprintf(err, "expects 2 arguments: <engine-id> <group-id>");
4642 return ACT_RET_PRS_ERR;
4643 }
4644 rule->arg.act.p[0] = strdup(args[*orig_arg]); /* Copy the SPOE engine id */
4645 rule->arg.act.p[1] = strdup(args[*orig_arg+1]); /* Cope the SPOE group id */
4646
4647 (*orig_arg) += 2;
4648
4649 rule->action = ACT_CUSTOM;
4650 rule->action_ptr = spoe_send_group;
4651 rule->check_ptr = check_send_spoe_group;
4652 return ACT_RET_PRS_OK;
4653}
4654
Christopher Fauletf7e4e7e2016-10-27 22:29:49 +02004655
4656/* Declare the filter parser for "spoe" keyword */
4657static struct flt_kw_list flt_kws = { "SPOE", { }, {
4658 { "spoe", parse_spoe_flt, NULL },
4659 { NULL, NULL, NULL },
4660 }
4661};
4662
Willy Tarreau0108d902018-11-25 19:14:37 +01004663INITCALL1(STG_REGISTER, flt_register_keywords, &flt_kws);
4664
Christopher Faulet76c09ef2017-09-21 11:03:52 +02004665/* Delcate the action parser for "spoe-action" keyword */
4666static struct action_kw_list tcp_req_action_kws = { { }, {
4667 { "send-spoe-group", parse_send_spoe_group },
4668 { /* END */ },
4669 }
4670};
Willy Tarreau0108d902018-11-25 19:14:37 +01004671
4672INITCALL1(STG_REGISTER, tcp_req_cont_keywords_register, &tcp_req_action_kws);
4673
Christopher Faulet76c09ef2017-09-21 11:03:52 +02004674static struct action_kw_list tcp_res_action_kws = { { }, {
4675 { "send-spoe-group", parse_send_spoe_group },
4676 { /* END */ },
4677 }
4678};
Willy Tarreau0108d902018-11-25 19:14:37 +01004679
4680INITCALL1(STG_REGISTER, tcp_res_cont_keywords_register, &tcp_res_action_kws);
4681
Christopher Faulet76c09ef2017-09-21 11:03:52 +02004682static struct action_kw_list http_req_action_kws = { { }, {
4683 { "send-spoe-group", parse_send_spoe_group },
4684 { /* END */ },
4685 }
4686};
Willy Tarreau0108d902018-11-25 19:14:37 +01004687
4688INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_action_kws);
4689
Christopher Faulet76c09ef2017-09-21 11:03:52 +02004690static struct action_kw_list http_res_action_kws = { { }, {
4691 { "send-spoe-group", parse_send_spoe_group },
4692 { /* END */ },
4693 }
4694};
4695
Willy Tarreau0108d902018-11-25 19:14:37 +01004696INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_action_kws);