blob: 137ec7b6c3717b75b411dccfb34eb4e95e80f53a [file] [log] [blame]
Willy Tarreau8f7133e2022-05-06 18:00:24 +02001Instantiation of applet contexts (appctx) in 2.6.
2
3
41. Background
5
6Most applets are in fact simplified services that are called by the CLI when a
7registered keyword is matched. Some of them only have a ->parse() function
8which immediately returns with a final result, while others will return zero
9asking for the->io_handler() one to be called till the end. For these ones, a
10context is generally needed between calls to know where to restart from.
11
12Other applets are completely autonomous applets with their init function and
13an I/O handler, and these ones also need a persistent context between calls to
14the I/O handler. These ones are typically instantiated by "use-service" or by
15other means.
16
17Originally a few integers were provided to keep a trivial state (st0, st1, st2)
18and these ones progressively proved insufficient, leading to a "ctx.cli" sub-
19context that was allowed to use extra fields of various types. Other applets
20preferred to use their own context definition.
21
22All this resulted in the appctx->ctx to contain a myriad of definitions of
Ilya Shipitsin3b64a282022-07-29 22:26:53 +050023various service contexts, and in some services abusing other services'
Willy Tarreau8f7133e2022-05-06 18:00:24 +020024definitions by laziness, and others being extended to use their own definition
25after having run for a long time on the generic types, some of which were not
26noticed and mistakenly used the same storage locations by accident. A massive
27cleanup was needed.
28
29
302. New approach in 2.6
31
32In 2.6, there's an "svcctx" pointer that's initialized to NULL before any
33instantiation of an applet or of a CLI keyword's function. Applets and keyword
34handlers are free to make it point wherever they want, and to find it unaltered
35between subsequent calls, including up to the ->release() call. The "st2" state
36that was totally abused with random enums is not used anymore and was marked as
37deprecated. It's still initialized to zero before the first call though.
38
39One special area, "svc.storage[]", is large enough to contain any of the
40contexts that used to be present under "appctx->ctx". The "svcctx" may be set
41to point to this area so that a small structure can be allocated for free and
42without requiring error checking. In order to make this easier, a specially
43purposed function is provided: "applet_reserve_svcctx()". This function will
44require the caller to indicate how large an area it needs, and will return a
45pointer to this area after checking that it fits. If it does not, haproxy will
46crash. This is purposely done so that it's known during development that if a
Ilya Shipitsin3b64a282022-07-29 22:26:53 +050047small structure doesn't fit, a different approach is required.
Willy Tarreau8f7133e2022-05-06 18:00:24 +020048
49As such, for the vast majority of commands, the process is the following one:
50
51 struct foo_ctx {
52 int myfield1;
53 int myfield2;
54 char *myfield3;
55 };
56
57 int io_handler(struct appctx *appctx)
58 {
59 struct foo_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
60
61 if (!ctx->myfield1) {
62 /* first call */
63 ctx->myfield1++;
64 }
65 ...
66 }
67
68The pointer may be directly accessed from the I/O handler if it's known that it
69was already reserved by the init handler or parsing function. Otherwise it's
70guaranteed to be NULL so that can also serve as a test for a first call:
71
72 int parse_handler(struct appctx *appctx)
73 {
74 struct foo_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
75 ctx->myfield1 = 12;
76 return 0;
77 }
78
79 int io_handler(struct appctx *appctx)
80 {
81 struct foo_ctx *ctx = appctx->svcctx;
82
83 for (; !ctx->myfield1; ctx->myfield1--) {
84 do_something();
85 }
86 ...
87 }
88
89There is no need to free anything because that space is not allocated but just
90points to a reserved area.
91
92If it is too small (its size is APPLET_MAX_SVCCTX bytes), it is preferable to
93use it with dynamically allocated structures (pools, malloc, etc). For example:
94
95 int io_handler(struct appctx *appctx)
96 {
97 struct foo_ctx *ctx = appctx->svcctx;
98
99 if (!ctx) {
100 /* first call */
101 ctx = pool_alloc(pool_foo_ctx);
102 if (!ctx)
103 return 1;
104 }
105 ...
106 }
107
108 void io_release(struct appctx *appctx)
109 {
110 pool_free(pool_foo_ctx, appctx->svcctx);
111 }
112
113The CLI code itself uses this mechanism for the cli_print_*() functions. Since
114these functions are terminal (i.e. not meant to be used in the middle of an I/O
115handler as they share the same contextual space), they always reset the svcctx
116pointer to place it to the "cli_print_ctx" mapped in ->svc.storage.
117
118
1193. Transition for old code
120
121A lot of care was taken to make the transition as smooth as possible for
122out-of-tree code since that's an API change. A dummy "ctx.cli" struct still
123exists in the appctx struct, and it happens to map perfectly to the one set by
124cli_print_*, so that if some code uses a mix of both, it will still work.
125However, it will build with "deprecated" warnings allowing to spot the
126remaining places. It's a good exercise to rename "ctx.cli" in "appctx" and see
127if the code still compiles.
128
129Regarding the "st2" sub-state, it will disappear as well after 2.6, but is
130still provided and initialized so that code relying on it will still work even
131if it builds with deprecation warnings. The correct approach is to move this
132state into the newly defined applet's context, and to stop using the stats
133enums STAT_ST_* that often barely match the needs and result in code that is
134more complicated than desired (the STAT_ST_* enum values have also been marked
135as deprecated).
136
137The code dealing with "show fd", "show sess" and the peers applet show good
138examples of how to convert a registered keyword or an applet.
139
140All this transition code requires complex layouts that will be removed during
1412.7-dev so there is no other long-term option but to update the code (or better
142get it merged if it can be useful to other users).