2015-04-13 17:11:11 +02:00
/*
* Functions managing applets
*
* Copyright 2000 - 2015 Willy Tarreau < w @ 1 wt . eu >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*
*/
# include <stdio.h>
# include <stdlib.h>
2020-05-27 12:58:42 +02:00
# include <haproxy/api.h>
2020-06-09 09:07:15 +02:00
# include <haproxy/applet.h>
2020-06-04 21:07:02 +02:00
# include <haproxy/channel.h>
2022-03-25 16:43:49 +01:00
# include <haproxy/conn_stream.h>
# include <haproxy/cs_utils.h>
2020-05-27 18:01:47 +02:00
# include <haproxy/list.h>
2020-06-04 23:46:14 +02:00
# include <haproxy/stream.h>
2020-06-04 17:25:40 +02:00
# include <haproxy/task.h>
2015-04-13 17:11:11 +02:00
2016-12-06 09:13:22 +01:00
unsigned int nb_applets = 0 ;
2017-06-19 12:38:55 +02:00
2019-07-18 10:41:36 +02:00
DECLARE_POOL ( pool_head_appctx , " appctx " , sizeof ( struct appctx ) ) ;
2022-05-05 19:55:03 +02:00
/* Tries to allocate a new appctx and initialize all of its fields. The appctx
2022-03-23 11:46:56 +01:00
* is returned on success , NULL on failure . The appctx must be released using
* appctx_free ( ) . < applet > is assigned as the applet , but it can be NULL . The
* applet ' s task is always created on the current thread .
*/
2022-03-23 15:15:29 +01:00
struct appctx * appctx_new ( struct applet * applet , struct cs_endpoint * endp )
2022-03-23 11:46:56 +01:00
{
struct appctx * appctx ;
2022-05-05 19:55:03 +02:00
appctx = pool_zalloc ( pool_head_appctx ) ;
2022-03-23 15:15:29 +01:00
if ( unlikely ( ! appctx ) )
goto fail_appctx ;
2022-05-05 19:55:03 +02:00
LIST_INIT ( & appctx - > wait_entry ) ;
2022-03-23 15:15:29 +01:00
appctx - > obj_type = OBJ_TYPE_APPCTX ;
appctx - > applet = applet ;
2022-05-09 08:08:26 +02:00
appctx - > sess = NULL ;
2022-03-23 15:15:29 +01:00
if ( ! endp ) {
endp = cs_endpoint_new ( ) ;
if ( ! endp )
goto fail_endp ;
endp - > target = appctx ;
endp - > ctx = appctx ;
endp - > flags | = ( CS_EP_T_APPLET | CS_EP_ORPHAN ) ;
2022-03-23 11:46:56 +01:00
}
2022-03-23 15:15:29 +01:00
appctx - > endp = endp ;
appctx - > t = task_new_here ( ) ;
if ( unlikely ( ! appctx - > t ) )
goto fail_task ;
appctx - > t - > process = task_run_applet ;
appctx - > t - > context = appctx ;
LIST_INIT ( & appctx - > buffer_wait . list ) ;
appctx - > buffer_wait . target = appctx ;
appctx - > buffer_wait . wakeup_cb = appctx_buf_available ;
_HA_ATOMIC_INC ( & nb_applets ) ;
2022-03-23 11:46:56 +01:00
return appctx ;
2022-03-23 15:15:29 +01:00
fail_task :
cs_endpoint_free ( appctx - > endp ) ;
fail_endp :
pool_free ( pool_head_appctx , appctx ) ;
fail_appctx :
return NULL ;
2022-03-23 11:46:56 +01:00
}
2022-05-12 15:15:53 +02:00
/* Finalize the frontend appctx startup. It must not be called for a backend
* appctx . This function is responsible to create the appctx ' s session and the
* frontend conn - stream . By transitivity , the stream is also created .
*
* It returns 0 on success and - 1 on error . In this case , it is the caller
* responsibility to release the appctx . However , the session is released if it
* was created . On success , if an error is encountered in the caller function ,
2022-05-12 15:18:48 +02:00
* the stream must be released instead of the appctx . To be sure ,
* appctx_free_on_early_error ( ) must be called in this case .
2022-05-12 15:15:53 +02:00
*/
int appctx_finalize_startup ( struct appctx * appctx , struct proxy * px , struct buffer * input )
{
struct session * sess ;
BUG_ON ( appctx - > sess | | ! ( appctx - > endp - > flags & CS_EP_ORPHAN ) ) ;
sess = session_new ( px , NULL , & appctx - > obj_type ) ;
if ( ! sess )
return - 1 ;
if ( ! cs_new_from_endp ( appctx - > endp , sess , input ) ) {
session_free ( sess ) ;
return - 1 ;
}
appctx - > sess = sess ;
return 0 ;
}
2022-05-12 15:18:48 +02:00
/* Release function to call when an error occurred during init stage of a
* frontend appctx . For a backend appctx , it just calls appctx_free ( )
*/
void appctx_free_on_early_error ( struct appctx * appctx )
{
/* If a frontend apctx is attached to a conn-stream, release the stream
* instead of the appctx .
*/
if ( ! ( appctx - > endp - > flags & CS_EP_ORPHAN ) & & ! ( appctx_cs ( appctx ) - > flags & CS_FL_ISBACK ) ) {
stream_free ( appctx_strm ( appctx ) ) ;
return ;
}
appctx_free ( appctx ) ;
}
2022-05-02 14:57:03 +02:00
/* reserves a command context of at least <size> bytes in the <appctx>, for
* use by a CLI command or any regular applet . The pointer to this context is
* stored in ctx . svcctx and is returned . The caller doesn ' t need to release
* it as it ' s allocated from reserved space . If the size is larger than
* APPLET_MAX_SVCCTX a crash will occur ( hence that will never happen outside
* of development ) .
*
* Note that the command does * not * initialize the area , so that it can easily
* be used upon each entry in a function . It ' s left to the initialization code
* to do it if needed . The CLI will always zero the whole area before calling
* a keyword ' s - > parse ( ) function .
*/
void * applet_reserve_svcctx ( struct appctx * appctx , size_t size )
{
BUG_ON ( size > APPLET_MAX_SVCCTX ) ;
appctx - > svcctx = & appctx - > svc . storage ;
return appctx - > svcctx ;
}
2022-05-10 19:42:22 +02:00
/* call the applet's release() function if any, and marks the endp as shut.
* Needs to be called upon close ( ) .
*/
void appctx_shut ( struct appctx * appctx )
{
if ( appctx - > endp - > flags & ( CS_EP_SHR | CS_EP_SHW ) )
return ;
if ( appctx - > applet - > release )
appctx - > applet - > release ( appctx ) ;
appctx - > endp - > flags | = CS_EP_SHRR | CS_EP_SHWN ;
}
2018-11-06 17:32:37 +01:00
/* Callback used to wake up an applet when a buffer is available. The applet
* < appctx > is woken up if an input buffer was requested for the associated
2022-04-04 11:29:28 +02:00
* conn - stream . In this case the buffer is immediately allocated and the
2018-11-06 17:32:37 +01:00
* function returns 1. Otherwise it returns 0. Note that this automatically
* covers multiple wake - up attempts by ensuring that the same buffer will not
* be accounted for multiple times .
*/
int appctx_buf_available ( void * arg )
{
struct appctx * appctx = arg ;
2022-05-11 14:09:57 +02:00
struct conn_stream * cs = appctx_cs ( appctx ) ;
2018-11-06 17:32:37 +01:00
/* allocation requested ? */
2022-05-10 11:32:31 +02:00
if ( ! ( appctx - > endp - > flags & CS_EP_RXBLK_BUFF ) )
2018-11-14 15:12:08 +01:00
return 0 ;
2022-04-04 07:51:21 +02:00
cs_rx_buff_rdy ( cs ) ;
2018-11-14 15:12:08 +01:00
/* was already allocated another way ? if so, don't take this one */
2022-03-25 16:43:49 +01:00
if ( c_size ( cs_ic ( cs ) ) | | cs_ic ( cs ) - > pipe )
2018-11-06 17:32:37 +01:00
return 0 ;
/* allocation possible now ? */
2022-03-25 16:43:49 +01:00
if ( ! b_alloc ( & cs_ic ( cs ) - > buf ) ) {
2022-04-04 07:51:21 +02:00
cs_rx_buff_blk ( cs ) ;
2018-11-06 17:32:37 +01:00
return 0 ;
2018-11-14 15:12:08 +01:00
}
2018-11-06 17:32:37 +01:00
task_wakeup ( appctx - > t , TASK_WOKEN_RES ) ;
return 1 ;
}
/* Default applet handler */
2021-03-02 16:09:26 +01:00
struct task * task_run_applet ( struct task * t , void * context , unsigned int state )
2015-04-19 09:59:31 +02:00
{
2018-05-25 16:58:52 +02:00
struct appctx * app = context ;
2022-05-11 14:09:57 +02:00
struct conn_stream * cs = appctx_cs ( app ) ;
2019-04-25 19:12:26 +02:00
unsigned int rate ;
2021-04-27 17:08:10 +02:00
size_t count ;
2015-04-19 09:59:31 +02:00
2018-05-25 16:58:52 +02:00
if ( app - > state & APPLET_WANT_DIE ) {
__appctx_free ( app ) ;
return NULL ;
2017-06-26 16:36:53 +02:00
}
2018-05-25 16:58:52 +02:00
/* We always pretend the applet can't get and doesn't want to
* put , it ' s up to it to change this if needed . This ensures
* that one applet which ignores any event will not spin .
2015-09-25 17:56:16 +02:00
*/
2022-04-04 07:51:21 +02:00
cs_cant_get ( cs ) ;
cs_rx_endp_done ( cs ) ;
2015-04-19 09:59:31 +02:00
2018-11-14 15:12:08 +01:00
/* Now we'll try to allocate the input buffer. We wake up the applet in
* all cases . So this is the applet ' s responsibility to check if this
* buffer was allocated or not . This leaves a chance for applets to do
* some other processing if needed . The applet doesn ' t have anything to
* do if it needs the buffer , it will be called again upon readiness .
*/
2022-04-01 17:19:36 +02:00
if ( ! cs_alloc_ibuf ( cs , & app - > buffer_wait ) )
2022-04-04 07:51:21 +02:00
cs_rx_endp_more ( cs ) ;
2018-11-14 15:12:08 +01:00
2022-03-25 16:43:49 +01:00
count = co_data ( cs_oc ( cs ) ) ;
2018-05-25 16:58:52 +02:00
app - > applet - > fct ( app ) ;
2019-10-11 14:15:47 +02:00
2021-04-27 17:08:10 +02:00
/* now check if the applet has released some room and forgot to
* notify the other side about it .
*/
2022-03-25 16:43:49 +01:00
if ( count ! = co_data ( cs_oc ( cs ) ) ) {
cs_oc ( cs ) - > flags | = CF_WRITE_PARTIAL | CF_WROTE_DATA ;
2022-04-04 07:51:21 +02:00
cs_rx_room_rdy ( cs_opposite ( cs ) ) ;
2021-04-27 17:08:10 +02:00
}
2019-10-11 14:15:47 +02:00
/* measure the call rate and check for anomalies when too high */
rate = update_freq_ctr ( & app - > call_rate , 1 ) ;
if ( rate > = 100000 & & app - > call_rate . prev_ctr & & // looped more than 100k times over last second
2022-05-10 11:32:31 +02:00
( ( b_size ( cs_ib ( cs ) ) & & app - > endp - > flags & CS_EP_RXBLK_BUFF ) | | // asks for a buffer which is present
( b_size ( cs_ib ( cs ) ) & & ! b_data ( cs_ib ( cs ) ) & & app - > endp - > flags & CS_EP_RXBLK_ROOM ) | | // asks for room in an empty buffer
2022-04-04 07:51:21 +02:00
( b_data ( cs_ob ( cs ) ) & & cs_tx_endp_ready ( cs ) & & ! cs_tx_blocked ( cs ) ) | | // asks for data already present
2022-03-25 16:43:49 +01:00
( ! b_data ( cs_ib ( cs ) ) & & b_data ( cs_ob ( cs ) ) & & // didn't return anything ...
( cs_oc ( cs ) - > flags & ( CF_WRITE_PARTIAL | CF_SHUTW_NOW ) ) = = CF_SHUTW_NOW ) ) ) { // ... and left data pending after a shut
2019-10-11 14:15:47 +02:00
stream_dump_and_crash ( & app - > obj_type , read_freq_ctr ( & app - > call_rate ) ) ;
}
2022-04-01 16:34:53 +02:00
cs - > data_cb - > wake ( cs ) ;
2022-03-25 16:43:49 +01:00
channel_release_buffer ( cs_ic ( cs ) , & app - > buffer_wait ) ;
2018-05-25 16:58:52 +02:00
return t ;
2015-04-19 09:59:31 +02:00
}