chiark / gitweb /
@@@ fltfmt mess
[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 "growbuf.h"
48 #include "lbuf.h"
49 #include "mdup.h"
50 #include "quis.h"
51
52 #include "tvec.h"
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"
58
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
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
70
71 #if CLANG_VERSION_P(3, 1)
72 #  pragma clang diagnostic ignored "-Wdangling-else"
73 #endif
74
75 /*----- Basic I/O ---------------------------------------------------------*/
76
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
89 static void init_comms(struct tvec_remotecomms *rc)
90 {
91   rc->bin = 0; rc->binsz = 0; DBCREATE(&rc->bout);
92   rc->infd = rc->outfd = -1; rc->f = 0;
93 }
94
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
107 static void close_comms(struct tvec_remotecomms *rc)
108 {
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;
116 }
117
118 /* --- @release_comms@ --- *
119  *
120  * Arguments:   @struct tvec_state *tv@ = test-vector state
121  *              @struct tvec_remotecomms *rc@ = communication state
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
130 static void release_comms(struct tvec_state *tv, struct tvec_remotecomms *rc)
131   { close_comms(rc); x_free(tv->a, rc->bin); DBDESTROY(&rc->bout); }
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  */
144
145 static void setup_comms(struct tvec_remotecomms *rc, int infd, int outfd)
146 {
147   rc->infd = infd; rc->outfd = outfd;
148   rc->binoff = rc->binlen = 0;
149   rc->f &= ~0xffu;
150 }
151
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
164 static PRINTF_LIKE(3, 4)
165   int ioerr(struct tvec_state *tv, struct tvec_remotecomms *rc,
166             const char *msg, ...)
167 {
168   va_list ap;
169
170   va_start(ap, msg);
171   close_comms(rc); rc->f |= TVRF_BROKEN;
172   tvec_report_v(tv, TVLEV_ERR, msg, &ap);
173   va_end(ap);
174   return (-1);
175 }
176
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
189 static int send_all(struct tvec_state *tv, struct tvec_remotecomms *rc,
190                     const unsigned char *p, size_t sz)
191 {
192   ssize_t n;
193   int ret;
194
195   while (sz) {
196     n = write(rc->outfd, p, sz);
197     if (n > 0)
198       { p += n; sz -= n; }
199     else {
200       ret = ioerr(tv, rc, "failed to send: %s",
201                  n ? strerror(errno) : "empty write");
202       goto end;
203     }
204   }
205   ret = 0;
206 end:
207   return (ret);
208 }
209
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  *
219  * Returns:     A @RECV_...@ code.
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
234 #define RCVF_ALLOWEOF 1u
235
236 enum {
237   RECV_FAIL = -1,
238   RECV_OK = 0,
239   RECV_EOF = 1
240 };
241
242 static int recv_all(struct tvec_state *tv, struct tvec_remotecomms *rc,
243                     unsigned f, unsigned char *p, size_t sz,
244                     size_t min, size_t *n_out)
245 {
246   size_t tot = 0;
247   ssize_t n;
248
249   while (sz) {
250     n = read(rc->infd, p, sz);
251     if (n > 0) {
252       p += n; sz -= n; tot += n;
253       if (tot >= min) break;
254     } else if (!n && !tot && (f&RCVF_ALLOWEOF))
255       { rc->f |= TVRF_BROKEN; return (RECV_EOF); }
256     else
257       return (ioerr(tv, rc, "failed to receive: %s",
258                     n ? strerror(errno) : "unexpected end-of-file"));
259   }
260   *n_out = tot; return (RECV_OK);
261
262 #undef f_any
263 }
264
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
275 static 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
288 static 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
307 static int remote_send(struct tvec_state *tv, struct tvec_remotecomms *rc)
308 {
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; }
315
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     }
322
323   /* Transmit the packet. */
324   if (send_all(tv, rc, DBBASE(&rc->bout), DBLEN(&rc->bout)))
325     { ret = -1; goto end; }
326
327   /* Done.  Put things back the way we found them. */
328   ret = 0;
329 end:
330   DBRESET(&rc->bout);
331   if (opipe != SIG_ERR) signal(SIGPIPE, opipe);
332   return (ret);
333 }
334
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  *
342  * Returns:     A @RECV_...@ code.
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
376 static int receive_buffered(struct tvec_state *tv,
377                             struct tvec_remotecomms *rc,
378                             unsigned f, size_t want)
379 {
380   size_t sz = 0;
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. */
389   GROWBUF_EXTEND(size_t, tv->a, rc->bin, rc->binsz, want, RECVBUFSZ, 1);
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  *
413  * Returns:     A @RECV_...@ code.
414  *
415  * Use:         Receive a packet into the input buffer @rc->bin@ and
416  *              establish @*b_out@ to read from it.
417  */
418
419 static int remote_recv(struct tvec_state *tv, struct tvec_remotecomms *rc,
420                        unsigned f, buf *b_out)
421 {
422   kludge64 k, szmax;
423   size_t want;
424   int ret;
425
426   ASSIGN64(szmax, (size_t)-1);
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;
434   if (CMP64(k, >, szmax))
435     return (ioerr(tv, rc, "packet size 0x%08lx%08lx out of range",
436                   (unsigned long)HI64(k), (unsigned long)LO64(k)));
437   want = GET64(size_t, k);
438
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);
443 }
444
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  */
462
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))
479
480 /*----- Packet types ------------------------------------------------------*/
481
482 #define TVPF_ACK        0x0001u
483
484 #define TVPK_VER        0x0000u         /* --> min, max: u16 *
485                                          * <-- ver: u16 */
486 #define TVPK_BGROUP     0x0002u         /* --> name: str16
487                                          * <-- --- */
488 #define TVPK_SETVAR     0x0004u         /* --> name: str16, rv: value
489                                          * <-- rc: u8 */
490 #define TVPK_TEST       0x0006u         /* --> in: regs
491                                          * <-- --- */
492 #define TVPK_EGROUP     0x0008u         /* --> --- *
493                                          * <-- --- */
494
495 #define TVPK_REPORT     0x0100u         /* <-- level: u16; msg: string */
496 #define TVPK_PROGRESS   0x0102u         /* <-- st: str16 */
497
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;
502                                          *     flag: u8, rv: value */
503 #define TVPK_BBENCH     0x010cu         /* <-- unit: u16, ident: regs */
504 #define TVPK_EBENCH     0x010eu         /* <-- unit: u16; flags: u16;
505                                          *     n, t, cy: f64 */
506
507 /*----- Server ------------------------------------------------------------*/
508
509 /* Forward declaration of output operations. */
510 static const struct tvec_outops remote_ops;
511
512 static struct tvec_state srvtv;         /* server's test-vector state */
513 static struct tvec_remotecomms srvrc = TVEC_REMOTECOMMS_INIT; /* comms */
514 static struct tvec_output srvout = { &remote_ops }; /* output state */
515
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
545 int 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
554 int tvec_setprogress_v(const char *status, va_list *ap)
555 {
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);
561   else return (-1);
562   return (0);
563 }
564
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
581 int tvec_remoteserver(int infd, int outfd, const struct tvec_config *config)
582 {
583   uint16 pk, u, v;
584   unsigned i;
585   buf b;
586   dstr d = DSTR_INIT;
587   const struct tvec_test *t, *const *tt;
588   void *p; size_t sz;
589   const struct tvec_env *env = 0;
590   const struct tvec_vardef *vd = 0; void *varctx;
591   struct tvec_reg *r = 0, rbuf, *r_alloc = 0; size_t rsz = 0;
592   void *ctx = 0;
593   int rc;
594
595   /* Initialize the communication machinery. */
596   setup_comms(&srvrc, infd, outfd);
597
598   /* Begin a test session using our custom output driver. */
599   tvec_begin(&srvtv, config, &srvout);
600
601   /* Version negotiation.  Expect a @TVPK_VER@ packet.  At the moment,
602    * there's only version zero, so we return that.
603    */
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;
611   }
612   if (buf_getu16l(&b, &u) || buf_getu16l(&b, &v)) goto bad;
613   QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_VER | TVPF_ACK)
614     dbuf_putu16l(&srvrc.bout, 0);
615   else { rc = -1; goto end; }
616
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    */
635   for (;;) {
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      */
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;
644
645     switch (pk) {
646
647       case TVPK_BGROUP:
648         /* Start a group. */
649
650         /* Parse the packet payload. */
651         p = buf_getmem16l(&b, &sz); if (!p) goto bad;
652         if (BLEFT(&b)) goto bad;
653
654         /* Find the group given its name. */
655         for (tt = srvtv.cfg.tests; *tt; tt++) {
656           t = *tt;
657           if (strlen(t->name) == sz && MEMCMP(t->name, ==, p, sz))
658             goto found_group;
659         }
660         rc = ioerr(&srvtv, &srvrc, "unknown test group `%.*s'",
661                    (int)sz, (char *)p);
662         goto end;
663
664       found_group:
665         /* Set up the test environment. */
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;
670         else ctx = x_alloc(srvtv.a, env->ctxsz);
671         if (env && env->setup) env->setup(&srvtv, env, 0, ctx);
672
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);
680         else { rc = -1; goto end; }
681
682         /* Handle packets until we're told to end the group. */
683         for (;;) {
684
685           /* Read a packet.  We expect @TVPK_EGROUP@ or @TVPK_TEST@. */
686           if (remote_recv(&srvtv, &srvrc, 0, &b)) { rc = -1; goto end; }
687           if (buf_getu16l(&b, &pk)) goto bad;
688
689           switch (pk) {
690
691             case TVPK_EGROUP:
692               /* End the group. */
693
694               /* Check the payload. */
695               if (BLEFT(&b)) goto bad;
696
697               /* Leave the group loop. */
698               goto endgroup;
699
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               }
712               rc = tvec_unkregerr(&srvtv, d.buf); goto setvar_end;
713             found_var:
714
715               /* Set up the register. */
716               if (vd->regsz <= sizeof(rbuf))
717                 r = &rbuf;
718               else {
719                 GROWBUF_REPLACE(size_t, srvtv.a, r_alloc, rsz, vd->regsz,
720                                 8*sizeof(void *), 1);
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
738             case TVPK_TEST:
739               /* Run a test. */
740
741               /* Parse the packet payload. */
742               if (tvec_deserialize(srvtv.in, &b, srvtv.test->regs,
743                                    0, 0, srvtv.cfg.nreg, srvtv.cfg.regsz))
744                 goto bad;
745               if (BLEFT(&b)) goto bad;
746
747               /* If we're not skipping the test group, then actually try to
748                * run the test.
749                */
750               if (!(srvtv.f&TVSF_SKIP)) {
751
752                 /* Prepare the output registers and reset the test outcome.
753                  * (The environment may force a skip.)
754                  */
755                 for (i = 0; i < srvtv.cfg.nrout; i++)
756                   if (TVEC_REG(&srvtv, in, i)->f&TVRF_LIVE)
757                     TVEC_REG(&srvtv, out, i)->f |= TVRF_LIVE;
758                 srvtv.f |= TVSF_ACTIVE; srvtv.f &= ~TVSF_OUTMASK;
759
760                 /* Invoke the environment @before@ function. */
761                 tvec_setprogress("%%SETUP");
762                 if (env && env->before) env->before(&srvtv, ctx);
763
764                 /* Run the actual test. */
765                 if (!(srvtv.f&TVSF_ACTIVE))
766                   /* setup forced a skip */;
767                 else {
768                   tvec_setprogress("%%RUN");
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                 }
776
777                 /* Conclude the test. */
778                 tvec_setprogress("%%DONE");
779                 if (env && env->after) env->after(&srvtv, ctx);
780                 tvec_endtest(&srvtv);
781               }
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);
786               else { rc = -1; goto end; }
787               break;
788
789             default:
790               /* Some other kind of packet.  Complain. */
791
792               rc = ioerr(&srvtv, &srvrc,
793                          "unexpected packet type 0x%04x during test group",
794                          pk);
795               goto end;
796
797           }
798         }
799
800       endgroup:
801         /* The test group completed. */
802
803         /* Tear down the environment and release other resources. */
804         if (env && env->teardown) env->teardown(&srvtv, ctx);
805         tvec_releaseregs(&srvtv);
806         x_free(srvtv.a, ctx); srvtv.test = 0; env = 0; ctx = 0;
807
808         /* Report completion. */
809         QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_EGROUP | TVPF_ACK);
810         else { rc = -1; goto end; }
811         break;
812
813       default:
814         rc = ioerr(&srvtv, &srvrc,
815                    "unexpected packet type 0x%04x at top level", pk);
816     }
817   }
818   rc = 0;
819
820 end:
821   /* Clean up and return. */
822   if (env && env->teardown) env->teardown(&srvtv, ctx);
823   if (vd) vd->def.ty->release(&r->v, &vd->def);
824   x_free(srvtv.a, ctx); x_free(srvtv.a, r_alloc);
825   if (srvtv.test) tvec_releaseregs(&srvtv);
826   release_comms(&srvtv, &srvrc); tvec_end(&srvtv);
827   return (rc ? 2 : 0);
828
829 bad:
830   /* Miscellaneous malformed packet. */
831   rc = malformed(&srvtv, &srvrc); goto end;
832 }
833
834 /*----- Server output driver ----------------------------------------------*/
835
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
848 static 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
863 static 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
877 static 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
893 static void remote_skipgroup(struct tvec_output *o,
894                              const char *excuse, va_list *ap)
895 {
896   QUEUEPK(&srvtv, &srvrc, 0, TVPK_SKIPGRP)
897     dbuf_vputstrf16l(&srvrc.bout, excuse, ap);
898 }
899
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
911 static 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
925 static 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
945 static void remote_skip(struct tvec_output *o,
946                         const char *excuse, va_list *ap)
947 {
948   QUEUEPK(&srvtv, &srvrc, 0, TVPK_SKIP)
949     dbuf_vputstrf16l(&srvrc.bout, excuse, ap);
950 }
951
952 static void remote_fail(struct tvec_output *o,
953                         const char *detail, va_list *ap)
954 {
955   QUEUEPK(&srvtv, &srvrc, 0, TVPK_FAIL)
956     if (!detail)
957       dbuf_putbyte(&srvrc.bout, 0);
958     else {
959       dbuf_putbyte(&srvrc.bout, 1);
960       dbuf_vputstrf16l(&srvrc.bout, detail, ap);
961     }
962 }
963
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
980 static void remote_dumpreg(struct tvec_output *o,
981                            unsigned disp, const union tvec_regval *rv,
982                            const struct tvec_regdef *rd)
983 {
984   const struct tvec_regdef *reg;
985   unsigned r;
986
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
992 found:
993   QUEUEPK(&srvtv, &srvrc, 0, TVPK_DUMPREG) {
994     dbuf_putu16l(&srvrc.bout, r);
995     dbuf_putu16l(&srvrc.bout, disp);
996     if (!rv)
997       dbuf_putbyte(&srvrc.bout, 0);
998     else {
999       dbuf_putbyte(&srvrc.bout, 1);
1000       rd->ty->tobuf(DBUF_BUF(&srvrc.bout), rv, rd);
1001     }
1002   }
1003 }
1004
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
1017 static void remote_etest(struct tvec_output *o, unsigned outcome)
1018   { ; }
1019
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
1037 static 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
1050 /* --- @remote_bbench@ --- *
1051  *
1052  * Arguments:   @struct tvec_output *o@ = output sink (ignored)
1053  *              @const char *desc@ = adhoc test description, must be null
1054  *              @unsigned unit@ = measurement unit (@BTU_...@)
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
1063 static void remote_bbench(struct tvec_output *o,
1064                           const char *desc, unsigned unit)
1065 {
1066   assert(!desc);
1067   QUEUEPK(&srvtv, &srvrc, 0, TVPK_BBENCH) {
1068     dbuf_putu16l(&srvrc.bout, unit);
1069     tvec_serialize(srvtv.in, DBUF_BUF(&srvrc.bout), srvtv.test->regs,
1070                    TVRF_ID, TVRF_ID, srvtv.cfg.nreg, srvtv.cfg.regsz);
1071   }
1072 }
1073
1074 /* --- @remote_ebench@ --- *
1075  *
1076  * Arguments:   @struct tvec_output *o@ = output sink (ignored)
1077  *              @const char *desc@ = adhoc test description, must be null
1078  *              @unsigned unit@ = measurement unit (@BTU_...@)
1079  *              @const struct bench_timing *t@ = measurement
1080  *
1081  * Returns:     ---
1082  *
1083  * Use:         Report a benchmark's results.
1084  *
1085  *              The remote driver sends a @TVPK_EBENCH@ packet to its client.
1086  */
1087
1088 static void remote_ebench(struct tvec_output *o,
1089                           const char *desc, unsigned unit,
1090                           const struct bench_timing *t)
1091 {
1092   assert(!desc);
1093   QUEUEPK(&srvtv, &srvrc, 0, TVPK_EBENCH) {
1094     dbuf_putu16l(&srvrc.bout, unit);
1095     if (!t || !(t->f&BTF_ANY))
1096       dbuf_putu16l(&srvrc.bout, 0);
1097     else {
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);
1102     }
1103   }
1104 }
1105
1106 static const struct tvec_benchoutops remote_benchops =
1107   { remote_bbench, remote_ebench };
1108
1109 /* --- @remote_extend@ --- *
1110  *
1111  * Arguments:   @struct tvec_output *o@ = output sink (ignored)
1112  *              @const char *name@ = extension name
1113  *
1114  * Returns:     A pointer to the extension implementation, or null.
1115  */
1116
1117 static const void *remote_extend(struct tvec_output *o, const char *name)
1118 {
1119   if (STRCMP(name, ==, TVEC_BENCHOUTEXT)) return (&remote_benchops);
1120   else return (0);
1121 }
1122
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
1134 static void remote_destroy(struct tvec_output *o)
1135   { ; }
1136
1137 static const struct tvec_outops remote_ops = {
1138   remote_bsession, remote_esession,
1139   remote_bgroup, remote_skipgroup, remote_egroup,
1140   remote_btest, remote_skip, remote_fail, remote_dumpreg, remote_etest,
1141   remote_report, remote_extend, remote_destroy
1142 };
1143
1144 /*----- Pseudoregister definitions ----------------------------------------*/
1145
1146 static tvec_setvarfn setvar_local, setvar_remote;
1147
1148 static const struct tvec_flag exit_flags[] = {
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
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
1309   /* This should be folded into the signal entries above. */
1310   { "signal",           TVXF_SIG,               TVXF_SIG },
1311
1312   TVEC_ENDFLAGS
1313 };
1314 static const struct tvec_flaginfo exit_flaginfo =
1315   { "exit-status", exit_flags, &tvrange_uint };
1316 static const struct tvec_vardef exit_var =
1317   { sizeof(struct tvec_reg), setvar_local,
1318     { "@exit", &tvty_flags, -1, 0, { &exit_flaginfo } } };
1319
1320 /* Progress. */
1321
1322 static const struct tvec_vardef progress_var =
1323   { sizeof(struct tvec_reg), setvar_local,
1324     { "@progress", &tvty_text, -1, 0 } };
1325
1326 /* Reconnection. */
1327
1328 static const struct tvec_uassoc reconn_assocs[] = {
1329   { "on-demand",        TVRCN_DEMAND },
1330   { "force",            TVRCN_FORCE },
1331   { "skip",             TVRCN_SKIP },
1332   TVEC_ENDENUM
1333 };
1334 static const struct tvec_uenuminfo reconn_enuminfo =
1335   { "remote-reconnection", reconn_assocs, &tvrange_uint };
1336 static const struct tvec_vardef reconn_var =
1337   { sizeof(struct tvec_reg), setvar_local,
1338     { "@reconnect", &tvty_uenum, -1, 0, { &reconn_enuminfo } } };
1339
1340 /*----- Client ------------------------------------------------------------*/
1341
1342 /* Connection state. */
1343 enum {
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
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  */
1379
1380 static int handle_packets(struct tvec_state *tv, struct tvec_remotectx *r,
1381                           unsigned f, uint16 end, buf *b_out)
1382 {
1383   struct tvec_output *o;
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;
1390   const struct tvec_benchoutops *bo;
1391   struct tvec_fallbackoutput fo;
1392   struct tvec_reg *reg = 0; size_t rsz = 0;
1393   unsigned i;
1394   int rc;
1395
1396   for (;;) {
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;
1403     if (buf_getu16l(b, &pk)) goto bad;
1404     if (pk == end) { rc = 0; break; }
1405
1406     /* Dispatch based on the packet type. */
1407     switch (pk) {
1408
1409       case TVPK_PROGRESS:
1410         /* A progress report.  Update the saved progress. */
1411
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:
1419         /* A report.  Recover the message and pass it along. */
1420
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:
1430         /* A request to skip the group.  Recover the excuse message and pass
1431          * it along.
1432          */
1433
1434         p = buf_getmem16l(b, &n); if (!p) goto bad;
1435         if (BLEFT(b)) goto bad;
1436
1437         DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1438         tvec_skipgroup(tv, "%s", d.buf);
1439         break;
1440
1441       case TVPK_SKIP:
1442         /* A request to skip the test.  Recover the excuse message and pass
1443          * it along, if it's not unreasonable.
1444          */
1445
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:
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
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;
1474
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;
1482
1483       case TVPK_DUMPREG:
1484         /* A request to dump a register. */
1485
1486         /* Find the register definition. */
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         }
1499
1500         /* Read the flag.  If there's no register value, then `dump' its
1501          * absence.  Otherwise retrieve the register value and dump it.
1502          */
1503         rc = buf_getbyte(b); if (rc < 0) goto bad;
1504         if (!rc)
1505           tvec_dumpreg(tv, v, 0, rd);
1506         else {
1507           GROWBUF_REPLACE(size_t, tv->a, reg, rsz, tv->cfg.regsz,
1508                           8*sizeof(void *), 1);
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:
1519         /* A report that we're starting a benchmark.  Pass this along. */
1520
1521         if (buf_getu16l(b, &u)) goto bad;
1522         if (tvec_deserialize(tv->in, b, tv->test->regs,
1523                              TVRF_ID, TVRF_ID, tv->cfg.nreg, tv->cfg.regsz))
1524           goto bad;
1525         if (BLEFT(b)) goto bad;
1526         if (u >= BTU_LIMIT) {
1527           rc = ioerr(tv, &r->rc, "unit code %u out of range", u);
1528           goto end;
1529         }
1530
1531         bo = tvec_outputext(tv, &o, &fo,
1532                             TVEC_BENCHOUTEXT, &tvec_benchoutputfallback);
1533         bo->bbench(o, 0, u);
1534         break;
1535
1536       case TVPK_EBENCH:
1537         /* A report that a benchmark completed.  Pass this along. */
1538
1539         if (buf_getu16l(b, &u) || buf_getu16l(b, &v)) goto bad;
1540         if (u >= BTU_LIMIT) {
1541           rc = ioerr(tv, &r->rc, "unit code %u out of range", u);
1542           goto end;
1543         }
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;
1548         bt.f = v;
1549
1550         bo = tvec_outputext(tv, &o, &fo,
1551                             TVEC_BENCHOUTEXT, &tvec_benchoutputfallback);
1552         bo->ebench(o, 0, u, v&BTF_ANY ? &bt : 0);
1553         break;
1554
1555       default:
1556         /* Something else.  This is unexpected. */
1557
1558         rc = ioerr(tv, &r->rc, "unexpected packet type 0x%04x", pk);
1559         goto end;
1560     }
1561   }
1562
1563 end:
1564   DDESTROY(&d);
1565   x_free(tv->a, reg);
1566   return (rc);
1567 bad:
1568   rc = malformed(tv, &r->rc); goto end;
1569 }
1570
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
1595 static void reap_kid(struct tvec_state *tv, struct tvec_remotectx *r)
1596 {
1597   pid_t kid;
1598   int st;
1599
1600   assert(r->rc.f&TVRF_BROKEN);
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
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
1643 static 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
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
1675 #define ERF_SILENT 0x0001u
1676 #define ERF_CLOSE 0x0002u
1677 static 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
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    */
1688   if (r->errfd < 0) { rc = 0; goto end; }
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
1697   /* Read pieces of error output and feed them into the line buffer. */
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   }
1712
1713   /* Done. */
1714   rc = 0;
1715 end:
1716   if (f&ERF_CLOSE) {
1717     lbuf_close(&r->errbuf);
1718     close(r->errfd); r->errfd = -1;
1719   }
1720   return (rc);
1721 }
1722
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
1741 #define DCF_KILL 0x0100u
1742 #define DCF_QUITOK 0200u
1743 static void disconnect_remote(struct tvec_state *tv,
1744                               struct tvec_remotectx *r, unsigned f)
1745 {
1746   union tvec_regval rv;
1747   dstr d = DSTR_INIT;
1748
1749   if (r->kid > 0 && (f&DCF_KILL)) kill(r->kid, SIGTERM);
1750   close_comms(&r->rc);
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);
1763 }
1764
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
1775 static 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
1783   /* If we're already connected, then there's nothing to do. */
1784   if (r->kid >= 0) { rc = 0; goto end; }
1785
1786   /* Set the preliminary progress indication. */
1787   DRESET(&r->progress); DPUTS(&r->progress, "%INIT");
1788
1789   /* Call the connection function to establish descriptors. */
1790   if (re->r.connect(&kid, &infd, &outfd, &errfd, tv, re))
1791     { rc = -1; goto end; }
1792
1793   /* Establish communications state. */
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
1798   /* Do version negotiation. */
1799   QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_VER) {
1800     dbuf_putu16l(&r->rc.bout, 0);
1801     dbuf_putu16l(&r->rc.bout, 0);
1802   } else { rc = -1; goto end; }
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   }
1811   r->ver = v;
1812
1813   /* Begin the test group at the server. */
1814   QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_BGROUP)
1815     dbuf_putstr16l(&r->rc.bout, tv->test->name);
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; }
1820
1821   /* Done. */
1822   rc = 0;
1823 end:
1824   if (rc) disconnect_remote(tv, r, DCF_KILL);
1825   return (rc);
1826 bad:
1827   rc = malformed(tv, &r->rc); goto end;
1828 }
1829
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
1843 static 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
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
1864 static 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
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
1902 static void reset_vars(struct tvec_remotectx *r)
1903 {
1904   const struct tvec_remoteenv *re = r->re;
1905
1906   r->exwant = TVXST_RUN;
1907   r->rc.f = (r->rc.f&~(TVRF_RCNMASK | TVRF_SETMASK)) |
1908             (re->r.dflt_reconn&TVRF_RCNMASK);
1909   DRESET(&r->prgwant); DPUTS(&r->prgwant, "%DONE");
1910 }
1911
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
1928 void 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;
1933   const struct tvec_env *subenv = re->r.env;
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);
1942   if (subenv && subenv->ctxsz) r->subctx = x_alloc(tv->a, subenv->ctxsz);
1943   else r->subctx = 0;
1944   if (subenv && subenv->setup) subenv->setup(tv, subenv, r, r->subctx);
1945 }
1946
1947 /* --- @tvec_remotefindvar@, @setvar_local@, @setvar_remote@ --- *
1948  *
1949  * Arguments:   @struct tvec_state *tv@ = test vector state
1950  *              @const char *var@ = variable name to set
1951  *              @const union tvec_regval *rv@ = register value
1952  *              @void **ctx_out@ = where to put the @setvar@ context
1953  *              @void *ctx@ = context pointer
1954  *
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.
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
1973 static int setvar_local(struct tvec_state *tv, const char *var,
1974                         const union tvec_regval *rv, void *ctx)
1975 {
1976   struct tvec_remotectx *r = ctx;
1977
1978   if (STRCMP(var, ==, "@exit")) {
1979     if (r->rc.f&TVRF_SETEXIT) return (tvec_dupregerr(tv, var));
1980     r->exwant = rv->u; r->rc.f |= TVRF_SETEXIT; return (0);
1981   } else if (STRCMP(var, ==, "@progress")) {
1982     if (r->rc.f&TVRF_SETPRG) return (tvec_dupregerr(tv, var));
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);
1986   } else if (STRCMP(var, ==, "@reconnect")) {
1987     if (r->rc.f&TVRF_SETRCN) return (tvec_dupregerr(tv, var));
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
1993 static 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;
1999
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;
2014 end:
2015   return (rc);
2016 }
2017
2018 const 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
2052 void 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
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  */
2071
2072 void 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
2083   /* Reconnect to the server according to policy. */
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
2091   /* Set initial progress state. */
2092   DRESET(&r->progress); DPUTS(&r->progress, "%IDLE");
2093
2094   /* Send the command to the server and handle output. */
2095   QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_TEST)
2096     tvec_serialize(tv->in, DBUF_BUF(&r->rc.bout), tv->test->regs,
2097                    0, 0, tv->cfg.nreg, tv->cfg.regsz);
2098   else { goto fail; }
2099   rc = handle_packets(tv, r, RCVF_ALLOWEOF, TVPK_TEST | TVPF_ACK, &b);
2100
2101   /* Deal with the outcome. */
2102   switch (rc) {
2103
2104     case RECV_FAIL:
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
2112     case RECV_EOF:
2113       /* End-of-file at a packet boundary.  The server crashed trying to run
2114        * our test.  Collect the exit status and continue.
2115        */
2116       reap_kid(tv, r);
2117       /* fall through */
2118
2119     case RECV_OK:
2120       /* Successful completion (or EOF). */
2121
2122       /* Notice if the exit status isn't right. */
2123       if (r->exit != r->exwant) f |= f_exit;
2124
2125       /* Notice if the progress token isn't right. */
2126       if (r->progress.len != r->prgwant.len ||
2127           MEMCMP(r->progress.buf, !=, r->prgwant.buf, r->progress.len))
2128         f |= f_progress;
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        */
2133       if (f && (tv->f&TVSF_ACTIVE))
2134         { tvec_fail(tv, 0); tvec_mismatch(tv, TVMF_IN); }
2135
2136       /* If the test failed, then report the exit and progress states
2137        * relative to their expectations.
2138        */
2139       if (!(tv->f&TVSF_ACTIVE) &&
2140           (tv->f&TVSF_OUTMASK) == (TVOUT_LOSE << TVSF_OUTSHIFT)) {
2141
2142         /* Note here that the test failed. */
2143         f |= f_fail;
2144
2145         /* Report exit status. */
2146         rv.u = r->exit;
2147         tvec_dumpreg(tv, f&f_exit ? TVRD_FOUND : TVRD_MATCH,
2148                      &rv, &exit_var.def);
2149         if (f&f_exit) {
2150           rv.u = r->exwant;
2151           tvec_dumpreg(tv, TVRD_EXPECT, &rv, &exit_var.def);
2152         }
2153
2154         /* Report progress token. */
2155         rv.text.p = r->progress.buf; rv.text.sz = r->progress.len;
2156         tvec_dumpreg(tv, f&f_progress ? TVRD_FOUND : TVRD_MATCH,
2157                      &rv, &progress_var.def);
2158         if (f&f_progress) {
2159           rv.text.p = r->prgwant.buf; rv.text.sz = r->prgwant.len;
2160           tvec_dumpreg(tv, TVRD_EXPECT, &rv, &progress_var.def);
2161         }
2162       }
2163
2164       /* If we received end-of-file, then close the connection.  Suppress
2165        * error output if the test passed: it was presumably expected.
2166        */
2167       if (rc == RECV_EOF)
2168         disconnect_remote(tv, r, f ? 0 : ERF_SILENT);
2169       break;
2170   }
2171
2172 #undef f_exit
2173 #undef f_progress
2174 #undef f_fail
2175 }
2176
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
2187 void tvec_remoteafter(struct tvec_state *tv, void *ctx)
2188 {
2189   struct tvec_remotectx *r = ctx;
2190   const struct tvec_remoteenv *re = r->re;
2191   const struct tvec_env *subenv = re->r.env;
2192
2193   reset_vars(r);
2194   if (subenv && subenv->after) subenv->after(tv, r->subctx);
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
2207 void tvec_remoteteardown(struct tvec_state *tv, void *ctx)
2208 {
2209   struct tvec_remotectx *r = ctx;
2210   const struct tvec_remoteenv *re = r->re;
2211   const struct tvec_env *subenv = re->r.env;
2212   buf b;
2213
2214   if (subenv && subenv->teardown) subenv->teardown(tv, r->subctx);
2215   x_free(tv->a, r->subctx);
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);
2220   }
2221   disconnect_remote(tv, r, DCF_QUITOK); release_comms(tv, &r->rc);
2222   DDESTROY(&r->prgwant); DDESTROY(&r->progress);
2223 }
2224
2225 /*----- Connectors --------------------------------------------------------*/
2226
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
2248 static 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
2255   /* Try to create the pipes. */
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
2264   /* Flush all of the stream buffers so that we don't get duplicated
2265    * output.
2266    */
2267   fflush(0);
2268
2269   /* Try to set up the child process. */
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) {
2277     /* Child process. */
2278
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));
2285       _exit(127);
2286     }
2287   } else {
2288     /* Parent process. */
2289
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
2296   /* All done. */
2297   rc = 0;
2298
2299 end:
2300   /* Clean up.  So much of this... */
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
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
2325 int 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) {
2336     if (tv->fp) fclose(tv->fp);
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;
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;
2345 end:
2346   return (rc);
2347 }
2348
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
2365 int 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;
2390 end:
2391   return (rc);
2392 }
2393
2394 /*----- That's all, folks -------------------------------------------------*/