Commit | Line | Data |
---|---|---|
b1a20bee MW |
1 | /* -*-c-*- |
2 | * | |
3 | * Test-vector framework remote testing extension | |
4 | * | |
5 | * (c) 2024 Straylight/Edgeware | |
6 | */ | |
7 | ||
8 | /*----- Licensing notice --------------------------------------------------* | |
9 | * | |
10 | * This file is part of the mLib utilities library. | |
11 | * | |
12 | * mLib is free software: you can redistribute it and/or modify it under | |
13 | * the terms of the GNU Library General Public License as published by | |
14 | * the Free Software Foundation; either version 2 of the License, or (at | |
15 | * your option) any later version. | |
16 | * | |
17 | * mLib is distributed in the hope that it will be useful, but WITHOUT | |
18 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
19 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public | |
20 | * License for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU Library General Public | |
23 | * License along with mLib. If not, write to the Free Software | |
24 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |
25 | * USA. | |
26 | */ | |
27 | ||
28 | #ifndef MLIB_TVEC_REMOTE_H | |
29 | #define MLIB_TVEC_REMOTE_H | |
30 | ||
31 | #ifdef __cplusplus | |
32 | extern "C" { | |
33 | #endif | |
34 | ||
35 | /*----- Header files ------------------------------------------------------*/ | |
36 | ||
37 | #include <stddef.h> | |
38 | ||
39 | #include <sys/types.h> | |
40 | ||
41 | #ifndef MLIB_BUF_H | |
42 | # include "buf.h" | |
43 | #endif | |
44 | ||
45 | #ifndef MLIB_DSTR_H | |
46 | # include "dstr.h" | |
47 | #endif | |
48 | ||
49 | #ifndef MLIB_LBUF_H | |
50 | # include "lbuf.h" | |
51 | #endif | |
52 | ||
53 | #ifndef MLIB_TVEC_H | |
54 | # include "tvec.h" | |
55 | #endif | |
56 | ||
57 | /*----- Test environment --------------------------------------------------*/ | |
58 | ||
59 | struct tvec_remoteenv; | |
60 | ||
61 | typedef int tvec_connectfn(pid_t */*kid_out*/, int */*infd_out*/, | |
62 | int */*outfd_out*/, int */*errfd_out*/, | |
63 | struct tvec_state */*tv*/, | |
64 | const struct tvec_remoteenv */*env*/); | |
65 | /* A connection function. On entry, @tv@ holds the test-vector state, and | |
66 | * @env@ is the test group's remote environment structure, which will | |
67 | * typically really be some subclass of @struct tvec_remoteenv@ containing | |
68 | * additional parameters for establishing the child process. | |
69 | * | |
70 | * On successful completion, the function stores input and output | |
71 | * descriptors (which need not be distinct) in @*infd_out@ and | |
72 | * @*outfd_out@, and returns zero; if it creates a child process, it should | |
73 | * additionally store the child's process-id in @*kid_out@ and store in | |
74 | * @*errfd_out@ a descriptor from which the child's error output can be | |
75 | * read. On error, the function should report an appropriate message via | |
76 | * @tvec_error@ and return %$-1$%. | |
77 | */ | |
78 | ||
79 | struct tvec_remoteenv_slots { | |
80 | /* Additional slots for the remote-environment base class. */ | |
81 | ||
82 | tvec_connectfn *connect; /* connection function */ | |
83 | const struct tvec_env *env; /* subordinate environment */ | |
84 | unsigned dflt_reconn; /* default reconnection */ | |
85 | }; | |
86 | ||
87 | struct tvec_remoteenv { | |
88 | /* Remote environment base class instance structure. */ | |
89 | ||
90 | struct tvec_env _env; | |
91 | struct tvec_remoteenv_slots r; | |
92 | }; | |
93 | ||
94 | struct tvec_remotefork_slots { | |
95 | /* Additional slots for the remote-environment forking class. */ | |
96 | ||
97 | const struct tvec_test **tests; /* child tests (or null) */ | |
98 | }; | |
99 | ||
100 | struct tvec_remotefork { | |
101 | /* Remote environment forking class instance structure. */ | |
102 | ||
103 | struct tvec_env _env; | |
104 | struct tvec_remoteenv_slots r; | |
105 | struct tvec_remotefork_slots f; | |
106 | }; | |
107 | ||
108 | struct tvec_remoteexec_slots { | |
109 | /* Additional slots for the remote-environment exec class. */ | |
110 | ||
111 | const char *const *args; /* command line to execute */ | |
112 | }; | |
113 | ||
114 | struct tvec_remoteexec { | |
115 | /* Remote environment exec class instance structure. */ | |
116 | ||
117 | struct tvec_env _env; | |
118 | struct tvec_remoteenv_slots r; | |
119 | struct tvec_remoteexec_slots x; | |
120 | }; | |
121 | ||
122 | union tvec_remoteenv_subclass_kludge { | |
123 | /* Pointless union to engage the common-prefix rule. */ | |
124 | ||
125 | struct tvec_env _env; | |
126 | struct tvec_remoteenv renv; | |
127 | struct tvec_remotefork fork; | |
128 | struct tvec_remoteexec exec; | |
129 | }; | |
130 | ||
131 | struct tvec_remotecomms { | |
132 | /* Remote communications state; private. */ | |
133 | ||
134 | int infd, outfd; /* input and output descriptors */ | |
135 | dbuf bout; /* output buffer */ | |
136 | unsigned char *bin; /* input buffer */ | |
137 | size_t binoff, binlen, binsz; /* input offset, length, and size */ | |
138 | size_t t; /* temporary offset */ | |
139 | unsigned f; /* flags */ | |
140 | #define TVRF_BROKEN 0x0001u /* communications have failed */ | |
141 | }; | |
142 | #define TVEC_REMOTECOMMS_INIT { -1, -1, DBUF_INIT, 0, 0, 0, 0, 0, 0 } | |
143 | ||
144 | struct tvec_remotectx { | |
145 | /* Remote environment context; private. */ | |
146 | ||
147 | struct tvec_state *tv; /* test vector state */ | |
148 | struct tvec_remotecomms rc; /* communication state */ | |
149 | const struct tvec_remoteenv *re; /* environment configuration */ | |
150 | void *subctx; /* subenvironment context */ | |
151 | struct tvec_vardef vd; /* temporary variable definition */ | |
152 | unsigned ver; /* protocol version */ | |
153 | pid_t kid; /* child process id */ | |
154 | int errfd; /* child stderr descriptor */ | |
155 | lbuf errbuf; /* child stderr line buffer */ | |
156 | dstr prgwant, progress; /* progress: wanted/reported */ | |
157 | unsigned exwant, exit; /* exit status wanted/reported */ | |
158 | #define TVRF_RCNMASK 0x0300u /* reconnection behaviour: */ | |
159 | #define TVRCN_DEMAND 0x0000u /* connect on demand */ | |
160 | #define TVRCN_SKIP 0x0100u /* skip unless connected */ | |
161 | #define TVRCN_FORCE 0x0200u /* force reconnection */ | |
162 | #define TVRF_MUFFLE 0x0400u /* muffle child stderr */ | |
163 | #define TVRF_SETEXIT 0x0800u /* set `@exit' */ | |
164 | #define TVRF_SETPRG 0x1000u /* set `@progress' */ | |
165 | #define TVRF_SETRCN 0x2000u /* set `@reconnect' */ | |
166 | #define TVRF_SETMASK (TVRF_SETEXIT | TVRF_SETPRG | TVRF_SETRCN) | |
167 | /* mask of @TVTF_SET...@ */ | |
168 | }; | |
169 | ||
170 | /* Exit status. | |
171 | * | |
172 | * We don't use the conventional encoding returned by the @wait@(2) family of | |
173 | * system calls because it's too hard for our flags type to decode. Instead, | |
174 | * we use our own encoding. | |
175 | * | |
176 | * The exit code or signal number ends up in the `value' field in the low 12 | |
177 | * bits; bit 12 is set if the value field holds a signal, and it if holds an | |
178 | * exit code. Bits 13--15 hold a code which describes the status of a child | |
179 | * process or connection. | |
180 | */ | |
181 | #define TVXF_VALMASK 0x0fffu /* value (exit code or signal) */ | |
182 | #define TVXF_SIG 0x1000u /* value is signal, not exit code */ | |
183 | #define TVXF_CAUSEMASK 0xe000u /* mask for cause bits */ | |
184 | #define TVXST_RUN 0x0000u /* still running */ | |
185 | #define TVXST_EXIT 0x2000u /* child exited */ | |
186 | #define TVXST_KILL 0x4000u /* child killed by signal */ | |
187 | #define TVXST_CONT 0x6000u /* child continued (?) */ | |
188 | #define TVXST_STOP 0x8000u /* child stopped (?) */ | |
189 | #define TVXST_DISCONN 0xa000u /* disconnected */ | |
190 | #define TVXST_UNK 0xc000u /* unknown */ | |
191 | #define TVXST_ERR 0xe000u /* local error prevented diagnosis */ | |
192 | ||
193 | /* --- Environment implementation --- * | |
194 | * | |
195 | * The following special variables are supported. | |
196 | * | |
197 | * * %|@exit|% is the expected exit status; see @TVXF_...@ and @TVXST_...@. | |
198 | * | |
199 | * * %|progress|% is the expected progress token when the test completes. | |
200 | * On successful completion, this will be %|%DONE|%; it's %|%RUN|% on | |
201 | * entry to the test function, but that can call @tvec_setprogress@ to | |
202 | * change it. | |
203 | * | |
204 | * * %|reconnect|% is a reconnection policy; see @TVRCN_...@. | |
205 | * | |
206 | * Unrecognized variables are passed to the remote server, as are the | |
207 | * environment events. As a convenience for @fork@-based remote execution, | |
208 | * If the outermost environment in the server's test definition has | |
209 | * @tvec_remotesetup@ as its @setup@ function, then its subordinate | |
210 | * environment is used instead. | |
211 | */ | |
212 | ||
213 | extern tvec_envsetupfn tvec_remotesetup; | |
214 | extern tvec_envfindvarfn tvec_remotefindvar; | |
215 | extern tvec_envbeforefn tvec_remotebefore; | |
216 | extern tvec_envrunfn tvec_remoterun; | |
217 | extern tvec_envafterfn tvec_remoteafter; | |
218 | extern tvec_envteardownfn tvec_remoteteardown; | |
219 | ||
220 | #define TVEC_REMOTEENV \ | |
221 | { sizeof(struct tvec_remotectx), \ | |
222 | tvec_remotesetup, \ | |
223 | tvec_remotefindvar, \ | |
224 | tvec_remotebefore, \ | |
225 | tvec_remoterun, \ | |
226 | tvec_remoteafter, \ | |
227 | tvec_remoteteardown } | |
228 | ||
229 | extern tvec_connectfn tvec_fork, tvec_exec; | |
230 | ||
231 | #define TVEC_REMOTEFORK(subenv, tests) \ | |
232 | TVEC_REMOTEENV, { tvec_fork, subenv }, { tests } | |
233 | ||
234 | #define TVEC_REMOTEEXEC(subenv, args) \ | |
235 | TVEC_REMOTEENV, { tvec_exec, subenv }, { args } | |
236 | ||
237 | /*----- Functions provided ------------------------------------------------*/ | |
238 | ||
239 | /* --- @tvec_setprogress@, @tvec_setprogress_v@ --- * | |
240 | * | |
241 | * Arguments: @const char *status@ = progress status token format | |
242 | * @va_list ap@ = argument tail | |
243 | * | |
244 | * Returns: --- | |
245 | * | |
246 | * Use: Reports the progress of a test execution to the client. | |
247 | * | |
248 | * The framework makes use of tokens beginning with %|%|%: | |
249 | * | |
250 | * * %|%IDLE|%: during the top-level server code; | |
251 | * | |
252 | * * %|%SETUP|%: during the enclosing environment's @before@ | |
253 | * function; | |
254 | * | |
255 | * * %|%RUN|%: during the environment's @run@ function, or the | |
256 | * test function; and | |
257 | * | |
258 | * * %|%DONE|%: during the enclosing environment's @after@ | |
259 | * function. | |
260 | * | |
261 | * The intent is that a test can use the progress token to check | |
262 | * that a function which is expected to crash does so at the | |
263 | * correct point, so it's expected that more complex test | |
264 | * functions and/or environments will set their own progress | |
265 | * tokens to reflect what's going on. | |
266 | */ | |
267 | ||
268 | extern PRINTF_LIKE(1, 2) int tvec_setprogress(const char */*status*/, ...); | |
269 | extern int tvec_setprogress_v(const char */*status*/, va_list */*ap*/); | |
270 | ||
271 | /* --- @tvec_remoteserver@ --- * | |
272 | * | |
273 | * Arguments: @int infd@, @int outfd@ = input and output file descriptors | |
274 | * @const struct tvec_config *config@ = test configuration | |
275 | * | |
276 | * Returns: Suggested exit code. | |
277 | * | |
278 | * Use: Run a test server, reading packets from @infd@ and writing | |
279 | * responses and notifications to @outfd@, and invoking tests as | |
280 | * described by @config@. | |
281 | * | |
282 | * This function is not particularly general purpose. It | |
283 | * expects to `take over' the process, and makes use of private | |
284 | * global variables. | |
285 | */ | |
286 | ||
287 | extern int tvec_remoteserver(int /*infd*/, int /*outfd*/, | |
288 | const struct tvec_config */*config*/); | |
289 | ||
290 | /*----- That's all, folks -------------------------------------------------*/ | |
291 | ||
292 | #ifdef __cplusplus | |
293 | } | |
294 | #endif | |
295 | ||
296 | #endif |