X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/mLib/blobdiff_plain/67b5031ec6d160b5cae425466a34d1be3b211dd4..c91413e6acbc8d157ff52ceb8cd78cee97403584:/test/tvec-remote.c?ds=sidebyside diff --git a/test/tvec-remote.c b/test/tvec-remote.c index a00f046..3dbb5d3 100644 --- a/test/tvec-remote.c +++ b/test/tvec-remote.c @@ -28,71 +28,108 @@ /*----- Header files ------------------------------------------------------*/ #include +#include #include #include #include #include #include -#include +#include #include #include #include "alloc.h" +#include "bench.h" #include "buf.h" +#include "compiler.h" +#include "fdflags.h" +#include "lbuf.h" +#include "mdup.h" +#include "quis.h" #include "tvec.h" -/*----- Data structures ---------------------------------------------------*/ +#if GCC_VERSION_P(7, 1) +# pragma GCC diagnostic ignored "-Wdangling-else" +#elif GCC_VERSION_P(4, 2) +# pragma GCC diagnostic ignored "-Wparentheses" +#endif -struct tvec_remote { - int infd, outfd; - dbuf bin, bout; - unsigned f; -#define TVRF_BROKEN 1u -}; +#if CLANG_VERSION_P(3, 1) +# pragma clang diagnostic ignored "-Wdangling-else" +#endif -struct tvec_remotectx { - struct tvec_remote r; - pid_t kid; -}; +/*----- Basic I/O ---------------------------------------------------------*/ -struct remote_output { - struct tvec_output _o; - struct tvec_remote r; -}; +static void init_comms(struct tvec_remotecomms *rc) +{ + dbuf_create(&rc->bin); dbuf_create(&rc->bout); + rc->infd = rc->outfd = -1; rc->f = 0; +} -/*----- Basic I/O ---------------------------------------------------------*/ +static void close_comms(struct tvec_remotecomms *rc) +{ + if (rc->infd >= 0) { close(rc->infd); rc->infd = -1; } + if (rc->outfd >= 0) { close(rc->outfd); rc->outfd = -1; } +} + +static void release_comms(struct tvec_remotecomms *rc) + { close_comms(rc); dbuf_destroy(&rc->bin); dbuf_destroy(&rc->bout); } + +static void setup_comms(struct tvec_remotecomms *rc, int infd, int outfd) +{ + rc->infd = infd; rc->outfd = outfd; rc->f &= ~0xffu; + dbuf_reset(&rc->bin); dbuf_reset(&rc->bout); +} static int PRINTF_LIKE(3, 4) - ioerr(struct tvec_state *tv, struct tvec_remote *r, const char *msg, ...) + ioerr(struct tvec_state *tv, struct tvec_remotecomms *rc, + const char *msg, ...) { va_list ap; va_start(ap, msg); - r->f |= TVRF_BROKEN; - tvec_write(tv, msg, &ap); + close_comms(rc); rc->f |= TVRF_BROKEN; + tvec_report_v(tv, TVLEV_ERR, msg, &ap); va_end(ap); return (-1); } -static int send_all(struct tvec_state *tv, struct tvec_remote *r, +static int send_all(struct tvec_state *tv, struct tvec_remotecomms *rc, const unsigned char *p, size_t sz) { + void (*opipe)(int) = SIG_ERR; ssize_t n; + int ret; + opipe = signal(SIGPIPE, SIG_IGN); + if (opipe == SIG_ERR) { + ret = ioerr(tv, rc, "failed to ignore `SIGPIPE': %s", strerror(errno)); + goto end; + } while (sz) { - n = write(r->outfd, p, sz); + n = write(rc->outfd, p, sz); if (n > 0) { p += n; sz -= n; } - else - return (ioerr(tv, r, "failed to send: %s", - n ? strerror(errno) : "empty write")); + else { + ret = ioerr(tv, rc, "failed to send: %s", + n ? strerror(errno) : "empty write"); + goto end; + } } - return (0); + ret = 0; +end: + if (opipe != SIG_ERR) signal(SIGPIPE, opipe); + return (ret); } #define RCVF_ALLOWEOF 1u -static int recv_all(struct tvec_state *tv, struct tvec_remote *r, +enum { + RECV_FAIL = -1, + RECV_OK = 0, + RECV_EOF = 1 +}; +static int recv_all(struct tvec_state *tv, struct tvec_remotecomms *rc, unsigned char *p, size_t sz, unsigned f) { ssize_t n; @@ -100,196 +137,1146 @@ static int recv_all(struct tvec_state *tv, struct tvec_remote *r, #define f_any 1u while (sz) { - n = read(r->infd, p, sz); + n = read(rc->infd, p, sz); if (n > 0) { p += n; sz -= n; ff |= f_any; } else if (!n && (f&RCVF_ALLOWEOF) && !(ff&f_any)) - return (1); + return (RECV_EOF); else - return (ioerr(tv, r, "failed to receive: %s", + return (ioerr(tv, rc, "failed to receive: %s", n ? strerror(errno) : "unexpected end-of-file")); } - return (0); + return (RECV_OK); #undef f_any } -int tvec_send(struct tvec_state *tv, struct tvec_remote *r) +static int remote_send(struct tvec_state *tv, struct tvec_remotecomms *rc) { kludge64 k; unsigned char lenbuf[8]; - const char *p; size_t sz; + const unsigned char *p; size_t sz; - if (r->f&TVRF_BROKEN) return (-1); - if (BBAD(&r->bout.b)) - return (ioerr(tv, r, "failed to build output packet buffer"); + if (rc->f&TVRF_BROKEN) return (-1); + if (BBAD(&rc->bout._b)) + return (ioerr(tv, rc, "failed to build output packet buffer")); - p = BBASE(r->bout.b); sz = BLEN(&r->bout.b); + p = BBASE(&rc->bout._b); sz = BLEN(&rc->bout._b); ASSIGN64(k, sz); STORE64_L_(lenbuf, k); - if (send_all(tv, r, lenbuf, sizeof(lenbuf))) return (-1); - if (send_all(tv, r, p, sz)) return (-1); + if (send_all(tv, rc, lenbuf, sizeof(lenbuf))) return (-1); + if (send_all(tv, rc, p, sz)) return (-1); return (0); } -int tvec_recv(struct tvec_state *tv, struct tvec_remote *r, buf *b_out) +static int remote_recv(struct tvec_state *tv, struct tvec_remotecomms *rc, + unsigned f, buf *b_out) { kludge64 k, szmax; unsigned char lenbuf[8]; unsigned char *p; size_t sz; - int rc; + int ret; - if (r->f&TVRF_BROKEN) return (-1); - ASSIGN64(k, (size_t)-1); - rc = recv_all(tv, r, lenbuf, sizeof(lenbuf), RCVF_ALLOWEOF); - if (rc) return (rc); + if (rc->f&TVRF_BROKEN) return (RECV_FAIL); + ASSIGN64(szmax, (size_t)-1); + ret = recv_all(tv, rc, lenbuf, sizeof(lenbuf), f); + if (ret) return (ret); LOAD64_L_(k, lenbuf); if (CMP64(k, >, szmax)) - return (ioerr(tv, r, "packet size 0x%08lx%08lx out of range", + return (ioerr(tv, rc, "packet size 0x%08lx%08lx out of range", (unsigned long)HI64(k), (unsigned long)LO64(k))); - sz = GET64(size_t, k); buf_reset(&r->bin); p = buf_get(&r->bin.b, sz); - if (!p) return (ioerr(tv, r, "failed to allocate receive buffer")); - if (recv_all(tv, r, p, sz, 0)) return (-1); - buf_init(b_out, p, sz); return (0); + sz = GET64(size_t, k); dbuf_reset(&rc->bin); p = buf_get(&rc->bin._b, sz); + if (!p) return (ioerr(tv, rc, "failed to allocate receive buffer")); + if (recv_all(tv, rc, p, sz, 0)) return (RECV_FAIL); + buf_init(b_out, p, sz); return (RECV_OK); } -/*----- Data formatting primitives ----------------------------------------*/ +#define SENDPK(tv, rc, pk) \ + if ((rc)->f&TVRF_BROKEN) MC_GOELSE(body); else \ + MC_BEFORE(setpk, \ + { dbuf_reset(&(rc)->bout); \ + buf_putu16l(&(rc)->bout._b, (pk)); }) \ + MC_ALLOWELSE(body) \ + MC_AFTER(send, \ + { if (remote_send(tv, rc)) MC_GOELSE(body); }) \ +static int malformed(struct tvec_state *tv, struct tvec_remotecomms *rc) + { return (ioerr(tv, rc, "received malformed packet")); } /*----- Packet types ------------------------------------------------------*/ -#define TVPK_ERROR 0x0001 /* msg: string */ -#define TVPK_NOTICE 0x0002 /* msg: string */ -#define TVPK_STATUS 0x0003 /* st: char */ +#define TVPF_ACK 0x0001u -#define TVPK_BGROUP 0x0101 /* name: string */ -#define TVPK_TEST 0x0102 /* in: regs */ -#define TVPK_EGROUP 0x0103 /* --- */ +#define TVPK_VER 0x0000u /* --> min, max: u16 */ + /* <-- ver: u16 */ -#define TVPK_SKIPGRP 0x0201 /* excuse: string */ -#define TVPK_SKIP 0x0202 /* excuse: string */ -#define TVPK_FAIL 0x0203 /* detail: string */ -#define TVPK_MISMATCH 0x0204 /* in, out: regs */ -#define TVPK_BBENCH 0x0205 /* in: regs */ -#define TVPK_EBENCH 0x0206 /* flags: u16; n: u64; t, cy: float */ +#define TVPK_REPORT 0x0100u /* <-- level: u16; msg: string */ +#define TVPK_PROGRESS 0x0102u /* <-- st: str16 */ -/*----- The output driver -------------------------------------------------*/ +#define TVPK_BGROUP 0x0200u /* --> name: str16 + * <-- --- */ +#define TVPK_TEST 0x0202u /* --> in: regs + * <-- --- */ +#define TVPK_EGROUP 0x0204u /* --> --- */ -#define SENDPK(ro, pk) \ - if ((ro)->r.f&TVRF_BROKEN) /* do nothing */; else \ - MC_BEFORE(setpk, \ - { buf_reset(&(ro)->r.bout); \ - buf_putu16l(&(ro)->r.bout.b, (pk)); }) \ - MC_AFTER(send, \ - { tvec_send(&ro->_o.tv, &ro->r); }) +#define TVPK_SKIPGRP 0x0300u /* <-- excuse: str16 */ +#define TVPK_SKIP 0x0302u /* <-- excuse: str16 */ +#define TVPK_FAIL 0x0304u /* <-- flag: u8, detail: str16 */ +#define TVPK_DUMPREG 0x0306u /* <-- ri: u16; disp: u16; + * flag: u8, rv: value */ +#define TVPK_BBENCH 0x0308u /* <-- ident: str32; unit: u16 */ +#define TVPK_EBENCH 0x030au /* <-- ident: str32; unit: u16; + * flags: u16; n, t, cy: f64 */ -static int sendstr(struct tvec_output *o, unsigned pk, - const char *p, va_list *ap) -{ - struct remote_output *ro = (struct remote_output *)o; +/*----- Server ------------------------------------------------------------*/ + +static const struct tvec_outops remote_ops; + +static struct tvec_state srvtv; +static struct tvec_remotecomms srvrc = TVEC_REMOTECOMMS_INIT; +static struct tvec_output srvout = { &remote_ops }; - SENDPK(ro, pk) buf_vputstrf16l(&ro->r.bout.b, msg, ap); - return (ro->r.f&TVRF_BROKEN ? -1 : 0); +int tvec_setprogress(const char *status) +{ + SENDPK(&srvtv, &srvrc, TVPK_PROGRESS) + buf_putstr16l(&srvrc.bout._b, status); + else return (-1); + return (0); } -static void report(struct tvec_output *o, unsigned pk, const char *what, - const char *msg, va_list *ap) +int tvec_remoteserver(int infd, int outfd, const struct tvec_config *config) { - if (sendstr(o, pk, msg, ap)) { - fprintf(stderr, "%s %s: ", QUIS, what); - vfprintf(stderr, msg, *ap); - fputc('\n', stderr); + uint16 pk, u, v; + unsigned i; + buf b; + const struct tvec_test *t; + void *p; size_t sz; + const struct tvec_env *env = 0; + unsigned f = 0; +#define f_regslive 1u + void *ctx = 0; + int rc; + + setup_comms(&srvrc, infd, outfd); + tvec_begin(&srvtv, config, &srvout); + + if (remote_recv(&srvtv, &srvrc, 0, &b)) { rc = -1; goto end; } + if (buf_getu16l(&b, &pk)) goto bad; + if (pk != TVPK_VER) { + rc = ioerr(&srvtv, &srvrc, + "unexpected packet type 0x%04x instead of client version", + pk); + goto end; } -} + if (buf_getu16l(&b, &u) || buf_getu16l(&b, &v)) goto bad; + SENDPK(&srvtv, &srvrc, TVPK_VER | TVPF_ACK) buf_putu16l(&srvrc.bout._b, 0); + else { rc = -1; goto end; } -static void remote_error(struct tvec_output *o, const char *msg, va_list *ap) - { report(o, TVPK_ERROR, "ERROR", msg, ap); } + tvec_setprogress("%IDLE"); -static void remote_notice(struct tvec_output *o, - const char *msg, va_list *ap) - { report(o, TVPK_NOTICE, "notice", msg, ap); } + for (;;) { + rc = remote_recv(&srvtv, &srvrc, RCVF_ALLOWEOF, &b); + if (rc == RECV_EOF) break; + else if (rc == RECV_FAIL) goto end; + if (buf_getu16l(&b, &pk)) goto bad; -static void remote_setstatus(struct tvec_ouptut *o, int st) -{ - struct remote_output *ro = (struct remote_output *)o; - SENDPK(ro, TVPK_STATUS) buf_putbyte(&ro->r.bout.b, st); + switch (pk) { + + case TVPK_BGROUP: + p = buf_getmem16l(&b, &sz); if (!p) goto bad; + if (BLEFT(&b)) goto bad; + for (t = srvtv.tests; t->name; t++) + if (strlen(t->name) == sz && MEMCMP(t->name, ==, p, sz)) + goto found_group; + rc = ioerr(&srvtv, &srvrc, "unknown test group `%.*s'", + (int)sz, (char *)p); + goto end; + + found_group: + srvtv.test = t; env = t->env; + if (env && env->setup == tvec_remotesetup) + env = ((struct tvec_remoteenv *)env)->r.env; + if (!env || !env->ctxsz) ctx = 0; + else ctx = xmalloc(env->ctxsz); + if (env && env->setup) env->setup(&srvtv, env, 0, ctx); + + SENDPK(&srvtv, &srvrc, TVPK_BGROUP | TVPF_ACK); + else { rc = -1; goto end; } + + for (;;) { + if (remote_recv(&srvtv, &srvrc, 0, &b)) { rc = -1; goto end; } + if (buf_getu16l(&b, &pk)) goto bad; + switch (pk) { + + case TVPK_EGROUP: + if (BLEFT(&b)) goto bad; + goto endgroup; + + case TVPK_TEST: + tvec_initregs(&srvtv); f |= f_regslive; + if (tvec_deserialize(srvtv.in, &b, srvtv.test->regs, + srvtv.nreg, srvtv.regsz)) + goto bad; + if (BLEFT(&b)) goto bad; + + if (!(srvtv.f&TVSF_SKIP)) { + srvtv.f |= TVSF_ACTIVE; srvtv.f &= ~TVSF_OUTMASK; + tvec_setprogress("%SETUP"); + if (env && env->before) env->before(&srvtv, ctx); + if (!(srvtv.f&TVSF_ACTIVE)) + /* setup forced a skip */; + else { + for (i = 0; i < srvtv.nrout; i++) + if (TVEC_REG(&srvtv, in, i)->f&TVRF_LIVE) + TVEC_REG(&srvtv, out, i)->f |= TVRF_LIVE; + tvec_setprogress("%RUN"); + if (env && env->run) + env->run(&srvtv, t->fn, ctx); + else { + t->fn(srvtv.in, srvtv.out, ctx); + tvec_check(&srvtv, 0); + } + } + tvec_setprogress("%DONE"); + if (env && env->after) env->after(&srvtv, ctx); + tvec_endtest(&srvtv); + } + tvec_releaseregs(&srvtv); f &= ~f_regslive; + SENDPK(&srvtv, &srvrc, TVPK_TEST | TVPF_ACK); + else { rc = -1; goto end; } + tvec_setprogress("%IDLE"); + break; + + default: + rc = ioerr(&srvtv, &srvrc, + "unexpected packet type 0x%04x", pk); + goto end; + + } + } + + endgroup: + if (env && env->teardown) env->teardown(&srvtv, ctx); + xfree(ctx); t = 0; env = 0; ctx = 0; + break; + + default: + goto bad; + } + } + rc = 0; + +end: + if (env && env->teardown) env->teardown(&srvtv, ctx); + xfree(ctx); + if (f&f_regslive) tvec_releaseregs(&srvtv); + release_comms(&srvrc); + return (rc ? 2 : 0); + +bad: + rc = malformed(&srvtv, &srvrc); goto end; + +#undef f_regslive } +/*----- Server output driver ----------------------------------------------*/ + static void remote_skipgroup(struct tvec_output *o, const char *excuse, va_list *ap) - { sendstr(o, TVPK_SKIPGRP, excuse, ap); } +{ + SENDPK(&srvtv, &srvrc, TVPK_SKIPGRP) + buf_vputstrf16l(&srvrc.bout._b, excuse, ap); +} static void remote_skip(struct tvec_output *o, const char *excuse, va_list *ap) - { sendstr(o, TVPK_SKIP, excuse, ap); } +{ + SENDPK(&srvtv, &srvrc, TVPK_SKIP) + buf_vputstrf16l(&srvrc.bout._b, excuse, ap); +} static void remote_fail(struct tvec_output *o, const char *detail, va_list *ap) - { sendstr(o, TVPK_FAIL, detail, ap); } +{ + SENDPK(&srvtv, &srvrc, TVPK_FAIL) + if (!detail) + buf_putbyte(&srvrc.bout._b, 0); + else { + buf_putbyte(&srvrc.bout._b, 1); + buf_vputstrf16l(&srvrc.bout._b, detail, ap); + } +} -static void remote_mismatch(struct tvec_output *o) +static void remote_dumpreg(struct tvec_output *o, + unsigned disp, const union tvec_regval *rv, + const struct tvec_regdef *rd) { - struct remote_output *ro = (struct remote_output *)o; - struct tvec_state *rv = ro->_o.tv; + const struct tvec_regdef *reg; + unsigned r; - SENDPK(ro, TVPK_MISMATCH) { - tvec_serialize(tv, &ro->r.bout.b, tv->in, tv->nreg, tv->regsz); - tvec_serialize(tv, &ro->r.bout.b, tv->out, tv->nrout, tv->regsz); + /* Find the register definition. */ + for (reg = srvtv.test->regs, r = 0; reg->name; reg++, r++) + if (reg == rd) goto found; + assert(!"unexpected register definition"); + +found: + SENDPK(&srvtv, &srvrc, TVPK_DUMPREG) { + buf_putu16l(&srvrc.bout._b, r); + buf_putu16l(&srvrc.bout._b, disp); + if (!rv) + buf_putbyte(&srvrc.bout._b, 0); + else { + buf_putbyte(&srvrc.bout._b, 1); + rd->ty->tobuf(&srvrc.bout._b, rv, rd); + } } } -static void remote_bbench(struct tvec_output *o) +static void remote_bbench(struct tvec_output *o, + const char *ident, unsigned unit) { - struct remote_output *ro = (struct remote_output *)o; - struct tvec_state *rv = ro->_o.tv; - - SENDPK(ro, TVPK_BBENCH) - tvec_serialize(tv, &ro->r.bout.b, tv->in, tv->nreg, tv->regsz); + SENDPK(&srvtv, &srvrc, TVPK_BBENCH) { + buf_putstr32l(&srvrc.bout._b, ident); + buf_putu16l(&srvrc.bout._b, unit); + } } static void remote_ebench(struct tvec_output *o, + const char *ident, unsigned unit, const struct bench_timing *t) { - struct remote_output *ro = (struct remote_output *)o; - kludge64 k; + SENDPK(&srvtv, &srvrc, TVPK_EBENCH) { + buf_putstr32l(&srvrc.bout._b, ident); + buf_putu16l(&srvrc.bout._b, unit); + if (!t || !(t->f&BTF_ANY)) + buf_putu16l(&srvrc.bout._b, 0); + else { + buf_putu16l(&srvrc.bout._b, t->f); + buf_putf64l(&srvrc.bout._b, t->n); + if (t->f&BTF_TIMEOK) buf_putf64l(&srvrc.bout._b, t->t); + if (t->f&BTF_CYOK) buf_putf64l(&srvrc.bout._b, t->cy); + } + } +} + +static void remote_report(struct tvec_output *o, unsigned level, + const char *msg, va_list *ap) +{ + const char *what; - SENDPK(ro, TVPK_EBENCH) { - buf_putu16l(&ro->r.bout.b, t->f); - ASSIGN64(k, t->n); buf_putk64l(&ro->r.bout.b, k); - if (t->f&BTF_TIMEOK) buf_putf64l(&ro->r.bout.b, t->t); - if (t->f&BTF_CYOK) buf_putf64l(&ro->r.bout.b, t->cy); + SENDPK(&srvtv, &srvrc, TVPK_REPORT) { + buf_putu16l(&srvrc.bout._b, level); + buf_vputstrf16l(&srvrc.bout._b, msg, ap); + } else { + switch (level) { + case TVLEV_NOTE: what = "notice"; break; + case TVLEV_ERR: what = "ERROR"; break; + default: what = "(?level)"; break; + } + fprintf(stderr, "%s %s: ", QUIS, what); + vfprintf(stderr, msg, *ap); + fputc('\n', stderr); } } -static void remote_write(struct tvec_output *o, const char *p, size_t sz) - { assert(!"remote_write"); } -static void remote_bsession(struct tvec_output *o) - { assert(!"remote_bsession"); } +static void remote_bsession(struct tvec_output *o, struct tvec_state *tv) + { ; } static int remote_esession(struct tvec_output *o) - { assert(!"remote_esession"); return (-1); } + { return (srvtv.f&TVSF_ERROR ? 2 : 0); } +static void remote_destroy(struct tvec_output *o) + { ; } +static void remote_etest(struct tvec_output *o, unsigned outcome) + { ; } + static void remote_bgroup(struct tvec_output *o) { assert(!"remote_bgroup"); } +static void remote_egroup(struct tvec_output *o) + { assert(!"remote_egroup"); } static void remote_btest(struct tvec_output *o) { assert(!"remote_btest"); } -static void remote_egroup(struct tvec_output *o, unsigned outcome) - { assert(!"remote_egroup"); } -static void remote_etest(struct tvec_output *o, unsigned outcome) - { assert(!"remote_etest"); } - -static void remote_destroy(struct tvec_output *o) -{ -} static const struct tvec_outops remote_ops = { - remote_error, remote_notice, remote_setstatus, remote_write, remote_bsession, remote_esession, - remote_bgroup, remote_egroup, remote_skip, - remote_btest, remote_skip, remote_fail, remote_mismatch, remote_etest, + remote_bgroup, remote_skipgroup, remote_egroup, + remote_btest, remote_skip, remote_fail, remote_dumpreg, remote_etest, remote_bbench, remote_ebench, + remote_report, remote_destroy +}; + +/*----- Client ------------------------------------------------------------*/ + +#define TVXF_VALMASK 0x0fffu +#define TVXF_SIG 0x1000u +#define TVXF_CAUSEMASK 0xe000u +#define TVXST_RUN 0x0000u +#define TVXST_EXIT 0x2000u +#define TVXST_KILL 0x4000u +#define TVXST_CONT 0x6000u +#define TVXST_STOP 0x8000u +#define TVXST_DISCONN 0xa000u +#define TVXST_UNK 0xc000u +#define TVXST_ERR 0xe000u + +static const struct tvec_flag exit_flags[] = { + /* + ;;; The signal name table is very boring to type. To make life less + ;;; awful, put the signal names in this list and evaluate the code to + ;;; get Emacs to regenerate it. + + (let ((signals '(HUP INT QUIT ILL TRAP ABRT IOT EMT FPE KILL BUS SEGV SYS + PIPE ALRM TERM URG STOP TSTP CONT CHLD CLD TTIN TTOU + POLL IO TIN XCPU XFSZ VTALRM PROF WINCH USR1 USR2 + STKFLT INFO PWR THR LWP LIBRT LOST))) + (save-excursion + (goto-char (point-min)) + (search-forward (concat "***" "BEGIN siglist" "***")) + (beginning-of-line 2) + (delete-region (point) + (progn + (search-forward "***END***") + (beginning-of-line) + (point))) + (dolist (sig signals) + (insert (format "#ifdef SIG%s\n { \"SIG%s\", TVXF_VALMASK | TVXF_SIG, SIG%s | TVXF_SIG },\n#endif\n" + sig sig sig))))) + */ + + /***BEGIN siglist***/ +#ifdef SIGHUP + { "SIGHUP", TVXF_VALMASK | TVXF_SIG, SIGHUP | TVXF_SIG }, +#endif +#ifdef SIGINT + { "SIGINT", TVXF_VALMASK | TVXF_SIG, SIGINT | TVXF_SIG }, +#endif +#ifdef SIGQUIT + { "SIGQUIT", TVXF_VALMASK | TVXF_SIG, SIGQUIT | TVXF_SIG }, +#endif +#ifdef SIGILL + { "SIGILL", TVXF_VALMASK | TVXF_SIG, SIGILL | TVXF_SIG }, +#endif +#ifdef SIGTRAP + { "SIGTRAP", TVXF_VALMASK | TVXF_SIG, SIGTRAP | TVXF_SIG }, +#endif +#ifdef SIGABRT + { "SIGABRT", TVXF_VALMASK | TVXF_SIG, SIGABRT | TVXF_SIG }, +#endif +#ifdef SIGIOT + { "SIGIOT", TVXF_VALMASK | TVXF_SIG, SIGIOT | TVXF_SIG }, +#endif +#ifdef SIGEMT + { "SIGEMT", TVXF_VALMASK | TVXF_SIG, SIGEMT | TVXF_SIG }, +#endif +#ifdef SIGFPE + { "SIGFPE", TVXF_VALMASK | TVXF_SIG, SIGFPE | TVXF_SIG }, +#endif +#ifdef SIGKILL + { "SIGKILL", TVXF_VALMASK | TVXF_SIG, SIGKILL | TVXF_SIG }, +#endif +#ifdef SIGBUS + { "SIGBUS", TVXF_VALMASK | TVXF_SIG, SIGBUS | TVXF_SIG }, +#endif +#ifdef SIGSEGV + { "SIGSEGV", TVXF_VALMASK | TVXF_SIG, SIGSEGV | TVXF_SIG }, +#endif +#ifdef SIGSYS + { "SIGSYS", TVXF_VALMASK | TVXF_SIG, SIGSYS | TVXF_SIG }, +#endif +#ifdef SIGPIPE + { "SIGPIPE", TVXF_VALMASK | TVXF_SIG, SIGPIPE | TVXF_SIG }, +#endif +#ifdef SIGALRM + { "SIGALRM", TVXF_VALMASK | TVXF_SIG, SIGALRM | TVXF_SIG }, +#endif +#ifdef SIGTERM + { "SIGTERM", TVXF_VALMASK | TVXF_SIG, SIGTERM | TVXF_SIG }, +#endif +#ifdef SIGURG + { "SIGURG", TVXF_VALMASK | TVXF_SIG, SIGURG | TVXF_SIG }, +#endif +#ifdef SIGSTOP + { "SIGSTOP", TVXF_VALMASK | TVXF_SIG, SIGSTOP | TVXF_SIG }, +#endif +#ifdef SIGTSTP + { "SIGTSTP", TVXF_VALMASK | TVXF_SIG, SIGTSTP | TVXF_SIG }, +#endif +#ifdef SIGCONT + { "SIGCONT", TVXF_VALMASK | TVXF_SIG, SIGCONT | TVXF_SIG }, +#endif +#ifdef SIGCHLD + { "SIGCHLD", TVXF_VALMASK | TVXF_SIG, SIGCHLD | TVXF_SIG }, +#endif +#ifdef SIGCLD + { "SIGCLD", TVXF_VALMASK | TVXF_SIG, SIGCLD | TVXF_SIG }, +#endif +#ifdef SIGTTIN + { "SIGTTIN", TVXF_VALMASK | TVXF_SIG, SIGTTIN | TVXF_SIG }, +#endif +#ifdef SIGTTOU + { "SIGTTOU", TVXF_VALMASK | TVXF_SIG, SIGTTOU | TVXF_SIG }, +#endif +#ifdef SIGPOLL + { "SIGPOLL", TVXF_VALMASK | TVXF_SIG, SIGPOLL | TVXF_SIG }, +#endif +#ifdef SIGIO + { "SIGIO", TVXF_VALMASK | TVXF_SIG, SIGIO | TVXF_SIG }, +#endif +#ifdef SIGTIN + { "SIGTIN", TVXF_VALMASK | TVXF_SIG, SIGTIN | TVXF_SIG }, +#endif +#ifdef SIGXCPU + { "SIGXCPU", TVXF_VALMASK | TVXF_SIG, SIGXCPU | TVXF_SIG }, +#endif +#ifdef SIGXFSZ + { "SIGXFSZ", TVXF_VALMASK | TVXF_SIG, SIGXFSZ | TVXF_SIG }, +#endif +#ifdef SIGVTALRM + { "SIGVTALRM", TVXF_VALMASK | TVXF_SIG, SIGVTALRM | TVXF_SIG }, +#endif +#ifdef SIGPROF + { "SIGPROF", TVXF_VALMASK | TVXF_SIG, SIGPROF | TVXF_SIG }, +#endif +#ifdef SIGWINCH + { "SIGWINCH", TVXF_VALMASK | TVXF_SIG, SIGWINCH | TVXF_SIG }, +#endif +#ifdef SIGUSR1 + { "SIGUSR1", TVXF_VALMASK | TVXF_SIG, SIGUSR1 | TVXF_SIG }, +#endif +#ifdef SIGUSR2 + { "SIGUSR2", TVXF_VALMASK | TVXF_SIG, SIGUSR2 | TVXF_SIG }, +#endif +#ifdef SIGSTKFLT + { "SIGSTKFLT", TVXF_VALMASK | TVXF_SIG, SIGSTKFLT | TVXF_SIG }, +#endif +#ifdef SIGINFO + { "SIGINFO", TVXF_VALMASK | TVXF_SIG, SIGINFO | TVXF_SIG }, +#endif +#ifdef SIGPWR + { "SIGPWR", TVXF_VALMASK | TVXF_SIG, SIGPWR | TVXF_SIG }, +#endif +#ifdef SIGTHR + { "SIGTHR", TVXF_VALMASK | TVXF_SIG, SIGTHR | TVXF_SIG }, +#endif +#ifdef SIGLWP + { "SIGLWP", TVXF_VALMASK | TVXF_SIG, SIGLWP | TVXF_SIG }, +#endif +#ifdef SIGLIBRT + { "SIGLIBRT", TVXF_VALMASK | TVXF_SIG, SIGLIBRT | TVXF_SIG }, +#endif +#ifdef SIGLOST + { "SIGLOST", TVXF_VALMASK | TVXF_SIG, SIGLOST | TVXF_SIG }, +#endif + /***END***/ + + { "signal", TVXF_SIG, TVXF_SIG }, + + { "running", TVXF_CAUSEMASK, TVXST_RUN }, + { "exited", TVXF_CAUSEMASK, TVXST_EXIT }, + { "killed", TVXF_CAUSEMASK, TVXST_KILL }, + { "stopped", TVXF_CAUSEMASK, TVXST_STOP }, + { "continued", TVXF_CAUSEMASK, TVXST_CONT }, + { "disconnected", TVXF_CAUSEMASK, TVXST_DISCONN }, + { "unknown", TVXF_CAUSEMASK, TVXST_UNK }, + { "error", TVXF_CAUSEMASK, TVXST_ERR }, + + TVEC_ENDFLAGS +}; + +static const struct tvec_flaginfo exit_flaginfo = + { "exit-status", exit_flags, &tvrange_uint }; +static const struct tvec_regdef exit_regdef = + { "@exit", 0, &tvty_flags, 0, { &exit_flaginfo } }; + +static const struct tvec_regdef progress_regdef = + { "@progress", 0, &tvty_string, 0 }; + +static const struct tvec_uassoc reconn_assocs[] = { + { "on-demand", TVRCN_DEMAND }, + { "force", TVRCN_FORCE }, + { "skip", TVRCN_SKIP }, + TVEC_ENDENUM +}; + +enum { + CONN_BROKEN = -2, /* previously broken */ + CONN_FAILED = -1, /* attempt freshly failed */ + CONN_ESTABLISHED = 0, /* previously established */ + CONN_FRESH = 1 /* freshly connected */ +}; + +static const struct tvec_uenuminfo reconn_enuminfo = + { "remote-reconnection", reconn_assocs, &tvrange_uint }; +static const struct tvec_regdef reconn_regdef = + { "@reconnect", 0, &tvty_uenum, 0, { &reconn_enuminfo } }; + +static int handle_packets(struct tvec_state *tv, struct tvec_remotectx *r, + unsigned f, uint16 end, buf *b_out) +{ + struct tvec_output *o = tv->output; + uint16 pk, u, v; + const char *p; size_t n; + dstr d = DSTR_INIT; + buf *b = b_out; + const struct tvec_regdef *rd; + struct bench_timing bt; + struct tvec_reg *reg = 0; + unsigned i; + int rc; + + for (;;) { + rc = remote_recv(tv, &r->rc, f, b); if (rc) goto end; + if (buf_getu16l(b, &pk)) goto bad; + + switch (pk) { + + case TVPK_PROGRESS: + p = buf_getmem16l(b, &n); if (!p) goto bad; + if (BLEFT(b)) goto bad; + + DRESET(&r->progress); DPUTM(&r->progress, p, n); DPUTZ(&r->progress); + break; + + case TVPK_REPORT: + if (buf_getu16l(b, &u)) goto bad; + p = buf_getmem16l(b, &n); if (!p) goto bad; + if (BLEFT(b)) goto bad; + + DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d); + tvec_report(tv, u, "%s", d.buf); + break; + + case TVPK_SKIPGRP: + p = buf_getmem16l(b, &n); if (!p) goto bad; + DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d); + if (BLEFT(b)) goto bad; + + tvec_skipgroup(tv, "%s", d.buf); + break; + + case TVPK_SKIP: + if (!(tv->f&TVSF_ACTIVE)) { + rc = ioerr(tv, &r->rc, "test `%s' not active", tv->test->name); + goto end; + } + + p = buf_getmem16l(b, &n); if (!p) goto bad; + if (BLEFT(b)) goto bad; + + DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d); + tvec_skip(tv, "%s", d.buf); + break; + + case TVPK_FAIL: + if (!(tv->f&TVSF_ACTIVE) && + ((tv->f&TVSF_OUTMASK) != (TVOUT_LOSE << TVSF_OUTSHIFT))) { + rc = ioerr(tv, &r->rc, "test `%s' not active or failing", + tv->test->name); + goto end; + } + + rc = buf_getbyte(b); if (rc < 0) goto bad; + if (rc) { p = buf_getmem16l(b, &n); if (!p) goto bad; } + else p = 0; + if (BLEFT(b)) goto bad; -/*----- Main code ---------------------------------------------------------*/ + if (!p) + tvec_fail(tv, 0); + else { + DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d); + tvec_fail(tv, "%s", d.buf); + } + break; + case TVPK_DUMPREG: + if (buf_getu16l(b, &u) || buf_getu16l(b, &v)) goto bad; + for (rd = tv->test->regs, i = 0; rd->name; rd++, i++) + if (i == u) goto found_reg; + rc = ioerr(tv, &r->rc, + "register definition %u out of range for test `%s'", + u, tv->test->name); + goto end; + found_reg: + if (v >= TVRD_LIMIT) { + rc = ioerr(tv, &r->rc, "register disposition %u out of range", v); + goto end; + } + rc = buf_getbyte(b); if (rc < 0) goto bad; + if (!rc) + tvec_dumpreg(tv, v, 0, rd); + else { + if (!reg) reg = xmalloc(tv->regsz); + rd->ty->init(®->v, rd); + rc = rd->ty->frombuf(b, ®->v, rd); + if (!rc) tvec_dumpreg(tv, v, ®->v, rd); + rd->ty->release(®->v, rd); + if (rc) goto bad; + } + if (BLEFT(b)) goto bad; + break; + + case TVPK_BBENCH: + p = buf_getmem32l(b, &n); if (!p) goto bad; + if (buf_getu16l(b, &u)) goto bad; + if (BLEFT(b)) goto bad; + if (u >= TVBU_LIMIT) { + rc = ioerr(tv, &r->rc, "unit code %u out of range", u); + goto end; + } + + DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d); + o->ops->bbench(o, d.buf, u); + break; + + case TVPK_EBENCH: + p = buf_getmem32l(b, &n); if (!p) goto bad; + if (buf_getu16l(b, &u) || buf_getu16l(b, &v)) goto bad; + if (u >= TVBU_LIMIT) + { rc = ioerr(tv, &r->rc, "unit code %u out of range", u); goto end; } + if ((v&BTF_ANY) && buf_getf64l(b, &bt.n)) goto bad; + if ((v&BTF_TIMEOK) && buf_getf64l(b, &bt.t)) goto bad; + if ((v&BTF_CYOK) && buf_getf64l(b, &bt.cy)) goto bad; + if (BLEFT(b)) goto bad; + + DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d); + o->ops->ebench(o, d.buf, u, v&BTF_ANY ? &bt : 0); + break; + + default: + if (pk == end) { rc = 0; goto end; } + rc = ioerr(tv, &r->rc, "unexpected packet type 0x%04x", pk); + goto end; + } + } + +end: + DDESTROY(&d); + xfree(reg); + return (rc); +bad: + rc = malformed(tv, &r->rc); goto end; +} + +static void reap_kid(struct tvec_state *tv, struct tvec_remotectx *r) +{ + pid_t kid; + int st; + + if (!r->kid) + { r->exit = TVXST_DISCONN; r->kid = -1; } + else if (r->kid > 0) { + kid = waitpid(r->kid, &st, 0); + if (kid < 0) { + tvec_notice(tv, "failed to wait for remote child: %s", + strerror(errno)); + r->exit = TVXST_ERR; + } else if (!kid) { + tvec_notice(tv, "remote child vanished without a trace"); + r->exit = TVXST_ERR; + } else if (WIFCONTINUED(st)) + r->exit = TVXST_CONT; + else if (WIFSIGNALED(st)) + r->exit = TVXST_KILL | TVXF_SIG | WTERMSIG(st); + else if (WIFSTOPPED(st)) + r->exit = TVXST_STOP | TVXF_SIG | WSTOPSIG(st); + else if (WIFEXITED(st)) + r->exit = TVXST_EXIT | WEXITSTATUS(st); + else { + tvec_notice(tv, "remote child died with unknown status 0x%04x", + (unsigned)st); + r->exit = TVXST_UNK; + } + r->kid = -1; + } +} + +static void report_errline(char *p, size_t n, void *ctx) +{ + struct tvec_remotectx *r = ctx; + struct tvec_state *tv = r->tv; + + if (p && !(r->rc.f&TVRF_MUFFLE)) + tvec_notice(tv, "child process stderr: %s", p); +} + +#define ERF_SILENT 0x0001u +#define ERF_CLOSE 0x0002u +static int drain_errfd(struct tvec_state *tv, struct tvec_remotectx *r, + unsigned f) +{ + char *p; size_t sz; + ssize_t n; + int rc; + + if (f&ERF_SILENT) r->rc.f |= TVRF_MUFFLE; + else r->rc.f &= ~TVRF_MUFFLE; + if (fdflags(r->errfd, O_NONBLOCK, f&ERF_CLOSE ? 0 : O_NONBLOCK, 0, 0)) { + rc = ioerr(tv, &r->rc, "failed to %s error non-blocking flag", + f&ERF_CLOSE ? "clear" : "set"); + goto end; + } + + for (;;) { + sz = lbuf_free(&r->errbuf, &p); + n = read(r->errfd, p, sz); + if (!n) break; + if (n < 0) { + if (errno == EINTR) continue; + if (!(f&ERF_CLOSE) && (errno == EWOULDBLOCK || errno == EAGAIN)) + break; + rc = ioerr(tv, &r->rc, "failed to read child stderr: %s", + strerror(errno)); + goto end; + } + lbuf_flush(&r->errbuf, p, n); + } + rc = 0; +end: + if (f&ERF_CLOSE) { + lbuf_close(&r->errbuf); + close(r->errfd); + } + return (rc); +} + +#define DCF_KILL 0x0100u +static void disconnect_remote(struct tvec_state *tv, + struct tvec_remotectx *r, unsigned f) +{ + if (r->kid < 0) return; + if (r->kid > 0 && (f&DCF_KILL)) kill(r->kid, SIGTERM); + close_comms(&r->rc); + if (r->kid > 0) kill(r->kid, SIGTERM); + drain_errfd(tv, r, f | ERF_CLOSE); reap_kid(tv, r); +} + +static int connect_remote(struct tvec_state *tv, struct tvec_remotectx *r) +{ + const struct tvec_remoteenv *re = r->re; + pid_t kid = 0; + buf b; + uint16 v; + int infd = -1, outfd = -1, errfd = -1, rc; + + DRESET(&r->progress); DPUTS(&r->progress, "%INIT"); + if (r->kid >= 0) { rc = 0; goto end; } + if (re->r.connect(&kid, &infd, &outfd, &errfd, tv, re)) + { rc = -1; goto end; } + setup_comms(&r->rc, infd, outfd); r->kid = kid; r->errfd = errfd; + lbuf_init(&r->errbuf, report_errline, r); + r->exit = TVXST_RUN; r->rc.f &= ~TVRF_BROKEN; + + SENDPK(tv, &r->rc, TVPK_VER) { + buf_putu16l(&r->rc.bout._b, 0); + buf_putu16l(&r->rc.bout._b, 0); + } else { rc = -1; goto end; } + + if (handle_packets(tv, r, 0, TVPK_VER | TVPF_ACK, &b)) + { rc = -1; goto end; } + if (buf_getu16l(&b, &v)) goto bad; + if (BLEFT(&b)) { rc = malformed(tv, &r->rc); goto end; } + if (v) { + rc = ioerr(tv, &r->rc, "protocol version %u not supported", v); + goto end; + } + + SENDPK(tv, &r->rc, TVPK_BGROUP) + buf_putstr16l(&r->rc.bout._b, tv->test->name); + else { rc = -1; goto end; } + if (handle_packets(tv, r, 0, TVPK_BGROUP | TVPF_ACK, &b)) + { rc = -1; goto end; } + if (BLEFT(&b)) { rc = malformed(tv, &r->rc); goto end; } + r->ver = v; rc = 0; +end: + if (rc) disconnect_remote(tv, r, DCF_KILL); + return (rc); +bad: + rc = malformed(tv, &r->rc); goto end; +} + +static int check_comms(struct tvec_state *tv, struct tvec_remotectx *r) +{ + if (r->kid < 0) + return (CONN_BROKEN); + else if (r->rc.f&TVRF_BROKEN) + { disconnect_remote(tv, r, DCF_KILL); return (CONN_FAILED); } + else + return (CONN_ESTABLISHED); +} + +static int try_reconnect(struct tvec_state *tv, struct tvec_remotectx *r) +{ + int rc; + + switch (r->rc.f&TVRF_RCNMASK) { + case TVRCN_DEMAND: + rc = check_comms(tv, r); + if (rc < CONN_ESTABLISHED) { + close_comms(&r->rc); + if (connect_remote(tv, r)) rc = CONN_FAILED; + else rc = CONN_FRESH; + } + break; + case TVRCN_FORCE: + disconnect_remote(tv, r, DCF_KILL); + if (connect_remote(tv, r)) rc = CONN_FAILED; + else rc = CONN_FRESH; + break; + case TVRCN_SKIP: + rc = check_comms(tv, r); + break; + default: + abort(); + } + return (rc); +} + +static void reset_vars(struct tvec_remotectx *r) +{ + r->exwant = TVXST_RUN; r->rc.f = (r->rc.f&~TVRF_RCNMASK) | TVRCN_DEMAND; + DRESET(&r->prgwant); DPUTS(&r->prgwant, "%DONE"); +} + +void tvec_remotesetup(struct tvec_state *tv, const struct tvec_env *env, + void *pctx, void *ctx) +{ + struct tvec_remotectx *r = ctx; + const struct tvec_remoteenv *re = (const struct tvec_remoteenv *)env; + + assert(!re->r.env || tv->test->env == &re->_env); + + r->tv = tv; + init_comms(&r->rc); + r->re = re; r->kid = -1; + DCREATE(&r->prgwant); DCREATE(&r->progress); + if (connect_remote(tv, r)) + tvec_skipgroup(tv, "failed to connect to test backend"); + reset_vars(r); +} + +int tvec_remoteset(struct tvec_state *tv, const char *var, + const struct tvec_env *env, void *ctx) +{ + struct tvec_remotectx *r = ctx; + union tvec_regval rv; + int rc; + + if (STRCMP(var, ==, "@exit")) { + if (tvty_flags.parse(&rv, &exit_regdef, tv)) { rc = -1; goto end; } + if (r) r->exwant = rv.u; + rc = 1; + } else if (STRCMP(var, ==, "@progress")) { + tvty_string.init(&rv, &progress_regdef); + rc = tvty_string.parse(&rv, &progress_regdef, tv); + if (r && !rc) + { DRESET(&r->prgwant); DPUTM(&r->prgwant, rv.str.p, rv.str.sz); } + tvty_string.release(&rv, &progress_regdef); + if (rc) { rc = -1; goto end; } + rc = 1; + } else if (STRCMP(var, ==, "@reconnect")) { + if (tvty_uenum.parse(&rv, &reconn_regdef, tv)) { rc = -1; goto end; } + if (r) r->rc.f = (r->rc.f&~TVRF_RCNMASK) | (rv.u&TVRF_RCNMASK); + rc = 1; + } else + rc = 0; + +end: + return (rc); +} + +void tvec_remoteafter(struct tvec_state *tv, void *ctx) +{ + struct tvec_remotectx *r = ctx; + + reset_vars(r); +} + +void tvec_remoterun(struct tvec_state *tv, tvec_testfn *fn, void *ctx) +{ + struct tvec_remotectx *r = ctx; + union tvec_regval rv; + unsigned f = 0; +#define f_exit 1u +#define f_progress 2u +#define f_fail 4u + buf b; + int rc; + + switch (try_reconnect(tv, r)) { + case CONN_FAILED: + tvec_skip(tv, "failed to connect to test backend"); return; + case CONN_BROKEN: + tvec_skip(tv, "no connection"); return; + } + + SENDPK(tv, &r->rc, TVPK_TEST) + tvec_serialize(tv->in, &r->rc.bout._b, + tv->test->regs, tv->nreg, tv->regsz); + else { rc = -1; goto end; } + rc = handle_packets(tv, r, RCVF_ALLOWEOF, TVPK_TEST | TVPF_ACK, &b); + switch (rc) { + case RECV_FAIL: + goto end; + case RECV_EOF: + reap_kid(tv, r); + /* fall through */ + case RECV_OK: + if (r->exit != r->exwant) f |= f_exit; + if (r->progress.len != r->prgwant.len || + MEMCMP(r->progress.buf, !=, r->prgwant.buf, r->progress.len)) + f |= f_progress; + if (f && (tv->f&TVSF_ACTIVE)) + { tvec_fail(tv, 0); tvec_mismatch(tv, TVMF_IN); } + if (!(tv->f&TVSF_ACTIVE) && + (tv->f&TVSF_OUTMASK) == (TVOUT_LOSE << TVSF_OUTSHIFT)) { + f |= f_fail; + + rv.u = r->exit; + tvec_dumpreg(tv, f&f_exit ? TVRD_FOUND : TVRD_MATCH, + &rv, &exit_regdef); + if (f&f_exit) { + rv.u = r->exwant; + tvec_dumpreg(tv, TVRD_EXPECT, &rv, &exit_regdef); + } + + rv.str.p = r->progress.buf; rv.str.sz = r->progress.len; + tvec_dumpreg(tv, f&f_progress ? TVRD_FOUND : TVRD_MATCH, + &rv, &progress_regdef); + if (f&f_progress) { + rv.str.p = r->prgwant.buf; rv.str.sz = r->prgwant.len; + tvec_dumpreg(tv, TVRD_EXPECT, &rv, &progress_regdef); + } + } + + if (rc == RECV_EOF) + disconnect_remote(tv, r, f ? 0 : ERF_SILENT); + break; + } + +end: + if (rc) { + if ((tv->f&TVSF_ACTIVE) && f) + tvec_skip(tv, "remote test runner communications failed"); + disconnect_remote(tv, r, 0); + } + +#undef f_exit +#undef f_progress +#undef f_fail +} + +void tvec_remoteteardown(struct tvec_state *tv, void *ctx) +{ + struct tvec_remotectx *r = ctx; + + if (r) { + disconnect_remote(tv, r, 0); release_comms(&r->rc); + DDESTROY(&r->prgwant); DDESTROY(&r->progress); + } +} + +/*----- Connectors --------------------------------------------------------*/ + +static int fork_common(pid_t *kid_out, int *infd_out, int *outfd_out, + int *errfd_out, struct tvec_state *tv) +{ + int p0[2] = { -1, -1 }, p1[2] = { -1, -1 }, pe[2] = { -1, -1 }; + pid_t kid = -1; + int rc; + + if (pipe(p0) || pipe(p1) || pipe(pe) || + fdflags(p0[1], 0, 0, FD_CLOEXEC, FD_CLOEXEC) || + fdflags(p1[0], 0, 0, FD_CLOEXEC, FD_CLOEXEC) || + fdflags(pe[0], 0, 0, FD_CLOEXEC, FD_CLOEXEC)) { + tvec_error(tv, "pipe failed: %s", strerror(errno)); + rc = -1; goto end; + } + + fflush(0); + + kid = fork(); + if (kid < 0) { + tvec_error(tv, "fork failed: %s", strerror(errno)); + rc = -1; goto end; + } + + if (!kid) { + *kid_out = 0; + *infd_out = p0[0]; p0[0] = -1; + *outfd_out = p1[1]; p1[1] = -1; + if (pe[1] != STDERR_FILENO && dup2(pe[1], STDERR_FILENO) < 0) { + fprintf(stderr, "failed to establish child stderr: %s", + strerror(errno)); + exit(127); + } + } else { + *kid_out = kid; kid = -1; + *infd_out = p1[0]; p1[0] = -1; + *outfd_out = p0[1]; p0[1] = -1; + *errfd_out = pe[0]; pe[0] = -1; + } + + rc = 0; +end: + if (p0[0] >= 0) close(p0[0]); + if (p0[1] >= 0) close(p0[1]); + if (p1[0] >= 0) close(p1[0]); + if (p1[1] >= 0) close(p1[1]); + if (pe[0] >= 0) close(pe[0]); + if (pe[1] >= 0) close(pe[1]); + return (rc); +} + +int tvec_fork(pid_t *kid_out, int *infd_out, int *outfd_out, int *errfd_out, + struct tvec_state *tv, const struct tvec_remoteenv *env) +{ + struct tvec_config config; + const struct tvec_remotefork *rf = (const struct tvec_remotefork *)env; + pid_t kid = -1; + int infd = -1, outfd = -1, errfd = -1; + int rc; + + if (fork_common(&kid, &infd, &outfd, &errfd, tv)) { rc = -1; goto end; } + if (!kid) { + config.tests = rf->f.tests ? rf->f.tests : tv->tests; + config.nrout = tv->nrout; config.nreg = tv->nreg; + config.regsz = tv->regsz; + _exit(tvec_remoteserver(infd, outfd, &config)); + } + + *kid_out = kid; *infd_out = infd; *outfd_out = outfd; *errfd_out = errfd; + rc = 0; +end: + return (rc); +} + +int tvec_exec(pid_t *kid_out, int *infd_out, int *outfd_out, int *errfd_out, + struct tvec_state *tv, const struct tvec_remoteenv *env) +{ + const struct tvec_remoteexec *rx = (const struct tvec_remoteexec *)env; + pid_t kid = -1; + int infd = -1, outfd = -1, errfd = -1; + mdup_fd v[2]; + int rc; + + if (fork_common(&kid, &infd, &outfd, &errfd, tv)) { rc = -1; goto end; } + if (!kid) { + v[0].cur = infd; v[0].want = STDIN_FILENO; + v[1].cur = outfd; v[1].want = STDOUT_FILENO; + if (mdup(v, 2)) { + fprintf(stderr, "failed to establish standard file descriptors: %s", + strerror(errno)); + exit(127); + } + execvp(rx->x.args[0], (/*uncosnt*/ char *const *)rx->x.args); + fprintf(stderr, "failed to invoke test runner: %s", strerror(errno)); + exit(127); + } + + *kid_out = kid; *infd_out = infd; *outfd_out = outfd; *errfd_out = errfd; + rc = 0; +end: + return (rc); +} /*----- That's all, folks -------------------------------------------------*/