@@@ fltfmt mess
[mLib] / test / tvec-remote.c
CommitLineData
e63124bc
MW
1/* -*-c-*-
2 *
3 * Remote testing
4 *
5 * (c) 2023 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/*----- Header files ------------------------------------------------------*/
29
30#include <errno.h>
c91413e6 31#include <signal.h>
e63124bc
MW
32#include <stdarg.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36
37#include <sys/types.h>
c91413e6 38#include <sys/wait.h>
e63124bc
MW
39#include <fcntl.h>
40#include <unistd.h>
41
42#include "alloc.h"
c91413e6 43#include "bench.h"
e63124bc 44#include "buf.h"
c91413e6
MW
45#include "compiler.h"
46#include "fdflags.h"
adec5584 47#include "growbuf.h"
c91413e6
MW
48#include "lbuf.h"
49#include "mdup.h"
50#include "quis.h"
b1a20bee 51
e63124bc 52#include "tvec.h"
b1a20bee
MW
53#include "tvec-adhoc.h"
54#include "tvec-bench.h"
55#include "tvec-output.h"
56#include "tvec-remote.h"
57#include "tvec-types.h"
e63124bc 58
31d0247c
MW
59/*----- Preliminaries -----------------------------------------------------*/
60
61/* The control macros I'm using below provoke `dangling-else' warnings from
62 * compilers. Suppress them. I generally don't care.
63 */
64
c91413e6
MW
65#if GCC_VERSION_P(7, 1)
66# pragma GCC diagnostic ignored "-Wdangling-else"
67#elif GCC_VERSION_P(4, 2)
68# pragma GCC diagnostic ignored "-Wparentheses"
69#endif
e63124bc 70
c91413e6
MW
71#if CLANG_VERSION_P(3, 1)
72# pragma clang diagnostic ignored "-Wdangling-else"
73#endif
e63124bc 74
c91413e6 75/*----- Basic I/O ---------------------------------------------------------*/
e63124bc 76
31d0247c
MW
77/* --- @init_comms@ --- *
78 *
79 * Arguments: @struct tvec_remotecomms *rc@ = communication state
80 *
81 * Returns: ---
82 *
83 * Use: Initialize a communication state. This doesn't allocate any
84 * resurces: it just ensures that everything is set up so that
85 * subsequent operations -- in particular @release_comms@ --
86 * behave sensibly.
87 */
88
c91413e6
MW
89static void init_comms(struct tvec_remotecomms *rc)
90{
adec5584 91 rc->bin = 0; rc->binsz = 0; DBCREATE(&rc->bout);
c91413e6
MW
92 rc->infd = rc->outfd = -1; rc->f = 0;
93}
e63124bc 94
31d0247c
MW
95/* --- @close_comms@ --- *
96 *
97 * Arguments: @struct tvec_remotecomms *rc@ = communication state
98 *
99 * Returns: ---
100 *
101 * Use: Close the input and output descriptors.
102 *
103 * If the descriptors are already closed -- or were never opened
104 * -- then nothing happens.
105 */
106
c91413e6
MW
107static void close_comms(struct tvec_remotecomms *rc)
108{
c81c35df
MW
109 if (rc->infd >= 0) {
110 if (rc->infd != rc->outfd) close(rc->infd);
111 rc->infd = -1;
112 }
113 if (rc->outfd >= 0)
114 { close(rc->outfd); rc->outfd = -1; }
115 rc->f |= TVRF_BROKEN;
c91413e6
MW
116}
117
31d0247c
MW
118/* --- @release_comms@ --- *
119 *
b1a20bee
MW
120 * Arguments: @struct tvec_state *tv@ = test-vector state
121 * @struct tvec_remotecomms *rc@ = communication state
31d0247c
MW
122 *
123 * Returns: ---
124 *
125 * Use: Releases the resources -- most notably the input and output
126 * buffers -- held by the communication state. Also calls
127 * @close_comms@.
128 */
129
b1a20bee
MW
130static void release_comms(struct tvec_state *tv, struct tvec_remotecomms *rc)
131 { close_comms(rc); x_free(tv->a, rc->bin); DBDESTROY(&rc->bout); }
31d0247c
MW
132
133/* --- @setup_comms@ --- *
134 *
135 * Arguments: @struct tvec_remotecomms *rc@ = communication state
136 * @int infd, outfd@ = input and output file descriptors
137 *
138 * Returns: ---
139 *
140 * Use: Use the given descriptors for communication.
141 *
142 * Clears the private flags.
143 */
c91413e6
MW
144
145static void setup_comms(struct tvec_remotecomms *rc, int infd, int outfd)
146{
31d0247c
MW
147 rc->infd = infd; rc->outfd = outfd;
148 rc->binoff = rc->binlen = 0;
149 rc->f &= ~0xffu;
c91413e6 150}
e63124bc 151
31d0247c
MW
152/* --- @ioerr@ --- *
153 *
154 * Arguments: @struct tvec_state *tv@ = test-vector state
155 * @struct tvec_remotecomms *rc@ = communication state
156 * @const char *msg, ...@ = format string and arguments
157 *
158 * Returns: %$-1$%.
159 *
160 * Use: Reports the message as an error, closes communications and
161 * marks them as broken.
162 */
163
164static PRINTF_LIKE(3, 4)
165 int ioerr(struct tvec_state *tv, struct tvec_remotecomms *rc,
166 const char *msg, ...)
e63124bc
MW
167{
168 va_list ap;
169
170 va_start(ap, msg);
c91413e6
MW
171 close_comms(rc); rc->f |= TVRF_BROKEN;
172 tvec_report_v(tv, TVLEV_ERR, msg, &ap);
e63124bc
MW
173 va_end(ap);
174 return (-1);
175}
176
31d0247c
MW
177/* --- @send_all@ --- *
178 *
179 * Arguments: @struct tvec_state *tv@ = test-vector state
180 * @struct tvec_remotecomms *rc@ = communication state
181 * @const unsigned char *p@, @size_t sz@ = output buffer
182 *
183 * Returns: Zero on success, %$-1$% on error.
184 *
185 * Use: Send the output buffer over the communication state's output
186 * descriptor, even if it has to be written in multiple pieces.
187 */
188
c91413e6 189static int send_all(struct tvec_state *tv, struct tvec_remotecomms *rc,
e63124bc
MW
190 const unsigned char *p, size_t sz)
191{
192 ssize_t n;
c91413e6 193 int ret;
e63124bc
MW
194
195 while (sz) {
c91413e6 196 n = write(rc->outfd, p, sz);
e63124bc
MW
197 if (n > 0)
198 { p += n; sz -= n; }
c91413e6
MW
199 else {
200 ret = ioerr(tv, rc, "failed to send: %s",
201 n ? strerror(errno) : "empty write");
202 goto end;
203 }
e63124bc 204 }
c91413e6
MW
205 ret = 0;
206end:
c91413e6 207 return (ret);
e63124bc
MW
208}
209
31d0247c
MW
210/* --- @recv_all@ --- *
211 *
212 * Arguments: @struct tvec_state *tv@ = test-vector state
213 * @struct tvec_remotecomms *rc@ = communication state
214 * @unsigned f@ = flags (@RCVF_...@)
215 * @unsigned char *p@, @size_t sz@ = input buffer
216 * @size_t min@ = minimum acceptable size to read
217 * @size_t *n_out@ = size read
218 *
c81c35df 219 * Returns: A @RECV_...@ code.
31d0247c
MW
220 *
221 * Use: Receive data on the communication state's input descriptor to
222 * read at least @min@ bytes into the input buffer, even if it
223 * has to be done in multiple pieces. If more data is readily
224 * available, then up to @sz@ bytes will be read in total.
225 *
226 * If the descriptor immediately reports end-of-file, and
227 * @RCVF_ALLOWEOF@ is set in @f@, then return @RECV_EOF@.
228 * Otherwise, EOF is treated as an I/O error, resulting in a
229 * call to @ioerr@ and a return code of @RECV_FAIL@. If the
230 * read succeeded, then set @*n_out@ to the number of bytes read
231 * and return @RECV_OK@.
232 */
233
e63124bc 234#define RCVF_ALLOWEOF 1u
31d0247c 235
c91413e6
MW
236enum {
237 RECV_FAIL = -1,
238 RECV_OK = 0,
239 RECV_EOF = 1
240};
31d0247c 241
c91413e6 242static int recv_all(struct tvec_state *tv, struct tvec_remotecomms *rc,
31d0247c
MW
243 unsigned f, unsigned char *p, size_t sz,
244 size_t min, size_t *n_out)
e63124bc 245{
31d0247c 246 size_t tot = 0;
e63124bc 247 ssize_t n;
e63124bc
MW
248
249 while (sz) {
c91413e6 250 n = read(rc->infd, p, sz);
31d0247c
MW
251 if (n > 0) {
252 p += n; sz -= n; tot += n;
253 if (tot >= min) break;
254 } else if (!n && !tot && (f&RCVF_ALLOWEOF))
c81c35df 255 { rc->f |= TVRF_BROKEN; return (RECV_EOF); }
e63124bc 256 else
c91413e6 257 return (ioerr(tv, rc, "failed to receive: %s",
e63124bc
MW
258 n ? strerror(errno) : "unexpected end-of-file"));
259 }
31d0247c 260 *n_out = tot; return (RECV_OK);
e63124bc
MW
261
262#undef f_any
263}
264
31d0247c
MW
265/* --- @buferr@ --- *
266 *
267 * Arguments: @struct tvec_state *tv@ = test-vector state
268 * @struct tvec_remotecomms *rc@ = communication state
269 *
270 * Returns: %$-$%.
271 *
272 * Use: Report a problem preparing the output buffer.
273 */
274
275static int buferr(struct tvec_state *tv, struct tvec_remotecomms *rc)
276 { return (ioerr(tv, rc, "failed to build output packet")); }
277
278/* --- @malformed@ --- *
279 *
280 * Arguments: @struct tvec_state *tv@ = test-vector state
281 * @struct tvec_remotecomms *rc@ = communication state
282 *
283 * Returns: %$-$%.
284 *
285 * Use: Report an I/O error that the incoming packet is malformed.
286 */
287
288static int malformed(struct tvec_state *tv, struct tvec_remotecomms *rc)
289 { return (ioerr(tv, rc, "received malformed packet")); }
290
291/* --- @remote_send@ --- *
292 *
293 * Arguments: @struct tvec_state *tv@ = test-vector state
294 * @struct tvec_remotecomms *rc@ = communication state
295 *
296 * Returns: Zero on success, %$-1$% on error.
297 *
298 * Use: Send the accuulated contents of the output buffer @rc->bout@.
299 *
300 * The function arranges to convert @SIGPIPE@ into an error.
301 *
302 * If the output buffer is broken, report this as an I/O error.
303 */
304
305#define SENDBUFSZ 4096
306
c91413e6 307static int remote_send(struct tvec_state *tv, struct tvec_remotecomms *rc)
e63124bc 308{
31d0247c
MW
309 void (*opipe)(int) = SIG_ERR;
310 int ret;
311
312 /* Various preflight checks. */
313 if (rc->f&TVRF_BROKEN) { ret = -1; goto end; }
314 if (DBBAD(&rc->bout)) { ret = buferr(tv, rc); goto end; }
e63124bc 315
31d0247c
MW
316 /* Arrange to trap broken-pipe errors. */
317 opipe = signal(SIGPIPE, SIG_IGN);
318 if (opipe == SIG_ERR) {
319 ret = ioerr(tv, rc, "failed to ignore `SIGPIPE': %s", strerror(errno));
320 goto end;
321 }
e63124bc 322
31d0247c
MW
323 /* Transmit the packet. */
324 if (send_all(tv, rc, DBBASE(&rc->bout), DBLEN(&rc->bout)))
325 { ret = -1; goto end; }
e63124bc 326
31d0247c
MW
327 /* Done. Put things back the way we found them. */
328 ret = 0;
329end:
330 DBRESET(&rc->bout);
331 if (opipe != SIG_ERR) signal(SIGPIPE, opipe);
332 return (ret);
e63124bc
MW
333}
334
31d0247c
MW
335/* --- @receive_buffered@ --- *
336 *
337 * Arguments: @struct tvec_state *tv@ = test-vector state
338 * @struct tvec_remotecomms *rc@ = communication state
339 * @unsigned f@ = flags (@RCVF_...@)
340 * @size_t want@ = data block size required
341 *
c81c35df 342 * Returns: A @RECV_...@ code.
31d0247c
MW
343 *
344 * Use: Reads a block of data from the input descriptor into the
345 * input buffer.
346 *
347 * This is the main machinery for manipulating the input buffer.
348 * The buffer has three regions:
349 *
350 * * from the buffer start to @rc->binoff@ is `consumed';
351 * * from @rc->binoff@ to @rc->binlen@ is `available'; and
352 * * from @rc->binlen@ to @rc->binsz@ is `free'.
353 *
354 * Data is read into the start of the `free' region, and the
355 * `available' region is extended to include it. Data in the
356 * `consumed' region is periodically discarded by moving the
357 * data from the `available' region to the start of the buffer
358 * and decreasing @rc->binoff@ and @rc->binlen@.
359 *
360 * This function ensures that the `available' region contains at
361 * least @want@ bytes, by (a) extending the buffer, if
362 * necessary, so that @rc->binsz >= rc->binoff + want@, and (b)
363 * reading fresh data from the input descriptor to extend the
364 * `available' region.
365 *
366 * If absolutely no data is available, and @RCVF_ALLOWEOF@ is
367 * set in @f@, then return @RECV_EOF@. On I/O errors, including
368 * a short read or end-of-file if @RCVF_ALLOWEOF@ is clear,
369 * return @RECV_FAIL@. On success, return @RECV_OK@. The
370 * amount of data read is indicated by updating the input buffer
371 * variables as described above.
372 */
373
374#define RECVBUFSZ 4096u
375
376static int receive_buffered(struct tvec_state *tv,
377 struct tvec_remotecomms *rc,
378 unsigned f, size_t want)
379{
13ee7406 380 size_t sz = 0;
31d0247c
MW
381 int ret;
382
383 /* If we can supply the caller's requirement from the buffer then do
384 * that.
385 */
386 if (rc->binlen - rc->binoff >= want) return (RECV_OK);
387
388 /* If the buffer is too small then we must grow it. */
b1a20bee 389 GROWBUF_EXTEND(size_t, tv->a, rc->bin, rc->binsz, want, RECVBUFSZ, 1);
31d0247c
MW
390
391 /* Shunt the unused existing material to the start of the buffer. */
392 memmove(rc->bin, rc->bin + rc->binoff, rc->binlen - rc->binoff);
393 rc->binlen -= rc->binoff; rc->binoff = 0;
394
395 /* Satisfy the caller from the input stream, and try to fill up as much of
396 * the rest of the buffer as we can.
397 */
398 ret = recv_all(tv, rc, rc->binlen ? 0 : f,
399 rc->bin + rc->binlen, rc->binsz - rc->binlen,
400 want - rc->binlen, &sz);
401 if (ret) return (ret);
402
403 /* Note how much material we have and return. */
404 rc->binlen += sz; return (RECV_OK);
405}
406
407/* --- @remote_recv@ --- *
408 *
409 * Arguments: @struct tvec_state *tv@ = test-vector state
410 * @unsigned f@ = flags (@RCVF_...@)
411 * @buf *b_out@ = buffer to establish around the packet contents
412 *
c81c35df 413 * Returns: A @RECV_...@ code.
31d0247c
MW
414 *
415 * Use: Receive a packet into the input buffer @rc->bin@ and
416 * establish @*b_out@ to read from it.
417 */
418
c91413e6
MW
419static int remote_recv(struct tvec_state *tv, struct tvec_remotecomms *rc,
420 unsigned f, buf *b_out)
e63124bc 421{
31d0247c
MW
422 kludge64 k, szmax;
423 size_t want;
c91413e6 424 int ret;
e63124bc 425
c91413e6 426 ASSIGN64(szmax, (size_t)-1);
31d0247c
MW
427
428 /* Preflight checks. */
429 if (rc->f&TVRF_BROKEN) return (RECV_FAIL);
430
431 /* See if we can read the next packet length from what we already have. */
432 ret = receive_buffered(tv, rc, f, 8); if (ret) return (ret);
433 LOAD64_L_(k, rc->bin + rc->binoff); rc->binoff += 8;
e63124bc 434 if (CMP64(k, >, szmax))
c91413e6 435 return (ioerr(tv, rc, "packet size 0x%08lx%08lx out of range",
e63124bc 436 (unsigned long)HI64(k), (unsigned long)LO64(k)));
31d0247c 437 want = GET64(size_t, k);
e63124bc 438
31d0247c
MW
439 /* Read the next packet payload. */
440 ret = receive_buffered(tv, rc, 0, want); if (ret) return (ret);
441 buf_init(b_out, rc->bin + rc->binoff, want); rc->binoff += want;
442 return (RECV_OK);
e63124bc
MW
443}
444
31d0247c
MW
445/* --- @QUEUEPK_TAG@, @QUEUEPK@ --- *
446 *
447 * Arguments: @tag@ = control structure tag
448 * @struct tvec_state *tv@ = test-vector state
449 * @struct tvec_remotecomms *rc@ = communication state
450 * @unsigned fl@ = flags (@QF_...@)
451 * @unsigned pk@ = packet type
452 *
453 * Use: This is syntactically a statement head: the syntax is
454 * @QUEUEPK(tv, rc, f) body [else alt]@. The @body@ should
455 * write material to the output buffer @rc->bout@. The macro
456 * applies appropriate framing. If enough material has been
457 * collected, or if @QF_FORCE@ is set in @fl@, then
458 * @remote_send@ is invoked to transmit the buffered packets.
459 * If there is an error of any kind, then the @alt@ statement,
460 * if any, is executed.
461 */
e63124bc 462
31d0247c
MW
463#define QF_FORCE 1u
464#define QUEUEPK_TAG(tag, tv, rc, fl, pk) \
465 if ((rc)->f&TVRF_BROKEN) MC_GOELSE(tag##__else); else \
466 MC_ALLOWELSE(tag##__else) \
467 MC_AFTER(tag##__send, { \
468 if ((DBBAD(&(rc)->bout) && (buferr((tv), (rc)), 1)) || \
469 ((((fl)&QF_FORCE) || DBLEN(&(rc)->bout) >= SENDBUFSZ) && \
470 remote_send(tv, rc))) \
471 MC_GOELSE(tag##__else); \
472 }) \
473 DBUF_ENCLOSEITAG(tag##__frame, &(rc)->bout, (rc)->t, 64_L) \
474 MC_BEFORE(tag##__pkty, { \
475 dbuf_putu16l(&(rc)->bout, (pk)); \
476 })
477
478#define QUEUEPK(tv, rc, fl, pk) QUEUEPK_TAG(queue, (tv), (rc), (fl), (pk))
e63124bc
MW
479
480/*----- Packet types ------------------------------------------------------*/
481
c91413e6 482#define TVPF_ACK 0x0001u
e63124bc 483
31d0247c
MW
484#define TVPK_VER 0x0000u /* --> min, max: u16 *
485 * <-- ver: u16 */
486#define TVPK_BGROUP 0x0002u /* --> name: str16
487 * <-- --- */
814e42ff
MW
488#define TVPK_SETVAR 0x0004u /* --> name: str16, rv: value
489 * <-- rc: u8 */
490#define TVPK_TEST 0x0006u /* --> in: regs
31d0247c 491 * <-- --- */
814e42ff 492#define TVPK_EGROUP 0x0008u /* --> --- *
31d0247c 493 * <-- --- */
e63124bc 494
c91413e6
MW
495#define TVPK_REPORT 0x0100u /* <-- level: u16; msg: string */
496#define TVPK_PROGRESS 0x0102u /* <-- st: str16 */
e63124bc 497
31d0247c
MW
498#define TVPK_SKIPGRP 0x0104u /* <-- excuse: str16 */
499#define TVPK_SKIP 0x0106u /* <-- excuse: str16 */
500#define TVPK_FAIL 0x0108u /* <-- flag: u8, detail: str16 */
501#define TVPK_DUMPREG 0x010au /* <-- ri: u16; disp: u16;
c91413e6 502 * flag: u8, rv: value */
b1a20bee
MW
503#define TVPK_BBENCH 0x010cu /* <-- unit: u16, ident: regs */
504#define TVPK_EBENCH 0x010eu /* <-- unit: u16; flags: u16;
505 * n, t, cy: f64 */
e63124bc 506
c91413e6
MW
507/*----- Server ------------------------------------------------------------*/
508
31d0247c 509/* Forward declaration of output operations. */
c91413e6
MW
510static const struct tvec_outops remote_ops;
511
31d0247c
MW
512static struct tvec_state srvtv; /* server's test-vector state */
513static struct tvec_remotecomms srvrc = TVEC_REMOTECOMMS_INIT; /* comms */
514static struct tvec_output srvout = { &remote_ops }; /* output state */
e63124bc 515
31d0247c
MW
516/* --- @tvec_setprogress@, @tvec_setprogress_v@ --- *
517 *
518 * Arguments: @const char *status@ = progress status token format
519 * @va_list ap@ = argument tail
520 *
521 * Returns: ---
522 *
523 * Use: Reports the progress of a test execution to the client.
524 *
525 * The framework makes use of tokens beginning with %|%|%:
526 *
527 * * %|%IDLE|%: during the top-level server code;
528 *
529 * * %|%SETUP|%: during the enclosing environment's @before@
530 * function;
531 *
532 * * %|%RUN|%: during the environment's @run@ function, or the
533 * test function; and
534 *
535 * * %|%DONE|%: during the enclosing environment's @after@
536 * function.
537 *
538 * The intent is that a test can use the progress token to check
539 * that a function which is expected to crash does so at the
540 * correct point, so it's expected that more complex test
541 * functions and/or environments will set their own progress
542 * tokens to reflect what's going on.
543 */
544
545int tvec_setprogress(const char *status, ...)
546{
547 va_list ap;
548 int rc;
549
550 va_start(ap, status); rc = tvec_setprogress_v(status, &ap); va_end(ap);
551 return (rc);
552}
553
554int tvec_setprogress_v(const char *status, va_list *ap)
c91413e6 555{
31d0247c
MW
556 /* Force immediate output in case we crash before the buffer is output
557 * organically.
558 */
559 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_PROGRESS)
560 dbuf_vputstrf16l(&srvrc.bout, status, ap);
c91413e6
MW
561 else return (-1);
562 return (0);
e63124bc
MW
563}
564
31d0247c
MW
565/* --- @tvec_remoteserver@ --- *
566 *
567 * Arguments: @int infd@, @int outfd@ = input and output file descriptors
568 * @const struct tvec_config *config@ = test configuration
569 *
570 * Returns: Suggested exit code.
571 *
572 * Use: Run a test server, reading packets from @infd@ and writing
573 * responses and notifications to @outfd@, and invoking tests as
574 * described by @config@.
575 *
576 * This function is not particularly general purpose. It
577 * expects to `take over' the process, and makes use of private
578 * global variables.
579 */
580
c91413e6 581int tvec_remoteserver(int infd, int outfd, const struct tvec_config *config)
e63124bc 582{
c91413e6
MW
583 uint16 pk, u, v;
584 unsigned i;
585 buf b;
814e42ff 586 dstr d = DSTR_INIT;
b1a20bee 587 const struct tvec_test *t, *const *tt;
c91413e6
MW
588 void *p; size_t sz;
589 const struct tvec_env *env = 0;
814e42ff
MW
590 const struct tvec_vardef *vd = 0; void *varctx;
591 struct tvec_reg *r = 0, rbuf, *r_alloc = 0; size_t rsz = 0;
c91413e6
MW
592 void *ctx = 0;
593 int rc;
594
31d0247c 595 /* Initialize the communication machinery. */
c91413e6 596 setup_comms(&srvrc, infd, outfd);
31d0247c
MW
597
598 /* Begin a test session using our custom output driver. */
c91413e6
MW
599 tvec_begin(&srvtv, config, &srvout);
600
31d0247c
MW
601 /* Version negotiation. Expect a @TVPK_VER@ packet. At the moment,
602 * there's only version zero, so we return that.
603 */
c91413e6
MW
604 if (remote_recv(&srvtv, &srvrc, 0, &b)) { rc = -1; goto end; }
605 if (buf_getu16l(&b, &pk)) goto bad;
606 if (pk != TVPK_VER) {
607 rc = ioerr(&srvtv, &srvrc,
608 "unexpected packet type 0x%04x instead of client version",
609 pk);
610 goto end;
e63124bc 611 }
c91413e6 612 if (buf_getu16l(&b, &u) || buf_getu16l(&b, &v)) goto bad;
31d0247c
MW
613 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_VER | TVPF_ACK)
614 dbuf_putu16l(&srvrc.bout, 0);
c91413e6 615 else { rc = -1; goto end; }
e63124bc 616
31d0247c
MW
617 /* Handle packets until the server closes the connection.
618 *
619 * The protocol looks much simpler from our point of view than from the
620 * client.
621 *
622 * * Receive @TVPK_VER@; respond with @TVPK_VER | TVPF_ACK@.
623 *
624 * * Receive zero or more @TVPK_BGROUP@. Open a test group, producing
625 * output packets, and eventually answer with @TVPK_BGROUP | TVPF_ACK@.
626 *
627 * -- Receive zero or more @TVPK_TEST@. Run a test, producing output
628 * packets, and eventually answer with @TVPK_TEST | TVPF_ACK@.
629 *
630 * -- Receive @TVPK_EGROUP@. Maybe produce output packets, and
631 * answer with @TVPK_EGROUP | TVPF_ACK@.
632 *
633 * * Read EOF. Stop.
634 */
c91413e6 635 for (;;) {
31d0247c
MW
636
637 /* Read a packet. End-of-file is expected here (and pretty much nowhere
638 * else). Otherwise, we expect to see @TVPK_BGROUP@.
639 */
c91413e6
MW
640 rc = remote_recv(&srvtv, &srvrc, RCVF_ALLOWEOF, &b);
641 if (rc == RECV_EOF) break;
642 else if (rc == RECV_FAIL) goto end;
643 if (buf_getu16l(&b, &pk)) goto bad;
e63124bc 644
c91413e6
MW
645 switch (pk) {
646
647 case TVPK_BGROUP:
31d0247c
MW
648 /* Start a group. */
649
650 /* Parse the packet payload. */
c91413e6
MW
651 p = buf_getmem16l(&b, &sz); if (!p) goto bad;
652 if (BLEFT(&b)) goto bad;
31d0247c
MW
653
654 /* Find the group given its name. */
b1a20bee
MW
655 for (tt = srvtv.cfg.tests; *tt; tt++) {
656 t = *tt;
c91413e6
MW
657 if (strlen(t->name) == sz && MEMCMP(t->name, ==, p, sz))
658 goto found_group;
b1a20bee 659 }
c91413e6
MW
660 rc = ioerr(&srvtv, &srvrc, "unknown test group `%.*s'",
661 (int)sz, (char *)p);
662 goto end;
663
664 found_group:
31d0247c 665 /* Set up the test environment. */
c91413e6
MW
666 srvtv.test = t; env = t->env;
667 if (env && env->setup == tvec_remotesetup)
668 env = ((struct tvec_remoteenv *)env)->r.env;
669 if (!env || !env->ctxsz) ctx = 0;
b1a20bee 670 else ctx = x_alloc(srvtv.a, env->ctxsz);
c91413e6
MW
671 if (env && env->setup) env->setup(&srvtv, env, 0, ctx);
672
31d0247c
MW
673 /* Initialize the registers. */
674 tvec_initregs(&srvtv);
675
676 /* Report that the group has been opened and that we're ready to run
677 * tests.
678 */
679 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_BGROUP | TVPF_ACK);
c91413e6
MW
680 else { rc = -1; goto end; }
681
31d0247c 682 /* Handle packets until we're told to end the group. */
c91413e6 683 for (;;) {
31d0247c
MW
684
685 /* Read a packet. We expect @TVPK_EGROUP@ or @TVPK_TEST@. */
c91413e6
MW
686 if (remote_recv(&srvtv, &srvrc, 0, &b)) { rc = -1; goto end; }
687 if (buf_getu16l(&b, &pk)) goto bad;
31d0247c 688
c91413e6
MW
689 switch (pk) {
690
691 case TVPK_EGROUP:
31d0247c
MW
692 /* End the group. */
693
694 /* Check the payload. */
c91413e6 695 if (BLEFT(&b)) goto bad;
31d0247c
MW
696
697 /* Leave the group loop. */
c91413e6
MW
698 goto endgroup;
699
814e42ff
MW
700 case TVPK_SETVAR:
701 /* Set a subenvironment variable. */
702
703 /* Get the variable name. */
704 p = buf_getmem16l(&b, &sz); if (!p) goto bad;
705 DRESET(&d); DPUTM(&d, p, sz); DPUTZ(&d);
706
707 /* Look up the variable definition. */
708 if (env && env->findvar) {
709 vd = env->findvar(&srvtv, d.buf, &varctx, ctx);
710 if (vd) goto found_var;
711 }
6e683a79 712 rc = tvec_unkregerr(&srvtv, d.buf); goto setvar_end;
814e42ff
MW
713 found_var:
714
715 /* Set up the register. */
716 if (vd->regsz <= sizeof(rbuf))
717 r = &rbuf;
718 else {
b1a20bee 719 GROWBUF_REPLACE(size_t, srvtv.a, r_alloc, rsz, vd->regsz,
adec5584 720 8*sizeof(void *), 1);
814e42ff
MW
721 r = r_alloc;
722 }
723
724 /* Collect and set the value. */
725 vd->def.ty->init(&r->v, &vd->def);
726 if (vd->def.ty->frombuf(&b, &r->v, &vd->def)) goto bad;
727 if (BLEFT(&b)) goto bad;
728 rc = vd->setvar(&srvtv, d.buf, &r->v, varctx);
729
730 /* Send the reply. */
731 setvar_end:
732 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_SETVAR | TVPF_ACK)
733 dbuf_putbyte(&srvrc.bout, rc ? 0xff : 0);
734 else { rc = -1; goto end; }
735 if (vd) { vd->def.ty->release(&r->v, &vd->def); vd = 0; }
736 break;
737
c91413e6 738 case TVPK_TEST:
31d0247c
MW
739 /* Run a test. */
740
741 /* Parse the packet payload. */
c91413e6 742 if (tvec_deserialize(srvtv.in, &b, srvtv.test->regs,
b1a20bee 743 0, 0, srvtv.cfg.nreg, srvtv.cfg.regsz))
c91413e6
MW
744 goto bad;
745 if (BLEFT(&b)) goto bad;
746
31d0247c
MW
747 /* If we're not skipping the test group, then actually try to
748 * run the test.
749 */
c91413e6 750 if (!(srvtv.f&TVSF_SKIP)) {
31d0247c
MW
751
752 /* Prepare the output registers and reset the test outcome.
753 * (The environment may force a skip.)
754 */
d056fbdf 755 for (i = 0; i < srvtv.cfg.nrout; i++)
31d0247c
MW
756 if (TVEC_REG(&srvtv, in, i)->f&TVRF_LIVE)
757 TVEC_REG(&srvtv, out, i)->f |= TVRF_LIVE;
c91413e6 758 srvtv.f |= TVSF_ACTIVE; srvtv.f &= ~TVSF_OUTMASK;
31d0247c
MW
759
760 /* Invoke the environment @before@ function. */
761 tvec_setprogress("%%SETUP");
c91413e6 762 if (env && env->before) env->before(&srvtv, ctx);
31d0247c
MW
763
764 /* Run the actual test. */
c91413e6
MW
765 if (!(srvtv.f&TVSF_ACTIVE))
766 /* setup forced a skip */;
767 else {
31d0247c 768 tvec_setprogress("%%RUN");
c91413e6
MW
769 if (env && env->run)
770 env->run(&srvtv, t->fn, ctx);
771 else {
772 t->fn(srvtv.in, srvtv.out, ctx);
773 tvec_check(&srvtv, 0);
774 }
775 }
31d0247c
MW
776
777 /* Conclude the test. */
778 tvec_setprogress("%%DONE");
c91413e6
MW
779 if (env && env->after) env->after(&srvtv, ctx);
780 tvec_endtest(&srvtv);
781 }
31d0247c
MW
782
783 /* Reset the input registers and report completion. */
784 tvec_releaseregs(&srvtv); tvec_initregs(&srvtv);
785 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_TEST | TVPF_ACK);
c91413e6 786 else { rc = -1; goto end; }
c91413e6
MW
787 break;
788
789 default:
31d0247c
MW
790 /* Some other kind of packet. Complain. */
791
c91413e6 792 rc = ioerr(&srvtv, &srvrc,
31d0247c
MW
793 "unexpected packet type 0x%04x during test group",
794 pk);
c91413e6
MW
795 goto end;
796
797 }
798 }
799
800 endgroup:
31d0247c
MW
801 /* The test group completed. */
802
803 /* Tear down the environment and release other resources. */
c91413e6 804 if (env && env->teardown) env->teardown(&srvtv, ctx);
31d0247c 805 tvec_releaseregs(&srvtv);
b1a20bee 806 x_free(srvtv.a, ctx); srvtv.test = 0; env = 0; ctx = 0;
31d0247c
MW
807
808 /* Report completion. */
809 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_EGROUP | TVPF_ACK);
810 else { rc = -1; goto end; }
c91413e6
MW
811 break;
812
813 default:
31d0247c
MW
814 rc = ioerr(&srvtv, &srvrc,
815 "unexpected packet type 0x%04x at top level", pk);
c91413e6
MW
816 }
817 }
818 rc = 0;
819
820end:
31d0247c 821 /* Clean up and return. */
c91413e6 822 if (env && env->teardown) env->teardown(&srvtv, ctx);
814e42ff 823 if (vd) vd->def.ty->release(&r->v, &vd->def);
b1a20bee 824 x_free(srvtv.a, ctx); x_free(srvtv.a, r_alloc);
31d0247c 825 if (srvtv.test) tvec_releaseregs(&srvtv);
b1a20bee 826 release_comms(&srvtv, &srvrc); tvec_end(&srvtv);
c91413e6
MW
827 return (rc ? 2 : 0);
828
829bad:
31d0247c 830 /* Miscellaneous malformed packet. */
c91413e6 831 rc = malformed(&srvtv, &srvrc); goto end;
e63124bc
MW
832}
833
c91413e6
MW
834/*----- Server output driver ----------------------------------------------*/
835
c81c35df
MW
836/* --- @remote_bsession@ --- *
837 *
838 * Arguments: @struct tvec_output *o@ = output sink (ignored)
839 * @struct tvec_state *tv@ = the test state producing output
840 *
841 * Returns: ---
842 *
843 * Use: Begin a test session.
844 *
845 * The remote driver does nothing at all.
846 */
847
848static void remote_bsession(struct tvec_output *o, struct tvec_state *tv)
849 { ; }
850
851/* --- @remote_esession@ --- *
852 *
853 * Arguments: @struct tvec_output *o@ = output sink (ignored)
854 *
855 * Returns: Suggested exit code.
856 *
857 * Use: End a test session.
858 *
859 * The remote driver returns a suitable exit code without
860 * printing anything.
861 */
862
863static int remote_esession(struct tvec_output *o)
864 { return (srvtv.f&TVSF_ERROR ? 2 : 0); }
865
866/* --- @remote_bgroup@ --- *
867 *
868 * Arguments: @struct tvec_output *o@ = output sink (ignored)
869 *
870 * Returns: ---
871 *
872 * Use: Begin a test group.
873 *
874 * This is a stub which should never be called.
875 */
876
877static void remote_bgroup(struct tvec_output *o)
878 { assert(!"remote_bgroup"); }
879
880/* --- @remote_skipgroup@ --- *
881 *
882 * Arguments: @struct tvec_output *o@ = output sink (ignored)
883 * @const char *excuse@, @va_list *ap@ = reason for skipping the
884 * group, or null
885 *
886 * Returns: ---
887 *
888 * Use: Report that a test group is being skipped.
889 *
890 * The remote driver sends a @TVPK_SKIP@ packet to its client.
891 */
892
e63124bc
MW
893static void remote_skipgroup(struct tvec_output *o,
894 const char *excuse, va_list *ap)
c91413e6 895{
31d0247c
MW
896 QUEUEPK(&srvtv, &srvrc, 0, TVPK_SKIPGRP)
897 dbuf_vputstrf16l(&srvrc.bout, excuse, ap);
c91413e6 898}
e63124bc 899
c81c35df
MW
900/* --- @remote_egroup@ --- *
901 *
902 * Arguments: @struct tvec_output *o@ = output sink (ignored)
903 *
904 * Returns: ---
905 *
906 * Use: Report that a test group has finished.
907 *
908 * This is a stub which should never be called.
909 */
910
911static void remote_egroup(struct tvec_output *o)
912 { assert(!"remote_egroup"); }
913
914/* --- @remote_btest@ --- *
915 *
916 * Arguments: @struct tvec_output *o@ = output sink (ignored)
917 *
918 * Returns: ---
919 *
920 * Use: Report that a test is starting.
921 *
922 * This is a stub which should never be called.
923 */
924
925static void remote_btest(struct tvec_output *o)
926 { assert(!"remote_btest"); }
927
928/* --- @remote_skip@, @remote_fail@ --- *
929 *
930 * Arguments: @struct tvec_output *o@ = output sink (ignored)
931 * @unsigned attr@ = attribute to apply to the outcome
932 * @const char *outcome@ = outcome string to report
933 * @const char *detail@, @va_list *ap@ = a detail message
934 * @const char *excuse@, @va_list *ap@ = reason for skipping the
935 * test
936 *
937 * Returns: ---
938 *
939 * Use: Report that a test has been skipped or failed.
940 *
941 * The remote driver sends a @TVPK_SKIP@ or @TVPK_FAIL@ packet
942 * to its client as appropriate.
943 */
944
e63124bc
MW
945static void remote_skip(struct tvec_output *o,
946 const char *excuse, va_list *ap)
c91413e6 947{
31d0247c
MW
948 QUEUEPK(&srvtv, &srvrc, 0, TVPK_SKIP)
949 dbuf_vputstrf16l(&srvrc.bout, excuse, ap);
c91413e6 950}
e63124bc
MW
951
952static void remote_fail(struct tvec_output *o,
953 const char *detail, va_list *ap)
c91413e6 954{
31d0247c 955 QUEUEPK(&srvtv, &srvrc, 0, TVPK_FAIL)
c91413e6 956 if (!detail)
31d0247c 957 dbuf_putbyte(&srvrc.bout, 0);
c91413e6 958 else {
31d0247c
MW
959 dbuf_putbyte(&srvrc.bout, 1);
960 dbuf_vputstrf16l(&srvrc.bout, detail, ap);
c91413e6
MW
961 }
962}
e63124bc 963
c81c35df
MW
964/* --- @remote_dumpreg@ --- *
965 *
966 * Arguments: @struct tvec_output *o@ = output sink (ignored)
967 * @unsigned disp@ = register disposition
968 * @const union tvec_regval *rv@ = register value
969 * @const struct tvec_regdef *rd@ = register definition
970 *
971 * Returns: ---
972 *
973 * Use: Dump a register.
974 *
975 * The remote driver sends a @TVPK_DUMPREG@ packet to its
976 * client. This will only work if the register definition is
977 * one of those listed in the current test definition.
978 */
979
c91413e6
MW
980static void remote_dumpreg(struct tvec_output *o,
981 unsigned disp, const union tvec_regval *rv,
982 const struct tvec_regdef *rd)
e63124bc 983{
c91413e6
MW
984 const struct tvec_regdef *reg;
985 unsigned r;
e63124bc 986
c91413e6
MW
987 /* Find the register definition. */
988 for (reg = srvtv.test->regs, r = 0; reg->name; reg++, r++)
989 if (reg == rd) goto found;
990 assert(!"unexpected register definition");
991
992found:
31d0247c
MW
993 QUEUEPK(&srvtv, &srvrc, 0, TVPK_DUMPREG) {
994 dbuf_putu16l(&srvrc.bout, r);
995 dbuf_putu16l(&srvrc.bout, disp);
c91413e6 996 if (!rv)
31d0247c 997 dbuf_putbyte(&srvrc.bout, 0);
c91413e6 998 else {
31d0247c
MW
999 dbuf_putbyte(&srvrc.bout, 1);
1000 rd->ty->tobuf(DBUF_BUF(&srvrc.bout), rv, rd);
c91413e6 1001 }
e63124bc
MW
1002 }
1003}
1004
c81c35df
MW
1005/* --- @remote_etest@ --- *
1006 *
1007 * Arguments: @struct tvec_output *o@ = output sink (ignored)
1008 * @unsigned outcome@ = the test outcome
1009 *
1010 * Returns: ---
1011 *
1012 * Use: Report that a test has finished.
1013 *
1014 * The remote driver does nothing at all.
1015 */
1016
1017static void remote_etest(struct tvec_output *o, unsigned outcome)
1018 { ; }
1019
b1a20bee
MW
1020/* --- @remote_report@ --- *
1021 *
1022 * Arguments: @struct tvec_output *o@ = output sink (ignored)
1023 * @unsigned level@ = message level (@TVLEV_...@)
1024 * @const char *msg@, @va_list *ap@ = format string and
1025 * arguments
1026 *
1027 * Returns: ---
1028 *
1029 * Use: Report a message to the user.
1030 *
1031 * The remote driver sends a @TVPK_REPORT@ packet to its
1032 * client. If its attempt to transmit the packet fails, then
1033 * the message is written to the standard error stream instead,
1034 * in the hope that this will help it be noticed.
1035 */
1036
1037static void remote_report(struct tvec_output *o, unsigned level,
1038 const char *msg, va_list *ap)
1039{
1040 QUEUEPK(&srvtv, &srvrc, 0, TVPK_REPORT) {
1041 dbuf_putu16l(&srvrc.bout, level);
1042 dbuf_vputstrf16l(&srvrc.bout, msg, ap);
1043 } else {
1044 fprintf(stderr, "%s %s: ", QUIS, tvec_strlevel(level));
1045 vfprintf(stderr, msg, *ap);
1046 fputc('\n', stderr);
1047 }
1048}
1049
c81c35df
MW
1050/* --- @remote_bbench@ --- *
1051 *
1052 * Arguments: @struct tvec_output *o@ = output sink (ignored)
b1a20bee
MW
1053 * @const char *desc@ = adhoc test description, must be null
1054 * @unsigned unit@ = measurement unit (@BTU_...@)
c81c35df
MW
1055 *
1056 * Returns: ---
1057 *
1058 * Use: Report that a benchmark has started.
1059 *
1060 * The remote driver sends a @TVPK_BBENCH@ packet to its client.
1061 */
1062
c91413e6 1063static void remote_bbench(struct tvec_output *o,
b1a20bee 1064 const char *desc, unsigned unit)
e63124bc 1065{
b1a20bee 1066 assert(!desc);
31d0247c 1067 QUEUEPK(&srvtv, &srvrc, 0, TVPK_BBENCH) {
31d0247c 1068 dbuf_putu16l(&srvrc.bout, unit);
b1a20bee
MW
1069 tvec_serialize(srvtv.in, DBUF_BUF(&srvrc.bout), srvtv.test->regs,
1070 TVRF_ID, TVRF_ID, srvtv.cfg.nreg, srvtv.cfg.regsz);
c91413e6 1071 }
e63124bc
MW
1072}
1073
c81c35df
MW
1074/* --- @remote_ebench@ --- *
1075 *
1076 * Arguments: @struct tvec_output *o@ = output sink (ignored)
b1a20bee
MW
1077 * @const char *desc@ = adhoc test description, must be null
1078 * @unsigned unit@ = measurement unit (@BTU_...@)
1079 * @const struct bench_timing *t@ = measurement
c81c35df
MW
1080 *
1081 * Returns: ---
1082 *
b1a20bee 1083 * Use: Report a benchmark's results.
c81c35df
MW
1084 *
1085 * The remote driver sends a @TVPK_EBENCH@ packet to its client.
1086 */
1087
e63124bc 1088static void remote_ebench(struct tvec_output *o,
b1a20bee 1089 const char *desc, unsigned unit,
e63124bc
MW
1090 const struct bench_timing *t)
1091{
b1a20bee 1092 assert(!desc);
31d0247c 1093 QUEUEPK(&srvtv, &srvrc, 0, TVPK_EBENCH) {
31d0247c 1094 dbuf_putu16l(&srvrc.bout, unit);
c91413e6 1095 if (!t || !(t->f&BTF_ANY))
31d0247c 1096 dbuf_putu16l(&srvrc.bout, 0);
c91413e6 1097 else {
31d0247c
MW
1098 dbuf_putu16l(&srvrc.bout, t->f);
1099 dbuf_putf64l(&srvrc.bout, t->n);
1100 if (t->f&BTF_TIMEOK) dbuf_putf64l(&srvrc.bout, t->t);
1101 if (t->f&BTF_CYOK) dbuf_putf64l(&srvrc.bout, t->cy);
c91413e6
MW
1102 }
1103 }
1104}
1105
b1a20bee
MW
1106static const struct tvec_benchoutops remote_benchops =
1107 { remote_bbench, remote_ebench };
1108
1109/* --- @remote_extend@ --- *
c81c35df
MW
1110 *
1111 * Arguments: @struct tvec_output *o@ = output sink (ignored)
b1a20bee 1112 * @const char *name@ = extension name
c81c35df 1113 *
b1a20bee 1114 * Returns: A pointer to the extension implementation, or null.
c81c35df
MW
1115 */
1116
b1a20bee 1117static const void *remote_extend(struct tvec_output *o, const char *name)
c91413e6 1118{
b1a20bee
MW
1119 if (STRCMP(name, ==, TVEC_BENCHOUTEXT)) return (&remote_benchops);
1120 else return (0);
e63124bc
MW
1121}
1122
c81c35df
MW
1123/* --- @remote_destroy@ --- *
1124 *
1125 * Arguments: @struct tvec_output *o@ = output sink (ignored)
1126 *
1127 * Returns: ---
1128 *
1129 * Use: Release the resources held by the output driver.
1130 *
1131 * The remote driver does nothing at all.
1132 */
1133
c91413e6
MW
1134static void remote_destroy(struct tvec_output *o)
1135 { ; }
e63124bc
MW
1136
1137static const struct tvec_outops remote_ops = {
e63124bc 1138 remote_bsession, remote_esession,
c91413e6
MW
1139 remote_bgroup, remote_skipgroup, remote_egroup,
1140 remote_btest, remote_skip, remote_fail, remote_dumpreg, remote_etest,
b1a20bee 1141 remote_report, remote_extend, remote_destroy
c91413e6
MW
1142};
1143
c81c35df 1144/*----- Pseudoregister definitions ----------------------------------------*/
c91413e6 1145
814e42ff
MW
1146static tvec_setvarfn setvar_local, setvar_remote;
1147
c91413e6 1148static const struct tvec_flag exit_flags[] = {
c81c35df
MW
1149
1150 /* Cause codes. */
1151 { "running", TVXF_CAUSEMASK, TVXST_RUN },
1152 { "exited", TVXF_CAUSEMASK, TVXST_EXIT },
1153 { "killed", TVXF_CAUSEMASK, TVXST_KILL },
1154 { "stopped", TVXF_CAUSEMASK, TVXST_STOP },
1155 { "continued", TVXF_CAUSEMASK, TVXST_CONT },
1156 { "disconnected", TVXF_CAUSEMASK, TVXST_DISCONN },
1157 { "unknown", TVXF_CAUSEMASK, TVXST_UNK },
1158 { "error", TVXF_CAUSEMASK, TVXST_ERR },
1159
c91413e6
MW
1160 /*
1161 ;;; The signal name table is very boring to type. To make life less
1162 ;;; awful, put the signal names in this list and evaluate the code to
1163 ;;; get Emacs to regenerate it.
1164
1165 (let ((signals '(HUP INT QUIT ILL TRAP ABRT IOT EMT FPE KILL BUS SEGV SYS
1166 PIPE ALRM TERM URG STOP TSTP CONT CHLD CLD TTIN TTOU
1167 POLL IO TIN XCPU XFSZ VTALRM PROF WINCH USR1 USR2
1168 STKFLT INFO PWR THR LWP LIBRT LOST)))
1169 (save-excursion
1170 (goto-char (point-min))
1171 (search-forward (concat "***" "BEGIN siglist" "***"))
1172 (beginning-of-line 2)
1173 (delete-region (point)
1174 (progn
1175 (search-forward "***END***")
1176 (beginning-of-line)
1177 (point)))
1178 (dolist (sig signals)
1179 (insert (format "#ifdef SIG%s\n { \"SIG%s\", TVXF_VALMASK | TVXF_SIG, SIG%s | TVXF_SIG },\n#endif\n"
1180 sig sig sig)))))
1181 */
1182
1183 /***BEGIN siglist***/
1184#ifdef SIGHUP
1185 { "SIGHUP", TVXF_VALMASK | TVXF_SIG, SIGHUP | TVXF_SIG },
1186#endif
1187#ifdef SIGINT
1188 { "SIGINT", TVXF_VALMASK | TVXF_SIG, SIGINT | TVXF_SIG },
1189#endif
1190#ifdef SIGQUIT
1191 { "SIGQUIT", TVXF_VALMASK | TVXF_SIG, SIGQUIT | TVXF_SIG },
1192#endif
1193#ifdef SIGILL
1194 { "SIGILL", TVXF_VALMASK | TVXF_SIG, SIGILL | TVXF_SIG },
1195#endif
1196#ifdef SIGTRAP
1197 { "SIGTRAP", TVXF_VALMASK | TVXF_SIG, SIGTRAP | TVXF_SIG },
1198#endif
1199#ifdef SIGABRT
1200 { "SIGABRT", TVXF_VALMASK | TVXF_SIG, SIGABRT | TVXF_SIG },
1201#endif
1202#ifdef SIGIOT
1203 { "SIGIOT", TVXF_VALMASK | TVXF_SIG, SIGIOT | TVXF_SIG },
1204#endif
1205#ifdef SIGEMT
1206 { "SIGEMT", TVXF_VALMASK | TVXF_SIG, SIGEMT | TVXF_SIG },
1207#endif
1208#ifdef SIGFPE
1209 { "SIGFPE", TVXF_VALMASK | TVXF_SIG, SIGFPE | TVXF_SIG },
1210#endif
1211#ifdef SIGKILL
1212 { "SIGKILL", TVXF_VALMASK | TVXF_SIG, SIGKILL | TVXF_SIG },
1213#endif
1214#ifdef SIGBUS
1215 { "SIGBUS", TVXF_VALMASK | TVXF_SIG, SIGBUS | TVXF_SIG },
1216#endif
1217#ifdef SIGSEGV
1218 { "SIGSEGV", TVXF_VALMASK | TVXF_SIG, SIGSEGV | TVXF_SIG },
1219#endif
1220#ifdef SIGSYS
1221 { "SIGSYS", TVXF_VALMASK | TVXF_SIG, SIGSYS | TVXF_SIG },
1222#endif
1223#ifdef SIGPIPE
1224 { "SIGPIPE", TVXF_VALMASK | TVXF_SIG, SIGPIPE | TVXF_SIG },
1225#endif
1226#ifdef SIGALRM
1227 { "SIGALRM", TVXF_VALMASK | TVXF_SIG, SIGALRM | TVXF_SIG },
1228#endif
1229#ifdef SIGTERM
1230 { "SIGTERM", TVXF_VALMASK | TVXF_SIG, SIGTERM | TVXF_SIG },
1231#endif
1232#ifdef SIGURG
1233 { "SIGURG", TVXF_VALMASK | TVXF_SIG, SIGURG | TVXF_SIG },
1234#endif
1235#ifdef SIGSTOP
1236 { "SIGSTOP", TVXF_VALMASK | TVXF_SIG, SIGSTOP | TVXF_SIG },
1237#endif
1238#ifdef SIGTSTP
1239 { "SIGTSTP", TVXF_VALMASK | TVXF_SIG, SIGTSTP | TVXF_SIG },
1240#endif
1241#ifdef SIGCONT
1242 { "SIGCONT", TVXF_VALMASK | TVXF_SIG, SIGCONT | TVXF_SIG },
1243#endif
1244#ifdef SIGCHLD
1245 { "SIGCHLD", TVXF_VALMASK | TVXF_SIG, SIGCHLD | TVXF_SIG },
1246#endif
1247#ifdef SIGCLD
1248 { "SIGCLD", TVXF_VALMASK | TVXF_SIG, SIGCLD | TVXF_SIG },
1249#endif
1250#ifdef SIGTTIN
1251 { "SIGTTIN", TVXF_VALMASK | TVXF_SIG, SIGTTIN | TVXF_SIG },
1252#endif
1253#ifdef SIGTTOU
1254 { "SIGTTOU", TVXF_VALMASK | TVXF_SIG, SIGTTOU | TVXF_SIG },
1255#endif
1256#ifdef SIGPOLL
1257 { "SIGPOLL", TVXF_VALMASK | TVXF_SIG, SIGPOLL | TVXF_SIG },
1258#endif
1259#ifdef SIGIO
1260 { "SIGIO", TVXF_VALMASK | TVXF_SIG, SIGIO | TVXF_SIG },
1261#endif
1262#ifdef SIGTIN
1263 { "SIGTIN", TVXF_VALMASK | TVXF_SIG, SIGTIN | TVXF_SIG },
1264#endif
1265#ifdef SIGXCPU
1266 { "SIGXCPU", TVXF_VALMASK | TVXF_SIG, SIGXCPU | TVXF_SIG },
1267#endif
1268#ifdef SIGXFSZ
1269 { "SIGXFSZ", TVXF_VALMASK | TVXF_SIG, SIGXFSZ | TVXF_SIG },
1270#endif
1271#ifdef SIGVTALRM
1272 { "SIGVTALRM", TVXF_VALMASK | TVXF_SIG, SIGVTALRM | TVXF_SIG },
1273#endif
1274#ifdef SIGPROF
1275 { "SIGPROF", TVXF_VALMASK | TVXF_SIG, SIGPROF | TVXF_SIG },
1276#endif
1277#ifdef SIGWINCH
1278 { "SIGWINCH", TVXF_VALMASK | TVXF_SIG, SIGWINCH | TVXF_SIG },
1279#endif
1280#ifdef SIGUSR1
1281 { "SIGUSR1", TVXF_VALMASK | TVXF_SIG, SIGUSR1 | TVXF_SIG },
1282#endif
1283#ifdef SIGUSR2
1284 { "SIGUSR2", TVXF_VALMASK | TVXF_SIG, SIGUSR2 | TVXF_SIG },
1285#endif
1286#ifdef SIGSTKFLT
1287 { "SIGSTKFLT", TVXF_VALMASK | TVXF_SIG, SIGSTKFLT | TVXF_SIG },
1288#endif
1289#ifdef SIGINFO
1290 { "SIGINFO", TVXF_VALMASK | TVXF_SIG, SIGINFO | TVXF_SIG },
1291#endif
1292#ifdef SIGPWR
1293 { "SIGPWR", TVXF_VALMASK | TVXF_SIG, SIGPWR | TVXF_SIG },
1294#endif
1295#ifdef SIGTHR
1296 { "SIGTHR", TVXF_VALMASK | TVXF_SIG, SIGTHR | TVXF_SIG },
1297#endif
1298#ifdef SIGLWP
1299 { "SIGLWP", TVXF_VALMASK | TVXF_SIG, SIGLWP | TVXF_SIG },
1300#endif
1301#ifdef SIGLIBRT
1302 { "SIGLIBRT", TVXF_VALMASK | TVXF_SIG, SIGLIBRT | TVXF_SIG },
1303#endif
1304#ifdef SIGLOST
1305 { "SIGLOST", TVXF_VALMASK | TVXF_SIG, SIGLOST | TVXF_SIG },
1306#endif
1307 /***END***/
1308
c81c35df 1309 /* This should be folded into the signal entries above. */
c91413e6
MW
1310 { "signal", TVXF_SIG, TVXF_SIG },
1311
c91413e6
MW
1312 TVEC_ENDFLAGS
1313};
c91413e6
MW
1314static const struct tvec_flaginfo exit_flaginfo =
1315 { "exit-status", exit_flags, &tvrange_uint };
814e42ff
MW
1316static const struct tvec_vardef exit_var =
1317 { sizeof(struct tvec_reg), setvar_local,
d056fbdf 1318 { "@exit", &tvty_flags, -1, 0, { &exit_flaginfo } } };
c91413e6 1319
c81c35df
MW
1320/* Progress. */
1321
814e42ff
MW
1322static const struct tvec_vardef progress_var =
1323 { sizeof(struct tvec_reg), setvar_local,
d056fbdf 1324 { "@progress", &tvty_text, -1, 0 } };
c81c35df
MW
1325
1326/* Reconnection. */
c91413e6
MW
1327
1328static const struct tvec_uassoc reconn_assocs[] = {
1329 { "on-demand", TVRCN_DEMAND },
1330 { "force", TVRCN_FORCE },
1331 { "skip", TVRCN_SKIP },
1332 TVEC_ENDENUM
1333};
c81c35df
MW
1334static const struct tvec_uenuminfo reconn_enuminfo =
1335 { "remote-reconnection", reconn_assocs, &tvrange_uint };
814e42ff
MW
1336static const struct tvec_vardef reconn_var =
1337 { sizeof(struct tvec_reg), setvar_local,
d056fbdf 1338 { "@reconnect", &tvty_uenum, -1, 0, { &reconn_enuminfo } } };
c81c35df
MW
1339
1340/*----- Client ------------------------------------------------------------*/
1341
1342/* Connection state. */
c91413e6
MW
1343enum {
1344 CONN_BROKEN = -2, /* previously broken */
1345 CONN_FAILED = -1, /* attempt freshly failed */
1346 CONN_ESTABLISHED = 0, /* previously established */
1347 CONN_FRESH = 1 /* freshly connected */
1348};
1349
c81c35df
MW
1350/* --- @handle_packets@ --- *
1351 *
1352 * Arguments: @struct tvec_state *tv@ = test-vector state
1353 * @struct tvec_remotectx *r@ = remote client context
1354 * @unsigned f@ = receive flags (@RCVF_...@)
1355 * @uint16 end@ = expected end packet type
1356 * @buf *b_out@ = buffer in which to return end packet payload
1357 *
1358 * Returns: A @RECV_...@ code.
1359 *
1360 * Use: Handles notification packets from the server until a final
1361 * termination packet is received.
1362 *
1363 * The client/server protocol consists of a number of flows,
1364 * beginning with a request from the client, followed by a
1365 * number of notifications from the server, and terminated by an
1366 * acknowledgement to the original request indicating that the
1367 * server has completed acting on the original request.
1368 *
1369 * This function handles the notifications issued by the server,
1370 * returning when one of the following occurs: (a) a packet of
1371 * type @end@ is received, in which case the function returns
1372 * @RECV_OK@ and the remainder of the packet payload is left in
1373 * @b_out@; (b) the flag @RCVF_ALLOWEOF@ was set in @f@ on entry
1374 * and end-of-file is received at a packet boundary, in which
1375 * case the function returns @RECV_EOF@; or (c) an I/O error
1376 * occurs, in which case @ioerr@ is called and the function
1377 * returns @RECV_FAIL@.
1378 */
c91413e6
MW
1379
1380static int handle_packets(struct tvec_state *tv, struct tvec_remotectx *r,
1381 unsigned f, uint16 end, buf *b_out)
1382{
b1a20bee 1383 struct tvec_output *o;
c91413e6
MW
1384 uint16 pk, u, v;
1385 const char *p; size_t n;
1386 dstr d = DSTR_INIT;
1387 buf *b = b_out;
1388 const struct tvec_regdef *rd;
1389 struct bench_timing bt;
b1a20bee
MW
1390 const struct tvec_benchoutops *bo;
1391 struct tvec_fallbackoutput fo;
d056fbdf 1392 struct tvec_reg *reg = 0; size_t rsz = 0;
c91413e6
MW
1393 unsigned i;
1394 int rc;
1395
1396 for (;;) {
c81c35df
MW
1397
1398 /* Read the next packet. If we didn't receive one then end the loop.
1399 * Otherwise, retrieve the packet type and check it against @end@: quit
1400 * the loop if we get a match.
1401 */
1402 rc = remote_recv(tv, &r->rc, f, b); if (rc) break;
c91413e6 1403 if (buf_getu16l(b, &pk)) goto bad;
c81c35df 1404 if (pk == end) { rc = 0; break; }
c91413e6 1405
c81c35df 1406 /* Dispatch based on the packet type. */
c91413e6
MW
1407 switch (pk) {
1408
1409 case TVPK_PROGRESS:
c81c35df
MW
1410 /* A progress report. Update the saved progress. */
1411
c91413e6
MW
1412 p = buf_getmem16l(b, &n); if (!p) goto bad;
1413 if (BLEFT(b)) goto bad;
1414
1415 DRESET(&r->progress); DPUTM(&r->progress, p, n); DPUTZ(&r->progress);
1416 break;
1417
1418 case TVPK_REPORT:
c81c35df
MW
1419 /* A report. Recover the message and pass it along. */
1420
c91413e6
MW
1421 if (buf_getu16l(b, &u)) goto bad;
1422 p = buf_getmem16l(b, &n); if (!p) goto bad;
1423 if (BLEFT(b)) goto bad;
1424
1425 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1426 tvec_report(tv, u, "%s", d.buf);
1427 break;
1428
1429 case TVPK_SKIPGRP:
c81c35df
MW
1430 /* A request to skip the group. Recover the excuse message and pass
1431 * it along.
1432 */
1433
c91413e6 1434 p = buf_getmem16l(b, &n); if (!p) goto bad;
c91413e6
MW
1435 if (BLEFT(b)) goto bad;
1436
c81c35df 1437 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
c91413e6
MW
1438 tvec_skipgroup(tv, "%s", d.buf);
1439 break;
1440
1441 case TVPK_SKIP:
c81c35df
MW
1442 /* A request to skip the test. Recover the excuse message and pass
1443 * it along, if it's not unreasonable.
1444 */
1445
c91413e6
MW
1446 if (!(tv->f&TVSF_ACTIVE)) {
1447 rc = ioerr(tv, &r->rc, "test `%s' not active", tv->test->name);
1448 goto end;
1449 }
1450
1451 p = buf_getmem16l(b, &n); if (!p) goto bad;
1452 if (BLEFT(b)) goto bad;
1453
1454 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1455 tvec_skip(tv, "%s", d.buf);
1456 break;
1457
1458 case TVPK_FAIL:
c81c35df
MW
1459 /* A report that the test failed. Recover the detail message, if
1460 * any, and pass it along, if it's not unreasonable.
1461 */
1462
c91413e6
MW
1463 if (!(tv->f&TVSF_ACTIVE) &&
1464 ((tv->f&TVSF_OUTMASK) != (TVOUT_LOSE << TVSF_OUTSHIFT))) {
1465 rc = ioerr(tv, &r->rc, "test `%s' not active or failing",
1466 tv->test->name);
1467 goto end;
1468 }
1469
1470 rc = buf_getbyte(b); if (rc < 0) goto bad;
1471 if (rc) { p = buf_getmem16l(b, &n); if (!p) goto bad; }
1472 else p = 0;
1473 if (BLEFT(b)) goto bad;
e63124bc 1474
c91413e6
MW
1475 if (!p)
1476 tvec_fail(tv, 0);
1477 else {
1478 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1479 tvec_fail(tv, "%s", d.buf);
1480 }
1481 break;
e63124bc 1482
c91413e6 1483 case TVPK_DUMPREG:
c81c35df
MW
1484 /* A request to dump a register. */
1485
1486 /* Find the register definition. */
c91413e6
MW
1487 if (buf_getu16l(b, &u) || buf_getu16l(b, &v)) goto bad;
1488 for (rd = tv->test->regs, i = 0; rd->name; rd++, i++)
1489 if (i == u) goto found_reg;
1490 rc = ioerr(tv, &r->rc,
1491 "register definition %u out of range for test `%s'",
1492 u, tv->test->name);
1493 goto end;
1494 found_reg:
1495 if (v >= TVRD_LIMIT) {
1496 rc = ioerr(tv, &r->rc, "register disposition %u out of range", v);
1497 goto end;
1498 }
e63124bc 1499
c81c35df
MW
1500 /* Read the flag. If there's no register value, then `dump' its
1501 * absence. Otherwise retrieve the register value and dump it.
1502 */
c91413e6
MW
1503 rc = buf_getbyte(b); if (rc < 0) goto bad;
1504 if (!rc)
1505 tvec_dumpreg(tv, v, 0, rd);
1506 else {
b1a20bee 1507 GROWBUF_REPLACE(size_t, tv->a, reg, rsz, tv->cfg.regsz,
d056fbdf 1508 8*sizeof(void *), 1);
c91413e6
MW
1509 rd->ty->init(&reg->v, rd);
1510 rc = rd->ty->frombuf(b, &reg->v, rd);
1511 if (!rc) tvec_dumpreg(tv, v, &reg->v, rd);
1512 rd->ty->release(&reg->v, rd);
1513 if (rc) goto bad;
1514 }
1515 if (BLEFT(b)) goto bad;
1516 break;
1517
1518 case TVPK_BBENCH:
c81c35df
MW
1519 /* A report that we're starting a benchmark. Pass this along. */
1520
c91413e6 1521 if (buf_getu16l(b, &u)) goto bad;
b1a20bee
MW
1522 if (tvec_deserialize(tv->in, b, tv->test->regs,
1523 TVRF_ID, TVRF_ID, tv->cfg.nreg, tv->cfg.regsz))
1524 goto bad;
c91413e6 1525 if (BLEFT(b)) goto bad;
b1a20bee 1526 if (u >= BTU_LIMIT) {
c91413e6
MW
1527 rc = ioerr(tv, &r->rc, "unit code %u out of range", u);
1528 goto end;
1529 }
1530
b1a20bee
MW
1531 bo = tvec_outputext(tv, &o, &fo,
1532 TVEC_BENCHOUTEXT, &tvec_benchoutputfallback);
1533 bo->bbench(o, 0, u);
c91413e6
MW
1534 break;
1535
1536 case TVPK_EBENCH:
c81c35df
MW
1537 /* A report that a benchmark completed. Pass this along. */
1538
c91413e6 1539 if (buf_getu16l(b, &u) || buf_getu16l(b, &v)) goto bad;
b1a20bee 1540 if (u >= BTU_LIMIT) {
31d0247c
MW
1541 rc = ioerr(tv, &r->rc, "unit code %u out of range", u);
1542 goto end;
1543 }
c91413e6
MW
1544 if ((v&BTF_ANY) && buf_getf64l(b, &bt.n)) goto bad;
1545 if ((v&BTF_TIMEOK) && buf_getf64l(b, &bt.t)) goto bad;
1546 if ((v&BTF_CYOK) && buf_getf64l(b, &bt.cy)) goto bad;
1547 if (BLEFT(b)) goto bad;
b1a20bee 1548 bt.f = v;
c91413e6 1549
b1a20bee
MW
1550 bo = tvec_outputext(tv, &o, &fo,
1551 TVEC_BENCHOUTEXT, &tvec_benchoutputfallback);
1552 bo->ebench(o, 0, u, v&BTF_ANY ? &bt : 0);
c91413e6
MW
1553 break;
1554
1555 default:
c81c35df
MW
1556 /* Something else. This is unexpected. */
1557
c91413e6
MW
1558 rc = ioerr(tv, &r->rc, "unexpected packet type 0x%04x", pk);
1559 goto end;
1560 }
1561 }
1562
1563end:
1564 DDESTROY(&d);
b1a20bee 1565 x_free(tv->a, reg);
c91413e6
MW
1566 return (rc);
1567bad:
1568 rc = malformed(tv, &r->rc); goto end;
1569}
1570
c81c35df
MW
1571/* --- @reap_kid@ --- *
1572 *
1573 * Arguments: @struct tvec_state *tv@ = test-vector state
1574 * @struct tvec_remotectx *r@ = remote client context
1575 *
1576 * Returns: ---
1577 *
1578 * Use: Determine the exit status of a broken connection, setting
1579 * @r->exit@ appropriately.
1580 *
1581 * If @r->kid@ is negative, the exit status has already been
1582 * set, and nothing further happens; this is not an error.
1583 *
1584 * If @r->kid@ is zero, then there is no real child process
1585 * (e.g., because the remote connection is a network connection
1586 * or similar), so @r->exit@ is set equal to @RVXST_DISCONN@.
1587 *
1588 * If @r->kid@ is positive, then it holds a child process id;
1589 * the function waits for it to end and collects its exit status
1590 *
1591 * It is an error to call this function if the connection is not
1592 * broken.
1593 */
1594
c91413e6
MW
1595static void reap_kid(struct tvec_state *tv, struct tvec_remotectx *r)
1596{
1597 pid_t kid;
1598 int st;
1599
c81c35df 1600 assert(r->rc.f&TVRF_BROKEN);
c91413e6
MW
1601 if (!r->kid)
1602 { r->exit = TVXST_DISCONN; r->kid = -1; }
1603 else if (r->kid > 0) {
1604 kid = waitpid(r->kid, &st, 0);
1605 if (kid < 0) {
1606 tvec_notice(tv, "failed to wait for remote child: %s",
1607 strerror(errno));
1608 r->exit = TVXST_ERR;
1609 } else if (!kid) {
1610 tvec_notice(tv, "remote child vanished without a trace");
1611 r->exit = TVXST_ERR;
1612 } else if (WIFCONTINUED(st))
1613 r->exit = TVXST_CONT;
1614 else if (WIFSIGNALED(st))
1615 r->exit = TVXST_KILL | TVXF_SIG | WTERMSIG(st);
1616 else if (WIFSTOPPED(st))
1617 r->exit = TVXST_STOP | TVXF_SIG | WSTOPSIG(st);
1618 else if (WIFEXITED(st))
1619 r->exit = TVXST_EXIT | WEXITSTATUS(st);
1620 else {
1621 tvec_notice(tv, "remote child died with unknown status 0x%04x",
1622 (unsigned)st);
1623 r->exit = TVXST_UNK;
1624 }
1625 r->kid = -1;
1626 }
1627}
1628
c81c35df
MW
1629/* --- @report_errline@ --- *
1630 *
1631 * Arguments: @char *p@ = pointer to the line
1632 * @size_t n@ = length in characters
1633 * @void *ctx@ = context, secretly a @struct tvec_remotectx@
1634 *
1635 * Returns: ---
1636 *
1637 * Use: Print a line of stderr output from the child. If
1638 * @TVRF_MUFFLE@ is set, then discard the line silently.
1639 *
1640 * This is an @lbuf_func@, invoked via @drain_errfd@.
1641 */
1642
c91413e6
MW
1643static void report_errline(char *p, size_t n, void *ctx)
1644{
1645 struct tvec_remotectx *r = ctx;
1646 struct tvec_state *tv = r->tv;
1647
1648 if (p && !(r->rc.f&TVRF_MUFFLE))
1649 tvec_notice(tv, "child process stderr: %s", p);
1650}
1651
c81c35df
MW
1652/* --- @drain_errfd@ --- *
1653 *
1654 * Arguments: @struct tvec_state *tv@ = test-vector state
1655 * @struct tvec_remotectx *r@ = remote client context
1656 * @unsigned f@ = receive flags (@ERF_...@)
1657 *
1658 * Returns: Zero on success, %$-1$% on error.
1659 *
1660 * Use: Collect material written by the child to its stderr stream
1661 * and report it.
1662 *
1663 * If @f@ has @ERF_SILENT@ set, then discard the stderr material
1664 * without reporting it. Otherwise it is reported as
1665 * @TVLEV_NOTE@.
1666 *
1667 * if @f@ has @ERF_CLOSE@ set, then continue reading until
1668 * end-of-file is received; also, report any final partial line,
1669 * and close @r->errfd@.
1670 *
1671 * If @r->errfd@ is already closed, or never established, then
1672 * do nothing and return successfully.
1673 */
1674
c91413e6
MW
1675#define ERF_SILENT 0x0001u
1676#define ERF_CLOSE 0x0002u
1677static int drain_errfd(struct tvec_state *tv, struct tvec_remotectx *r,
1678 unsigned f)
1679{
1680 char *p; size_t sz;
1681 ssize_t n;
1682 int rc;
1683
c81c35df
MW
1684 /* Preliminaries. Bail if there is no error stream to fetch. Arrange
1685 * (rather clumsily) to muffle the output if we're supposed to be client.
1686 * And set the nonblocking state on @errfd@ appropriately.
1687 */
31d0247c 1688 if (r->errfd < 0) { rc = 0; goto end; }
c91413e6
MW
1689 if (f&ERF_SILENT) r->rc.f |= TVRF_MUFFLE;
1690 else r->rc.f &= ~TVRF_MUFFLE;
1691 if (fdflags(r->errfd, O_NONBLOCK, f&ERF_CLOSE ? 0 : O_NONBLOCK, 0, 0)) {
1692 rc = ioerr(tv, &r->rc, "failed to %s error non-blocking flag",
1693 f&ERF_CLOSE ? "clear" : "set");
1694 goto end;
1695 }
1696
c81c35df 1697 /* Read pieces of error output and feed them into the line buffer. */
c91413e6
MW
1698 for (;;) {
1699 sz = lbuf_free(&r->errbuf, &p);
1700 n = read(r->errfd, p, sz);
1701 if (!n) break;
1702 if (n < 0) {
1703 if (errno == EINTR) continue;
1704 if (!(f&ERF_CLOSE) && (errno == EWOULDBLOCK || errno == EAGAIN))
1705 break;
1706 rc = ioerr(tv, &r->rc, "failed to read child stderr: %s",
1707 strerror(errno));
1708 goto end;
1709 }
1710 lbuf_flush(&r->errbuf, p, n);
1711 }
c81c35df
MW
1712
1713 /* Done. */
c91413e6
MW
1714 rc = 0;
1715end:
1716 if (f&ERF_CLOSE) {
1717 lbuf_close(&r->errbuf);
31d0247c 1718 close(r->errfd); r->errfd = -1;
c91413e6
MW
1719 }
1720 return (rc);
1721}
1722
c81c35df
MW
1723/* --- @disconnect_remote@ --- *
1724 *
1725 * Arguments: @struct tvec_state *tv@ = test-vector state
1726 * @struct tvec_remotectx *r@ = remote client context
1727 * @unsigned f@ = receive flags (@DCF_...@)
1728 *
1729 * Returns: ---
1730 *
1731 * Use: Disconnect and shut down all of the remote client state.
1732 *
1733 * If @f@ has @DCF_KILL@ set then send the child process (if
1734 * any) @SIGTERM@ to make sure it shuts down in a timely manner.
1735 *
1736 * In detail: this function closes the @infd@ and @outfd@
1737 * descriptors, drains and closes @errfd@, and collects the exit
1738 * status (if any).
1739 */
1740
c91413e6 1741#define DCF_KILL 0x0100u
b1a20bee 1742#define DCF_QUITOK 0200u
c91413e6
MW
1743static void disconnect_remote(struct tvec_state *tv,
1744 struct tvec_remotectx *r, unsigned f)
1745{
b1a20bee
MW
1746 union tvec_regval rv;
1747 dstr d = DSTR_INIT;
1748
c91413e6
MW
1749 if (r->kid > 0 && (f&DCF_KILL)) kill(r->kid, SIGTERM);
1750 close_comms(&r->rc);
b1a20bee
MW
1751 drain_errfd(tv, r, f | ERF_CLOSE);
1752 if (r->kid >= 0) {
1753 reap_kid(tv, r);
1754 if (!(f&ERF_SILENT) &&
1755 (!(f&DCF_QUITOK) || (r->exit != TVXST_EXIT &&
1756 r->exit != TVXST_DISCONN))) {
1757 rv.u = r->exit;
1758 tvty_flags.dump(&rv, &exit_var.def, TVSF_COMPACT, &dstr_printops, &d);
1759 tvec_error(tv, "remote connection closed with status %s", d.buf);
1760 }
1761 }
1762 dstr_destroy(&d);
c91413e6
MW
1763}
1764
c81c35df
MW
1765/* --- @connect_remote@ --- *
1766 *
1767 * Arguments: @struct tvec_state *tv@ = test-vector state
1768 * @struct tvec_remotectx *r@ = remote client context
1769 *
1770 * Returns: Zero on success, %$-1$% on error.
1771 *
1772 * Use: Connect to the test server.
1773 */
1774
c91413e6
MW
1775static int connect_remote(struct tvec_state *tv, struct tvec_remotectx *r)
1776{
1777 const struct tvec_remoteenv *re = r->re;
1778 pid_t kid = 0;
1779 buf b;
1780 uint16 v;
1781 int infd = -1, outfd = -1, errfd = -1, rc;
1782
c81c35df 1783 /* If we're already connected, then there's nothing to do. */
c91413e6 1784 if (r->kid >= 0) { rc = 0; goto end; }
c81c35df
MW
1785
1786 /* Set the preliminary progress indication. */
1787 DRESET(&r->progress); DPUTS(&r->progress, "%INIT");
1788
1789 /* Call the connection function to establish descriptors. */
c91413e6
MW
1790 if (re->r.connect(&kid, &infd, &outfd, &errfd, tv, re))
1791 { rc = -1; goto end; }
c81c35df
MW
1792
1793 /* Establish communications state. */
c91413e6
MW
1794 setup_comms(&r->rc, infd, outfd); r->kid = kid; r->errfd = errfd;
1795 lbuf_init(&r->errbuf, report_errline, r);
1796 r->exit = TVXST_RUN; r->rc.f &= ~TVRF_BROKEN;
1797
c81c35df 1798 /* Do version negotiation. */
31d0247c
MW
1799 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_VER) {
1800 dbuf_putu16l(&r->rc.bout, 0);
1801 dbuf_putu16l(&r->rc.bout, 0);
c91413e6 1802 } else { rc = -1; goto end; }
c91413e6
MW
1803 if (handle_packets(tv, r, 0, TVPK_VER | TVPF_ACK, &b))
1804 { rc = -1; goto end; }
1805 if (buf_getu16l(&b, &v)) goto bad;
1806 if (BLEFT(&b)) { rc = malformed(tv, &r->rc); goto end; }
1807 if (v) {
1808 rc = ioerr(tv, &r->rc, "protocol version %u not supported", v);
1809 goto end;
1810 }
c81c35df 1811 r->ver = v;
c91413e6 1812
c81c35df 1813 /* Begin the test group at the server. */
31d0247c
MW
1814 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_BGROUP)
1815 dbuf_putstr16l(&r->rc.bout, tv->test->name);
c91413e6
MW
1816 else { rc = -1; goto end; }
1817 if (handle_packets(tv, r, 0, TVPK_BGROUP | TVPF_ACK, &b))
1818 { rc = -1; goto end; }
1819 if (BLEFT(&b)) { rc = malformed(tv, &r->rc); goto end; }
c81c35df
MW
1820
1821 /* Done. */
1822 rc = 0;
c91413e6
MW
1823end:
1824 if (rc) disconnect_remote(tv, r, DCF_KILL);
1825 return (rc);
1826bad:
1827 rc = malformed(tv, &r->rc); goto end;
1828}
1829
c81c35df
MW
1830/* --- @check_comms@ --- *
1831 *
1832 * Arguments: @struct tvec_state *tv@ = test-vector state
1833 * @struct tvec_remotectx *r@ = remote client context
1834 *
1835 * Returns: A @CONN_...@ code reflecting the current communication
1836 * state.
1837 *
1838 * Use: Determine the current connection state. If the connection
1839 * has recently broken (i.e., @TVRF_BROKEN@ is set in @r->rc.f@)
1840 * since the last time we checked then disconnect.
1841 */
1842
c91413e6
MW
1843static int check_comms(struct tvec_state *tv, struct tvec_remotectx *r)
1844{
1845 if (r->kid < 0)
1846 return (CONN_BROKEN);
1847 else if (r->rc.f&TVRF_BROKEN)
1848 { disconnect_remote(tv, r, DCF_KILL); return (CONN_FAILED); }
1849 else
1850 return (CONN_ESTABLISHED);
1851}
1852
c81c35df
MW
1853/* --- @try_reconnect@ --- *
1854 *
1855 * Arguments: @struct tvec_state *tv@ = test-vector state
1856 * @struct tvec_remotectx *r@ = remote client context
1857 *
1858 * Returns: A @CONN_...@ code reflecting the new communication state.
1859 *
1860 * Use: Reconnects to the server according to the configured
1861 * @TVRCN_...@ policy.
1862 */
1863
c91413e6
MW
1864static int try_reconnect(struct tvec_state *tv, struct tvec_remotectx *r)
1865{
1866 int rc;
1867
1868 switch (r->rc.f&TVRF_RCNMASK) {
1869 case TVRCN_DEMAND:
1870 rc = check_comms(tv, r);
1871 if (rc < CONN_ESTABLISHED) {
1872 close_comms(&r->rc);
1873 if (connect_remote(tv, r)) rc = CONN_FAILED;
1874 else rc = CONN_FRESH;
1875 }
1876 break;
1877 case TVRCN_FORCE:
1878 disconnect_remote(tv, r, DCF_KILL);
1879 if (connect_remote(tv, r)) rc = CONN_FAILED;
1880 else rc = CONN_FRESH;
1881 break;
1882 case TVRCN_SKIP:
1883 rc = check_comms(tv, r);
1884 break;
1885 default:
1886 abort();
1887 }
1888 return (rc);
1889}
1890
c81c35df
MW
1891/*----- Remote environment ------------------------------------------------*/
1892
1893/* --- @reset_vars@ --- *
1894 *
1895 * Arguments: @struct tvec_remotectx *r@ = remote client context
1896 *
1897 * Returns: ---
1898 *
1899 * Use: Reset the pseudoregisters set through @tvec_remoteset@.
1900 */
1901
c91413e6
MW
1902static void reset_vars(struct tvec_remotectx *r)
1903{
814e42ff
MW
1904 const struct tvec_remoteenv *re = r->re;
1905
31d0247c 1906 r->exwant = TVXST_RUN;
814e42ff
MW
1907 r->rc.f = (r->rc.f&~(TVRF_RCNMASK | TVRF_SETMASK)) |
1908 (re->r.dflt_reconn&TVRF_RCNMASK);
c91413e6
MW
1909 DRESET(&r->prgwant); DPUTS(&r->prgwant, "%DONE");
1910}
1911
c81c35df
MW
1912/* --- @tvec_remotesetup@ --- *
1913 *
1914 * Arguments: @struct tvec_state *tv@ = test vector state
1915 * @const struct tvec_env *env@ = environment description
1916 * @void *pctx@ = parent context (ignored)
1917 * @void *ctx@ = context pointer to initialize
1918 *
1919 * Returns: ---
1920 *
1921 * Use: Initialize a timeout environment context.
1922 *
1923 * The environment description should be a @struct
1924 * tvec_remoteenv@ subclass suitable for use by the @connect@
1925 * function.
1926 */
1927
c91413e6
MW
1928void tvec_remotesetup(struct tvec_state *tv, const struct tvec_env *env,
1929 void *pctx, void *ctx)
1930{
1931 struct tvec_remotectx *r = ctx;
1932 const struct tvec_remoteenv *re = (const struct tvec_remoteenv *)env;
814e42ff 1933 const struct tvec_env *subenv = re->r.env;
c91413e6
MW
1934
1935 r->tv = tv;
1936 init_comms(&r->rc);
1937 r->re = re; r->kid = -1;
1938 DCREATE(&r->prgwant); DCREATE(&r->progress);
1939 if (connect_remote(tv, r))
1940 tvec_skipgroup(tv, "failed to connect to test backend");
1941 reset_vars(r);
b1a20bee 1942 if (subenv && subenv->ctxsz) r->subctx = x_alloc(tv->a, subenv->ctxsz);
814e42ff
MW
1943 else r->subctx = 0;
1944 if (subenv && subenv->setup) subenv->setup(tv, subenv, r, r->subctx);
c91413e6
MW
1945}
1946
814e42ff 1947/* --- @tvec_remotefindvar@, @setvar_local@, @setvar_remote@ --- *
c81c35df
MW
1948 *
1949 * Arguments: @struct tvec_state *tv@ = test vector state
1950 * @const char *var@ = variable name to set
814e42ff
MW
1951 * @const union tvec_regval *rv@ = register value
1952 * @void **ctx_out@ = where to put the @setvar@ context
c81c35df
MW
1953 * @void *ctx@ = context pointer
1954 *
814e42ff
MW
1955 * Returns: @tvec_remotefindvar@ returns a pointer to the variable
1956 * definition, or null; @remote_setvar@ returns zero on success
1957 * or %$-1$% on error.
c81c35df
MW
1958 *
1959 * Use: Set a special variable. The following special variables are
1960 * supported.
1961 *
1962 * * %|@exit|% is the expected exit status; see @TVXF_...@ and
1963 * @TVXST_...@.
1964 *
1965 * * %|progress|% is the expected progress token when the test
1966 * completes. On successful completion, this will be
1967 * %|%DONE|%; it's %|%RUN|% on entry to the test function,
1968 * but that can call @tvec_setprogress@ to change it.
1969 *
1970 * * %|reconnect|% is a reconnection policy; see @TVRCN_...@.
1971 */
1972
814e42ff
MW
1973static int setvar_local(struct tvec_state *tv, const char *var,
1974 const union tvec_regval *rv, void *ctx)
c91413e6
MW
1975{
1976 struct tvec_remotectx *r = ctx;
c91413e6
MW
1977
1978 if (STRCMP(var, ==, "@exit")) {
6e683a79 1979 if (r->rc.f&TVRF_SETEXIT) return (tvec_dupregerr(tv, var));
814e42ff 1980 r->exwant = rv->u; r->rc.f |= TVRF_SETEXIT; return (0);
c91413e6 1981 } else if (STRCMP(var, ==, "@progress")) {
6e683a79 1982 if (r->rc.f&TVRF_SETPRG) return (tvec_dupregerr(tv, var));
814e42ff
MW
1983 DRESET(&r->prgwant); DPUTM(&r->prgwant, rv->text.p, rv->text.sz);
1984 DPUTZ(&r->prgwant);
1985 r->rc.f |= TVRF_SETPRG; return (0);
c91413e6 1986 } else if (STRCMP(var, ==, "@reconnect")) {
6e683a79 1987 if (r->rc.f&TVRF_SETRCN) return (tvec_dupregerr(tv, var));
814e42ff
MW
1988 r->rc.f = (r->rc.f&~TVRF_RCNMASK) | (rv->u&TVRF_RCNMASK) | TVRF_SETRCN;
1989 return (0);
1990 } else assert(!"unknown var");
1991}
1992
1993static int setvar_remote(struct tvec_state *tv, const char *var,
1994 const union tvec_regval *rv, void *ctx)
1995{
1996 struct tvec_remotectx *r = ctx;
1997 buf b;
1998 int ch, rc;
c91413e6 1999
814e42ff
MW
2000 if (try_reconnect(tv, r) < 0) { rc = 0; goto end; }
2001
2002 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_SETVAR) {
2003 dbuf_putstr16l(&r->rc.bout, var);
2004 r->vd.def.ty->tobuf(DBUF_BUF(&r->rc.bout), rv, &r->vd.def);
2005 } else { rc = -1; goto end; }
2006
2007 rc = handle_packets(tv, r, 0, TVPK_SETVAR | TVPF_ACK, &b);
2008 if (rc) goto end;
2009 ch = buf_getbyte(&b);
2010 if (ch < 0) { rc = malformed(tv, &r->rc); goto end; }
2011 if (BLEFT(&b)) { rc = malformed(tv, &r->rc); goto end; }
2012
2013 rc = ch ? -1 : 0;
c91413e6
MW
2014end:
2015 return (rc);
2016}
2017
814e42ff
MW
2018const struct tvec_vardef *tvec_remotefindvar
2019 (struct tvec_state *tv, const char *var, void **ctx_out, void *ctx)
2020{
2021 struct tvec_remotectx *r = ctx;
2022 const struct tvec_remoteenv *re = r->re;
2023 const struct tvec_env *subenv = re->r.env;
2024 const struct tvec_vardef *vd; void *varctx;
2025
2026 if (STRCMP(var, ==, "@exit"))
2027 { *ctx_out = r; return (&exit_var); }
2028 else if (STRCMP(var, ==, "@progress"))
2029 { *ctx_out = r; return (&progress_var); }
2030 else if (STRCMP(var, ==, "@reconnect"))
2031 { *ctx_out = r; return (&reconn_var); }
2032 else if (subenv && subenv->findvar) {
2033 vd = subenv->findvar(tv, var, &varctx, r->subctx);
2034 if (!vd) return (0);
2035 r->vd.regsz = vd->regsz; r->vd.setvar = setvar_remote;
2036 r->vd.def = vd->def;
2037 *ctx_out = r; return (&r->vd);
2038 } else
2039 return (0);
2040}
2041
2042/* --- @tvec_remotebefore@ --- *
2043 *
2044 * Arguments: @struct tvec_state *tv@ = test vector state
2045 * @void *ctx@ = context pointer
2046 *
2047 * Returns: ---
2048 *
2049 * Use: Invoke the subordinate environment's @before@ function.
2050 */
2051
2052void tvec_remotebefore(struct tvec_state *tv, void *ctx)
2053{
2054 struct tvec_remotectx *r = ctx;
2055 const struct tvec_remoteenv *re = r->re;
2056 const struct tvec_env *subenv = re->r.env;
2057
2058 if (subenv && subenv->before) subenv->before(tv, r->subctx);
2059}
2060
c81c35df
MW
2061/* --- @tvec_remoterun@ --- *
2062 *
2063 * Arguments: @struct tvec_state *tv@ = test vector state
2064 * @tvec_testfn *fn@ = test function to run
2065 * @void *ctx@ = context pointer for the test function
2066 *
2067 * Returns: ---
2068 *
2069 * Use: Run a test on a remote server.
2070 */
c91413e6
MW
2071
2072void tvec_remoterun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
2073{
2074 struct tvec_remotectx *r = ctx;
2075 union tvec_regval rv;
2076 unsigned f = 0;
2077#define f_exit 1u
2078#define f_progress 2u
2079#define f_fail 4u
2080 buf b;
2081 int rc;
2082
c81c35df 2083 /* Reconnect to the server according to policy. */
c91413e6
MW
2084 switch (try_reconnect(tv, r)) {
2085 case CONN_FAILED:
2086 tvec_skip(tv, "failed to connect to test backend"); return;
2087 case CONN_BROKEN:
2088 tvec_skip(tv, "no connection"); return;
2089 }
2090
c81c35df 2091 /* Set initial progress state. */
31d0247c 2092 DRESET(&r->progress); DPUTS(&r->progress, "%IDLE");
c81c35df
MW
2093
2094 /* Send the command to the server and handle output. */
31d0247c 2095 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_TEST)
b1a20bee
MW
2096 tvec_serialize(tv->in, DBUF_BUF(&r->rc.bout), tv->test->regs,
2097 0, 0, tv->cfg.nreg, tv->cfg.regsz);
c81c35df 2098 else { goto fail; }
c91413e6 2099 rc = handle_packets(tv, r, RCVF_ALLOWEOF, TVPK_TEST | TVPF_ACK, &b);
c81c35df
MW
2100
2101 /* Deal with the outcome. */
c91413e6 2102 switch (rc) {
c81c35df 2103
c91413e6 2104 case RECV_FAIL:
c81c35df
MW
2105 /* Some kind of error. Abandon ship. */
2106
2107 fail:
2108 tvec_skip(tv, "remote test runner communications failed");
2109 disconnect_remote(tv, r, 0);
2110 break;
2111
c91413e6 2112 case RECV_EOF:
c81c35df
MW
2113 /* End-of-file at a packet boundary. The server crashed trying to run
2114 * our test. Collect the exit status and continue.
2115 */
c91413e6
MW
2116 reap_kid(tv, r);
2117 /* fall through */
c81c35df 2118
c91413e6 2119 case RECV_OK:
c81c35df
MW
2120 /* Successful completion (or EOF). */
2121
2122 /* Notice if the exit status isn't right. */
c91413e6 2123 if (r->exit != r->exwant) f |= f_exit;
c81c35df
MW
2124
2125 /* Notice if the progress token isn't right. */
c91413e6
MW
2126 if (r->progress.len != r->prgwant.len ||
2127 MEMCMP(r->progress.buf, !=, r->prgwant.buf, r->progress.len))
2128 f |= f_progress;
c81c35df
MW
2129
2130 /* If we found something wrong but the test is passing so far, then
2131 * report the failure and dump the input registers.
2132 */
c91413e6
MW
2133 if (f && (tv->f&TVSF_ACTIVE))
2134 { tvec_fail(tv, 0); tvec_mismatch(tv, TVMF_IN); }
c81c35df
MW
2135
2136 /* If the test failed, then report the exit and progress states
2137 * relative to their expectations.
2138 */
c91413e6
MW
2139 if (!(tv->f&TVSF_ACTIVE) &&
2140 (tv->f&TVSF_OUTMASK) == (TVOUT_LOSE << TVSF_OUTSHIFT)) {
c81c35df
MW
2141
2142 /* Note here that the test failed. */
c91413e6
MW
2143 f |= f_fail;
2144
c81c35df 2145 /* Report exit status. */
c91413e6
MW
2146 rv.u = r->exit;
2147 tvec_dumpreg(tv, f&f_exit ? TVRD_FOUND : TVRD_MATCH,
814e42ff 2148 &rv, &exit_var.def);
c91413e6
MW
2149 if (f&f_exit) {
2150 rv.u = r->exwant;
814e42ff 2151 tvec_dumpreg(tv, TVRD_EXPECT, &rv, &exit_var.def);
c91413e6
MW
2152 }
2153
c81c35df
MW
2154 /* Report progress token. */
2155 rv.text.p = r->progress.buf; rv.text.sz = r->progress.len;
c91413e6 2156 tvec_dumpreg(tv, f&f_progress ? TVRD_FOUND : TVRD_MATCH,
814e42ff 2157 &rv, &progress_var.def);
c91413e6 2158 if (f&f_progress) {
c81c35df 2159 rv.text.p = r->prgwant.buf; rv.text.sz = r->prgwant.len;
814e42ff 2160 tvec_dumpreg(tv, TVRD_EXPECT, &rv, &progress_var.def);
c91413e6
MW
2161 }
2162 }
2163
c81c35df
MW
2164 /* If we received end-of-file, then close the connection. Suppress
2165 * error output if the test passed: it was presumably expected.
2166 */
c91413e6
MW
2167 if (rc == RECV_EOF)
2168 disconnect_remote(tv, r, f ? 0 : ERF_SILENT);
2169 break;
2170 }
2171
c91413e6
MW
2172#undef f_exit
2173#undef f_progress
2174#undef f_fail
2175}
2176
c81c35df
MW
2177/* --- @tvec_remoteafter@ --- *
2178 *
2179 * Arguments: @struct tvec_state *tv@ = test vector state
2180 * @void *ctx@ = context pointer
2181 *
2182 * Returns: ---
2183 *
2184 * Use: Reset variables to their default values.
2185 */
2186
2187void tvec_remoteafter(struct tvec_state *tv, void *ctx)
2188{
2189 struct tvec_remotectx *r = ctx;
814e42ff
MW
2190 const struct tvec_remoteenv *re = r->re;
2191 const struct tvec_env *subenv = re->r.env;
c81c35df
MW
2192
2193 reset_vars(r);
814e42ff 2194 if (subenv && subenv->after) subenv->after(tv, r->subctx);
c81c35df
MW
2195}
2196
2197/* --- @tvec_remoteteardown@ --- *
2198 *
2199 * Arguments: @struct tvec_state *tv@ = test vector state
2200 * @void *ctx@ = context pointer
2201 *
2202 * Returns: ---
2203 *
2204 * Use: Tear down the remote environment.
2205 */
2206
c91413e6
MW
2207void tvec_remoteteardown(struct tvec_state *tv, void *ctx)
2208{
2209 struct tvec_remotectx *r = ctx;
814e42ff
MW
2210 const struct tvec_remoteenv *re = r->re;
2211 const struct tvec_env *subenv = re->r.env;
31d0247c 2212 buf b;
c91413e6 2213
814e42ff 2214 if (subenv && subenv->teardown) subenv->teardown(tv, r->subctx);
b1a20bee 2215 x_free(tv->a, r->subctx);
31d0247c
MW
2216 if (r->rc.outfd >= 0) {
2217 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_EGROUP);
2218 if (!handle_packets(tv, r, RCVF_ALLOWEOF, TVPK_EGROUP | TVPF_ACK, &b))
2219 if (BLEFT(&b)) malformed(tv, &r->rc);
c91413e6 2220 }
b1a20bee 2221 disconnect_remote(tv, r, DCF_QUITOK); release_comms(tv, &r->rc);
31d0247c 2222 DDESTROY(&r->prgwant); DDESTROY(&r->progress);
c91413e6
MW
2223}
2224
2225/*----- Connectors --------------------------------------------------------*/
2226
c81c35df
MW
2227/* --- @fork_common@ --- *
2228 *
2229 * Arguments: @pid_t *kid_out@ = where to put child process-id
2230 * @int *infd_out, *outfd_out, *errfd_out@ = where to put file
2231 * descriptors
2232 * @struct tvec_state *tv@ = test vector state
2233 *
2234 * Returns: Zero on success, %$-1$% on failure.
2235 *
2236 * Use: Common @fork@ machinery for the connectors. Create a
2237 * subprocess. On successful return, in the subprocess,
2238 * @*kid_out@ is zero, and the error descriptor replaces the
2239 * standard-error descriptor; in the parent, @*kid_out@ is the
2240 * child process-id, and @*errfd_out@ is a descriptor on which
2241 * the child's standard-error output can be read; in both
2242 * @*infd_out@ and @*outfd_out@ are descriptors for input and
2243 * output respectively -- they're opposite ends of pipes, but
2244 * obviously they're crossed over so that the parent's output
2245 * matches the child's input and %%\emph{vice versa}%%.
2246 */
2247
c91413e6
MW
2248static int fork_common(pid_t *kid_out, int *infd_out, int *outfd_out,
2249 int *errfd_out, struct tvec_state *tv)
2250{
2251 int p0[2] = { -1, -1 }, p1[2] = { -1, -1 }, pe[2] = { -1, -1 };
2252 pid_t kid = -1;
2253 int rc;
2254
c81c35df 2255 /* Try to create the pipes. */
c91413e6
MW
2256 if (pipe(p0) || pipe(p1) || pipe(pe) ||
2257 fdflags(p0[1], 0, 0, FD_CLOEXEC, FD_CLOEXEC) ||
2258 fdflags(p1[0], 0, 0, FD_CLOEXEC, FD_CLOEXEC) ||
2259 fdflags(pe[0], 0, 0, FD_CLOEXEC, FD_CLOEXEC)) {
2260 tvec_error(tv, "pipe failed: %s", strerror(errno));
2261 rc = -1; goto end;
2262 }
2263
c81c35df
MW
2264 /* Flush all of the stream buffers so that we don't get duplicated
2265 * output.
2266 */
c91413e6
MW
2267 fflush(0);
2268
c81c35df 2269 /* Try to set up the child process. */
c91413e6
MW
2270 kid = fork();
2271 if (kid < 0) {
2272 tvec_error(tv, "fork failed: %s", strerror(errno));
2273 rc = -1; goto end;
2274 }
2275
2276 if (!kid) {
c81c35df
MW
2277 /* Child process. */
2278
c91413e6
MW
2279 *kid_out = 0;
2280 *infd_out = p0[0]; p0[0] = -1;
2281 *outfd_out = p1[1]; p1[1] = -1;
2282 if (pe[1] != STDERR_FILENO && dup2(pe[1], STDERR_FILENO) < 0) {
2283 fprintf(stderr, "failed to establish child stderr: %s",
2284 strerror(errno));
c81c35df 2285 _exit(127);
c91413e6
MW
2286 }
2287 } else {
c81c35df
MW
2288 /* Parent process. */
2289
c91413e6
MW
2290 *kid_out = kid; kid = -1;
2291 *infd_out = p1[0]; p1[0] = -1;
2292 *outfd_out = p0[1]; p0[1] = -1;
2293 *errfd_out = pe[0]; pe[0] = -1;
2294 }
2295
c81c35df 2296 /* All done. */
c91413e6 2297 rc = 0;
c81c35df 2298
c91413e6 2299end:
c81c35df 2300 /* Clean up. So much of this... */
c91413e6
MW
2301 if (p0[0] >= 0) close(p0[0]);
2302 if (p0[1] >= 0) close(p0[1]);
2303 if (p1[0] >= 0) close(p1[0]);
2304 if (p1[1] >= 0) close(p1[1]);
2305 if (pe[0] >= 0) close(pe[0]);
2306 if (pe[1] >= 0) close(pe[1]);
2307 return (rc);
2308}
2309
c81c35df
MW
2310/* --- @tvec_fork@ --- *
2311 *
2312 * Arguments: @pid_t *kid_out@ = where to put child process-id
2313 * @int *infd_out, *outfd_out, *errfd_out@ = where to put file
2314 * descriptors
2315 * @struct tvec_state *tv@ = test vector state
2316 * @const struct tvec_remoteenv@ = the remote environment
2317 *
2318 * Returns: Zero on success, %$-1$% on failure.
2319 *
2320 * Use: Starts a remote server running in a fork of the main
2321 * process. This is useful for testing functions which might --
2322 * or are even intended to -- crash.
2323 */
2324
c91413e6
MW
2325int tvec_fork(pid_t *kid_out, int *infd_out, int *outfd_out, int *errfd_out,
2326 struct tvec_state *tv, const struct tvec_remoteenv *env)
2327{
2328 struct tvec_config config;
2329 const struct tvec_remotefork *rf = (const struct tvec_remotefork *)env;
2330 pid_t kid = -1;
2331 int infd = -1, outfd = -1, errfd = -1;
2332 int rc;
2333
2334 if (fork_common(&kid, &infd, &outfd, &errfd, tv)) { rc = -1; goto end; }
2335 if (!kid) {
31d0247c 2336 if (tv->fp) fclose(tv->fp);
d056fbdf
MW
2337 config.tests = rf->f.tests ? rf->f.tests : tv->cfg.tests;
2338 config.nrout = tv->cfg.nrout; config.nreg = tv->cfg.nreg;
2339 config.regsz = tv->cfg.regsz;
c91413e6
MW
2340 _exit(tvec_remoteserver(infd, outfd, &config));
2341 }
2342
2343 *kid_out = kid; *infd_out = infd; *outfd_out = outfd; *errfd_out = errfd;
2344 rc = 0;
2345end:
2346 return (rc);
2347}
2348
c81c35df
MW
2349/* --- @tvec_exec@ --- *
2350 *
2351 * Arguments: @pid_t *kid_out@ = where to put child process-id
2352 * @int *infd_out, *outfd_out, *errfd_out@ = where to put file
2353 * descriptors
2354 * @struct tvec_state *tv@ = test vector state
2355 * @const struct tvec_remoteenv@ = the remote environment
2356 *
2357 * Returns: Zero on success, %$-1$% on failure.
2358 *
2359 * Use: Starts a remote server by running some program. The command
2360 * given in the environment description will probably some hairy
2361 * shell rune allowing for configuration via files or
2362 * environment variables.
2363 */
2364
c91413e6
MW
2365int tvec_exec(pid_t *kid_out, int *infd_out, int *outfd_out, int *errfd_out,
2366 struct tvec_state *tv, const struct tvec_remoteenv *env)
2367{
2368 const struct tvec_remoteexec *rx = (const struct tvec_remoteexec *)env;
2369 pid_t kid = -1;
2370 int infd = -1, outfd = -1, errfd = -1;
2371 mdup_fd v[2];
2372 int rc;
2373
2374 if (fork_common(&kid, &infd, &outfd, &errfd, tv)) { rc = -1; goto end; }
2375 if (!kid) {
2376 v[0].cur = infd; v[0].want = STDIN_FILENO;
2377 v[1].cur = outfd; v[1].want = STDOUT_FILENO;
2378 if (mdup(v, 2)) {
2379 fprintf(stderr, "failed to establish standard file descriptors: %s",
2380 strerror(errno));
2381 exit(127);
2382 }
2383 execvp(rx->x.args[0], (/*uncosnt*/ char *const *)rx->x.args);
2384 fprintf(stderr, "failed to invoke test runner: %s", strerror(errno));
2385 exit(127);
2386 }
2387
2388 *kid_out = kid; *infd_out = infd; *outfd_out = outfd; *errfd_out = errfd;
2389 rc = 0;
2390end:
2391 return (rc);
2392}
e63124bc
MW
2393
2394/*----- That's all, folks -------------------------------------------------*/