diff --git a/include/haproxy/applet-t.h b/include/haproxy/applet-t.h index bd96403c5..0ee4fbc34 100644 --- a/include/haproxy/applet-t.h +++ b/include/haproxy/applet-t.h @@ -31,6 +31,10 @@ /* flags for appctx->state */ #define APPLET_WANT_DIE 0x01 /* applet was running and requested to die */ +#define APPLET_INBLK_ALLOC 0x02 +#define APPLET_INBLK_FULL 0x04 +#define APPLET_OUTBLK_ALLOC 0x08 +#define APPLET_OUTBLK_FULL 0x10 /* Room for per-command context (mostly CLI commands but not only) */ #define APPLET_MAX_SVCCTX 88 @@ -60,6 +64,10 @@ struct appctx { unsigned short state; /* Internal appctx state */ unsigned int st0; /* CLI state for stats, session state for peers */ unsigned int st1; /* prompt/payload (bitwise OR of APPCTX_CLI_ST1_*) for stats, session error for peers */ + + struct buffer inbuf; + struct buffer outbuf; + struct buffer *chunk; /* used to store unfinished commands */ struct applet *applet; /* applet this context refers to */ struct session *sess; /* session for frontend applets (NULL for backend applets) */ diff --git a/include/haproxy/applet.h b/include/haproxy/applet.h index b04ffd95c..9cc50c305 100644 --- a/include/haproxy/applet.h +++ b/include/haproxy/applet.h @@ -58,6 +58,35 @@ static inline struct appctx *appctx_new_anywhere(struct applet *applet, struct s return appctx_new_on(applet, sedesc, -1); } + +/* + * Release a buffer, if any, and try to wake up entities waiting in the buffer + * wait queue. + */ +static inline void appctx_release_buf(struct appctx *appctx, struct buffer *bptr) +{ + if (bptr->size) { + b_free(bptr); + offer_buffers(appctx->buffer_wait.target, 1); + } +} + +/* + * Allocate a buffer. If if fails, it adds the appctx in buffer wait queue. + */ +static inline struct buffer *appctx_get_buf(struct appctx *appctx, struct buffer *bptr) +{ + struct buffer *buf = NULL; + + if (likely(!LIST_INLIST(&appctx->buffer_wait.list)) && + unlikely((buf = b_alloc(bptr)) == NULL)) { + appctx->buffer_wait.target = appctx; + appctx->buffer_wait.wakeup_cb = appctx_buf_available; + LIST_APPEND(&th_ctx->buffer_wq, &appctx->buffer_wait.list); + } + return buf; +} + /* Helper function to call .init applet callback function, if it exists. Returns 0 * on success and -1 on error. */ @@ -78,6 +107,9 @@ static inline int appctx_init(struct appctx *appctx) /* Releases an appctx previously allocated by appctx_new(). */ static inline void __appctx_free(struct appctx *appctx) { + appctx_release_buf(appctx, &appctx->inbuf); + appctx_release_buf(appctx, &appctx->outbuf); + task_destroy(appctx->t); if (LIST_INLIST(&appctx->buffer_wait.list)) LIST_DEL_INIT(&appctx->buffer_wait.list); diff --git a/src/applet.c b/src/applet.c index 5e6312efe..71e21c23d 100644 --- a/src/applet.c +++ b/src/applet.c @@ -232,6 +232,9 @@ struct appctx *appctx_new_on(struct applet *applet, struct sedesc *sedesc, int t appctx->t->process = task_run_applet; appctx->t->context = appctx; + appctx->inbuf = BUF_NULL; + appctx->outbuf = BUF_NULL; + LIST_INIT(&appctx->buffer_wait.list); appctx->buffer_wait.target = appctx; appctx->buffer_wait.wakeup_cb = appctx_buf_available; @@ -379,6 +382,18 @@ int appctx_buf_available(void *arg) struct appctx *appctx = arg; struct stconn *sc = appctx_sc(appctx); + if ((appctx->state & APPLET_INBLK_ALLOC) && b_alloc(&appctx->inbuf)) { + appctx->state &= ~APPLET_INBLK_ALLOC; + task_wakeup(appctx->t, TASK_WOKEN_RES); + return 1; + } + + if ((appctx->state & APPLET_OUTBLK_ALLOC) && b_alloc(&appctx->outbuf)) { + appctx->state &= ~APPLET_OUTBLK_ALLOC; + task_wakeup(appctx->t, TASK_WOKEN_RES); + return 1; + } + /* allocation requested ? */ if (!(sc->flags & SC_FL_NEED_BUFF)) return 0;