5 * (c) 2023 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the mLib utilities library.
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.
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.
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,
28 /*----- Header files ------------------------------------------------------*/
37 #include <sys/types.h>
52 /*----- Preliminaries -----------------------------------------------------*/
54 /* The control macros I'm using below provoke `dangling-else' warnings from
55 * compilers. Suppress them. I generally don't care.
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"
64 #if CLANG_VERSION_P(3, 1)
65 # pragma clang diagnostic ignored "-Wdangling-else"
68 /*----- Basic I/O ---------------------------------------------------------*/
70 /* --- @init_comms@ --- *
72 * Arguments: @struct tvec_remotecomms *rc@ = communication state
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@ --
82 static void init_comms(struct tvec_remotecomms *rc)
84 rc->bin = 0; rc->binsz = 0; dbuf_create(&rc->bout);
85 rc->infd = rc->outfd = -1; rc->f = 0;
88 /* --- @close_comms@ --- *
90 * Arguments: @struct tvec_remotecomms *rc@ = communication state
94 * Use: Close the input and output descriptors.
96 * If the descriptors are already closed -- or were never opened
97 * -- then nothing happens.
100 static void close_comms(struct tvec_remotecomms *rc)
103 if (rc->infd != rc->outfd) close(rc->infd);
107 { close(rc->outfd); rc->outfd = -1; }
108 rc->f |= TVRF_BROKEN;
111 /* --- @release_comms@ --- *
113 * Arguments: @struct tvec_remotecomms *rc@ = communication state
117 * Use: Releases the resources -- most notably the input and output
118 * buffers -- held by the communication state. Also calls
122 static void release_comms(struct tvec_remotecomms *rc)
123 { close_comms(rc); xfree(rc->bin); dbuf_destroy(&rc->bout); }
125 /* --- @setup_comms@ --- *
127 * Arguments: @struct tvec_remotecomms *rc@ = communication state
128 * @int infd, outfd@ = input and output file descriptors
132 * Use: Use the given descriptors for communication.
134 * Clears the private flags.
137 static void setup_comms(struct tvec_remotecomms *rc, int infd, int outfd)
139 rc->infd = infd; rc->outfd = outfd;
140 rc->binoff = rc->binlen = 0;
146 * Arguments: @struct tvec_state *tv@ = test-vector state
147 * @struct tvec_remotecomms *rc@ = communication state
148 * @const char *msg, ...@ = format string and arguments
152 * Use: Reports the message as an error, closes communications and
153 * marks them as broken.
156 static PRINTF_LIKE(3, 4)
157 int ioerr(struct tvec_state *tv, struct tvec_remotecomms *rc,
158 const char *msg, ...)
163 close_comms(rc); rc->f |= TVRF_BROKEN;
164 tvec_report_v(tv, TVLEV_ERR, msg, &ap);
169 /* --- @send_all@ --- *
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
175 * Returns: Zero on success, %$-1$% on error.
177 * Use: Send the output buffer over the communication state's output
178 * descriptor, even if it has to be written in multiple pieces.
181 static int send_all(struct tvec_state *tv, struct tvec_remotecomms *rc,
182 const unsigned char *p, size_t sz)
188 n = write(rc->outfd, p, sz);
192 ret = ioerr(tv, rc, "failed to send: %s",
193 n ? strerror(errno) : "empty write");
202 /* --- @recv_all@ --- *
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
211 * Returns: A @RECV_...@ code.
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.
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@.
226 #define RCVF_ALLOWEOF 1u
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)
242 n = read(rc->infd, p, sz);
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); }
249 return (ioerr(tv, rc, "failed to receive: %s",
250 n ? strerror(errno) : "unexpected end-of-file"));
252 *n_out = tot; return (RECV_OK);
257 /* --- @buferr@ --- *
259 * Arguments: @struct tvec_state *tv@ = test-vector state
260 * @struct tvec_remotecomms *rc@ = communication state
264 * Use: Report a problem preparing the output buffer.
267 static int buferr(struct tvec_state *tv, struct tvec_remotecomms *rc)
268 { return (ioerr(tv, rc, "failed to build output packet")); }
270 /* --- @malformed@ --- *
272 * Arguments: @struct tvec_state *tv@ = test-vector state
273 * @struct tvec_remotecomms *rc@ = communication state
277 * Use: Report an I/O error that the incoming packet is malformed.
280 static int malformed(struct tvec_state *tv, struct tvec_remotecomms *rc)
281 { return (ioerr(tv, rc, "received malformed packet")); }
283 /* --- @remote_send@ --- *
285 * Arguments: @struct tvec_state *tv@ = test-vector state
286 * @struct tvec_remotecomms *rc@ = communication state
288 * Returns: Zero on success, %$-1$% on error.
290 * Use: Send the accuulated contents of the output buffer @rc->bout@.
292 * The function arranges to convert @SIGPIPE@ into an error.
294 * If the output buffer is broken, report this as an I/O error.
297 #define SENDBUFSZ 4096
299 static int remote_send(struct tvec_state *tv, struct tvec_remotecomms *rc)
301 void (*opipe)(int) = SIG_ERR;
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; }
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));
315 /* Transmit the packet. */
316 if (send_all(tv, rc, DBBASE(&rc->bout), DBLEN(&rc->bout)))
317 { ret = -1; goto end; }
319 /* Done. Put things back the way we found them. */
323 if (opipe != SIG_ERR) signal(SIGPIPE, opipe);
327 /* --- @receive_buffered@ --- *
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
334 * Returns: A @RECV_...@ code.
336 * Use: Reads a block of data from the input descriptor into the
339 * This is the main machinery for manipulating the input buffer.
340 * The buffer has three regions:
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'.
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@.
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.
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.
366 #define RECVBUFSZ 4096u
368 static int receive_buffered(struct tvec_state *tv,
369 struct tvec_remotecomms *rc,
370 unsigned f, size_t want)
375 /* If we can supply the caller's requirement from the buffer then do
378 if (rc->binlen - rc->binoff >= want) return (RECV_OK);
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);
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;
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.
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);
401 /* Note how much material we have and return. */
402 rc->binlen += sz; return (RECV_OK);
405 /* --- @remote_recv@ --- *
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
411 * Returns: A @RECV_...@ code.
413 * Use: Receive a packet into the input buffer @rc->bin@ and
414 * establish @*b_out@ to read from it.
417 static int remote_recv(struct tvec_state *tv, struct tvec_remotecomms *rc,
418 unsigned f, buf *b_out)
424 ASSIGN64(szmax, (size_t)-1);
426 /* Preflight checks. */
427 if (rc->f&TVRF_BROKEN) return (RECV_FAIL);
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);
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;
443 /* --- @QUEUEPK_TAG@, @QUEUEPK@ --- *
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
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.
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); \
471 DBUF_ENCLOSEITAG(tag##__frame, &(rc)->bout, (rc)->t, 64_L) \
472 MC_BEFORE(tag##__pkty, { \
473 dbuf_putu16l(&(rc)->bout, (pk)); \
476 #define QUEUEPK(tv, rc, fl, pk) QUEUEPK_TAG(queue, (tv), (rc), (fl), (pk))
478 /*----- Packet types ------------------------------------------------------*/
480 #define TVPF_ACK 0x0001u
482 #define TVPK_VER 0x0000u /* --> min, max: u16 *
484 #define TVPK_BGROUP 0x0002u /* --> name: str16
486 #define TVPK_TEST 0x0004u /* --> in: regs
488 #define TVPK_EGROUP 0x0006u /* --> --- *
491 #define TVPK_REPORT 0x0100u /* <-- level: u16; msg: string */
492 #define TVPK_PROGRESS 0x0102u /* <-- st: str16 */
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 */
503 /*----- Server ------------------------------------------------------------*/
505 /* Forward declaration of output operations. */
506 static const struct tvec_outops remote_ops;
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 */
512 /* --- @tvec_setprogress@, @tvec_setprogress_v@ --- *
514 * Arguments: @const char *status@ = progress status token format
515 * @va_list ap@ = argument tail
519 * Use: Reports the progress of a test execution to the client.
521 * The framework makes use of tokens beginning with %|%|%:
523 * * %|%IDLE|%: during the top-level server code;
525 * * %|%SETUP|%: during the enclosing environment's @before@
528 * * %|%RUN|%: during the environment's @run@ function, or the
531 * * %|%DONE|%: during the enclosing environment's @after@
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.
541 int tvec_setprogress(const char *status, ...)
546 va_start(ap, status); rc = tvec_setprogress_v(status, &ap); va_end(ap);
550 int tvec_setprogress_v(const char *status, va_list *ap)
552 /* Force immediate output in case we crash before the buffer is output
555 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_PROGRESS)
556 dbuf_vputstrf16l(&srvrc.bout, status, ap);
561 /* --- @tvec_remoteserver@ --- *
563 * Arguments: @int infd@, @int outfd@ = input and output file descriptors
564 * @const struct tvec_config *config@ = test configuration
566 * Returns: Suggested exit code.
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@.
572 * This function is not particularly general purpose. It
573 * expects to `take over' the process, and makes use of private
577 int tvec_remoteserver(int infd, int outfd, const struct tvec_config *config)
582 const struct tvec_test *t;
584 const struct tvec_env *env = 0;
588 /* Initialize the communication machinery. */
589 setup_comms(&srvrc, infd, outfd);
591 /* Begin a test session using our custom output driver. */
592 tvec_begin(&srvtv, config, &srvout);
594 /* Version negotiation. Expect a @TVPK_VER@ packet. At the moment,
595 * there's only version zero, so we return that.
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",
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; }
610 /* Handle packets until the server closes the connection.
612 * The protocol looks much simpler from our point of view than from the
615 * * Receive @TVPK_VER@; respond with @TVPK_VER | TVPF_ACK@.
617 * * Receive zero or more @TVPK_BGROUP@. Open a test group, producing
618 * output packets, and eventually answer with @TVPK_BGROUP | TVPF_ACK@.
620 * -- Receive zero or more @TVPK_TEST@. Run a test, producing output
621 * packets, and eventually answer with @TVPK_TEST | TVPF_ACK@.
623 * -- Receive @TVPK_EGROUP@. Maybe produce output packets, and
624 * answer with @TVPK_EGROUP | TVPF_ACK@.
630 /* Read a packet. End-of-file is expected here (and pretty much nowhere
631 * else). Otherwise, we expect to see @TVPK_BGROUP@.
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;
643 /* Parse the packet payload. */
644 p = buf_getmem16l(&b, &sz); if (!p) goto bad;
645 if (BLEFT(&b)) goto bad;
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))
651 rc = ioerr(&srvtv, &srvrc, "unknown test group `%.*s'",
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);
664 /* Initialize the registers. */
665 tvec_initregs(&srvtv);
667 /* Report that the group has been opened and that we're ready to run
670 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_BGROUP | TVPF_ACK);
671 else { rc = -1; goto end; }
673 /* Handle packets until we're told to end the group. */
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;
685 /* Check the payload. */
686 if (BLEFT(&b)) goto bad;
688 /* Leave the group loop. */
694 /* Parse the packet payload. */
695 if (tvec_deserialize(srvtv.in, &b, srvtv.test->regs,
696 srvtv.nreg, srvtv.regsz))
698 if (BLEFT(&b)) goto bad;
700 /* If we're not skipping the test group, then actually try to
703 if (!(srvtv.f&TVSF_SKIP)) {
705 /* Prepare the output registers and reset the test outcome.
706 * (The environment may force a skip.)
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;
713 /* Invoke the environment @before@ function. */
714 tvec_setprogress("%%SETUP");
715 if (env && env->before) env->before(&srvtv, ctx);
717 /* Run the actual test. */
718 if (!(srvtv.f&TVSF_ACTIVE))
719 /* setup forced a skip */;
721 tvec_setprogress("%%RUN");
723 env->run(&srvtv, t->fn, ctx);
725 t->fn(srvtv.in, srvtv.out, ctx);
726 tvec_check(&srvtv, 0);
730 /* Conclude the test. */
731 tvec_setprogress("%%DONE");
732 if (env && env->after) env->after(&srvtv, ctx);
733 tvec_endtest(&srvtv);
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; }
743 /* Some other kind of packet. Complain. */
745 rc = ioerr(&srvtv, &srvrc,
746 "unexpected packet type 0x%04x during test group",
754 /* The test group completed. */
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;
761 /* Report completion. */
762 QUEUEPK(&srvtv, &srvrc, QF_FORCE, TVPK_EGROUP | TVPF_ACK);
763 else { rc = -1; goto end; }
767 rc = ioerr(&srvtv, &srvrc,
768 "unexpected packet type 0x%04x at top level", pk);
774 /* Clean up and return. */
775 if (env && env->teardown) env->teardown(&srvtv, ctx);
777 if (srvtv.test) tvec_releaseregs(&srvtv);
778 release_comms(&srvrc); tvec_end(&srvtv);
782 /* Miscellaneous malformed packet. */
783 rc = malformed(&srvtv, &srvrc); goto end;
786 /*----- Server output driver ----------------------------------------------*/
788 /* --- @remote_bsession@ --- *
790 * Arguments: @struct tvec_output *o@ = output sink (ignored)
791 * @struct tvec_state *tv@ = the test state producing output
795 * Use: Begin a test session.
797 * The remote driver does nothing at all.
800 static void remote_bsession(struct tvec_output *o, struct tvec_state *tv)
803 /* --- @remote_esession@ --- *
805 * Arguments: @struct tvec_output *o@ = output sink (ignored)
807 * Returns: Suggested exit code.
809 * Use: End a test session.
811 * The remote driver returns a suitable exit code without
815 static int remote_esession(struct tvec_output *o)
816 { return (srvtv.f&TVSF_ERROR ? 2 : 0); }
818 /* --- @remote_bgroup@ --- *
820 * Arguments: @struct tvec_output *o@ = output sink (ignored)
824 * Use: Begin a test group.
826 * This is a stub which should never be called.
829 static void remote_bgroup(struct tvec_output *o)
830 { assert(!"remote_bgroup"); }
832 /* --- @remote_skipgroup@ --- *
834 * Arguments: @struct tvec_output *o@ = output sink (ignored)
835 * @const char *excuse@, @va_list *ap@ = reason for skipping the
840 * Use: Report that a test group is being skipped.
842 * The remote driver sends a @TVPK_SKIP@ packet to its client.
845 static void remote_skipgroup(struct tvec_output *o,
846 const char *excuse, va_list *ap)
848 QUEUEPK(&srvtv, &srvrc, 0, TVPK_SKIPGRP)
849 dbuf_vputstrf16l(&srvrc.bout, excuse, ap);
852 /* --- @remote_egroup@ --- *
854 * Arguments: @struct tvec_output *o@ = output sink (ignored)
858 * Use: Report that a test group has finished.
860 * This is a stub which should never be called.
863 static void remote_egroup(struct tvec_output *o)
864 { assert(!"remote_egroup"); }
866 /* --- @remote_btest@ --- *
868 * Arguments: @struct tvec_output *o@ = output sink (ignored)
872 * Use: Report that a test is starting.
874 * This is a stub which should never be called.
877 static void remote_btest(struct tvec_output *o)
878 { assert(!"remote_btest"); }
880 /* --- @remote_skip@, @remote_fail@ --- *
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
891 * Use: Report that a test has been skipped or failed.
893 * The remote driver sends a @TVPK_SKIP@ or @TVPK_FAIL@ packet
894 * to its client as appropriate.
897 static void remote_skip(struct tvec_output *o,
898 const char *excuse, va_list *ap)
900 QUEUEPK(&srvtv, &srvrc, 0, TVPK_SKIP)
901 dbuf_vputstrf16l(&srvrc.bout, excuse, ap);
904 static void remote_fail(struct tvec_output *o,
905 const char *detail, va_list *ap)
907 QUEUEPK(&srvtv, &srvrc, 0, TVPK_FAIL)
909 dbuf_putbyte(&srvrc.bout, 0);
911 dbuf_putbyte(&srvrc.bout, 1);
912 dbuf_vputstrf16l(&srvrc.bout, detail, ap);
916 /* --- @remote_dumpreg@ --- *
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
925 * Use: Dump a register.
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.
932 static void remote_dumpreg(struct tvec_output *o,
933 unsigned disp, const union tvec_regval *rv,
934 const struct tvec_regdef *rd)
936 const struct tvec_regdef *reg;
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");
945 QUEUEPK(&srvtv, &srvrc, 0, TVPK_DUMPREG) {
946 dbuf_putu16l(&srvrc.bout, r);
947 dbuf_putu16l(&srvrc.bout, disp);
949 dbuf_putbyte(&srvrc.bout, 0);
951 dbuf_putbyte(&srvrc.bout, 1);
952 rd->ty->tobuf(DBUF_BUF(&srvrc.bout), rv, rd);
957 /* --- @remote_etest@ --- *
959 * Arguments: @struct tvec_output *o@ = output sink (ignored)
960 * @unsigned outcome@ = the test outcome
964 * Use: Report that a test has finished.
966 * The remote driver does nothing at all.
969 static void remote_etest(struct tvec_output *o, unsigned outcome)
972 /* --- @remote_bbench@ --- *
974 * Arguments: @struct tvec_output *o@ = output sink (ignored)
975 * @const char *ident@ = identifying register values
976 * @unsigned unit@ = measurement unit (@TVBU_...@)
980 * Use: Report that a benchmark has started.
982 * The remote driver sends a @TVPK_BBENCH@ packet to its client.
985 static void remote_bbench(struct tvec_output *o,
986 const char *ident, unsigned unit)
988 QUEUEPK(&srvtv, &srvrc, 0, TVPK_BBENCH) {
989 dbuf_putstr32l(&srvrc.bout, ident);
990 dbuf_putu16l(&srvrc.bout, unit);
994 /* --- @remote_ebench@ --- *
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
1003 * Use: Report a benchmark's results
1005 * The remote driver sends a @TVPK_EBENCH@ packet to its client.
1008 static void remote_ebench(struct tvec_output *o,
1009 const char *ident, unsigned unit,
1010 const struct bench_timing *t)
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);
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);
1026 /* --- @remote_report@ --- *
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
1035 * Use: Report a message to the user.
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.
1043 static void remote_report(struct tvec_output *o, unsigned level,
1044 const char *msg, va_list *ap)
1046 QUEUEPK(&srvtv, &srvrc, 0, TVPK_REPORT) {
1047 dbuf_putu16l(&srvrc.bout, level);
1048 dbuf_vputstrf16l(&srvrc.bout, msg, ap);
1050 fprintf(stderr, "%s %s: ", QUIS, tvec_strlevel(level));
1051 vfprintf(stderr, msg, *ap);
1052 fputc('\n', stderr);
1056 /* --- @remote_destroy@ --- *
1058 * Arguments: @struct tvec_output *o@ = output sink (ignored)
1062 * Use: Release the resources held by the output driver.
1064 * The remote driver does nothing at all.
1067 static void remote_destroy(struct tvec_output *o)
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,
1079 /*----- Pseudoregister definitions ----------------------------------------*/
1081 static const struct tvec_flag exit_flags[] = {
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 },
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.
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)))
1103 (goto-char (point-min))
1104 (search-forward (concat "***" "BEGIN siglist" "***"))
1105 (beginning-of-line 2)
1106 (delete-region (point)
1108 (search-forward "***END***")
1111 (dolist (sig signals)
1112 (insert (format "#ifdef SIG%s\n { \"SIG%s\", TVXF_VALMASK | TVXF_SIG, SIG%s | TVXF_SIG },\n#endif\n"
1116 /***BEGIN siglist***/
1118 { "SIGHUP", TVXF_VALMASK | TVXF_SIG, SIGHUP | TVXF_SIG },
1121 { "SIGINT", TVXF_VALMASK | TVXF_SIG, SIGINT | TVXF_SIG },
1124 { "SIGQUIT", TVXF_VALMASK | TVXF_SIG, SIGQUIT | TVXF_SIG },
1127 { "SIGILL", TVXF_VALMASK | TVXF_SIG, SIGILL | TVXF_SIG },
1130 { "SIGTRAP", TVXF_VALMASK | TVXF_SIG, SIGTRAP | TVXF_SIG },
1133 { "SIGABRT", TVXF_VALMASK | TVXF_SIG, SIGABRT | TVXF_SIG },
1136 { "SIGIOT", TVXF_VALMASK | TVXF_SIG, SIGIOT | TVXF_SIG },
1139 { "SIGEMT", TVXF_VALMASK | TVXF_SIG, SIGEMT | TVXF_SIG },
1142 { "SIGFPE", TVXF_VALMASK | TVXF_SIG, SIGFPE | TVXF_SIG },
1145 { "SIGKILL", TVXF_VALMASK | TVXF_SIG, SIGKILL | TVXF_SIG },
1148 { "SIGBUS", TVXF_VALMASK | TVXF_SIG, SIGBUS | TVXF_SIG },
1151 { "SIGSEGV", TVXF_VALMASK | TVXF_SIG, SIGSEGV | TVXF_SIG },
1154 { "SIGSYS", TVXF_VALMASK | TVXF_SIG, SIGSYS | TVXF_SIG },
1157 { "SIGPIPE", TVXF_VALMASK | TVXF_SIG, SIGPIPE | TVXF_SIG },
1160 { "SIGALRM", TVXF_VALMASK | TVXF_SIG, SIGALRM | TVXF_SIG },
1163 { "SIGTERM", TVXF_VALMASK | TVXF_SIG, SIGTERM | TVXF_SIG },
1166 { "SIGURG", TVXF_VALMASK | TVXF_SIG, SIGURG | TVXF_SIG },
1169 { "SIGSTOP", TVXF_VALMASK | TVXF_SIG, SIGSTOP | TVXF_SIG },
1172 { "SIGTSTP", TVXF_VALMASK | TVXF_SIG, SIGTSTP | TVXF_SIG },
1175 { "SIGCONT", TVXF_VALMASK | TVXF_SIG, SIGCONT | TVXF_SIG },
1178 { "SIGCHLD", TVXF_VALMASK | TVXF_SIG, SIGCHLD | TVXF_SIG },
1181 { "SIGCLD", TVXF_VALMASK | TVXF_SIG, SIGCLD | TVXF_SIG },
1184 { "SIGTTIN", TVXF_VALMASK | TVXF_SIG, SIGTTIN | TVXF_SIG },
1187 { "SIGTTOU", TVXF_VALMASK | TVXF_SIG, SIGTTOU | TVXF_SIG },
1190 { "SIGPOLL", TVXF_VALMASK | TVXF_SIG, SIGPOLL | TVXF_SIG },
1193 { "SIGIO", TVXF_VALMASK | TVXF_SIG, SIGIO | TVXF_SIG },
1196 { "SIGTIN", TVXF_VALMASK | TVXF_SIG, SIGTIN | TVXF_SIG },
1199 { "SIGXCPU", TVXF_VALMASK | TVXF_SIG, SIGXCPU | TVXF_SIG },
1202 { "SIGXFSZ", TVXF_VALMASK | TVXF_SIG, SIGXFSZ | TVXF_SIG },
1205 { "SIGVTALRM", TVXF_VALMASK | TVXF_SIG, SIGVTALRM | TVXF_SIG },
1208 { "SIGPROF", TVXF_VALMASK | TVXF_SIG, SIGPROF | TVXF_SIG },
1211 { "SIGWINCH", TVXF_VALMASK | TVXF_SIG, SIGWINCH | TVXF_SIG },
1214 { "SIGUSR1", TVXF_VALMASK | TVXF_SIG, SIGUSR1 | TVXF_SIG },
1217 { "SIGUSR2", TVXF_VALMASK | TVXF_SIG, SIGUSR2 | TVXF_SIG },
1220 { "SIGSTKFLT", TVXF_VALMASK | TVXF_SIG, SIGSTKFLT | TVXF_SIG },
1223 { "SIGINFO", TVXF_VALMASK | TVXF_SIG, SIGINFO | TVXF_SIG },
1226 { "SIGPWR", TVXF_VALMASK | TVXF_SIG, SIGPWR | TVXF_SIG },
1229 { "SIGTHR", TVXF_VALMASK | TVXF_SIG, SIGTHR | TVXF_SIG },
1232 { "SIGLWP", TVXF_VALMASK | TVXF_SIG, SIGLWP | TVXF_SIG },
1235 { "SIGLIBRT", TVXF_VALMASK | TVXF_SIG, SIGLIBRT | TVXF_SIG },
1238 { "SIGLOST", TVXF_VALMASK | TVXF_SIG, SIGLOST | TVXF_SIG },
1242 /* This should be folded into the signal entries above. */
1243 { "signal", TVXF_SIG, TVXF_SIG },
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 } };
1255 static const struct tvec_regdef progress_regdef =
1256 { "@progress", 0, &tvty_text, 0 };
1260 static const struct tvec_uassoc reconn_assocs[] = {
1261 { "on-demand", TVRCN_DEMAND },
1262 { "force", TVRCN_FORCE },
1263 { "skip", TVRCN_SKIP },
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 } };
1272 /*----- Client ------------------------------------------------------------*/
1274 /* Connection state. */
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 */
1282 /* --- @handle_packets@ --- *
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
1290 * Returns: A @RECV_...@ code.
1292 * Use: Handles notification packets from the server until a final
1293 * termination packet is received.
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.
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@.
1312 static int handle_packets(struct tvec_state *tv, struct tvec_remotectx *r,
1313 unsigned f, uint16 end, buf *b_out)
1315 struct tvec_output *o = tv->output;
1317 const char *p; size_t n;
1320 const struct tvec_regdef *rd;
1321 struct bench_timing bt;
1322 struct tvec_reg *reg = 0;
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.
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; }
1336 /* Dispatch based on the packet type. */
1340 /* A progress report. Update the saved progress. */
1342 p = buf_getmem16l(b, &n); if (!p) goto bad;
1343 if (BLEFT(b)) goto bad;
1345 DRESET(&r->progress); DPUTM(&r->progress, p, n); DPUTZ(&r->progress);
1349 /* A report. Recover the message and pass it along. */
1351 if (buf_getu16l(b, &u)) goto bad;
1352 p = buf_getmem16l(b, &n); if (!p) goto bad;
1353 if (BLEFT(b)) goto bad;
1355 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1356 tvec_report(tv, u, "%s", d.buf);
1360 /* A request to skip the group. Recover the excuse message and pass
1364 p = buf_getmem16l(b, &n); if (!p) goto bad;
1365 if (BLEFT(b)) goto bad;
1367 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1368 tvec_skipgroup(tv, "%s", d.buf);
1372 /* A request to skip the test. Recover the excuse message and pass
1373 * it along, if it's not unreasonable.
1376 if (!(tv->f&TVSF_ACTIVE)) {
1377 rc = ioerr(tv, &r->rc, "test `%s' not active", tv->test->name);
1381 p = buf_getmem16l(b, &n); if (!p) goto bad;
1382 if (BLEFT(b)) goto bad;
1384 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1385 tvec_skip(tv, "%s", d.buf);
1389 /* A report that the test failed. Recover the detail message, if
1390 * any, and pass it along, if it's not unreasonable.
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",
1400 rc = buf_getbyte(b); if (rc < 0) goto bad;
1401 if (rc) { p = buf_getmem16l(b, &n); if (!p) goto bad; }
1403 if (BLEFT(b)) goto bad;
1408 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1409 tvec_fail(tv, "%s", d.buf);
1414 /* A request to dump a register. */
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'",
1425 if (v >= TVRD_LIMIT) {
1426 rc = ioerr(tv, &r->rc, "register disposition %u out of range", v);
1430 /* Read the flag. If there's no register value, then `dump' its
1431 * absence. Otherwise retrieve the register value and dump it.
1433 rc = buf_getbyte(b); if (rc < 0) goto bad;
1435 tvec_dumpreg(tv, v, 0, rd);
1437 if (!reg) reg = xmalloc(tv->regsz);
1438 rd->ty->init(®->v, rd);
1439 rc = rd->ty->frombuf(b, ®->v, rd);
1440 if (!rc) tvec_dumpreg(tv, v, ®->v, rd);
1441 rd->ty->release(®->v, rd);
1444 if (BLEFT(b)) goto bad;
1448 /* A report that we're starting a benchmark. Pass this along. */
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);
1458 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1459 o->ops->bbench(o, d.buf, u);
1463 /* A report that a benchmark completed. Pass this along. */
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);
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;
1476 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
1477 o->ops->ebench(o, d.buf, u, v&BTF_ANY ? &bt : 0);
1481 /* Something else. This is unexpected. */
1483 rc = ioerr(tv, &r->rc, "unexpected packet type 0x%04x", pk);
1493 rc = malformed(tv, &r->rc); goto end;
1496 /* --- @reap_kid@ --- *
1498 * Arguments: @struct tvec_state *tv@ = test-vector state
1499 * @struct tvec_remotectx *r@ = remote client context
1503 * Use: Determine the exit status of a broken connection, setting
1504 * @r->exit@ appropriately.
1506 * If @r->kid@ is negative, the exit status has already been
1507 * set, and nothing further happens; this is not an error.
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@.
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
1516 * It is an error to call this function if the connection is not
1520 static void reap_kid(struct tvec_state *tv, struct tvec_remotectx *r)
1525 assert(r->rc.f&TVRF_BROKEN);
1527 { r->exit = TVXST_DISCONN; r->kid = -1; }
1528 else if (r->kid > 0) {
1529 kid = waitpid(r->kid, &st, 0);
1531 tvec_notice(tv, "failed to wait for remote child: %s",
1533 r->exit = TVXST_ERR;
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);
1546 tvec_notice(tv, "remote child died with unknown status 0x%04x",
1548 r->exit = TVXST_UNK;
1554 /* --- @report_errline@ --- *
1556 * Arguments: @char *p@ = pointer to the line
1557 * @size_t n@ = length in characters
1558 * @void *ctx@ = context, secretly a @struct tvec_remotectx@
1562 * Use: Print a line of stderr output from the child. If
1563 * @TVRF_MUFFLE@ is set, then discard the line silently.
1565 * This is an @lbuf_func@, invoked via @drain_errfd@.
1568 static void report_errline(char *p, size_t n, void *ctx)
1570 struct tvec_remotectx *r = ctx;
1571 struct tvec_state *tv = r->tv;
1573 if (p && !(r->rc.f&TVRF_MUFFLE))
1574 tvec_notice(tv, "child process stderr: %s", p);
1577 /* --- @drain_errfd@ --- *
1579 * Arguments: @struct tvec_state *tv@ = test-vector state
1580 * @struct tvec_remotectx *r@ = remote client context
1581 * @unsigned f@ = receive flags (@ERF_...@)
1583 * Returns: Zero on success, %$-1$% on error.
1585 * Use: Collect material written by the child to its stderr stream
1588 * If @f@ has @ERF_SILENT@ set, then discard the stderr material
1589 * without reporting it. Otherwise it is reported as
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@.
1596 * If @r->errfd@ is already closed, or never established, then
1597 * do nothing and return successfully.
1600 #define ERF_SILENT 0x0001u
1601 #define ERF_CLOSE 0x0002u
1602 static int drain_errfd(struct tvec_state *tv, struct tvec_remotectx *r,
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.
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");
1622 /* Read pieces of error output and feed them into the line buffer. */
1624 sz = lbuf_free(&r->errbuf, &p);
1625 n = read(r->errfd, p, sz);
1628 if (errno == EINTR) continue;
1629 if (!(f&ERF_CLOSE) && (errno == EWOULDBLOCK || errno == EAGAIN))
1631 rc = ioerr(tv, &r->rc, "failed to read child stderr: %s",
1635 lbuf_flush(&r->errbuf, p, n);
1642 lbuf_close(&r->errbuf);
1643 close(r->errfd); r->errfd = -1;
1648 /* --- @disconnect_remote@ --- *
1650 * Arguments: @struct tvec_state *tv@ = test-vector state
1651 * @struct tvec_remotectx *r@ = remote client context
1652 * @unsigned f@ = receive flags (@DCF_...@)
1656 * Use: Disconnect and shut down all of the remote client state.
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.
1661 * In detail: this function closes the @infd@ and @outfd@
1662 * descriptors, drains and closes @errfd@, and collects the exit
1666 #define DCF_KILL 0x0100u
1667 static void disconnect_remote(struct tvec_state *tv,
1668 struct tvec_remotectx *r, unsigned f)
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);
1675 /* --- @connect_remote@ --- *
1677 * Arguments: @struct tvec_state *tv@ = test-vector state
1678 * @struct tvec_remotectx *r@ = remote client context
1680 * Returns: Zero on success, %$-1$% on error.
1682 * Use: Connect to the test server.
1685 static int connect_remote(struct tvec_state *tv, struct tvec_remotectx *r)
1687 const struct tvec_remoteenv *re = r->re;
1691 int infd = -1, outfd = -1, errfd = -1, rc;
1693 /* If we're already connected, then there's nothing to do. */
1694 if (r->kid >= 0) { rc = 0; goto end; }
1696 /* Set the preliminary progress indication. */
1697 DRESET(&r->progress); DPUTS(&r->progress, "%INIT");
1699 /* Call the connection function to establish descriptors. */
1700 if (re->r.connect(&kid, &infd, &outfd, &errfd, tv, re))
1701 { rc = -1; goto end; }
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;
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; }
1718 rc = ioerr(tv, &r->rc, "protocol version %u not supported", v);
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; }
1734 if (rc) disconnect_remote(tv, r, DCF_KILL);
1737 rc = malformed(tv, &r->rc); goto end;
1740 /* --- @check_comms@ --- *
1742 * Arguments: @struct tvec_state *tv@ = test-vector state
1743 * @struct tvec_remotectx *r@ = remote client context
1745 * Returns: A @CONN_...@ code reflecting the current communication
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.
1753 static int check_comms(struct tvec_state *tv, struct tvec_remotectx *r)
1756 return (CONN_BROKEN);
1757 else if (r->rc.f&TVRF_BROKEN)
1758 { disconnect_remote(tv, r, DCF_KILL); return (CONN_FAILED); }
1760 return (CONN_ESTABLISHED);
1763 /* --- @try_reconnect@ --- *
1765 * Arguments: @struct tvec_state *tv@ = test-vector state
1766 * @struct tvec_remotectx *r@ = remote client context
1768 * Returns: A @CONN_...@ code reflecting the new communication state.
1770 * Use: Reconnects to the server according to the configured
1771 * @TVRCN_...@ policy.
1774 static int try_reconnect(struct tvec_state *tv, struct tvec_remotectx *r)
1778 switch (r->rc.f&TVRF_RCNMASK) {
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;
1788 disconnect_remote(tv, r, DCF_KILL);
1789 if (connect_remote(tv, r)) rc = CONN_FAILED;
1790 else rc = CONN_FRESH;
1793 rc = check_comms(tv, r);
1801 /*----- Remote environment ------------------------------------------------*/
1803 /* --- @reset_vars@ --- *
1805 * Arguments: @struct tvec_remotectx *r@ = remote client context
1809 * Use: Reset the pseudoregisters set through @tvec_remoteset@.
1812 static void reset_vars(struct tvec_remotectx *r)
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");
1819 /* --- @tvec_remotesetup@ --- *
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
1828 * Use: Initialize a timeout environment context.
1830 * The environment description should be a @struct
1831 * tvec_remoteenv@ subclass suitable for use by the @connect@
1835 void tvec_remotesetup(struct tvec_state *tv, const struct tvec_env *env,
1836 void *pctx, void *ctx)
1838 struct tvec_remotectx *r = ctx;
1839 const struct tvec_remoteenv *re = (const struct tvec_remoteenv *)env;
1841 assert(!re->r.env || tv->test->env == &re->_env);
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");
1852 /* --- @tvec_remoteset@ --- *
1854 * Arguments: @struct tvec_state *tv@ = test vector state
1855 * @const char *var@ = variable name to set
1856 * @void *ctx@ = context pointer
1858 * Returns: %$+1$% on success, %$0$% if the variable name was not
1859 * recognized, or %$-1$% on any other error.
1861 * Use: Set a special variable. The following special variables are
1864 * * %|@exit|% is the expected exit status; see @TVXF_...@ and
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.
1872 * * %|reconnect|% is a reconnection policy; see @TVRCN_...@.
1875 int tvec_remoteset(struct tvec_state *tv, const char *var, void *ctx)
1877 struct tvec_remotectx *r = ctx;
1878 union tvec_regval rv;
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);
1890 DRESET(&r->prgwant); DPUTM(&r->prgwant, rv.text.p, rv.text.sz);
1891 r->rc.f |= TVRF_SETPRG;
1893 tvty_text.release(&rv, &progress_regdef);
1894 if (rc) { rc = -1; goto end; }
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;
1908 /* --- @tvec_remoterun@ --- *
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
1916 * Use: Run a test on a remote server.
1919 void tvec_remoterun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
1921 struct tvec_remotectx *r = ctx;
1922 union tvec_regval rv;
1925 #define f_progress 2u
1930 /* Reconnect to the server according to policy. */
1931 switch (try_reconnect(tv, r)) {
1933 tvec_skip(tv, "failed to connect to test backend"); return;
1935 tvec_skip(tv, "no connection"); return;
1938 /* Set initial progress state. */
1939 DRESET(&r->progress); DPUTS(&r->progress, "%IDLE");
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);
1946 rc = handle_packets(tv, r, RCVF_ALLOWEOF, TVPK_TEST | TVPF_ACK, &b);
1948 /* Deal with the outcome. */
1952 /* Some kind of error. Abandon ship. */
1955 tvec_skip(tv, "remote test runner communications failed");
1956 disconnect_remote(tv, r, 0);
1960 /* End-of-file at a packet boundary. The server crashed trying to run
1961 * our test. Collect the exit status and continue.
1967 /* Successful completion (or EOF). */
1969 /* Notice if the exit status isn't right. */
1970 if (r->exit != r->exwant) f |= f_exit;
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))
1977 /* If we found something wrong but the test is passing so far, then
1978 * report the failure and dump the input registers.
1980 if (f && (tv->f&TVSF_ACTIVE))
1981 { tvec_fail(tv, 0); tvec_mismatch(tv, TVMF_IN); }
1983 /* If the test failed, then report the exit and progress states
1984 * relative to their expectations.
1986 if (!(tv->f&TVSF_ACTIVE) &&
1987 (tv->f&TVSF_OUTMASK) == (TVOUT_LOSE << TVSF_OUTSHIFT)) {
1989 /* Note here that the test failed. */
1992 /* Report exit status. */
1994 tvec_dumpreg(tv, f&f_exit ? TVRD_FOUND : TVRD_MATCH,
1998 tvec_dumpreg(tv, TVRD_EXPECT, &rv, &exit_regdef);
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);
2006 rv.text.p = r->prgwant.buf; rv.text.sz = r->prgwant.len;
2007 tvec_dumpreg(tv, TVRD_EXPECT, &rv, &progress_regdef);
2011 /* If we received end-of-file, then close the connection. Suppress
2012 * error output if the test passed: it was presumably expected.
2015 disconnect_remote(tv, r, f ? 0 : ERF_SILENT);
2024 /* --- @tvec_remoteafter@ --- *
2026 * Arguments: @struct tvec_state *tv@ = test vector state
2027 * @void *ctx@ = context pointer
2031 * Use: Reset variables to their default values.
2034 void tvec_remoteafter(struct tvec_state *tv, void *ctx)
2036 struct tvec_remotectx *r = ctx;
2041 /* --- @tvec_remoteteardown@ --- *
2043 * Arguments: @struct tvec_state *tv@ = test vector state
2044 * @void *ctx@ = context pointer
2048 * Use: Tear down the remote environment.
2051 void tvec_remoteteardown(struct tvec_state *tv, void *ctx)
2053 struct tvec_remotectx *r = ctx;
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);
2061 disconnect_remote(tv, r, 0); release_comms(&r->rc);
2062 DDESTROY(&r->prgwant); DDESTROY(&r->progress);
2065 /*----- Connectors --------------------------------------------------------*/
2067 /* --- @fork_common@ --- *
2069 * Arguments: @pid_t *kid_out@ = where to put child process-id
2070 * @int *infd_out, *outfd_out, *errfd_out@ = where to put file
2072 * @struct tvec_state *tv@ = test vector state
2074 * Returns: Zero on success, %$-1$% on failure.
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}%%.
2088 static int fork_common(pid_t *kid_out, int *infd_out, int *outfd_out,
2089 int *errfd_out, struct tvec_state *tv)
2091 int p0[2] = { -1, -1 }, p1[2] = { -1, -1 }, pe[2] = { -1, -1 };
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));
2104 /* Flush all of the stream buffers so that we don't get duplicated
2109 /* Try to set up the child process. */
2112 tvec_error(tv, "fork failed: %s", strerror(errno));
2117 /* Child process. */
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",
2128 /* Parent process. */
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;
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]);
2150 /* --- @tvec_fork@ --- *
2152 * Arguments: @pid_t *kid_out@ = where to put child process-id
2153 * @int *infd_out, *outfd_out, *errfd_out@ = where to put file
2155 * @struct tvec_state *tv@ = test vector state
2156 * @const struct tvec_remoteenv@ = the remote environment
2158 * Returns: Zero on success, %$-1$% on failure.
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.
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)
2168 struct tvec_config config;
2169 const struct tvec_remotefork *rf = (const struct tvec_remotefork *)env;
2171 int infd = -1, outfd = -1, errfd = -1;
2174 if (fork_common(&kid, &infd, &outfd, &errfd, tv)) { rc = -1; goto end; }
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));
2183 *kid_out = kid; *infd_out = infd; *outfd_out = outfd; *errfd_out = errfd;
2189 /* --- @tvec_exec@ --- *
2191 * Arguments: @pid_t *kid_out@ = where to put child process-id
2192 * @int *infd_out, *outfd_out, *errfd_out@ = where to put file
2194 * @struct tvec_state *tv@ = test vector state
2195 * @const struct tvec_remoteenv@ = the remote environment
2197 * Returns: Zero on success, %$-1$% on failure.
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.
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)
2208 const struct tvec_remoteexec *rx = (const struct tvec_remoteexec *)env;
2210 int infd = -1, outfd = -1, errfd = -1;
2214 if (fork_common(&kid, &infd, &outfd, &errfd, tv)) { rc = -1; goto end; }
2216 v[0].cur = infd; v[0].want = STDIN_FILENO;
2217 v[1].cur = outfd; v[1].want = STDOUT_FILENO;
2219 fprintf(stderr, "failed to establish standard file descriptors: %s",
2223 execvp(rx->x.args[0], (/*uncosnt*/ char *const *)rx->x.args);
2224 fprintf(stderr, "failed to invoke test runner: %s", strerror(errno));
2228 *kid_out = kid; *infd_out = infd; *outfd_out = outfd; *errfd_out = errfd;
2234 /*----- That's all, folks -------------------------------------------------*/