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