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