@@@ tvec doc wip
[mLib] / test / tvec-remote.c
CommitLineData
e63124bc
MW
1/* -*-c-*-
2 *
3 * Remote testing
4 *
5 * (c) 2023 Straylight/Edgeware
6 */
7
8/*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of the mLib utilities library.
11 *
12 * mLib is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU Library General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or (at
15 * your option) any later version.
16 *
17 * mLib is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
20 * License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib. If not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
25 * USA.
26 */
27
28/*----- Header files ------------------------------------------------------*/
29
30#include <errno.h>
c91413e6 31#include <signal.h>
e63124bc
MW
32#include <stdarg.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36
37#include <sys/types.h>
c91413e6 38#include <sys/wait.h>
e63124bc
MW
39#include <fcntl.h>
40#include <unistd.h>
41
42#include "alloc.h"
c91413e6 43#include "bench.h"
e63124bc 44#include "buf.h"
c91413e6
MW
45#include "compiler.h"
46#include "fdflags.h"
47#include "lbuf.h"
48#include "mdup.h"
49#include "quis.h"
e63124bc
MW
50#include "tvec.h"
51
31d0247c
MW
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
c91413e6
MW
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
e63124bc 63
c91413e6
MW
64#if CLANG_VERSION_P(3, 1)
65# pragma clang diagnostic ignored "-Wdangling-else"
66#endif
e63124bc 67
c91413e6 68/*----- Basic I/O ---------------------------------------------------------*/
e63124bc 69
31d0247c
MW
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
c91413e6
MW
82static void init_comms(struct tvec_remotecomms *rc)
83{
31d0247c 84 rc->bin = 0; rc->binsz = 0; dbuf_create(&rc->bout);
c91413e6
MW
85 rc->infd = rc->outfd = -1; rc->f = 0;
86}
e63124bc 87
31d0247c
MW
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
c91413e6
MW
100static void close_comms(struct tvec_remotecomms *rc)
101{
c81c35df
MW
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;
c91413e6
MW
109}
110
31d0247c
MW
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
c91413e6 122static void release_comms(struct tvec_remotecomms *rc)
31d0247c
MW
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 */
c91413e6
MW
136
137static void setup_comms(struct tvec_remotecomms *rc, int infd, int outfd)
138{
31d0247c
MW
139 rc->infd = infd; rc->outfd = outfd;
140 rc->binoff = rc->binlen = 0;
141 rc->f &= ~0xffu;
c91413e6 142}
e63124bc 143
31d0247c
MW
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
156static PRINTF_LIKE(3, 4)
157 int ioerr(struct tvec_state *tv, struct tvec_remotecomms *rc,
158 const char *msg, ...)
e63124bc
MW
159{
160 va_list ap;
161
162 va_start(ap, msg);
c91413e6
MW
163 close_comms(rc); rc->f |= TVRF_BROKEN;
164 tvec_report_v(tv, TVLEV_ERR, msg, &ap);
e63124bc
MW
165 va_end(ap);
166 return (-1);
167}
168
31d0247c
MW
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
c91413e6 181static int send_all(struct tvec_state *tv, struct tvec_remotecomms *rc,
e63124bc
MW
182 const unsigned char *p, size_t sz)
183{
184 ssize_t n;
c91413e6 185 int ret;
e63124bc
MW
186
187 while (sz) {
c91413e6 188 n = write(rc->outfd, p, sz);
e63124bc
MW
189 if (n > 0)
190 { p += n; sz -= n; }
c91413e6
MW
191 else {
192 ret = ioerr(tv, rc, "failed to send: %s",
193 n ? strerror(errno) : "empty write");
194 goto end;
195 }
e63124bc 196 }
c91413e6
MW
197 ret = 0;
198end:
c91413e6 199 return (ret);
e63124bc
MW
200}
201
31d0247c
MW
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 *
c81c35df 211 * Returns: A @RECV_...@ code.
31d0247c
MW
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
e63124bc 226#define RCVF_ALLOWEOF 1u
31d0247c 227
c91413e6
MW
228enum {
229 RECV_FAIL = -1,
230 RECV_OK = 0,
231 RECV_EOF = 1
232};
31d0247c 233
c91413e6 234static int recv_all(struct tvec_state *tv, struct tvec_remotecomms *rc,
31d0247c
MW
235 unsigned f, unsigned char *p, size_t sz,
236 size_t min, size_t *n_out)
e63124bc 237{
31d0247c 238 size_t tot = 0;
e63124bc 239 ssize_t n;
e63124bc
MW
240
241 while (sz) {
c91413e6 242 n = read(rc->infd, p, sz);
31d0247c
MW
243 if (n > 0) {
244 p += n; sz -= n; tot += n;
245 if (tot >= min) break;
246 } else if (!n && !tot && (f&RCVF_ALLOWEOF))
c81c35df 247 { rc->f |= TVRF_BROKEN; return (RECV_EOF); }
e63124bc 248 else
c91413e6 249 return (ioerr(tv, rc, "failed to receive: %s",
e63124bc
MW
250 n ? strerror(errno) : "unexpected end-of-file"));
251 }
31d0247c 252 *n_out = tot; return (RECV_OK);
e63124bc
MW
253
254#undef f_any
255}
256
31d0247c
MW
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
267static 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
280static 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
c91413e6 299static int remote_send(struct tvec_state *tv, struct tvec_remotecomms *rc)
e63124bc 300{
31d0247c
MW
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; }
e63124bc 307
31d0247c
MW
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 }
e63124bc 314
31d0247c
MW
315 /* Transmit the packet. */
316 if (send_all(tv, rc, DBBASE(&rc->bout), DBLEN(&rc->bout)))
317 { ret = -1; goto end; }
e63124bc 318
31d0247c
MW
319 /* Done. Put things back the way we found them. */
320 ret = 0;
321end:
322 DBRESET(&rc->bout);
323 if (opipe != SIG_ERR) signal(SIGPIPE, opipe);
324 return (ret);
e63124bc
MW
325}
326
31d0247c
MW
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 *
c81c35df 334 * Returns: A @RECV_...@ code.
31d0247c
MW
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
368static 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 *
c81c35df 411 * Returns: A @RECV_...@ code.
31d0247c
MW
412 *
413 * Use: Receive a packet into the input buffer @rc->bin@ and
414 * establish @*b_out@ to read from it.
415 */
416
c91413e6
MW
417static int remote_recv(struct tvec_state *tv, struct tvec_remotecomms *rc,
418 unsigned f, buf *b_out)
e63124bc 419{
31d0247c
MW
420 kludge64 k, szmax;
421 size_t want;
c91413e6 422 int ret;
e63124bc 423
c91413e6 424 ASSIGN64(szmax, (size_t)-1);
31d0247c
MW
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;
e63124bc 432 if (CMP64(k, >, szmax))
c91413e6 433 return (ioerr(tv, rc, "packet size 0x%08lx%08lx out of range",
e63124bc 434 (unsigned long)HI64(k), (unsigned long)LO64(k)));
31d0247c 435 want = GET64(size_t, k);
e63124bc 436
31d0247c
MW
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);
e63124bc
MW
441}
442
31d0247c
MW
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 */
e63124bc 460
31d0247c
MW
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))
e63124bc
MW
477
478/*----- Packet types ------------------------------------------------------*/
479
c91413e6 480#define TVPF_ACK 0x0001u
e63124bc 481
31d0247c
MW
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 * <-- --- */
e63124bc 490
c91413e6
MW
491#define TVPK_REPORT 0x0100u /* <-- level: u16; msg: string */
492#define TVPK_PROGRESS 0x0102u /* <-- st: str16 */
e63124bc 493
31d0247c
MW
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;
c91413e6 498 * flag: u8, rv: value */
31d0247c
MW
499#define TVPK_BBENCH 0x010cu /* <-- ident: str32; unit: u16 */
500#define TVPK_EBENCH 0x010eu /* <-- ident: str32; unit: u16;
c91413e6 501 * flags: u16; n, t, cy: f64 */
e63124bc 502
c91413e6
MW
503/*----- Server ------------------------------------------------------------*/
504
31d0247c 505/* Forward declaration of output operations. */
c91413e6
MW
506static const struct tvec_outops remote_ops;
507
31d0247c
MW
508static struct tvec_state srvtv; /* server's test-vector state */
509static struct tvec_remotecomms srvrc = TVEC_REMOTECOMMS_INIT; /* comms */
510static struct tvec_output srvout = { &remote_ops }; /* output state */
e63124bc 511
31d0247c
MW
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
541int 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
550int tvec_setprogress_v(const char *status, va_list *ap)
c91413e6 551{
31d0247c
MW
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);
c91413e6
MW
557 else return (-1);
558 return (0);
e63124bc
MW
559}
560
31d0247c
MW
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
c91413e6 577int tvec_remoteserver(int infd, int outfd, const struct tvec_config *config)
e63124bc 578{
c91413e6
MW
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;
c91413e6
MW
585 void *ctx = 0;
586 int rc;
587
31d0247c 588 /* Initialize the communication machinery. */
c91413e6 589 setup_comms(&srvrc, infd, outfd);
31d0247c
MW
590
591 /* Begin a test session using our custom output driver. */
c91413e6
MW
592 tvec_begin(&srvtv, config, &srvout);
593
31d0247c
MW
594 /* Version negotiation. Expect a @TVPK_VER@ packet. At the moment,
595 * there's only version zero, so we return that.
596 */
c91413e6
MW
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;
e63124bc 604 }
c91413e6 605 if (buf_getu16l(&b, &u) || buf_getu16l(&b, &v)) goto bad;
31d0247c
MW
606 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_VER | TVPF_ACK)
607 dbuf_putu16l(&srvrc.bout, 0);
c91413e6 608 else { rc = -1; goto end; }
e63124bc 609
31d0247c
MW
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 */
c91413e6 628 for (;;) {
31d0247c
MW
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 */
c91413e6
MW
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;
e63124bc 637
c91413e6
MW
638 switch (pk) {
639
640 case TVPK_BGROUP:
31d0247c
MW
641 /* Start a group. */
642
643 /* Parse the packet payload. */
c91413e6
MW
644 p = buf_getmem16l(&b, &sz); if (!p) goto bad;
645 if (BLEFT(&b)) goto bad;
31d0247c
MW
646
647 /* Find the group given its name. */
c91413e6
MW
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:
31d0247c 656 /* Set up the test environment. */
c91413e6
MW
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
31d0247c
MW
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);
c91413e6
MW
671 else { rc = -1; goto end; }
672
31d0247c 673 /* Handle packets until we're told to end the group. */
c91413e6 674 for (;;) {
31d0247c
MW
675
676 /* Read a packet. We expect @TVPK_EGROUP@ or @TVPK_TEST@. */
c91413e6
MW
677 if (remote_recv(&srvtv, &srvrc, 0, &b)) { rc = -1; goto end; }
678 if (buf_getu16l(&b, &pk)) goto bad;
31d0247c 679
c91413e6
MW
680 switch (pk) {
681
682 case TVPK_EGROUP:
31d0247c
MW
683 /* End the group. */
684
685 /* Check the payload. */
c91413e6 686 if (BLEFT(&b)) goto bad;
31d0247c
MW
687
688 /* Leave the group loop. */
c91413e6
MW
689 goto endgroup;
690
691 case TVPK_TEST:
31d0247c
MW
692 /* Run a test. */
693
694 /* Parse the packet payload. */
c91413e6
MW
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
31d0247c
MW
700 /* If we're not skipping the test group, then actually try to
701 * run the test.
702 */
c91413e6 703 if (!(srvtv.f&TVSF_SKIP)) {
31d0247c
MW
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;
c91413e6 711 srvtv.f |= TVSF_ACTIVE; srvtv.f &= ~TVSF_OUTMASK;
31d0247c
MW
712
713 /* Invoke the environment @before@ function. */
714 tvec_setprogress("%%SETUP");
c91413e6 715 if (env && env->before) env->before(&srvtv, ctx);
31d0247c
MW
716
717 /* Run the actual test. */
c91413e6
MW
718 if (!(srvtv.f&TVSF_ACTIVE))
719 /* setup forced a skip */;
720 else {
31d0247c 721 tvec_setprogress("%%RUN");
c91413e6
MW
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 }
31d0247c
MW
729
730 /* Conclude the test. */
731 tvec_setprogress("%%DONE");
c91413e6
MW
732 if (env && env->after) env->after(&srvtv, ctx);
733 tvec_endtest(&srvtv);
734 }
31d0247c
MW
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);
c91413e6 739 else { rc = -1; goto end; }
c91413e6
MW
740 break;
741
742 default:
31d0247c
MW
743 /* Some other kind of packet. Complain. */
744
c91413e6 745 rc = ioerr(&srvtv, &srvrc,
31d0247c
MW
746 "unexpected packet type 0x%04x during test group",
747 pk);
c91413e6
MW
748 goto end;
749
750 }
751 }
752
753 endgroup:
31d0247c
MW
754 /* The test group completed. */
755
756 /* Tear down the environment and release other resources. */
c91413e6 757 if (env && env->teardown) env->teardown(&srvtv, ctx);
31d0247c
MW
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; }
c91413e6
MW
764 break;
765
766 default:
31d0247c
MW
767 rc = ioerr(&srvtv, &srvrc,
768 "unexpected packet type 0x%04x at top level", pk);
c91413e6
MW
769 }
770 }
771 rc = 0;
772
773end:
31d0247c 774 /* Clean up and return. */
c91413e6
MW
775 if (env && env->teardown) env->teardown(&srvtv, ctx);
776 xfree(ctx);
31d0247c
MW
777 if (srvtv.test) tvec_releaseregs(&srvtv);
778 release_comms(&srvrc); tvec_end(&srvtv);
c91413e6
MW
779 return (rc ? 2 : 0);
780
781bad:
31d0247c 782 /* Miscellaneous malformed packet. */
c91413e6 783 rc = malformed(&srvtv, &srvrc); goto end;
e63124bc
MW
784}
785
c91413e6
MW
786/*----- Server output driver ----------------------------------------------*/
787
c81c35df
MW
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
800static 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
815static 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
829static 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
e63124bc
MW
845static void remote_skipgroup(struct tvec_output *o,
846 const char *excuse, va_list *ap)
c91413e6 847{
31d0247c
MW
848 QUEUEPK(&srvtv, &srvrc, 0, TVPK_SKIPGRP)
849 dbuf_vputstrf16l(&srvrc.bout, excuse, ap);
c91413e6 850}
e63124bc 851
c81c35df
MW
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
863static 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
877static 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
e63124bc
MW
897static void remote_skip(struct tvec_output *o,
898 const char *excuse, va_list *ap)
c91413e6 899{
31d0247c
MW
900 QUEUEPK(&srvtv, &srvrc, 0, TVPK_SKIP)
901 dbuf_vputstrf16l(&srvrc.bout, excuse, ap);
c91413e6 902}
e63124bc
MW
903
904static void remote_fail(struct tvec_output *o,
905 const char *detail, va_list *ap)
c91413e6 906{
31d0247c 907 QUEUEPK(&srvtv, &srvrc, 0, TVPK_FAIL)
c91413e6 908 if (!detail)
31d0247c 909 dbuf_putbyte(&srvrc.bout, 0);
c91413e6 910 else {
31d0247c
MW
911 dbuf_putbyte(&srvrc.bout, 1);
912 dbuf_vputstrf16l(&srvrc.bout, detail, ap);
c91413e6
MW
913 }
914}
e63124bc 915
c81c35df
MW
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
c91413e6
MW
932static void remote_dumpreg(struct tvec_output *o,
933 unsigned disp, const union tvec_regval *rv,
934 const struct tvec_regdef *rd)
e63124bc 935{
c91413e6
MW
936 const struct tvec_regdef *reg;
937 unsigned r;
e63124bc 938
c91413e6
MW
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
944found:
31d0247c
MW
945 QUEUEPK(&srvtv, &srvrc, 0, TVPK_DUMPREG) {
946 dbuf_putu16l(&srvrc.bout, r);
947 dbuf_putu16l(&srvrc.bout, disp);
c91413e6 948 if (!rv)
31d0247c 949 dbuf_putbyte(&srvrc.bout, 0);
c91413e6 950 else {
31d0247c
MW
951 dbuf_putbyte(&srvrc.bout, 1);
952 rd->ty->tobuf(DBUF_BUF(&srvrc.bout), rv, rd);
c91413e6 953 }
e63124bc
MW
954 }
955}
956
c81c35df
MW
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
969static 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
c91413e6
MW
985static void remote_bbench(struct tvec_output *o,
986 const char *ident, unsigned unit)
e63124bc 987{
31d0247c
MW
988 QUEUEPK(&srvtv, &srvrc, 0, TVPK_BBENCH) {
989 dbuf_putstr32l(&srvrc.bout, ident);
990 dbuf_putu16l(&srvrc.bout, unit);
c91413e6 991 }
e63124bc
MW
992}
993
c81c35df
MW
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
e63124bc 1008static void remote_ebench(struct tvec_output *o,
c91413e6 1009 const char *ident, unsigned unit,
e63124bc
MW
1010 const struct bench_timing *t)
1011{
31d0247c
MW
1012 QUEUEPK(&srvtv, &srvrc, 0, TVPK_EBENCH) {
1013 dbuf_putstr32l(&srvrc.bout, ident);
1014 dbuf_putu16l(&srvrc.bout, unit);
c91413e6 1015 if (!t || !(t->f&BTF_ANY))
31d0247c 1016 dbuf_putu16l(&srvrc.bout, 0);
c91413e6 1017 else {
31d0247c
MW
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);
c91413e6
MW
1022 }
1023 }
1024}
1025
c81c35df
MW
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
c91413e6
MW
1043static void remote_report(struct tvec_output *o, unsigned level,
1044 const char *msg, va_list *ap)
1045{
31d0247c
MW
1046 QUEUEPK(&srvtv, &srvrc, 0, TVPK_REPORT) {
1047 dbuf_putu16l(&srvrc.bout, level);
1048 dbuf_vputstrf16l(&srvrc.bout, msg, ap);
c91413e6 1049 } else {
31d0247c 1050 fprintf(stderr, "%s %s: ", QUIS, tvec_strlevel(level));
c91413e6
MW
1051 vfprintf(stderr, msg, *ap);
1052 fputc('\n', stderr);
e63124bc
MW
1053 }
1054}
1055
c81c35df
MW
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
c91413e6
MW
1067static void remote_destroy(struct tvec_output *o)
1068 { ; }
e63124bc
MW
1069
1070static const struct tvec_outops remote_ops = {
e63124bc 1071 remote_bsession, remote_esession,
c91413e6
MW
1072 remote_bgroup, remote_skipgroup, remote_egroup,
1073 remote_btest, remote_skip, remote_fail, remote_dumpreg, remote_etest,
e63124bc 1074 remote_bbench, remote_ebench,
c91413e6 1075 remote_report,
e63124bc 1076 remote_destroy
c91413e6
MW
1077};
1078
c81c35df 1079/*----- Pseudoregister definitions ----------------------------------------*/
c91413e6
MW
1080
1081static const struct tvec_flag exit_flags[] = {
c81c35df
MW
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
c91413e6
MW
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
c81c35df 1242 /* This should be folded into the signal entries above. */
c91413e6
MW
1243 { "signal", TVXF_SIG, TVXF_SIG },
1244
c91413e6
MW
1245 TVEC_ENDFLAGS
1246};
1247
1248static const struct tvec_flaginfo exit_flaginfo =
1249 { "exit-status", exit_flags, &tvrange_uint };
1250static const struct tvec_regdef exit_regdef =
1251 { "@exit", 0, &tvty_flags, 0, { &exit_flaginfo } };
1252
c81c35df
MW
1253/* Progress. */
1254
c91413e6 1255static const struct tvec_regdef progress_regdef =
c81c35df
MW
1256 { "@progress", 0, &tvty_text, 0 };
1257
1258/* Reconnection. */
c91413e6
MW
1259
1260static const struct tvec_uassoc reconn_assocs[] = {
1261 { "on-demand", TVRCN_DEMAND },
1262 { "force", TVRCN_FORCE },
1263 { "skip", TVRCN_SKIP },
1264 TVEC_ENDENUM
1265};
1266
c81c35df
MW
1267static const struct tvec_uenuminfo reconn_enuminfo =
1268 { "remote-reconnection", reconn_assocs, &tvrange_uint };
1269static const struct tvec_regdef reconn_regdef =
1270 { "@reconnect", 0, &tvty_uenum, 0, { &reconn_enuminfo } };
1271
1272/*----- Client ------------------------------------------------------------*/
1273
1274/* Connection state. */
c91413e6
MW
1275enum {
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
c81c35df
MW
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 */
c91413e6
MW
1311
1312static 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 (;;) {
c81c35df
MW
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;
c91413e6 1333 if (buf_getu16l(b, &pk)) goto bad;
c81c35df 1334 if (pk == end) { rc = 0; break; }
c91413e6 1335
c81c35df 1336 /* Dispatch based on the packet type. */
c91413e6
MW
1337 switch (pk) {
1338
1339 case TVPK_PROGRESS:
c81c35df
MW
1340 /* A progress report. Update the saved progress. */
1341
c91413e6
MW
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:
c81c35df
MW
1349 /* A report. Recover the message and pass it along. */
1350
c91413e6
MW
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:
c81c35df
MW
1360 /* A request to skip the group. Recover the excuse message and pass
1361 * it along.
1362 */
1363
c91413e6 1364 p = buf_getmem16l(b, &n); if (!p) goto bad;
c91413e6
MW
1365 if (BLEFT(b)) goto bad;
1366
c81c35df 1367 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
c91413e6
MW
1368 tvec_skipgroup(tv, "%s", d.buf);
1369 break;
1370
1371 case TVPK_SKIP:
c81c35df
MW
1372 /* A request to skip the test. Recover the excuse message and pass
1373 * it along, if it's not unreasonable.
1374 */
1375
c91413e6
MW
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:
c81c35df
MW
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
c91413e6
MW
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;
e63124bc 1404
c91413e6
MW
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;
e63124bc 1412
c91413e6 1413 case TVPK_DUMPREG:
c81c35df
MW
1414 /* A request to dump a register. */
1415
1416 /* Find the register definition. */
c91413e6
MW
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 }
e63124bc 1429
c81c35df
MW
1430 /* Read the flag. If there's no register value, then `dump' its
1431 * absence. Otherwise retrieve the register value and dump it.
1432 */
c91413e6
MW
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:
c81c35df
MW
1448 /* A report that we're starting a benchmark. Pass this along. */
1449
c91413e6
MW
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:
c81c35df
MW
1463 /* A report that a benchmark completed. Pass this along. */
1464
c91413e6
MW
1465 p = buf_getmem32l(b, &n); if (!p) goto bad;
1466 if (buf_getu16l(b, &u) || buf_getu16l(b, &v)) goto bad;
31d0247c
MW
1467 if (u >= TVBU_LIMIT) {
1468 rc = ioerr(tv, &r->rc, "unit code %u out of range", u);
1469 goto end;
1470 }
c91413e6
MW
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:
c81c35df
MW
1481 /* Something else. This is unexpected. */
1482
c91413e6
MW
1483 rc = ioerr(tv, &r->rc, "unexpected packet type 0x%04x", pk);
1484 goto end;
1485 }
1486 }
1487
1488end:
1489 DDESTROY(&d);
1490 xfree(reg);
1491 return (rc);
1492bad:
1493 rc = malformed(tv, &r->rc); goto end;
1494}
1495
c81c35df
MW
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
c91413e6
MW
1520static void reap_kid(struct tvec_state *tv, struct tvec_remotectx *r)
1521{
1522 pid_t kid;
1523 int st;
1524
c81c35df 1525 assert(r->rc.f&TVRF_BROKEN);
c91413e6
MW
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
c81c35df
MW
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
c91413e6
MW
1568static 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
c81c35df
MW
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
c91413e6
MW
1600#define ERF_SILENT 0x0001u
1601#define ERF_CLOSE 0x0002u
1602static 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
c81c35df
MW
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 */
31d0247c 1613 if (r->errfd < 0) { rc = 0; goto end; }
c91413e6
MW
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
c81c35df 1622 /* Read pieces of error output and feed them into the line buffer. */
c91413e6
MW
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 }
c81c35df
MW
1637
1638 /* Done. */
c91413e6
MW
1639 rc = 0;
1640end:
1641 if (f&ERF_CLOSE) {
1642 lbuf_close(&r->errbuf);
31d0247c 1643 close(r->errfd); r->errfd = -1;
c91413e6
MW
1644 }
1645 return (rc);
1646}
1647
c81c35df
MW
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
c91413e6
MW
1666#define DCF_KILL 0x0100u
1667static void disconnect_remote(struct tvec_state *tv,
1668 struct tvec_remotectx *r, unsigned f)
1669{
c91413e6
MW
1670 if (r->kid > 0 && (f&DCF_KILL)) kill(r->kid, SIGTERM);
1671 close_comms(&r->rc);
c91413e6
MW
1672 drain_errfd(tv, r, f | ERF_CLOSE); reap_kid(tv, r);
1673}
1674
c81c35df
MW
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
c91413e6
MW
1685static 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
c81c35df 1693 /* If we're already connected, then there's nothing to do. */
c91413e6 1694 if (r->kid >= 0) { rc = 0; goto end; }
c81c35df
MW
1695
1696 /* Set the preliminary progress indication. */
1697 DRESET(&r->progress); DPUTS(&r->progress, "%INIT");
1698
1699 /* Call the connection function to establish descriptors. */
c91413e6
MW
1700 if (re->r.connect(&kid, &infd, &outfd, &errfd, tv, re))
1701 { rc = -1; goto end; }
c81c35df
MW
1702
1703 /* Establish communications state. */
c91413e6
MW
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
c81c35df 1708 /* Do version negotiation. */
31d0247c
MW
1709 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_VER) {
1710 dbuf_putu16l(&r->rc.bout, 0);
1711 dbuf_putu16l(&r->rc.bout, 0);
c91413e6 1712 } else { rc = -1; goto end; }
c91413e6
MW
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 }
c81c35df 1721 r->ver = v;
c91413e6 1722
c81c35df 1723 /* Begin the test group at the server. */
31d0247c
MW
1724 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_BGROUP)
1725 dbuf_putstr16l(&r->rc.bout, tv->test->name);
c91413e6
MW
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; }
c81c35df
MW
1730
1731 /* Done. */
1732 rc = 0;
c91413e6
MW
1733end:
1734 if (rc) disconnect_remote(tv, r, DCF_KILL);
1735 return (rc);
1736bad:
1737 rc = malformed(tv, &r->rc); goto end;
1738}
1739
c81c35df
MW
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
c91413e6
MW
1753static 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
c81c35df
MW
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
c91413e6
MW
1774static 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
c81c35df
MW
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
c91413e6
MW
1812static void reset_vars(struct tvec_remotectx *r)
1813{
31d0247c
MW
1814 r->exwant = TVXST_RUN;
1815 r->rc.f = (r->rc.f&~(TVRF_RCNMASK | TVRF_SETMASK)) | TVRCN_DEMAND;
c91413e6
MW
1816 DRESET(&r->prgwant); DPUTS(&r->prgwant, "%DONE");
1817}
1818
c81c35df
MW
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
c91413e6
MW
1835void 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
c81c35df
MW
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
31d0247c 1875int tvec_remoteset(struct tvec_state *tv, const char *var, void *ctx)
c91413e6
MW
1876{
1877 struct tvec_remotectx *r = ctx;
1878 union tvec_regval rv;
1879 int rc;
1880
1881 if (STRCMP(var, ==, "@exit")) {
31d0247c 1882 if (r->rc.f&TVRF_SETEXIT) { rc = tvec_dupreg(tv, var); goto end; }
c91413e6 1883 if (tvty_flags.parse(&rv, &exit_regdef, tv)) { rc = -1; goto end; }
31d0247c 1884 r->exwant = rv.u; r->rc.f |= TVRF_SETEXIT; rc = 1;
c91413e6 1885 } else if (STRCMP(var, ==, "@progress")) {
31d0247c 1886 if (r->rc.f&TVRF_SETPRG) { rc = tvec_dupreg(tv, var); goto end; }
c81c35df
MW
1887 tvty_text.init(&rv, &progress_regdef);
1888 rc = tvty_text.parse(&rv, &progress_regdef, tv);
31d0247c 1889 if (!rc) {
c81c35df 1890 DRESET(&r->prgwant); DPUTM(&r->prgwant, rv.text.p, rv.text.sz);
31d0247c
MW
1891 r->rc.f |= TVRF_SETPRG;
1892 }
c81c35df 1893 tvty_text.release(&rv, &progress_regdef);
c91413e6
MW
1894 if (rc) { rc = -1; goto end; }
1895 rc = 1;
1896 } else if (STRCMP(var, ==, "@reconnect")) {
31d0247c 1897 if (r->rc.f&TVRF_SETRCN) { rc = tvec_dupreg(tv, var); goto end; }
c91413e6 1898 if (tvty_uenum.parse(&rv, &reconn_regdef, tv)) { rc = -1; goto end; }
31d0247c 1899 r->rc.f = (r->rc.f&~TVRF_RCNMASK) | (rv.u&TVRF_RCNMASK) | TVRF_SETRCN;
c91413e6
MW
1900 rc = 1;
1901 } else
1902 rc = 0;
1903
1904end:
1905 return (rc);
1906}
1907
c81c35df
MW
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 */
c91413e6
MW
1918
1919void 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
c81c35df 1930 /* Reconnect to the server according to policy. */
c91413e6
MW
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
c81c35df 1938 /* Set initial progress state. */
31d0247c 1939 DRESET(&r->progress); DPUTS(&r->progress, "%IDLE");
c81c35df
MW
1940
1941 /* Send the command to the server and handle output. */
31d0247c
MW
1942 QUEUEPK(tv, &r->rc, QF_FORCE, TVPK_TEST)
1943 tvec_serialize(tv->in, DBUF_BUF(&r->rc.bout),
c91413e6 1944 tv->test->regs, tv->nreg, tv->regsz);
c81c35df 1945 else { goto fail; }
c91413e6 1946 rc = handle_packets(tv, r, RCVF_ALLOWEOF, TVPK_TEST | TVPF_ACK, &b);
c81c35df
MW
1947
1948 /* Deal with the outcome. */
c91413e6 1949 switch (rc) {
c81c35df 1950
c91413e6 1951 case RECV_FAIL:
c81c35df
MW
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
c91413e6 1959 case RECV_EOF:
c81c35df
MW
1960 /* End-of-file at a packet boundary. The server crashed trying to run
1961 * our test. Collect the exit status and continue.
1962 */
c91413e6
MW
1963 reap_kid(tv, r);
1964 /* fall through */
c81c35df 1965
c91413e6 1966 case RECV_OK:
c81c35df
MW
1967 /* Successful completion (or EOF). */
1968
1969 /* Notice if the exit status isn't right. */
c91413e6 1970 if (r->exit != r->exwant) f |= f_exit;
c81c35df
MW
1971
1972 /* Notice if the progress token isn't right. */
c91413e6
MW
1973 if (r->progress.len != r->prgwant.len ||
1974 MEMCMP(r->progress.buf, !=, r->prgwant.buf, r->progress.len))
1975 f |= f_progress;
c81c35df
MW
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 */
c91413e6
MW
1980 if (f && (tv->f&TVSF_ACTIVE))
1981 { tvec_fail(tv, 0); tvec_mismatch(tv, TVMF_IN); }
c81c35df
MW
1982
1983 /* If the test failed, then report the exit and progress states
1984 * relative to their expectations.
1985 */
c91413e6
MW
1986 if (!(tv->f&TVSF_ACTIVE) &&
1987 (tv->f&TVSF_OUTMASK) == (TVOUT_LOSE << TVSF_OUTSHIFT)) {
c81c35df
MW
1988
1989 /* Note here that the test failed. */
c91413e6
MW
1990 f |= f_fail;
1991
c81c35df 1992 /* Report exit status. */
c91413e6
MW
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
c81c35df
MW
2001 /* Report progress token. */
2002 rv.text.p = r->progress.buf; rv.text.sz = r->progress.len;
c91413e6
MW
2003 tvec_dumpreg(tv, f&f_progress ? TVRD_FOUND : TVRD_MATCH,
2004 &rv, &progress_regdef);
2005 if (f&f_progress) {
c81c35df 2006 rv.text.p = r->prgwant.buf; rv.text.sz = r->prgwant.len;
c91413e6
MW
2007 tvec_dumpreg(tv, TVRD_EXPECT, &rv, &progress_regdef);
2008 }
2009 }
2010
c81c35df
MW
2011 /* If we received end-of-file, then close the connection. Suppress
2012 * error output if the test passed: it was presumably expected.
2013 */
c91413e6
MW
2014 if (rc == RECV_EOF)
2015 disconnect_remote(tv, r, f ? 0 : ERF_SILENT);
2016 break;
2017 }
2018
c91413e6
MW
2019#undef f_exit
2020#undef f_progress
2021#undef f_fail
2022}
2023
c81c35df
MW
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
2034void 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
c91413e6
MW
2051void tvec_remoteteardown(struct tvec_state *tv, void *ctx)
2052{
2053 struct tvec_remotectx *r = ctx;
31d0247c 2054 buf b;
c91413e6 2055
31d0247c
MW
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);
c91413e6 2060 }
31d0247c
MW
2061 disconnect_remote(tv, r, 0); release_comms(&r->rc);
2062 DDESTROY(&r->prgwant); DDESTROY(&r->progress);
c91413e6
MW
2063}
2064
2065/*----- Connectors --------------------------------------------------------*/
2066
c81c35df
MW
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
c91413e6
MW
2088static 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
c81c35df 2095 /* Try to create the pipes. */
c91413e6
MW
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
c81c35df
MW
2104 /* Flush all of the stream buffers so that we don't get duplicated
2105 * output.
2106 */
c91413e6
MW
2107 fflush(0);
2108
c81c35df 2109 /* Try to set up the child process. */
c91413e6
MW
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) {
c81c35df
MW
2117 /* Child process. */
2118
c91413e6
MW
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));
c81c35df 2125 _exit(127);
c91413e6
MW
2126 }
2127 } else {
c81c35df
MW
2128 /* Parent process. */
2129
c91413e6
MW
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
c81c35df 2136 /* All done. */
c91413e6 2137 rc = 0;
c81c35df 2138
c91413e6 2139end:
c81c35df 2140 /* Clean up. So much of this... */
c91413e6
MW
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
c81c35df
MW
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
c91413e6
MW
2165int 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) {
31d0247c 2176 if (tv->fp) fclose(tv->fp);
c91413e6
MW
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;
2185end:
2186 return (rc);
2187}
2188
c81c35df
MW
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
c91413e6
MW
2205int 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;
2230end:
2231 return (rc);
2232}
e63124bc
MW
2233
2234/*----- That's all, folks -------------------------------------------------*/