chiark / gitweb /
62dd1509d965a48657124c282a3160d7d15e12ad
[mLib] / test / tvec-remote.c
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>
31 #include <signal.h>
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include <sys/types.h>
38 #include <sys/wait.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41
42 #include "alloc.h"
43 #include "bench.h"
44 #include "buf.h"
45 #include "compiler.h"
46 #include "fdflags.h"
47 #include "lbuf.h"
48 #include "mdup.h"
49 #include "quis.h"
50 #include "tvec.h"
51
52 /*----- Preliminaries -----------------------------------------------------*/
53
54 /* The control macros I'm using below provoke `dangling-else' warnings from
55  * compilers.  Suppress them.  I generally don't care.
56  */
57
58 #if GCC_VERSION_P(7, 1)
59 #  pragma GCC diagnostic ignored "-Wdangling-else"
60 #elif GCC_VERSION_P(4, 2)
61 #  pragma GCC diagnostic ignored "-Wparentheses"
62 #endif
63
64 #if CLANG_VERSION_P(3, 1)
65 #  pragma clang diagnostic ignored "-Wdangling-else"
66 #endif
67
68 /*----- Basic I/O ---------------------------------------------------------*/
69
70 /* --- @init_comms@ --- *
71  *
72  * Arguments:   @struct tvec_remotecomms *rc@ = communication state
73  *
74  * Returns:     ---
75  *
76  * Use:         Initialize a communication state.  This doesn't allocate any
77  *              resurces: it just ensures that everything is set up so that
78  *              subsequent operations -- in particular @release_comms@ --
79  *              behave sensibly.
80  */
81
82 static void init_comms(struct tvec_remotecomms *rc)
83 {
84   rc->bin = 0; rc->binsz = 0; dbuf_create(&rc->bout);
85   rc->infd = rc->outfd = -1; rc->f = 0;
86 }
87
88 /* --- @close_comms@ --- *
89  *
90  * Arguments:   @struct tvec_remotecomms *rc@ = communication state
91  *
92  * Returns:     ---
93  *
94  * Use:         Close the input and output descriptors.
95  *
96  *              If the descriptors are already closed -- or were never opened
97  *              -- then nothing happens.
98  */
99
100 static void close_comms(struct tvec_remotecomms *rc)
101 {
102   if (rc->infd >= 0) { close(rc->infd); rc->infd = -1; }
103   if (rc->outfd >= 0) { close(rc->outfd); rc->outfd = -1; }
104 }
105
106 /* --- @release_comms@ --- *
107  *
108  * Arguments:   @struct tvec_remotecomms *rc@ = communication state
109  *
110  * Returns:     ---
111  *
112  * Use:         Releases the resources -- most notably the input and output
113  *              buffers -- held by the communication state.  Also calls
114  *              @close_comms@.
115  */
116
117 static void release_comms(struct tvec_remotecomms *rc)
118   { close_comms(rc); xfree(rc->bin); dbuf_destroy(&rc->bout); }
119
120 /* --- @setup_comms@ --- *
121  *
122  * Arguments:   @struct tvec_remotecomms *rc@ = communication state
123  *              @int infd, outfd@ = input and output file descriptors
124  *
125  * Returns:     ---
126  *
127  * Use:         Use the given descriptors for communication.
128  *
129  *              Clears the private flags.
130  */
131
132 static void setup_comms(struct tvec_remotecomms *rc, int infd, int outfd)
133 {
134   rc->infd = infd; rc->outfd = outfd;
135   rc->binoff = rc->binlen = 0;
136   rc->f &= ~0xffu;
137 }
138
139 /* --- @ioerr@ --- *
140  *
141  * Arguments:   @struct tvec_state *tv@ = test-vector state
142  *              @struct tvec_remotecomms *rc@ = communication state
143  *              @const char *msg, ...@ = format string and arguments
144  *
145  * Returns:     %$-1$%.
146  *
147  * Use:         Reports the message as an error, closes communications and
148  *              marks them as broken.
149  */
150
151 static PRINTF_LIKE(3, 4)
152   int ioerr(struct tvec_state *tv, struct tvec_remotecomms *rc,
153             const char *msg, ...)
154 {
155   va_list ap;
156
157   va_start(ap, msg);
158   close_comms(rc); rc->f |= TVRF_BROKEN;
159   tvec_report_v(tv, TVLEV_ERR, msg, &ap);
160   va_end(ap);
161   return (-1);
162 }
163
164 /* --- @send_all@ --- *
165  *
166  * Arguments:   @struct tvec_state *tv@ = test-vector state
167  *              @struct tvec_remotecomms *rc@ = communication state
168  *              @const unsigned char *p@, @size_t sz@ = output buffer
169  *
170  * Returns:     Zero on success, %$-1$% on error.
171  *
172  * Use:         Send the output buffer over the communication state's output
173  *              descriptor, even if it has to be written in multiple pieces.
174  */
175
176 static int send_all(struct tvec_state *tv, struct tvec_remotecomms *rc,
177                     const unsigned char *p, size_t sz)
178 {
179   ssize_t n;
180   int ret;
181
182   while (sz) {
183     n = write(rc->outfd, p, sz);
184     if (n > 0)
185       { p += n; sz -= n; }
186     else {
187       ret = ioerr(tv, rc, "failed to send: %s",
188                  n ? strerror(errno) : "empty write");
189       goto end;
190     }
191   }
192   ret = 0;
193 end:
194   return (ret);
195 }
196
197 /* --- @recv_all@ --- *
198  *
199  * Arguments:   @struct tvec_state *tv@ = test-vector state
200  *              @struct tvec_remotecomms *rc@ = communication state
201  *              @unsigned f@ = flags (@RCVF_...@)
202  *              @unsigned char *p@, @size_t sz@ = input buffer
203  *              @size_t min@ = minimum acceptable size to read
204  *              @size_t *n_out@ = size read
205  *
206  * Returns:     An @RECV_...@ code.
207  *
208  * Use:         Receive data on the communication state's input descriptor to
209  *              read at least @min@ bytes into the input buffer, even if it
210  *              has to be done in multiple pieces.  If more data is readily
211  *              available, then up to @sz@ bytes will be read in total.
212  *
213  *              If the descriptor immediately reports end-of-file, and
214  *              @RCVF_ALLOWEOF@ is set in @f@, then return @RECV_EOF@.
215  *              Otherwise, EOF is treated as an I/O error, resulting in a
216  *              call to @ioerr@ and a return code of @RECV_FAIL@.  If the
217  *              read succeeded, then set @*n_out@ to the number of bytes read
218  *              and return @RECV_OK@.
219  */
220
221 #define RCVF_ALLOWEOF 1u
222
223 enum {
224   RECV_FAIL = -1,
225   RECV_OK = 0,
226   RECV_EOF = 1
227 };
228
229 static int recv_all(struct tvec_state *tv, struct tvec_remotecomms *rc,
230                     unsigned f, unsigned char *p, size_t sz,
231                     size_t min, size_t *n_out)
232 {
233   size_t tot = 0;
234   ssize_t n;
235
236   while (sz) {
237     n = read(rc->infd, p, sz);
238     if (n > 0) {
239       p += n; sz -= n; tot += n;
240       if (tot >= min) break;
241     } else if (!n && !tot && (f&RCVF_ALLOWEOF))
242       return (RECV_EOF);
243     else
244       return (ioerr(tv, rc, "failed to receive: %s",
245                     n ? strerror(errno) : "unexpected end-of-file"));
246   }
247   *n_out = tot; return (RECV_OK);
248
249 #undef f_any
250 }
251
252 /* --- @buferr@ --- *
253  *
254  * Arguments:   @struct tvec_state *tv@ = test-vector state
255  *              @struct tvec_remotecomms *rc@ = communication state
256  *
257  * Returns:     %$-$%.
258  *
259  * Use:         Report a problem preparing the output buffer.
260  */
261
262 static int buferr(struct tvec_state *tv, struct tvec_remotecomms *rc)
263   { return (ioerr(tv, rc, "failed to build output packet")); }
264
265 /* --- @malformed@ --- *
266  *
267  * Arguments:   @struct tvec_state *tv@ = test-vector state
268  *              @struct tvec_remotecomms *rc@ = communication state
269  *
270  * Returns:     %$-$%.
271  *
272  * Use:         Report an I/O error that the incoming packet is malformed.
273  */
274
275 static int malformed(struct tvec_state *tv, struct tvec_remotecomms *rc)
276   { return (ioerr(tv, rc, "received malformed packet")); }
277
278 /* --- @remote_send@ --- *
279  *
280  * Arguments:   @struct tvec_state *tv@ = test-vector state
281  *              @struct tvec_remotecomms *rc@ = communication state
282  *
283  * Returns:     Zero on success, %$-1$% on error.
284  *
285  * Use:         Send the accuulated contents of the output buffer @rc->bout@.
286  *
287  *              The function arranges to convert @SIGPIPE@ into an error.
288  *
289  *              If the output buffer is broken, report this as an I/O error.
290  */
291
292 #define SENDBUFSZ 4096
293
294 static int remote_send(struct tvec_state *tv, struct tvec_remotecomms *rc)
295 {
296   void (*opipe)(int) = SIG_ERR;
297   int ret;
298
299   /* Various preflight checks. */
300   if (rc->f&TVRF_BROKEN) { ret = -1; goto end; }
301   if (DBBAD(&rc->bout)) { ret = buferr(tv, rc); goto end; }
302
303   /* Arrange to trap broken-pipe errors. */
304   opipe = signal(SIGPIPE, SIG_IGN);
305     if (opipe == SIG_ERR) {
306       ret = ioerr(tv, rc, "failed to ignore `SIGPIPE': %s", strerror(errno));
307       goto end;
308     }
309
310   /* Transmit the packet. */
311   if (send_all(tv, rc, DBBASE(&rc->bout), DBLEN(&rc->bout)))
312     { ret = -1; goto end; }
313
314   /* Done.  Put things back the way we found them. */
315   ret = 0;
316 end:
317   DBRESET(&rc->bout);
318   if (opipe != SIG_ERR) signal(SIGPIPE, opipe);
319   return (ret);
320 }
321
322 /* --- @receive_buffered@ --- *
323  *
324  * Arguments:   @struct tvec_state *tv@ = test-vector state
325  *              @struct tvec_remotecomms *rc@ = communication state
326  *              @unsigned f@ = flags (@RCVF_...@)
327  *              @size_t want@ = data block size required
328  *
329  * Returns:     An @RECV_...@ code.
330  *
331  * Use:         Reads a block of data from the input descriptor into the
332  *              input buffer.
333  *
334  *              This is the main machinery for manipulating the input buffer.
335  *              The buffer has three regions:
336  *
337  *                * from the buffer start to @rc->binoff@ is `consumed';
338  *                * from @rc->binoff@ to @rc->binlen@ is `available'; and
339  *                * from @rc->binlen@ to @rc->binsz@ is `free'.
340  *
341  *              Data is read into the start of the `free' region, and the
342  *              `available' region is extended to include it.  Data in the
343  *              `consumed' region is periodically discarded by moving the
344  *              data from the `available' region to the start of the buffer
345  *              and decreasing @rc->binoff@ and @rc->binlen@.
346  *
347  *              This function ensures that the `available' region contains at
348  *              least @want@ bytes, by (a) extending the buffer, if
349  *              necessary, so that @rc->binsz >= rc->binoff + want@, and (b)
350  *              reading fresh data from the input descriptor to extend the
351  *              `available' region.
352  *
353  *              If absolutely no data is available, and @RCVF_ALLOWEOF@ is
354  *              set in @f@, then return @RECV_EOF@.  On I/O errors, including
355  *              a short read or end-of-file if @RCVF_ALLOWEOF@ is clear,
356  *              return @RECV_FAIL@.  On success, return @RECV_OK@.  The
357  *              amount of data read is indicated by updating the input buffer
358  *              variables as described above.
359  */
360
361 #define RECVBUFSZ 4096u
362
363 static int receive_buffered(struct tvec_state *tv,
364                             struct tvec_remotecomms *rc,
365                             unsigned f, size_t want)
366 {
367   size_t sz;
368   int ret;
369
370   /* If we can supply the caller's requirement from the buffer then do
371    * that.
372    */
373   if (rc->binlen - rc->binoff >= want) return (RECV_OK);
374
375   /* If the buffer is too small then we must grow it. */
376   if (want > rc->binsz) {
377     sz = rc->binsz; if (!sz) sz = RECVBUFSZ;
378     while (sz < want) { assert(sz < (size_t)-1/2); sz *= 2; }
379     if (!rc->bin) rc->bin = xmalloc(sz);
380     else rc->bin = xrealloc(rc->bin, sz, rc->binsz);
381     rc->binsz = sz;
382   }
383
384   /* Shunt the unused existing material to the start of the buffer. */
385   memmove(rc->bin, rc->bin + rc->binoff, rc->binlen - rc->binoff);
386   rc->binlen -= rc->binoff; rc->binoff = 0;
387
388   /* Satisfy the caller from the input stream, and try to fill up as much of
389    * the rest of the buffer as we can.
390    */
391   ret = recv_all(tv, rc, rc->binlen ? 0 : f,
392                  rc->bin + rc->binlen, rc->binsz - rc->binlen,
393                  want - rc->binlen, &sz);
394     if (ret) return (ret);
395
396   /* Note how much material we have and return. */
397   rc->binlen += sz; return (RECV_OK);
398 }
399
400 /* --- @remote_recv@ --- *
401  *
402  * Arguments:   @struct tvec_state *tv@ = test-vector state
403  *              @unsigned f@ = flags (@RCVF_...@)
404  *              @buf *b_out@ = buffer to establish around the packet contents
405  *
406  * Returns:     An @RECV_...@ code.
407  *
408  * Use:         Receive a packet into the input buffer @rc->bin@ and
409  *              establish @*b_out@ to read from it.
410  */
411
412 static int remote_recv(struct tvec_state *tv, struct tvec_remotecomms *rc,
413                        unsigned f, buf *b_out)
414 {
415   kludge64 k, szmax;
416   size_t want;
417   int ret;
418
419   ASSIGN64(szmax, (size_t)-1);
420
421   /* Preflight checks. */
422   if (rc->f&TVRF_BROKEN) return (RECV_FAIL);
423
424   /* See if we can read the next packet length from what we already have. */
425   ret = receive_buffered(tv, rc, f, 8); if (ret) return (ret);
426   LOAD64_L_(k, rc->bin + rc->binoff); rc->binoff += 8;
427   if (CMP64(k, >, szmax))
428     return (ioerr(tv, rc, "packet size 0x%08lx%08lx out of range",
429                   (unsigned long)HI64(k), (unsigned long)LO64(k)));
430   want = GET64(size_t, k);
431
432   /* Read the next packet payload. */
433   ret = receive_buffered(tv, rc, 0, want); if (ret) return (ret);
434   buf_init(b_out, rc->bin + rc->binoff, want); rc->binoff += want;
435   return (RECV_OK);
436 }
437
438 /* --- @QUEUEPK_TAG@, @QUEUEPK@ --- *
439  *
440  * Arguments:   @tag@ = control structure tag
441  *              @struct tvec_state *tv@ = test-vector state
442  *              @struct tvec_remotecomms *rc@ = communication state
443  *              @unsigned fl@ = flags (@QF_...@)
444  *              @unsigned pk@ = packet type
445  *
446  * Use:         This is syntactically a statement head: the syntax is
447  *              @QUEUEPK(tv, rc, f) body [else alt]@.  The @body@ should
448  *              write material to the output buffer @rc->bout@.  The macro
449  *              applies appropriate framing.  If enough material has been
450  *              collected, or if @QF_FORCE@ is set in @fl@, then
451  *              @remote_send@ is invoked to transmit the buffered packets.
452  *              If there is an error of any kind, then the @alt@ statement,
453  *              if any, is executed.
454  */
455
456 #define QF_FORCE 1u
457 #define QUEUEPK_TAG(tag, tv, rc, fl, pk)                                \
458         if ((rc)->f&TVRF_BROKEN) MC_GOELSE(tag##__else); else           \
459         MC_ALLOWELSE(tag##__else)                                       \
460         MC_AFTER(tag##__send, {                                         \
461           if ((DBBAD(&(rc)->bout) && (buferr((tv), (rc)), 1)) ||        \
462               ((((fl)&QF_FORCE) || DBLEN(&(rc)->bout) >= SENDBUFSZ) &&  \
463                remote_send(tv, rc)))                                    \
464             MC_GOELSE(tag##__else);                                     \
465         })                                                              \
466         DBUF_ENCLOSEITAG(tag##__frame, &(rc)->bout, (rc)->t, 64_L)      \
467         MC_BEFORE(tag##__pkty, {                                        \
468           dbuf_putu16l(&(rc)->bout, (pk));                              \
469         })
470
471 #define QUEUEPK(tv, rc, fl, pk) QUEUEPK_TAG(queue, (tv), (rc), (fl), (pk))
472
473 /*----- Packet types ------------------------------------------------------*/
474
475 #define TVPF_ACK        0x0001u
476
477 #define TVPK_VER        0x0000u         /* --> min, max: u16 *
478                                          * <-- ver: u16 */
479 #define TVPK_BGROUP     0x0002u         /* --> name: str16
480                                          * <-- --- */
481 #define TVPK_TEST       0x0004u         /* --> in: regs
482                                          * <-- --- */
483 #define TVPK_EGROUP     0x0006u         /* --> --- *
484                                          * <-- --- */
485
486 #define TVPK_REPORT     0x0100u         /* <-- level: u16; msg: string */
487 #define TVPK_PROGRESS   0x0102u         /* <-- st: str16 */
488
489 #define TVPK_SKIPGRP    0x0104u         /* <-- excuse: str16 */
490 #define TVPK_SKIP       0x0106u         /* <-- excuse: str16 */
491 #define TVPK_FAIL       0x0108u         /* <-- flag: u8, detail: str16 */
492 #define TVPK_DUMPREG    0x010au         /* <-- ri: u16; disp: u16;
493                                          *     flag: u8, rv: value */
494 #define TVPK_BBENCH     0x010cu         /* <-- ident: str32; unit: u16 */
495 #define TVPK_EBENCH     0x010eu         /* <-- ident: str32; unit: u16;
496                                          *     flags: u16; n, t, cy: f64 */
497
498 /*----- Server ------------------------------------------------------------*/
499
500 /* Forward declaration of output operations. */
501 static const struct tvec_outops remote_ops;
502
503 static struct tvec_state srvtv;         /* server's test-vector state */
504 static struct tvec_remotecomms srvrc = TVEC_REMOTECOMMS_INIT; /* comms */
505 static struct tvec_output srvout = { &remote_ops }; /* output state */
506
507 /* --- @tvec_setprogress@, @tvec_setprogress_v@ --- *
508  *
509  * Arguments:   @const char *status@ = progress status token format
510  *              @va_list ap@ = argument tail
511  *
512  * Returns:     ---
513  *
514  * Use:         Reports the progress of a test execution to the client.
515  *
516  *              The framework makes use of tokens beginning with %|%|%:
517  *
518  *                * %|%IDLE|%: during the top-level server code;
519  *
520  *                * %|%SETUP|%: during the enclosing environment's @before@
521  *                  function;
522  *
523  *                * %|%RUN|%: during the environment's @run@ function, or the
524  *                  test function; and
525  *
526  *                * %|%DONE|%: during the enclosing environment's @after@
527  *                  function.
528  *
529  *              The intent is that a test can use the progress token to check
530  *              that a function which is expected to crash does so at the
531  *              correct point, so it's expected that more complex test
532  *              functions and/or environments will set their own progress
533  *              tokens to reflect what's going on.
534  */
535
536 int tvec_setprogress(const char *status, ...)
537 {
538   va_list ap;
539   int rc;
540
541   va_start(ap, status); rc = tvec_setprogress_v(status, &ap); va_end(ap);
542   return (rc);
543 }
544
545 int tvec_setprogress_v(const char *status, va_list *ap)
546 {
547   /* Force immediate output in case we crash before the buffer is output
548    * organically.
549    */
550   QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_PROGRESS)
551     dbuf_vputstrf16l(&srvrc.bout, status, ap);
552   else return (-1);
553   return (0);
554 }
555
556 /* --- @tvec_remoteserver@ --- *
557  *
558  * Arguments:   @int infd@, @int outfd@ = input and output file descriptors
559  *              @const struct tvec_config *config@ = test configuration
560  *
561  * Returns:     Suggested exit code.
562  *
563  * Use:         Run a test server, reading packets from @infd@ and writing
564  *              responses and notifications to @outfd@, and invoking tests as
565  *              described by @config@.
566  *
567  *              This function is not particularly general purpose.  It
568  *              expects to `take over' the process, and makes use of private
569  *              global variables.
570  */
571
572 int tvec_remoteserver(int infd, int outfd, const struct tvec_config *config)
573 {
574   uint16 pk, u, v;
575   unsigned i;
576   buf b;
577   const struct tvec_test *t;
578   void *p; size_t sz;
579   const struct tvec_env *env = 0;
580   void *ctx = 0;
581   int rc;
582
583   /* Initialize the communication machinery. */
584   setup_comms(&srvrc, infd, outfd);
585
586   /* Begin a test session using our custom output driver. */
587   tvec_begin(&srvtv, config, &srvout);
588
589   /* Version negotiation.  Expect a @TVPK_VER@ packet.  At the moment,
590    * there's only version zero, so we return that.
591    */
592   if (remote_recv(&srvtv, &srvrc, 0, &b)) { rc = -1; goto end; }
593   if (buf_getu16l(&b, &pk)) goto bad;
594   if (pk != TVPK_VER) {
595     rc = ioerr(&srvtv, &srvrc,
596                "unexpected packet type 0x%04x instead of client version",
597                pk);
598     goto end;
599   }
600   if (buf_getu16l(&b, &u) || buf_getu16l(&b, &v)) goto bad;
601   QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_VER | TVPF_ACK)
602     dbuf_putu16l(&srvrc.bout, 0);
603   else { rc = -1; goto end; }
604
605   /* Handle packets until the server closes the connection.
606    *
607    * The protocol looks much simpler from our point of view than from the
608    * client.
609    *
610    *   * Receive @TVPK_VER@; respond with @TVPK_VER | TVPF_ACK@.
611    *
612    *   * Receive zero or more @TVPK_BGROUP@.  Open a test group, producing
613    *     output packets, and eventually answer with @TVPK_BGROUP | TVPF_ACK@.
614    *
615    *       -- Receive zero or more @TVPK_TEST@.  Run a test, producing output
616    *          packets, and eventually answer with @TVPK_TEST | TVPF_ACK@.
617    *
618    *       -- Receive @TVPK_EGROUP@.  Maybe produce output packets, and
619    *          answer with @TVPK_EGROUP | TVPF_ACK@.
620    *
621    *  * Read EOF.  Stop.
622    */
623   for (;;) {
624
625     /* Read a packet.  End-of-file is expected here (and pretty much nowhere
626      * else).   Otherwise, we expect to see @TVPK_BGROUP@.
627      */
628     rc = remote_recv(&srvtv, &srvrc, RCVF_ALLOWEOF, &b);
629       if (rc == RECV_EOF) break;
630       else if (rc == RECV_FAIL) goto end;
631     if (buf_getu16l(&b, &pk)) goto bad;
632
633     switch (pk) {
634
635       case TVPK_BGROUP:
636         /* Start a group. */
637
638         /* Parse the packet payload. */
639         p = buf_getmem16l(&b, &sz); if (!p) goto bad;
640         if (BLEFT(&b)) goto bad;
641
642         /* Find the group given its name. */
643         for (t = srvtv.tests; t->name; t++)
644           if (strlen(t->name) == sz && MEMCMP(t->name, ==, p, sz))
645             goto found_group;
646         rc = ioerr(&srvtv, &srvrc, "unknown test group `%.*s'",
647                    (int)sz, (char *)p);
648         goto end;
649
650       found_group:
651         /* Set up the test environment. */
652         srvtv.test = t; env = t->env;
653         if (env && env->setup == tvec_remotesetup)
654           env = ((struct tvec_remoteenv *)env)->r.env;
655         if (!env || !env->ctxsz) ctx = 0;
656         else ctx = xmalloc(env->ctxsz);
657         if (env && env->setup) env->setup(&srvtv, env, 0, ctx);
658
659         /* Initialize the registers. */
660         tvec_initregs(&srvtv);
661
662         /* Report that the group has been opened and that we're ready to run
663          * tests.
664          */
665         QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_BGROUP | TVPF_ACK);
666         else { rc = -1; goto end; }
667
668         /* Handle packets until we're told to end the group. */
669         for (;;) {
670
671           /* Read a packet.  We expect @TVPK_EGROUP@ or @TVPK_TEST@. */
672           if (remote_recv(&srvtv, &srvrc, 0, &b)) { rc = -1; goto end; }
673           if (buf_getu16l(&b, &pk)) goto bad;
674
675           switch (pk) {
676
677             case TVPK_EGROUP:
678               /* End the group. */
679
680               /* Check the payload. */
681               if (BLEFT(&b)) goto bad;
682
683               /* Leave the group loop. */
684               goto endgroup;
685
686             case TVPK_TEST:
687               /* Run a test. */
688
689               /* Parse the packet payload. */
690               if (tvec_deserialize(srvtv.in, &b, srvtv.test->regs,
691                                    srvtv.nreg, srvtv.regsz))
692                 goto bad;
693               if (BLEFT(&b)) goto bad;
694
695               /* If we're not skipping the test group, then actually try to
696                * run the test.
697                */
698               if (!(srvtv.f&TVSF_SKIP)) {
699
700                 /* Prepare the output registers and reset the test outcome.
701                  * (The environment may force a skip.)
702                  */
703                 for (i = 0; i < srvtv.nrout; i++)
704                   if (TVEC_REG(&srvtv, in, i)->f&TVRF_LIVE)
705                     TVEC_REG(&srvtv, out, i)->f |= TVRF_LIVE;
706                 srvtv.f |= TVSF_ACTIVE; srvtv.f &= ~TVSF_OUTMASK;
707
708                 /* Invoke the environment @before@ function. */
709                 tvec_setprogress("%%SETUP");
710                 if (env && env->before) env->before(&srvtv, ctx);
711
712                 /* Run the actual test. */
713                 if (!(srvtv.f&TVSF_ACTIVE))
714                   /* setup forced a skip */;
715                 else {
716                   tvec_setprogress("%%RUN");
717                   if (env && env->run)
718                     env->run(&srvtv, t->fn, ctx);
719                   else {
720                     t->fn(srvtv.in, srvtv.out, ctx);
721                     tvec_check(&srvtv, 0);
722                   }
723                 }
724
725                 /* Conclude the test. */
726                 tvec_setprogress("%%DONE");
727                 if (env && env->after) env->after(&srvtv, ctx);
728                 tvec_endtest(&srvtv);
729               }
730
731               /* Reset the input registers and report completion. */
732               tvec_releaseregs(&srvtv); tvec_initregs(&srvtv);
733               QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_TEST | TVPF_ACK);
734               else { rc = -1; goto end; }
735               break;
736
737             default:
738               /* Some other kind of packet.  Complain. */
739
740               rc = ioerr(&srvtv, &srvrc,
741                          "unexpected packet type 0x%04x during test group",
742                          pk);
743               goto end;
744
745           }
746         }
747
748       endgroup:
749         /* The test group completed. */
750
751         /* Tear down the environment and release other resources. */
752         if (env && env->teardown) env->teardown(&srvtv, ctx);
753         tvec_releaseregs(&srvtv);
754         xfree(ctx); srvtv.test = 0; env = 0; ctx = 0;
755
756         /* Report completion. */
757         QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_EGROUP | TVPF_ACK);
758         else { rc = -1; goto end; }
759         break;
760
761       default:
762         rc = ioerr(&srvtv, &srvrc,
763                    "unexpected packet type 0x%04x at top level", pk);
764     }
765   }
766   rc = 0;
767
768 end:
769   /* Clean up and return. */
770   if (env && env->teardown) env->teardown(&srvtv, ctx);
771   xfree(ctx);
772   if (srvtv.test) tvec_releaseregs(&srvtv);
773   release_comms(&srvrc); tvec_end(&srvtv);
774   return (rc ? 2 : 0);
775
776 bad:
777   /* Miscellaneous malformed packet. */
778   rc = malformed(&srvtv, &srvrc); goto end;
779 }
780
781 /*----- Server output driver ----------------------------------------------*/
782
783 static void remote_skipgroup(struct tvec_output *o,
784                              const char *excuse, va_list *ap)
785 {
786   QUEUEPK(&srvtv, &srvrc, 0, TVPK_SKIPGRP)
787     dbuf_vputstrf16l(&srvrc.bout, excuse, ap);
788 }
789
790 static void remote_skip(struct tvec_output *o,
791                         const char *excuse, va_list *ap)
792 {
793   QUEUEPK(&srvtv, &srvrc, 0, TVPK_SKIP)
794     dbuf_vputstrf16l(&srvrc.bout, excuse, ap);
795 }
796
797 static void remote_fail(struct tvec_output *o,
798                         const char *detail, va_list *ap)
799 {
800   QUEUEPK(&srvtv, &srvrc, 0, TVPK_FAIL)
801     if (!detail)
802       dbuf_putbyte(&srvrc.bout, 0);
803     else {
804       dbuf_putbyte(&srvrc.bout, 1);
805       dbuf_vputstrf16l(&srvrc.bout, detail, ap);
806     }
807 }
808
809 static void remote_dumpreg(struct tvec_output *o,
810                            unsigned disp, const union tvec_regval *rv,
811                            const struct tvec_regdef *rd)
812 {
813   const struct tvec_regdef *reg;
814   unsigned r;
815
816   /* Find the register definition. */
817   for (reg = srvtv.test->regs, r = 0; reg->name; reg++, r++)
818     if (reg == rd) goto found;
819   assert(!"unexpected register definition");
820
821 found:
822   QUEUEPK(&srvtv, &srvrc, 0, TVPK_DUMPREG) {
823     dbuf_putu16l(&srvrc.bout, r);
824     dbuf_putu16l(&srvrc.bout, disp);
825     if (!rv)
826       dbuf_putbyte(&srvrc.bout, 0);
827     else {
828       dbuf_putbyte(&srvrc.bout, 1);
829       rd->ty->tobuf(DBUF_BUF(&srvrc.bout), rv, rd);
830     }
831   }
832 }
833
834 static void remote_bbench(struct tvec_output *o,
835                           const char *ident, unsigned unit)
836 {
837   QUEUEPK(&srvtv, &srvrc, 0, TVPK_BBENCH) {
838     dbuf_putstr32l(&srvrc.bout, ident);
839     dbuf_putu16l(&srvrc.bout, unit);
840   }
841 }
842
843 static void remote_ebench(struct tvec_output *o,
844                           const char *ident, unsigned unit,
845                           const struct bench_timing *t)
846 {
847   QUEUEPK(&srvtv, &srvrc, 0, TVPK_EBENCH) {
848     dbuf_putstr32l(&srvrc.bout, ident);
849     dbuf_putu16l(&srvrc.bout, unit);
850     if (!t || !(t->f&BTF_ANY))
851       dbuf_putu16l(&srvrc.bout, 0);
852     else {
853       dbuf_putu16l(&srvrc.bout, t->f);
854       dbuf_putf64l(&srvrc.bout, t->n);
855       if (t->f&BTF_TIMEOK) dbuf_putf64l(&srvrc.bout, t->t);
856       if (t->f&BTF_CYOK) dbuf_putf64l(&srvrc.bout, t->cy);
857     }
858   }
859 }
860
861 static void remote_report(struct tvec_output *o, unsigned level,
862                           const char *msg, va_list *ap)
863 {
864   QUEUEPK(&srvtv, &srvrc, 0, TVPK_REPORT) {
865     dbuf_putu16l(&srvrc.bout, level);
866     dbuf_vputstrf16l(&srvrc.bout, msg, ap);
867   } else {
868     fprintf(stderr, "%s %s: ", QUIS, tvec_strlevel(level));
869     vfprintf(stderr, msg, *ap);
870     fputc('\n', stderr);
871   }
872 }
873
874 static void remote_bsession(struct tvec_output *o, struct tvec_state *tv)
875   { ; }
876 static int remote_esession(struct tvec_output *o)
877   { return (srvtv.f&TVSF_ERROR ? 2 : 0); }
878 static void remote_destroy(struct tvec_output *o)
879   { ; }
880 static void remote_etest(struct tvec_output *o, unsigned outcome)
881   { ; }
882
883 static void remote_bgroup(struct tvec_output *o)
884   { assert(!"remote_bgroup"); }
885 static void remote_egroup(struct tvec_output *o)
886   { assert(!"remote_egroup"); }
887 static void remote_btest(struct tvec_output *o)
888   { assert(!"remote_btest"); }
889
890 static const struct tvec_outops remote_ops = {
891   remote_bsession, remote_esession,
892   remote_bgroup, remote_skipgroup, remote_egroup,
893   remote_btest, remote_skip, remote_fail, remote_dumpreg, remote_etest,
894   remote_bbench, remote_ebench,
895   remote_report,
896   remote_destroy
897 };
898
899 /*----- Client ------------------------------------------------------------*/
900
901 #define TVXF_VALMASK 0x0fffu
902 #define TVXF_SIG 0x1000u
903 #define TVXF_CAUSEMASK 0xe000u
904 #define TVXST_RUN 0x0000u
905 #define TVXST_EXIT 0x2000u
906 #define TVXST_KILL 0x4000u
907 #define TVXST_CONT 0x6000u
908 #define TVXST_STOP 0x8000u
909 #define TVXST_DISCONN 0xa000u
910 #define TVXST_UNK 0xc000u
911 #define TVXST_ERR 0xe000u
912
913 static const struct tvec_flag exit_flags[] = {
914   /*
915     ;;; The signal name table is very boring to type.  To make life less
916     ;;; awful, put the signal names in this list and evaluate the code to
917     ;;; get Emacs to regenerate it.
918
919     (let ((signals '(HUP INT QUIT ILL TRAP ABRT IOT EMT FPE KILL BUS SEGV SYS
920                          PIPE ALRM TERM URG STOP TSTP CONT CHLD CLD TTIN TTOU
921                          POLL IO TIN XCPU XFSZ VTALRM PROF WINCH USR1 USR2
922                          STKFLT INFO PWR THR LWP LIBRT LOST)))
923       (save-excursion
924         (goto-char (point-min))
925         (search-forward (concat "***" "BEGIN siglist" "***"))
926         (beginning-of-line 2)
927         (delete-region (point)
928                        (progn
929                          (search-forward "***END***")
930                          (beginning-of-line)
931                          (point)))
932         (dolist (sig signals)
933           (insert (format "#ifdef SIG%s\n  { \"SIG%s\", TVXF_VALMASK | TVXF_SIG, SIG%s | TVXF_SIG },\n#endif\n"
934                           sig sig sig)))))
935   */
936
937   /***BEGIN siglist***/
938 #ifdef SIGHUP
939   { "SIGHUP", TVXF_VALMASK | TVXF_SIG, SIGHUP | TVXF_SIG },
940 #endif
941 #ifdef SIGINT
942   { "SIGINT", TVXF_VALMASK | TVXF_SIG, SIGINT | TVXF_SIG },
943 #endif
944 #ifdef SIGQUIT
945   { "SIGQUIT", TVXF_VALMASK | TVXF_SIG, SIGQUIT | TVXF_SIG },
946 #endif
947 #ifdef SIGILL
948   { "SIGILL", TVXF_VALMASK | TVXF_SIG, SIGILL | TVXF_SIG },
949 #endif
950 #ifdef SIGTRAP
951   { "SIGTRAP", TVXF_VALMASK | TVXF_SIG, SIGTRAP | TVXF_SIG },
952 #endif
953 #ifdef SIGABRT
954   { "SIGABRT", TVXF_VALMASK | TVXF_SIG, SIGABRT | TVXF_SIG },
955 #endif
956 #ifdef SIGIOT
957   { "SIGIOT", TVXF_VALMASK | TVXF_SIG, SIGIOT | TVXF_SIG },
958 #endif
959 #ifdef SIGEMT
960   { "SIGEMT", TVXF_VALMASK | TVXF_SIG, SIGEMT | TVXF_SIG },
961 #endif
962 #ifdef SIGFPE
963   { "SIGFPE", TVXF_VALMASK | TVXF_SIG, SIGFPE | TVXF_SIG },
964 #endif
965 #ifdef SIGKILL
966   { "SIGKILL", TVXF_VALMASK | TVXF_SIG, SIGKILL | TVXF_SIG },
967 #endif
968 #ifdef SIGBUS
969   { "SIGBUS", TVXF_VALMASK | TVXF_SIG, SIGBUS | TVXF_SIG },
970 #endif
971 #ifdef SIGSEGV
972   { "SIGSEGV", TVXF_VALMASK | TVXF_SIG, SIGSEGV | TVXF_SIG },
973 #endif
974 #ifdef SIGSYS
975   { "SIGSYS", TVXF_VALMASK | TVXF_SIG, SIGSYS | TVXF_SIG },
976 #endif
977 #ifdef SIGPIPE
978   { "SIGPIPE", TVXF_VALMASK | TVXF_SIG, SIGPIPE | TVXF_SIG },
979 #endif
980 #ifdef SIGALRM
981   { "SIGALRM", TVXF_VALMASK | TVXF_SIG, SIGALRM | TVXF_SIG },
982 #endif
983 #ifdef SIGTERM
984   { "SIGTERM", TVXF_VALMASK | TVXF_SIG, SIGTERM | TVXF_SIG },
985 #endif
986 #ifdef SIGURG
987   { "SIGURG", TVXF_VALMASK | TVXF_SIG, SIGURG | TVXF_SIG },
988 #endif
989 #ifdef SIGSTOP
990   { "SIGSTOP", TVXF_VALMASK | TVXF_SIG, SIGSTOP | TVXF_SIG },
991 #endif
992 #ifdef SIGTSTP
993   { "SIGTSTP", TVXF_VALMASK | TVXF_SIG, SIGTSTP | TVXF_SIG },
994 #endif
995 #ifdef SIGCONT
996   { "SIGCONT", TVXF_VALMASK | TVXF_SIG, SIGCONT | TVXF_SIG },
997 #endif
998 #ifdef SIGCHLD
999   { "SIGCHLD", TVXF_VALMASK | TVXF_SIG, SIGCHLD | TVXF_SIG },
1000 #endif
1001 #ifdef SIGCLD
1002   { "SIGCLD", TVXF_VALMASK | TVXF_SIG, SIGCLD | TVXF_SIG },
1003 #endif
1004 #ifdef SIGTTIN
1005   { "SIGTTIN", TVXF_VALMASK | TVXF_SIG, SIGTTIN | TVXF_SIG },
1006 #endif
1007 #ifdef SIGTTOU
1008   { "SIGTTOU", TVXF_VALMASK | TVXF_SIG, SIGTTOU | TVXF_SIG },
1009 #endif
1010 #ifdef SIGPOLL
1011   { "SIGPOLL", TVXF_VALMASK | TVXF_SIG, SIGPOLL | TVXF_SIG },
1012 #endif
1013 #ifdef SIGIO
1014   { "SIGIO", TVXF_VALMASK | TVXF_SIG, SIGIO | TVXF_SIG },
1015 #endif
1016 #ifdef SIGTIN
1017   { "SIGTIN", TVXF_VALMASK | TVXF_SIG, SIGTIN | TVXF_SIG },
1018 #endif
1019 #ifdef SIGXCPU
1020   { "SIGXCPU", TVXF_VALMASK | TVXF_SIG, SIGXCPU | TVXF_SIG },
1021 #endif
1022 #ifdef SIGXFSZ
1023   { "SIGXFSZ", TVXF_VALMASK | TVXF_SIG, SIGXFSZ | TVXF_SIG },
1024 #endif
1025 #ifdef SIGVTALRM
1026   { "SIGVTALRM", TVXF_VALMASK | TVXF_SIG, SIGVTALRM | TVXF_SIG },
1027 #endif
1028 #ifdef SIGPROF
1029   { "SIGPROF", TVXF_VALMASK | TVXF_SIG, SIGPROF | TVXF_SIG },
1030 #endif
1031 #ifdef SIGWINCH
1032   { "SIGWINCH", TVXF_VALMASK | TVXF_SIG, SIGWINCH | TVXF_SIG },
1033 #endif
1034 #ifdef SIGUSR1
1035   { "SIGUSR1", TVXF_VALMASK | TVXF_SIG, SIGUSR1 | TVXF_SIG },
1036 #endif
1037 #ifdef SIGUSR2
1038   { "SIGUSR2", TVXF_VALMASK | TVXF_SIG, SIGUSR2 | TVXF_SIG },
1039 #endif
1040 #ifdef SIGSTKFLT
1041   { "SIGSTKFLT", TVXF_VALMASK | TVXF_SIG, SIGSTKFLT | TVXF_SIG },
1042 #endif
1043 #ifdef SIGINFO
1044   { "SIGINFO", TVXF_VALMASK | TVXF_SIG, SIGINFO | TVXF_SIG },
1045 #endif
1046 #ifdef SIGPWR
1047   { "SIGPWR", TVXF_VALMASK | TVXF_SIG, SIGPWR | TVXF_SIG },
1048 #endif
1049 #ifdef SIGTHR
1050   { "SIGTHR", TVXF_VALMASK | TVXF_SIG, SIGTHR | TVXF_SIG },
1051 #endif
1052 #ifdef SIGLWP
1053   { "SIGLWP", TVXF_VALMASK | TVXF_SIG, SIGLWP | TVXF_SIG },
1054 #endif
1055 #ifdef SIGLIBRT
1056   { "SIGLIBRT", TVXF_VALMASK | TVXF_SIG, SIGLIBRT | TVXF_SIG },
1057 #endif
1058 #ifdef SIGLOST
1059   { "SIGLOST", TVXF_VALMASK | TVXF_SIG, SIGLOST | TVXF_SIG },
1060 #endif
1061   /***END***/
1062
1063   { "signal",           TVXF_SIG,               TVXF_SIG },
1064
1065   { "running",          TVXF_CAUSEMASK,         TVXST_RUN },
1066   { "exited",           TVXF_CAUSEMASK,         TVXST_EXIT },
1067   { "killed",           TVXF_CAUSEMASK,         TVXST_KILL },
1068   { "stopped",          TVXF_CAUSEMASK,         TVXST_STOP },
1069   { "continued",        TVXF_CAUSEMASK,         TVXST_CONT },
1070   { "disconnected",     TVXF_CAUSEMASK,         TVXST_DISCONN },
1071   { "unknown",          TVXF_CAUSEMASK,         TVXST_UNK },
1072   { "error",            TVXF_CAUSEMASK,         TVXST_ERR },
1073
1074   TVEC_ENDFLAGS
1075 };
1076
1077 static const struct tvec_flaginfo exit_flaginfo =
1078   { "exit-status", exit_flags, &tvrange_uint };
1079 static const struct tvec_regdef exit_regdef =
1080   { "@exit", 0, &tvty_flags, 0, { &exit_flaginfo } };
1081
1082 static const struct tvec_regdef progress_regdef =
1083   { "@progress", 0, &tvty_string, 0 };
1084
1085 static const struct tvec_uassoc reconn_assocs[] = {
1086   { "on-demand",        TVRCN_DEMAND },
1087   { "force",            TVRCN_FORCE },
1088   { "skip",             TVRCN_SKIP },
1089   TVEC_ENDENUM
1090 };
1091
1092 enum {
1093   CONN_BROKEN = -2,                     /* previously broken */
1094   CONN_FAILED = -1,                     /* attempt freshly failed */
1095   CONN_ESTABLISHED = 0,                 /* previously established */
1096   CONN_FRESH = 1                        /* freshly connected */
1097 };
1098
1099 static const struct tvec_uenuminfo reconn_enuminfo =
1100   { "remote-reconnection", reconn_assocs, &tvrange_uint };
1101 static const struct tvec_regdef reconn_regdef =
1102   { "@reconnect", 0, &tvty_uenum, 0, { &reconn_enuminfo } };
1103
1104 static int handle_packets(struct tvec_state *tv, struct tvec_remotectx *r,
1105                           unsigned f, uint16 end, buf *b_out)
1106 {
1107   struct tvec_output *o = tv->output;
1108   uint16 pk, u, v;
1109   const char *p; size_t n;
1110   dstr d = DSTR_INIT;
1111   buf *b = b_out;
1112   const struct tvec_regdef *rd;
1113   struct bench_timing bt;
1114   struct tvec_reg *reg = 0;
1115   unsigned i;
1116   int rc;
1117
1118   for (;;) {
1119     rc = remote_recv(tv, &r->rc, f, b); if (rc) goto end;
1120     if (buf_getu16l(b, &pk)) goto bad;
1121     if (pk == end) break;
1122
1123     switch (pk) {
1124
1125       case TVPK_PROGRESS:
1126         p = buf_getmem16l(b, &n); if (!p) goto bad;
1127         if (BLEFT(b)) goto bad;
1128
1129         DRESET(&r->progress); DPUTM(&r->progress, p, n); DPUTZ(&r->progress);
1130         break;
1131
1132       case TVPK_REPORT:
1133         if (buf_getu16l(b, &u)) goto bad;
1134         p = buf_getmem16l(b, &n); if (!p) goto bad;
1135         if (BLEFT(b)) goto bad;
1136
1137         DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1138         tvec_report(tv, u, "%s", d.buf);
1139         break;
1140
1141       case TVPK_SKIPGRP:
1142         p = buf_getmem16l(b, &n); if (!p) goto bad;
1143         DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1144         if (BLEFT(b)) goto bad;
1145
1146         tvec_skipgroup(tv, "%s", d.buf);
1147         break;
1148
1149       case TVPK_SKIP:
1150         if (!(tv->f&TVSF_ACTIVE)) {
1151           rc = ioerr(tv, &r->rc, "test `%s' not active", tv->test->name);
1152           goto end;
1153         }
1154
1155         p = buf_getmem16l(b, &n); if (!p) goto bad;
1156         if (BLEFT(b)) goto bad;
1157
1158         DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1159         tvec_skip(tv, "%s", d.buf);
1160         break;
1161
1162       case TVPK_FAIL:
1163         if (!(tv->f&TVSF_ACTIVE) &&
1164             ((tv->f&TVSF_OUTMASK) != (TVOUT_LOSE << TVSF_OUTSHIFT))) {
1165           rc = ioerr(tv, &r->rc, "test `%s' not active or failing",
1166                      tv->test->name);
1167           goto end;
1168         }
1169
1170         rc = buf_getbyte(b); if (rc < 0) goto bad;
1171         if (rc) { p = buf_getmem16l(b, &n); if (!p) goto bad; }
1172         else p = 0;
1173         if (BLEFT(b)) goto bad;
1174
1175         if (!p)
1176           tvec_fail(tv, 0);
1177         else {
1178           DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1179           tvec_fail(tv, "%s", d.buf);
1180         }
1181         break;
1182
1183       case TVPK_DUMPREG:
1184         if (buf_getu16l(b, &u) || buf_getu16l(b, &v)) goto bad;
1185         for (rd = tv->test->regs, i = 0; rd->name; rd++, i++)
1186           if (i == u) goto found_reg;
1187         rc = ioerr(tv, &r->rc,
1188                    "register definition %u out of range for test `%s'",
1189                    u, tv->test->name);
1190         goto end;
1191       found_reg:
1192         if (v >= TVRD_LIMIT) {
1193           rc = ioerr(tv, &r->rc, "register disposition %u out of range", v);
1194           goto end;
1195         }
1196
1197         rc = buf_getbyte(b); if (rc < 0) goto bad;
1198         if (!rc)
1199           tvec_dumpreg(tv, v, 0, rd);
1200         else {
1201           if (!reg) reg = xmalloc(tv->regsz);
1202           rd->ty->init(&reg->v, rd);
1203           rc = rd->ty->frombuf(b, &reg->v, rd);
1204           if (!rc) tvec_dumpreg(tv, v, &reg->v, rd);
1205           rd->ty->release(&reg->v, rd);
1206           if (rc) goto bad;
1207         }
1208         if (BLEFT(b)) goto bad;
1209         break;
1210
1211       case TVPK_BBENCH:
1212         p = buf_getmem32l(b, &n); if (!p) goto bad;
1213         if (buf_getu16l(b, &u)) goto bad;
1214         if (BLEFT(b)) goto bad;
1215         if (u >= TVBU_LIMIT) {
1216           rc = ioerr(tv, &r->rc, "unit code %u out of range", u);
1217           goto end;
1218         }
1219
1220         DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1221         o->ops->bbench(o, d.buf, u);
1222         break;
1223
1224       case TVPK_EBENCH:
1225         p = buf_getmem32l(b, &n); if (!p) goto bad;
1226         if (buf_getu16l(b, &u) || buf_getu16l(b, &v)) goto bad;
1227         if (u >= TVBU_LIMIT) {
1228           rc = ioerr(tv, &r->rc, "unit code %u out of range", u);
1229           goto end;
1230         }
1231         if ((v&BTF_ANY) && buf_getf64l(b, &bt.n)) goto bad;
1232         if ((v&BTF_TIMEOK) && buf_getf64l(b, &bt.t)) goto bad;
1233         if ((v&BTF_CYOK) && buf_getf64l(b, &bt.cy)) goto bad;
1234         if (BLEFT(b)) goto bad;
1235
1236         DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1237         o->ops->ebench(o, d.buf, u, v&BTF_ANY ? &bt : 0);
1238         break;
1239
1240       default:
1241         rc = ioerr(tv, &r->rc, "unexpected packet type 0x%04x", pk);
1242         goto end;
1243     }
1244   }
1245
1246   rc = RECV_OK;
1247 end:
1248   DDESTROY(&d);
1249   xfree(reg);
1250   return (rc);
1251 bad:
1252   rc = malformed(tv, &r->rc); goto end;
1253 }
1254
1255 static void reap_kid(struct tvec_state *tv, struct tvec_remotectx *r)
1256 {
1257   pid_t kid;
1258   int st;
1259
1260   if (!r->kid)
1261     { r->exit = TVXST_DISCONN; r->kid = -1; }
1262   else if (r->kid > 0) {
1263     kid = waitpid(r->kid, &st, 0);
1264     if (kid < 0) {
1265       tvec_notice(tv, "failed to wait for remote child: %s",
1266                   strerror(errno));
1267       r->exit = TVXST_ERR;
1268     } else if (!kid) {
1269       tvec_notice(tv, "remote child vanished without a trace");
1270       r->exit = TVXST_ERR;
1271     } else if (WIFCONTINUED(st))
1272       r->exit = TVXST_CONT;
1273     else if (WIFSIGNALED(st))
1274       r->exit = TVXST_KILL | TVXF_SIG | WTERMSIG(st);
1275     else if (WIFSTOPPED(st))
1276       r->exit = TVXST_STOP | TVXF_SIG | WSTOPSIG(st);
1277     else if (WIFEXITED(st))
1278       r->exit = TVXST_EXIT | WEXITSTATUS(st);
1279     else {
1280       tvec_notice(tv, "remote child died with unknown status 0x%04x",
1281                   (unsigned)st);
1282       r->exit = TVXST_UNK;
1283     }
1284     r->kid = -1;
1285   }
1286 }
1287
1288 static void report_errline(char *p, size_t n, void *ctx)
1289 {
1290   struct tvec_remotectx *r = ctx;
1291   struct tvec_state *tv = r->tv;
1292
1293   if (p && !(r->rc.f&TVRF_MUFFLE))
1294     tvec_notice(tv, "child process stderr: %s", p);
1295 }
1296
1297 #define ERF_SILENT 0x0001u
1298 #define ERF_CLOSE 0x0002u
1299 static int drain_errfd(struct tvec_state *tv, struct tvec_remotectx *r,
1300                        unsigned f)
1301 {
1302   char *p; size_t sz;
1303   ssize_t n;
1304   int rc;
1305
1306   if (r->errfd < 0) { rc = 0; goto end; }
1307   if (f&ERF_SILENT) r->rc.f |= TVRF_MUFFLE;
1308   else r->rc.f &= ~TVRF_MUFFLE;
1309   if (fdflags(r->errfd, O_NONBLOCK, f&ERF_CLOSE ? 0 : O_NONBLOCK, 0, 0)) {
1310     rc = ioerr(tv, &r->rc, "failed to %s error non-blocking flag",
1311                f&ERF_CLOSE ? "clear" : "set");
1312     goto end;
1313   }
1314
1315   for (;;) {
1316     sz = lbuf_free(&r->errbuf, &p);
1317     n = read(r->errfd, p, sz);
1318       if (!n) break;
1319       if (n < 0) {
1320         if (errno == EINTR) continue;
1321         if (!(f&ERF_CLOSE) && (errno == EWOULDBLOCK || errno == EAGAIN))
1322           break;
1323         rc = ioerr(tv, &r->rc, "failed to read child stderr: %s",
1324                    strerror(errno));
1325         goto end;
1326       }
1327     lbuf_flush(&r->errbuf, p, n);
1328   }
1329   rc = 0;
1330 end:
1331   if (f&ERF_CLOSE) {
1332     lbuf_close(&r->errbuf);
1333     close(r->errfd); r->errfd = -1;
1334   }
1335   return (rc);
1336 }
1337
1338 #define DCF_KILL 0x0100u
1339 static void disconnect_remote(struct tvec_state *tv,
1340                               struct tvec_remotectx *r, unsigned f)
1341 {
1342   if (r->kid > 0 && (f&DCF_KILL)) kill(r->kid, SIGTERM);
1343   close_comms(&r->rc);
1344   drain_errfd(tv, r, f | ERF_CLOSE); reap_kid(tv, r);
1345 }
1346
1347 static int connect_remote(struct tvec_state *tv, struct tvec_remotectx *r)
1348 {
1349   const struct tvec_remoteenv *re = r->re;
1350   pid_t kid = 0;
1351   buf b;
1352   uint16 v;
1353   int infd = -1, outfd = -1, errfd = -1, rc;
1354
1355   DRESET(&r->progress); DPUTS(&r->progress, "%INIT");
1356   if (r->kid >= 0) { rc = 0; goto end; }
1357   if (re->r.connect(&kid, &infd, &outfd, &errfd, tv, re))
1358     { rc = -1; goto end; }
1359   setup_comms(&r->rc, infd, outfd); r->kid = kid; r->errfd = errfd;
1360   lbuf_init(&r->errbuf, report_errline, r);
1361   r->exit = TVXST_RUN; r->rc.f &= ~TVRF_BROKEN;
1362
1363   QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_VER) {
1364     dbuf_putu16l(&r->rc.bout, 0);
1365     dbuf_putu16l(&r->rc.bout, 0);
1366   } else { rc = -1; goto end; }
1367
1368   if (handle_packets(tv, r, 0, TVPK_VER | TVPF_ACK, &b))
1369     { rc = -1; goto end; }
1370   if (buf_getu16l(&b, &v)) goto bad;
1371   if (BLEFT(&b)) { rc = malformed(tv, &r->rc); goto end; }
1372   if (v) {
1373     rc = ioerr(tv, &r->rc, "protocol version %u not supported", v);
1374     goto end;
1375   }
1376
1377   QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_BGROUP)
1378     dbuf_putstr16l(&r->rc.bout, tv->test->name);
1379   else { rc = -1; goto end; }
1380   if (handle_packets(tv, r, 0, TVPK_BGROUP | TVPF_ACK, &b))
1381     { rc = -1; goto end; }
1382   if (BLEFT(&b)) { rc = malformed(tv, &r->rc); goto end; }
1383   r->ver = v; rc = 0;
1384 end:
1385   if (rc) disconnect_remote(tv, r, DCF_KILL);
1386   return (rc);
1387 bad:
1388   rc = malformed(tv, &r->rc); goto end;
1389 }
1390
1391 static int check_comms(struct tvec_state *tv, struct tvec_remotectx *r)
1392 {
1393   if (r->kid < 0)
1394     return (CONN_BROKEN);
1395   else if (r->rc.f&TVRF_BROKEN)
1396     { disconnect_remote(tv, r, DCF_KILL); return (CONN_FAILED); }
1397   else
1398     return (CONN_ESTABLISHED);
1399 }
1400
1401 static int try_reconnect(struct tvec_state *tv, struct tvec_remotectx *r)
1402 {
1403   int rc;
1404
1405   switch (r->rc.f&TVRF_RCNMASK) {
1406     case TVRCN_DEMAND:
1407       rc = check_comms(tv, r);
1408       if (rc < CONN_ESTABLISHED) {
1409         close_comms(&r->rc);
1410         if (connect_remote(tv, r)) rc = CONN_FAILED;
1411         else rc = CONN_FRESH;
1412       }
1413       break;
1414     case TVRCN_FORCE:
1415       disconnect_remote(tv, r, DCF_KILL);
1416       if (connect_remote(tv, r)) rc = CONN_FAILED;
1417       else rc = CONN_FRESH;
1418       break;
1419     case TVRCN_SKIP:
1420       rc = check_comms(tv, r);
1421       break;
1422     default:
1423       abort();
1424   }
1425   return (rc);
1426 }
1427
1428 static void reset_vars(struct tvec_remotectx *r)
1429 {
1430   r->exwant = TVXST_RUN;
1431   r->rc.f = (r->rc.f&~(TVRF_RCNMASK | TVRF_SETMASK)) | TVRCN_DEMAND;
1432   DRESET(&r->prgwant); DPUTS(&r->prgwant, "%DONE");
1433 }
1434
1435 void tvec_remotesetup(struct tvec_state *tv, const struct tvec_env *env,
1436                       void *pctx, void *ctx)
1437 {
1438   struct tvec_remotectx *r = ctx;
1439   const struct tvec_remoteenv *re = (const struct tvec_remoteenv *)env;
1440
1441   assert(!re->r.env || tv->test->env == &re->_env);
1442
1443   r->tv = tv;
1444   init_comms(&r->rc);
1445   r->re = re; r->kid = -1;
1446   DCREATE(&r->prgwant); DCREATE(&r->progress);
1447   if (connect_remote(tv, r))
1448     tvec_skipgroup(tv, "failed to connect to test backend");
1449   reset_vars(r);
1450 }
1451
1452 int tvec_remoteset(struct tvec_state *tv, const char *var, void *ctx)
1453 {
1454   struct tvec_remotectx *r = ctx;
1455   union tvec_regval rv;
1456   int rc;
1457
1458   if (STRCMP(var, ==, "@exit")) {
1459     if (r->rc.f&TVRF_SETEXIT) { rc = tvec_dupreg(tv, var); goto end; }
1460     if (tvty_flags.parse(&rv, &exit_regdef, tv)) { rc = -1; goto end; }
1461     r->exwant = rv.u; r->rc.f |= TVRF_SETEXIT; rc = 1;
1462   } else if (STRCMP(var, ==, "@progress")) {
1463     if (r->rc.f&TVRF_SETPRG) { rc = tvec_dupreg(tv, var); goto end; }
1464     tvty_string.init(&rv, &progress_regdef);
1465     rc = tvty_string.parse(&rv, &progress_regdef, tv);
1466     if (!rc) {
1467       DRESET(&r->prgwant); DPUTM(&r->prgwant, rv.str.p, rv.str.sz);
1468       r->rc.f |= TVRF_SETPRG;
1469     }
1470     tvty_string.release(&rv, &progress_regdef);
1471     if (rc) { rc = -1; goto end; }
1472     rc = 1;
1473   } else if (STRCMP(var, ==, "@reconnect")) {
1474     if (r->rc.f&TVRF_SETRCN) { rc = tvec_dupreg(tv, var); goto end; }
1475     if (tvty_uenum.parse(&rv, &reconn_regdef, tv)) { rc = -1; goto end; }
1476     r->rc.f = (r->rc.f&~TVRF_RCNMASK) | (rv.u&TVRF_RCNMASK) | TVRF_SETRCN;
1477     rc = 1;
1478   } else
1479     rc = 0;
1480
1481 end:
1482   return (rc);
1483 }
1484
1485 void tvec_remoteafter(struct tvec_state *tv, void *ctx)
1486 {
1487   struct tvec_remotectx *r = ctx;
1488
1489   reset_vars(r);
1490 }
1491
1492 void tvec_remoterun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
1493 {
1494   struct tvec_remotectx *r = ctx;
1495   union tvec_regval rv;
1496   unsigned f = 0;
1497 #define f_exit 1u
1498 #define f_progress 2u
1499 #define f_fail 4u
1500   buf b;
1501   int rc;
1502
1503   switch (try_reconnect(tv, r)) {
1504     case CONN_FAILED:
1505       tvec_skip(tv, "failed to connect to test backend"); return;
1506     case CONN_BROKEN:
1507       tvec_skip(tv, "no connection"); return;
1508   }
1509
1510   DRESET(&r->progress); DPUTS(&r->progress, "%IDLE");
1511   QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_TEST)
1512     tvec_serialize(tv->in, DBUF_BUF(&r->rc.bout),
1513                    tv->test->regs, tv->nreg, tv->regsz);
1514   else { rc = -1; goto end; }
1515   rc = handle_packets(tv, r, RCVF_ALLOWEOF, TVPK_TEST | TVPF_ACK, &b);
1516   switch (rc) {
1517     case RECV_FAIL:
1518       goto end;
1519     case RECV_EOF:
1520       reap_kid(tv, r);
1521       /* fall through */
1522     case RECV_OK:
1523       if (r->exit != r->exwant) f |= f_exit;
1524       if (r->progress.len != r->prgwant.len ||
1525           MEMCMP(r->progress.buf, !=, r->prgwant.buf, r->progress.len))
1526         f |= f_progress;
1527       if (f && (tv->f&TVSF_ACTIVE))
1528         { tvec_fail(tv, 0); tvec_mismatch(tv, TVMF_IN); }
1529       if (!(tv->f&TVSF_ACTIVE) &&
1530           (tv->f&TVSF_OUTMASK) == (TVOUT_LOSE << TVSF_OUTSHIFT)) {
1531         f |= f_fail;
1532
1533         rv.u = r->exit;
1534         tvec_dumpreg(tv, f&f_exit ? TVRD_FOUND : TVRD_MATCH,
1535                      &rv, &exit_regdef);
1536         if (f&f_exit) {
1537           rv.u = r->exwant;
1538           tvec_dumpreg(tv, TVRD_EXPECT, &rv, &exit_regdef);
1539         }
1540
1541         rv.str.p = r->progress.buf; rv.str.sz = r->progress.len;
1542         tvec_dumpreg(tv, f&f_progress ? TVRD_FOUND : TVRD_MATCH,
1543                      &rv, &progress_regdef);
1544         if (f&f_progress) {
1545           rv.str.p = r->prgwant.buf; rv.str.sz = r->prgwant.len;
1546           tvec_dumpreg(tv, TVRD_EXPECT, &rv, &progress_regdef);
1547         }
1548       }
1549
1550       if (rc == RECV_EOF)
1551         disconnect_remote(tv, r, f ? 0 : ERF_SILENT);
1552       break;
1553   }
1554
1555 end:
1556   if (rc) {
1557     if ((tv->f&TVSF_ACTIVE) && f)
1558       tvec_skip(tv, "remote test runner communications failed");
1559     disconnect_remote(tv, r, 0);
1560   }
1561
1562 #undef f_exit
1563 #undef f_progress
1564 #undef f_fail
1565 }
1566
1567 void tvec_remoteteardown(struct tvec_state *tv, void *ctx)
1568 {
1569   struct tvec_remotectx *r = ctx;
1570   buf b;
1571
1572   if (r->rc.outfd >= 0) {
1573     QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_EGROUP);
1574     if (!handle_packets(tv, r, RCVF_ALLOWEOF, TVPK_EGROUP | TVPF_ACK, &b))
1575       if (BLEFT(&b)) malformed(tv, &r->rc);
1576   }
1577   disconnect_remote(tv, r, 0); release_comms(&r->rc);
1578   DDESTROY(&r->prgwant); DDESTROY(&r->progress);
1579 }
1580
1581 /*----- Connectors --------------------------------------------------------*/
1582
1583 static int fork_common(pid_t *kid_out, int *infd_out, int *outfd_out,
1584                        int *errfd_out, struct tvec_state *tv)
1585 {
1586   int p0[2] = { -1, -1 }, p1[2] = { -1, -1 }, pe[2] = { -1, -1 };
1587   pid_t kid = -1;
1588   int rc;
1589
1590   if (pipe(p0) || pipe(p1) || pipe(pe) ||
1591       fdflags(p0[1], 0, 0, FD_CLOEXEC, FD_CLOEXEC) ||
1592       fdflags(p1[0], 0, 0, FD_CLOEXEC, FD_CLOEXEC) ||
1593       fdflags(pe[0], 0, 0, FD_CLOEXEC, FD_CLOEXEC)) {
1594     tvec_error(tv, "pipe failed: %s", strerror(errno));
1595     rc = -1; goto end;
1596   }
1597
1598   fflush(0);
1599
1600   kid = fork();
1601   if (kid < 0) {
1602     tvec_error(tv, "fork failed: %s", strerror(errno));
1603     rc = -1; goto end;
1604   }
1605
1606   if (!kid) {
1607     *kid_out = 0;
1608     *infd_out = p0[0]; p0[0] = -1;
1609     *outfd_out = p1[1]; p1[1] = -1;
1610     if (pe[1] != STDERR_FILENO && dup2(pe[1], STDERR_FILENO) < 0) {
1611       fprintf(stderr, "failed to establish child stderr: %s",
1612               strerror(errno));
1613       exit(127);
1614     }
1615   } else {
1616     *kid_out = kid; kid = -1;
1617     *infd_out = p1[0]; p1[0] = -1;
1618     *outfd_out = p0[1]; p0[1] = -1;
1619     *errfd_out = pe[0]; pe[0] = -1;
1620   }
1621
1622   rc = 0;
1623 end:
1624   if (p0[0] >= 0) close(p0[0]);
1625   if (p0[1] >= 0) close(p0[1]);
1626   if (p1[0] >= 0) close(p1[0]);
1627   if (p1[1] >= 0) close(p1[1]);
1628   if (pe[0] >= 0) close(pe[0]);
1629   if (pe[1] >= 0) close(pe[1]);
1630   return (rc);
1631 }
1632
1633 int tvec_fork(pid_t *kid_out, int *infd_out, int *outfd_out, int *errfd_out,
1634               struct tvec_state *tv, const struct tvec_remoteenv *env)
1635 {
1636   struct tvec_config config;
1637   const struct tvec_remotefork *rf = (const struct tvec_remotefork *)env;
1638   pid_t kid = -1;
1639   int infd = -1, outfd = -1, errfd = -1;
1640   int rc;
1641
1642   if (fork_common(&kid, &infd, &outfd, &errfd, tv)) { rc = -1; goto end; }
1643   if (!kid) {
1644     if (tv->fp) fclose(tv->fp);
1645     config.tests = rf->f.tests ? rf->f.tests : tv->tests;
1646     config.nrout = tv->nrout; config.nreg = tv->nreg;
1647     config.regsz = tv->regsz;
1648     _exit(tvec_remoteserver(infd, outfd, &config));
1649   }
1650
1651   *kid_out = kid; *infd_out = infd; *outfd_out = outfd; *errfd_out = errfd;
1652   rc = 0;
1653 end:
1654   return (rc);
1655 }
1656
1657 int tvec_exec(pid_t *kid_out, int *infd_out, int *outfd_out, int *errfd_out,
1658               struct tvec_state *tv, const struct tvec_remoteenv *env)
1659 {
1660   const struct tvec_remoteexec *rx = (const struct tvec_remoteexec *)env;
1661   pid_t kid = -1;
1662   int infd = -1, outfd = -1, errfd = -1;
1663   mdup_fd v[2];
1664   int rc;
1665
1666   if (fork_common(&kid, &infd, &outfd, &errfd, tv)) { rc = -1; goto end; }
1667   if (!kid) {
1668     v[0].cur = infd; v[0].want = STDIN_FILENO;
1669     v[1].cur = outfd; v[1].want = STDOUT_FILENO;
1670     if (mdup(v, 2)) {
1671       fprintf(stderr, "failed to establish standard file descriptors: %s",
1672               strerror(errno));
1673       exit(127);
1674     }
1675     execvp(rx->x.args[0], (/*uncosnt*/ char *const *)rx->x.args);
1676     fprintf(stderr, "failed to invoke test runner: %s", strerror(errno));
1677     exit(127);
1678   }
1679
1680   *kid_out = kid; *infd_out = infd; *outfd_out = outfd; *errfd_out = errfd;
1681   rc = 0;
1682 end:
1683   return (rc);
1684 }
1685
1686 /*----- That's all, folks -------------------------------------------------*/