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 #if GCC_VERSION_P(7, 1)
53 # pragma GCC diagnostic ignored "-Wdangling-else"
54 #elif GCC_VERSION_P(4, 2)
55 # pragma GCC diagnostic ignored "-Wparentheses"
58 #if CLANG_VERSION_P(3, 1)
59 # pragma clang diagnostic ignored "-Wdangling-else"
62 /*----- Basic I/O ---------------------------------------------------------*/
64 static void init_comms(struct tvec_remotecomms *rc)
66 dbuf_create(&rc->bin); dbuf_create(&rc->bout);
67 rc->infd = rc->outfd = -1; rc->f = 0;
70 static void close_comms(struct tvec_remotecomms *rc)
72 if (rc->infd >= 0) { close(rc->infd); rc->infd = -1; }
73 if (rc->outfd >= 0) { close(rc->outfd); rc->outfd = -1; }
76 static void release_comms(struct tvec_remotecomms *rc)
77 { close_comms(rc); dbuf_destroy(&rc->bin); dbuf_destroy(&rc->bout); }
79 static void setup_comms(struct tvec_remotecomms *rc, int infd, int outfd)
81 rc->infd = infd; rc->outfd = outfd; rc->f &= ~0xffu;
82 dbuf_reset(&rc->bin); dbuf_reset(&rc->bout);
85 static int PRINTF_LIKE(3, 4)
86 ioerr(struct tvec_state *tv, struct tvec_remotecomms *rc,
92 close_comms(rc); rc->f |= TVRF_BROKEN;
93 tvec_report_v(tv, TVLEV_ERR, msg, &ap);
98 static int send_all(struct tvec_state *tv, struct tvec_remotecomms *rc,
99 const unsigned char *p, size_t sz)
101 void (*opipe)(int) = SIG_ERR;
105 opipe = signal(SIGPIPE, SIG_IGN);
106 if (opipe == SIG_ERR) {
107 ret = ioerr(tv, rc, "failed to ignore `SIGPIPE': %s", strerror(errno));
111 n = write(rc->outfd, p, sz);
115 ret = ioerr(tv, rc, "failed to send: %s",
116 n ? strerror(errno) : "empty write");
122 if (opipe != SIG_ERR) signal(SIGPIPE, opipe);
126 #define RCVF_ALLOWEOF 1u
132 static int recv_all(struct tvec_state *tv, struct tvec_remotecomms *rc,
133 unsigned char *p, size_t sz, unsigned f)
140 n = read(rc->infd, p, sz);
142 { p += n; sz -= n; ff |= f_any; }
143 else if (!n && (f&RCVF_ALLOWEOF) && !(ff&f_any))
146 return (ioerr(tv, rc, "failed to receive: %s",
147 n ? strerror(errno) : "unexpected end-of-file"));
154 static int remote_send(struct tvec_state *tv, struct tvec_remotecomms *rc)
156 kludge64 k; unsigned char lenbuf[8];
157 const unsigned char *p; size_t sz;
159 if (rc->f&TVRF_BROKEN) return (-1);
160 if (BBAD(&rc->bout._b))
161 return (ioerr(tv, rc, "failed to build output packet buffer"));
163 p = BBASE(&rc->bout._b); sz = BLEN(&rc->bout._b);
164 ASSIGN64(k, sz); STORE64_L_(lenbuf, k);
165 if (send_all(tv, rc, lenbuf, sizeof(lenbuf))) return (-1);
166 if (send_all(tv, rc, p, sz)) return (-1);
171 static int remote_recv(struct tvec_state *tv, struct tvec_remotecomms *rc,
172 unsigned f, buf *b_out)
174 kludge64 k, szmax; unsigned char lenbuf[8];
179 if (rc->f&TVRF_BROKEN) return (RECV_FAIL);
180 ASSIGN64(szmax, (size_t)-1);
181 ret = recv_all(tv, rc, lenbuf, sizeof(lenbuf), f);
182 if (ret) return (ret);
183 LOAD64_L_(k, lenbuf);
184 if (CMP64(k, >, szmax))
185 return (ioerr(tv, rc, "packet size 0x%08lx%08lx out of range",
186 (unsigned long)HI64(k), (unsigned long)LO64(k)));
188 sz = GET64(size_t, k); dbuf_reset(&rc->bin); p = buf_get(&rc->bin._b, sz);
189 if (!p) return (ioerr(tv, rc, "failed to allocate receive buffer"));
190 if (recv_all(tv, rc, p, sz, 0)) return (RECV_FAIL);
191 buf_init(b_out, p, sz); return (RECV_OK);
194 #define SENDPK(tv, rc, pk) \
195 if ((rc)->f&TVRF_BROKEN) MC_GOELSE(body); else \
197 { dbuf_reset(&(rc)->bout); \
198 buf_putu16l(&(rc)->bout._b, (pk)); }) \
201 { if (remote_send(tv, rc)) MC_GOELSE(body); }) \
203 static int malformed(struct tvec_state *tv, struct tvec_remotecomms *rc)
204 { return (ioerr(tv, rc, "received malformed packet")); }
206 /*----- Packet types ------------------------------------------------------*/
208 #define TVPF_ACK 0x0001u
210 #define TVPK_VER 0x0000u /* --> min, max: u16 */
213 #define TVPK_REPORT 0x0100u /* <-- level: u16; msg: string */
214 #define TVPK_PROGRESS 0x0102u /* <-- st: str16 */
216 #define TVPK_BGROUP 0x0200u /* --> name: str16
218 #define TVPK_TEST 0x0202u /* --> in: regs
220 #define TVPK_EGROUP 0x0204u /* --> --- */
222 #define TVPK_SKIPGRP 0x0300u /* <-- excuse: str16 */
223 #define TVPK_SKIP 0x0302u /* <-- excuse: str16 */
224 #define TVPK_FAIL 0x0304u /* <-- flag: u8, detail: str16 */
225 #define TVPK_DUMPREG 0x0306u /* <-- ri: u16; disp: u16;
226 * flag: u8, rv: value */
227 #define TVPK_BBENCH 0x0308u /* <-- ident: str32; unit: u16 */
228 #define TVPK_EBENCH 0x030au /* <-- ident: str32; unit: u16;
229 * flags: u16; n, t, cy: f64 */
231 /*----- Server ------------------------------------------------------------*/
233 static const struct tvec_outops remote_ops;
235 static struct tvec_state srvtv;
236 static struct tvec_remotecomms srvrc = TVEC_REMOTECOMMS_INIT;
237 static struct tvec_output srvout = { &remote_ops };
239 int tvec_setprogress(const char *status)
241 SENDPK(&srvtv, &srvrc, TVPK_PROGRESS)
242 buf_putstr16l(&srvrc.bout._b, status);
247 int tvec_remoteserver(int infd, int outfd, const struct tvec_config *config)
252 const struct tvec_test *t;
254 const struct tvec_env *env = 0;
256 #define f_regslive 1u
260 setup_comms(&srvrc, infd, outfd);
261 tvec_begin(&srvtv, config, &srvout);
263 if (remote_recv(&srvtv, &srvrc, 0, &b)) { rc = -1; goto end; }
264 if (buf_getu16l(&b, &pk)) goto bad;
265 if (pk != TVPK_VER) {
266 rc = ioerr(&srvtv, &srvrc,
267 "unexpected packet type 0x%04x instead of client version",
271 if (buf_getu16l(&b, &u) || buf_getu16l(&b, &v)) goto bad;
272 SENDPK(&srvtv, &srvrc, TVPK_VER | TVPF_ACK) buf_putu16l(&srvrc.bout._b, 0);
273 else { rc = -1; goto end; }
275 tvec_setprogress("%IDLE");
278 rc = remote_recv(&srvtv, &srvrc, RCVF_ALLOWEOF, &b);
279 if (rc == RECV_EOF) break;
280 else if (rc == RECV_FAIL) goto end;
281 if (buf_getu16l(&b, &pk)) goto bad;
286 p = buf_getmem16l(&b, &sz); if (!p) goto bad;
287 if (BLEFT(&b)) goto bad;
288 for (t = srvtv.tests; t->name; t++)
289 if (strlen(t->name) == sz && MEMCMP(t->name, ==, p, sz))
291 rc = ioerr(&srvtv, &srvrc, "unknown test group `%.*s'",
296 srvtv.test = t; env = t->env;
297 if (env && env->setup == tvec_remotesetup)
298 env = ((struct tvec_remoteenv *)env)->r.env;
299 if (!env || !env->ctxsz) ctx = 0;
300 else ctx = xmalloc(env->ctxsz);
301 if (env && env->setup) env->setup(&srvtv, env, 0, ctx);
303 SENDPK(&srvtv, &srvrc, TVPK_BGROUP | TVPF_ACK);
304 else { rc = -1; goto end; }
307 if (remote_recv(&srvtv, &srvrc, 0, &b)) { rc = -1; goto end; }
308 if (buf_getu16l(&b, &pk)) goto bad;
312 if (BLEFT(&b)) goto bad;
316 tvec_initregs(&srvtv); f |= f_regslive;
317 if (tvec_deserialize(srvtv.in, &b, srvtv.test->regs,
318 srvtv.nreg, srvtv.regsz))
320 if (BLEFT(&b)) goto bad;
322 if (!(srvtv.f&TVSF_SKIP)) {
323 srvtv.f |= TVSF_ACTIVE; srvtv.f &= ~TVSF_OUTMASK;
324 tvec_setprogress("%SETUP");
325 if (env && env->before) env->before(&srvtv, ctx);
326 if (!(srvtv.f&TVSF_ACTIVE))
327 /* setup forced a skip */;
329 for (i = 0; i < srvtv.nrout; i++)
330 if (TVEC_REG(&srvtv, in, i)->f&TVRF_LIVE)
331 TVEC_REG(&srvtv, out, i)->f |= TVRF_LIVE;
332 tvec_setprogress("%RUN");
334 env->run(&srvtv, t->fn, ctx);
336 t->fn(srvtv.in, srvtv.out, ctx);
337 tvec_check(&srvtv, 0);
340 tvec_setprogress("%DONE");
341 if (env && env->after) env->after(&srvtv, ctx);
342 tvec_endtest(&srvtv);
344 tvec_releaseregs(&srvtv); f &= ~f_regslive;
345 SENDPK(&srvtv, &srvrc, TVPK_TEST | TVPF_ACK);
346 else { rc = -1; goto end; }
347 tvec_setprogress("%IDLE");
351 rc = ioerr(&srvtv, &srvrc,
352 "unexpected packet type 0x%04x", pk);
359 if (env && env->teardown) env->teardown(&srvtv, ctx);
360 xfree(ctx); t = 0; env = 0; ctx = 0;
370 if (env && env->teardown) env->teardown(&srvtv, ctx);
372 if (f&f_regslive) tvec_releaseregs(&srvtv);
373 release_comms(&srvrc);
377 rc = malformed(&srvtv, &srvrc); goto end;
382 /*----- Server output driver ----------------------------------------------*/
384 static void remote_skipgroup(struct tvec_output *o,
385 const char *excuse, va_list *ap)
387 SENDPK(&srvtv, &srvrc, TVPK_SKIPGRP)
388 buf_vputstrf16l(&srvrc.bout._b, excuse, ap);
391 static void remote_skip(struct tvec_output *o,
392 const char *excuse, va_list *ap)
394 SENDPK(&srvtv, &srvrc, TVPK_SKIP)
395 buf_vputstrf16l(&srvrc.bout._b, excuse, ap);
398 static void remote_fail(struct tvec_output *o,
399 const char *detail, va_list *ap)
401 SENDPK(&srvtv, &srvrc, TVPK_FAIL)
403 buf_putbyte(&srvrc.bout._b, 0);
405 buf_putbyte(&srvrc.bout._b, 1);
406 buf_vputstrf16l(&srvrc.bout._b, detail, ap);
410 static void remote_dumpreg(struct tvec_output *o,
411 unsigned disp, const union tvec_regval *rv,
412 const struct tvec_regdef *rd)
414 const struct tvec_regdef *reg;
417 /* Find the register definition. */
418 for (reg = srvtv.test->regs, r = 0; reg->name; reg++, r++)
419 if (reg == rd) goto found;
420 assert(!"unexpected register definition");
423 SENDPK(&srvtv, &srvrc, TVPK_DUMPREG) {
424 buf_putu16l(&srvrc.bout._b, r);
425 buf_putu16l(&srvrc.bout._b, disp);
427 buf_putbyte(&srvrc.bout._b, 0);
429 buf_putbyte(&srvrc.bout._b, 1);
430 rd->ty->tobuf(&srvrc.bout._b, rv, rd);
435 static void remote_bbench(struct tvec_output *o,
436 const char *ident, unsigned unit)
438 SENDPK(&srvtv, &srvrc, TVPK_BBENCH) {
439 buf_putstr32l(&srvrc.bout._b, ident);
440 buf_putu16l(&srvrc.bout._b, unit);
444 static void remote_ebench(struct tvec_output *o,
445 const char *ident, unsigned unit,
446 const struct bench_timing *t)
448 SENDPK(&srvtv, &srvrc, TVPK_EBENCH) {
449 buf_putstr32l(&srvrc.bout._b, ident);
450 buf_putu16l(&srvrc.bout._b, unit);
451 if (!t || !(t->f&BTF_ANY))
452 buf_putu16l(&srvrc.bout._b, 0);
454 buf_putu16l(&srvrc.bout._b, t->f);
455 buf_putf64l(&srvrc.bout._b, t->n);
456 if (t->f&BTF_TIMEOK) buf_putf64l(&srvrc.bout._b, t->t);
457 if (t->f&BTF_CYOK) buf_putf64l(&srvrc.bout._b, t->cy);
462 static void remote_report(struct tvec_output *o, unsigned level,
463 const char *msg, va_list *ap)
467 SENDPK(&srvtv, &srvrc, TVPK_REPORT) {
468 buf_putu16l(&srvrc.bout._b, level);
469 buf_vputstrf16l(&srvrc.bout._b, msg, ap);
472 case TVLEV_NOTE: what = "notice"; break;
473 case TVLEV_ERR: what = "ERROR"; break;
474 default: what = "(?level)"; break;
476 fprintf(stderr, "%s %s: ", QUIS, what);
477 vfprintf(stderr, msg, *ap);
482 static void remote_bsession(struct tvec_output *o, struct tvec_state *tv)
484 static int remote_esession(struct tvec_output *o)
485 { return (srvtv.f&TVSF_ERROR ? 2 : 0); }
486 static void remote_destroy(struct tvec_output *o)
488 static void remote_etest(struct tvec_output *o, unsigned outcome)
491 static void remote_bgroup(struct tvec_output *o)
492 { assert(!"remote_bgroup"); }
493 static void remote_egroup(struct tvec_output *o)
494 { assert(!"remote_egroup"); }
495 static void remote_btest(struct tvec_output *o)
496 { assert(!"remote_btest"); }
498 static const struct tvec_outops remote_ops = {
499 remote_bsession, remote_esession,
500 remote_bgroup, remote_skipgroup, remote_egroup,
501 remote_btest, remote_skip, remote_fail, remote_dumpreg, remote_etest,
502 remote_bbench, remote_ebench,
507 /*----- Client ------------------------------------------------------------*/
509 #define TVXF_VALMASK 0x0fffu
510 #define TVXF_SIG 0x1000u
511 #define TVXF_CAUSEMASK 0xe000u
512 #define TVXST_RUN 0x0000u
513 #define TVXST_EXIT 0x2000u
514 #define TVXST_KILL 0x4000u
515 #define TVXST_CONT 0x6000u
516 #define TVXST_STOP 0x8000u
517 #define TVXST_DISCONN 0xa000u
518 #define TVXST_UNK 0xc000u
519 #define TVXST_ERR 0xe000u
521 static const struct tvec_flag exit_flags[] = {
523 ;;; The signal name table is very boring to type. To make life less
524 ;;; awful, put the signal names in this list and evaluate the code to
525 ;;; get Emacs to regenerate it.
527 (let ((signals '(HUP INT QUIT ILL TRAP ABRT IOT EMT FPE KILL BUS SEGV SYS
528 PIPE ALRM TERM URG STOP TSTP CONT CHLD CLD TTIN TTOU
529 POLL IO TIN XCPU XFSZ VTALRM PROF WINCH USR1 USR2
530 STKFLT INFO PWR THR LWP LIBRT LOST)))
532 (goto-char (point-min))
533 (search-forward (concat "***" "BEGIN siglist" "***"))
534 (beginning-of-line 2)
535 (delete-region (point)
537 (search-forward "***END***")
540 (dolist (sig signals)
541 (insert (format "#ifdef SIG%s\n { \"SIG%s\", TVXF_VALMASK | TVXF_SIG, SIG%s | TVXF_SIG },\n#endif\n"
545 /***BEGIN siglist***/
547 { "SIGHUP", TVXF_VALMASK | TVXF_SIG, SIGHUP | TVXF_SIG },
550 { "SIGINT", TVXF_VALMASK | TVXF_SIG, SIGINT | TVXF_SIG },
553 { "SIGQUIT", TVXF_VALMASK | TVXF_SIG, SIGQUIT | TVXF_SIG },
556 { "SIGILL", TVXF_VALMASK | TVXF_SIG, SIGILL | TVXF_SIG },
559 { "SIGTRAP", TVXF_VALMASK | TVXF_SIG, SIGTRAP | TVXF_SIG },
562 { "SIGABRT", TVXF_VALMASK | TVXF_SIG, SIGABRT | TVXF_SIG },
565 { "SIGIOT", TVXF_VALMASK | TVXF_SIG, SIGIOT | TVXF_SIG },
568 { "SIGEMT", TVXF_VALMASK | TVXF_SIG, SIGEMT | TVXF_SIG },
571 { "SIGFPE", TVXF_VALMASK | TVXF_SIG, SIGFPE | TVXF_SIG },
574 { "SIGKILL", TVXF_VALMASK | TVXF_SIG, SIGKILL | TVXF_SIG },
577 { "SIGBUS", TVXF_VALMASK | TVXF_SIG, SIGBUS | TVXF_SIG },
580 { "SIGSEGV", TVXF_VALMASK | TVXF_SIG, SIGSEGV | TVXF_SIG },
583 { "SIGSYS", TVXF_VALMASK | TVXF_SIG, SIGSYS | TVXF_SIG },
586 { "SIGPIPE", TVXF_VALMASK | TVXF_SIG, SIGPIPE | TVXF_SIG },
589 { "SIGALRM", TVXF_VALMASK | TVXF_SIG, SIGALRM | TVXF_SIG },
592 { "SIGTERM", TVXF_VALMASK | TVXF_SIG, SIGTERM | TVXF_SIG },
595 { "SIGURG", TVXF_VALMASK | TVXF_SIG, SIGURG | TVXF_SIG },
598 { "SIGSTOP", TVXF_VALMASK | TVXF_SIG, SIGSTOP | TVXF_SIG },
601 { "SIGTSTP", TVXF_VALMASK | TVXF_SIG, SIGTSTP | TVXF_SIG },
604 { "SIGCONT", TVXF_VALMASK | TVXF_SIG, SIGCONT | TVXF_SIG },
607 { "SIGCHLD", TVXF_VALMASK | TVXF_SIG, SIGCHLD | TVXF_SIG },
610 { "SIGCLD", TVXF_VALMASK | TVXF_SIG, SIGCLD | TVXF_SIG },
613 { "SIGTTIN", TVXF_VALMASK | TVXF_SIG, SIGTTIN | TVXF_SIG },
616 { "SIGTTOU", TVXF_VALMASK | TVXF_SIG, SIGTTOU | TVXF_SIG },
619 { "SIGPOLL", TVXF_VALMASK | TVXF_SIG, SIGPOLL | TVXF_SIG },
622 { "SIGIO", TVXF_VALMASK | TVXF_SIG, SIGIO | TVXF_SIG },
625 { "SIGTIN", TVXF_VALMASK | TVXF_SIG, SIGTIN | TVXF_SIG },
628 { "SIGXCPU", TVXF_VALMASK | TVXF_SIG, SIGXCPU | TVXF_SIG },
631 { "SIGXFSZ", TVXF_VALMASK | TVXF_SIG, SIGXFSZ | TVXF_SIG },
634 { "SIGVTALRM", TVXF_VALMASK | TVXF_SIG, SIGVTALRM | TVXF_SIG },
637 { "SIGPROF", TVXF_VALMASK | TVXF_SIG, SIGPROF | TVXF_SIG },
640 { "SIGWINCH", TVXF_VALMASK | TVXF_SIG, SIGWINCH | TVXF_SIG },
643 { "SIGUSR1", TVXF_VALMASK | TVXF_SIG, SIGUSR1 | TVXF_SIG },
646 { "SIGUSR2", TVXF_VALMASK | TVXF_SIG, SIGUSR2 | TVXF_SIG },
649 { "SIGSTKFLT", TVXF_VALMASK | TVXF_SIG, SIGSTKFLT | TVXF_SIG },
652 { "SIGINFO", TVXF_VALMASK | TVXF_SIG, SIGINFO | TVXF_SIG },
655 { "SIGPWR", TVXF_VALMASK | TVXF_SIG, SIGPWR | TVXF_SIG },
658 { "SIGTHR", TVXF_VALMASK | TVXF_SIG, SIGTHR | TVXF_SIG },
661 { "SIGLWP", TVXF_VALMASK | TVXF_SIG, SIGLWP | TVXF_SIG },
664 { "SIGLIBRT", TVXF_VALMASK | TVXF_SIG, SIGLIBRT | TVXF_SIG },
667 { "SIGLOST", TVXF_VALMASK | TVXF_SIG, SIGLOST | TVXF_SIG },
671 { "signal", TVXF_SIG, TVXF_SIG },
673 { "running", TVXF_CAUSEMASK, TVXST_RUN },
674 { "exited", TVXF_CAUSEMASK, TVXST_EXIT },
675 { "killed", TVXF_CAUSEMASK, TVXST_KILL },
676 { "stopped", TVXF_CAUSEMASK, TVXST_STOP },
677 { "continued", TVXF_CAUSEMASK, TVXST_CONT },
678 { "disconnected", TVXF_CAUSEMASK, TVXST_DISCONN },
679 { "unknown", TVXF_CAUSEMASK, TVXST_UNK },
680 { "error", TVXF_CAUSEMASK, TVXST_ERR },
685 static const struct tvec_flaginfo exit_flaginfo =
686 { "exit-status", exit_flags, &tvrange_uint };
687 static const struct tvec_regdef exit_regdef =
688 { "@exit", 0, &tvty_flags, 0, { &exit_flaginfo } };
690 static const struct tvec_regdef progress_regdef =
691 { "@progress", 0, &tvty_string, 0 };
693 static const struct tvec_uassoc reconn_assocs[] = {
694 { "on-demand", TVRCN_DEMAND },
695 { "force", TVRCN_FORCE },
696 { "skip", TVRCN_SKIP },
701 CONN_BROKEN = -2, /* previously broken */
702 CONN_FAILED = -1, /* attempt freshly failed */
703 CONN_ESTABLISHED = 0, /* previously established */
704 CONN_FRESH = 1 /* freshly connected */
707 static const struct tvec_uenuminfo reconn_enuminfo =
708 { "remote-reconnection", reconn_assocs, &tvrange_uint };
709 static const struct tvec_regdef reconn_regdef =
710 { "@reconnect", 0, &tvty_uenum, 0, { &reconn_enuminfo } };
712 static int handle_packets(struct tvec_state *tv, struct tvec_remotectx *r,
713 unsigned f, uint16 end, buf *b_out)
715 struct tvec_output *o = tv->output;
717 const char *p; size_t n;
720 const struct tvec_regdef *rd;
721 struct bench_timing bt;
722 struct tvec_reg *reg = 0;
727 rc = remote_recv(tv, &r->rc, f, b); if (rc) goto end;
728 if (buf_getu16l(b, &pk)) goto bad;
733 p = buf_getmem16l(b, &n); if (!p) goto bad;
734 if (BLEFT(b)) goto bad;
736 DRESET(&r->progress); DPUTM(&r->progress, p, n); DPUTZ(&r->progress);
740 if (buf_getu16l(b, &u)) goto bad;
741 p = buf_getmem16l(b, &n); if (!p) goto bad;
742 if (BLEFT(b)) goto bad;
744 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
745 tvec_report(tv, u, "%s", d.buf);
749 p = buf_getmem16l(b, &n); if (!p) goto bad;
750 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
751 if (BLEFT(b)) goto bad;
753 tvec_skipgroup(tv, "%s", d.buf);
757 if (!(tv->f&TVSF_ACTIVE)) {
758 rc = ioerr(tv, &r->rc, "test `%s' not active", tv->test->name);
762 p = buf_getmem16l(b, &n); if (!p) goto bad;
763 if (BLEFT(b)) goto bad;
765 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
766 tvec_skip(tv, "%s", d.buf);
770 if (!(tv->f&TVSF_ACTIVE) &&
771 ((tv->f&TVSF_OUTMASK) != (TVOUT_LOSE << TVSF_OUTSHIFT))) {
772 rc = ioerr(tv, &r->rc, "test `%s' not active or failing",
777 rc = buf_getbyte(b); if (rc < 0) goto bad;
778 if (rc) { p = buf_getmem16l(b, &n); if (!p) goto bad; }
780 if (BLEFT(b)) goto bad;
785 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
786 tvec_fail(tv, "%s", d.buf);
791 if (buf_getu16l(b, &u) || buf_getu16l(b, &v)) goto bad;
792 for (rd = tv->test->regs, i = 0; rd->name; rd++, i++)
793 if (i == u) goto found_reg;
794 rc = ioerr(tv, &r->rc,
795 "register definition %u out of range for test `%s'",
799 if (v >= TVRD_LIMIT) {
800 rc = ioerr(tv, &r->rc, "register disposition %u out of range", v);
804 rc = buf_getbyte(b); if (rc < 0) goto bad;
806 tvec_dumpreg(tv, v, 0, rd);
808 if (!reg) reg = xmalloc(tv->regsz);
809 rd->ty->init(®->v, rd);
810 rc = rd->ty->frombuf(b, ®->v, rd);
811 if (!rc) tvec_dumpreg(tv, v, ®->v, rd);
812 rd->ty->release(®->v, rd);
815 if (BLEFT(b)) goto bad;
819 p = buf_getmem32l(b, &n); if (!p) goto bad;
820 if (buf_getu16l(b, &u)) goto bad;
821 if (BLEFT(b)) goto bad;
822 if (u >= TVBU_LIMIT) {
823 rc = ioerr(tv, &r->rc, "unit code %u out of range", u);
827 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
828 o->ops->bbench(o, d.buf, u);
832 p = buf_getmem32l(b, &n); if (!p) goto bad;
833 if (buf_getu16l(b, &u) || buf_getu16l(b, &v)) goto bad;
835 { rc = ioerr(tv, &r->rc, "unit code %u out of range", u); goto end; }
836 if ((v&BTF_ANY) && buf_getf64l(b, &bt.n)) goto bad;
837 if ((v&BTF_TIMEOK) && buf_getf64l(b, &bt.t)) goto bad;
838 if ((v&BTF_CYOK) && buf_getf64l(b, &bt.cy)) goto bad;
839 if (BLEFT(b)) goto bad;
841 DRESET(&d); DPUTM(&d, p, n); DPUTZ(&d);
842 o->ops->ebench(o, d.buf, u, v&BTF_ANY ? &bt : 0);
846 if (pk == end) { rc = 0; goto end; }
847 rc = ioerr(tv, &r->rc, "unexpected packet type 0x%04x", pk);
857 rc = malformed(tv, &r->rc); goto end;
860 static void reap_kid(struct tvec_state *tv, struct tvec_remotectx *r)
866 { r->exit = TVXST_DISCONN; r->kid = -1; }
867 else if (r->kid > 0) {
868 kid = waitpid(r->kid, &st, 0);
870 tvec_notice(tv, "failed to wait for remote child: %s",
874 tvec_notice(tv, "remote child vanished without a trace");
876 } else if (WIFCONTINUED(st))
877 r->exit = TVXST_CONT;
878 else if (WIFSIGNALED(st))
879 r->exit = TVXST_KILL | TVXF_SIG | WTERMSIG(st);
880 else if (WIFSTOPPED(st))
881 r->exit = TVXST_STOP | TVXF_SIG | WSTOPSIG(st);
882 else if (WIFEXITED(st))
883 r->exit = TVXST_EXIT | WEXITSTATUS(st);
885 tvec_notice(tv, "remote child died with unknown status 0x%04x",
893 static void report_errline(char *p, size_t n, void *ctx)
895 struct tvec_remotectx *r = ctx;
896 struct tvec_state *tv = r->tv;
898 if (p && !(r->rc.f&TVRF_MUFFLE))
899 tvec_notice(tv, "child process stderr: %s", p);
902 #define ERF_SILENT 0x0001u
903 #define ERF_CLOSE 0x0002u
904 static int drain_errfd(struct tvec_state *tv, struct tvec_remotectx *r,
911 if (f&ERF_SILENT) r->rc.f |= TVRF_MUFFLE;
912 else r->rc.f &= ~TVRF_MUFFLE;
913 if (fdflags(r->errfd, O_NONBLOCK, f&ERF_CLOSE ? 0 : O_NONBLOCK, 0, 0)) {
914 rc = ioerr(tv, &r->rc, "failed to %s error non-blocking flag",
915 f&ERF_CLOSE ? "clear" : "set");
920 sz = lbuf_free(&r->errbuf, &p);
921 n = read(r->errfd, p, sz);
924 if (errno == EINTR) continue;
925 if (!(f&ERF_CLOSE) && (errno == EWOULDBLOCK || errno == EAGAIN))
927 rc = ioerr(tv, &r->rc, "failed to read child stderr: %s",
931 lbuf_flush(&r->errbuf, p, n);
936 lbuf_close(&r->errbuf);
942 #define DCF_KILL 0x0100u
943 static void disconnect_remote(struct tvec_state *tv,
944 struct tvec_remotectx *r, unsigned f)
946 if (r->kid < 0) return;
947 if (r->kid > 0 && (f&DCF_KILL)) kill(r->kid, SIGTERM);
949 if (r->kid > 0) kill(r->kid, SIGTERM);
950 drain_errfd(tv, r, f | ERF_CLOSE); reap_kid(tv, r);
953 static int connect_remote(struct tvec_state *tv, struct tvec_remotectx *r)
955 const struct tvec_remoteenv *re = r->re;
959 int infd = -1, outfd = -1, errfd = -1, rc;
961 DRESET(&r->progress); DPUTS(&r->progress, "%INIT");
962 if (r->kid >= 0) { rc = 0; goto end; }
963 if (re->r.connect(&kid, &infd, &outfd, &errfd, tv, re))
964 { rc = -1; goto end; }
965 setup_comms(&r->rc, infd, outfd); r->kid = kid; r->errfd = errfd;
966 lbuf_init(&r->errbuf, report_errline, r);
967 r->exit = TVXST_RUN; r->rc.f &= ~TVRF_BROKEN;
969 SENDPK(tv, &r->rc, TVPK_VER) {
970 buf_putu16l(&r->rc.bout._b, 0);
971 buf_putu16l(&r->rc.bout._b, 0);
972 } else { rc = -1; goto end; }
974 if (handle_packets(tv, r, 0, TVPK_VER | TVPF_ACK, &b))
975 { rc = -1; goto end; }
976 if (buf_getu16l(&b, &v)) goto bad;
977 if (BLEFT(&b)) { rc = malformed(tv, &r->rc); goto end; }
979 rc = ioerr(tv, &r->rc, "protocol version %u not supported", v);
983 SENDPK(tv, &r->rc, TVPK_BGROUP)
984 buf_putstr16l(&r->rc.bout._b, tv->test->name);
985 else { rc = -1; goto end; }
986 if (handle_packets(tv, r, 0, TVPK_BGROUP | TVPF_ACK, &b))
987 { rc = -1; goto end; }
988 if (BLEFT(&b)) { rc = malformed(tv, &r->rc); goto end; }
991 if (rc) disconnect_remote(tv, r, DCF_KILL);
994 rc = malformed(tv, &r->rc); goto end;
997 static int check_comms(struct tvec_state *tv, struct tvec_remotectx *r)
1000 return (CONN_BROKEN);
1001 else if (r->rc.f&TVRF_BROKEN)
1002 { disconnect_remote(tv, r, DCF_KILL); return (CONN_FAILED); }
1004 return (CONN_ESTABLISHED);
1007 static int try_reconnect(struct tvec_state *tv, struct tvec_remotectx *r)
1011 switch (r->rc.f&TVRF_RCNMASK) {
1013 rc = check_comms(tv, r);
1014 if (rc < CONN_ESTABLISHED) {
1015 close_comms(&r->rc);
1016 if (connect_remote(tv, r)) rc = CONN_FAILED;
1017 else rc = CONN_FRESH;
1021 disconnect_remote(tv, r, DCF_KILL);
1022 if (connect_remote(tv, r)) rc = CONN_FAILED;
1023 else rc = CONN_FRESH;
1026 rc = check_comms(tv, r);
1034 static void reset_vars(struct tvec_remotectx *r)
1036 r->exwant = TVXST_RUN; r->rc.f = (r->rc.f&~TVRF_RCNMASK) | TVRCN_DEMAND;
1037 DRESET(&r->prgwant); DPUTS(&r->prgwant, "%DONE");
1040 void tvec_remotesetup(struct tvec_state *tv, const struct tvec_env *env,
1041 void *pctx, void *ctx)
1043 struct tvec_remotectx *r = ctx;
1044 const struct tvec_remoteenv *re = (const struct tvec_remoteenv *)env;
1046 assert(!re->r.env || tv->test->env == &re->_env);
1050 r->re = re; r->kid = -1;
1051 DCREATE(&r->prgwant); DCREATE(&r->progress);
1052 if (connect_remote(tv, r))
1053 tvec_skipgroup(tv, "failed to connect to test backend");
1057 int tvec_remoteset(struct tvec_state *tv, const char *var,
1058 const struct tvec_env *env, void *ctx)
1060 struct tvec_remotectx *r = ctx;
1061 union tvec_regval rv;
1064 if (STRCMP(var, ==, "@exit")) {
1065 if (tvty_flags.parse(&rv, &exit_regdef, tv)) { rc = -1; goto end; }
1066 if (r) r->exwant = rv.u;
1068 } else if (STRCMP(var, ==, "@progress")) {
1069 tvty_string.init(&rv, &progress_regdef);
1070 rc = tvty_string.parse(&rv, &progress_regdef, tv);
1072 { DRESET(&r->prgwant); DPUTM(&r->prgwant, rv.str.p, rv.str.sz); }
1073 tvty_string.release(&rv, &progress_regdef);
1074 if (rc) { rc = -1; goto end; }
1076 } else if (STRCMP(var, ==, "@reconnect")) {
1077 if (tvty_uenum.parse(&rv, &reconn_regdef, tv)) { rc = -1; goto end; }
1078 if (r) r->rc.f = (r->rc.f&~TVRF_RCNMASK) | (rv.u&TVRF_RCNMASK);
1087 void tvec_remoteafter(struct tvec_state *tv, void *ctx)
1089 struct tvec_remotectx *r = ctx;
1094 void tvec_remoterun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
1096 struct tvec_remotectx *r = ctx;
1097 union tvec_regval rv;
1100 #define f_progress 2u
1105 switch (try_reconnect(tv, r)) {
1107 tvec_skip(tv, "failed to connect to test backend"); return;
1109 tvec_skip(tv, "no connection"); return;
1112 SENDPK(tv, &r->rc, TVPK_TEST)
1113 tvec_serialize(tv->in, &r->rc.bout._b,
1114 tv->test->regs, tv->nreg, tv->regsz);
1115 else { rc = -1; goto end; }
1116 rc = handle_packets(tv, r, RCVF_ALLOWEOF, TVPK_TEST | TVPF_ACK, &b);
1124 if (r->exit != r->exwant) f |= f_exit;
1125 if (r->progress.len != r->prgwant.len ||
1126 MEMCMP(r->progress.buf, !=, r->prgwant.buf, r->progress.len))
1128 if (f && (tv->f&TVSF_ACTIVE))
1129 { tvec_fail(tv, 0); tvec_mismatch(tv, TVMF_IN); }
1130 if (!(tv->f&TVSF_ACTIVE) &&
1131 (tv->f&TVSF_OUTMASK) == (TVOUT_LOSE << TVSF_OUTSHIFT)) {
1135 tvec_dumpreg(tv, f&f_exit ? TVRD_FOUND : TVRD_MATCH,
1139 tvec_dumpreg(tv, TVRD_EXPECT, &rv, &exit_regdef);
1142 rv.str.p = r->progress.buf; rv.str.sz = r->progress.len;
1143 tvec_dumpreg(tv, f&f_progress ? TVRD_FOUND : TVRD_MATCH,
1144 &rv, &progress_regdef);
1146 rv.str.p = r->prgwant.buf; rv.str.sz = r->prgwant.len;
1147 tvec_dumpreg(tv, TVRD_EXPECT, &rv, &progress_regdef);
1152 disconnect_remote(tv, r, f ? 0 : ERF_SILENT);
1158 if ((tv->f&TVSF_ACTIVE) && f)
1159 tvec_skip(tv, "remote test runner communications failed");
1160 disconnect_remote(tv, r, 0);
1168 void tvec_remoteteardown(struct tvec_state *tv, void *ctx)
1170 struct tvec_remotectx *r = ctx;
1173 disconnect_remote(tv, r, 0); release_comms(&r->rc);
1174 DDESTROY(&r->prgwant); DDESTROY(&r->progress);
1178 /*----- Connectors --------------------------------------------------------*/
1180 static int fork_common(pid_t *kid_out, int *infd_out, int *outfd_out,
1181 int *errfd_out, struct tvec_state *tv)
1183 int p0[2] = { -1, -1 }, p1[2] = { -1, -1 }, pe[2] = { -1, -1 };
1187 if (pipe(p0) || pipe(p1) || pipe(pe) ||
1188 fdflags(p0[1], 0, 0, FD_CLOEXEC, FD_CLOEXEC) ||
1189 fdflags(p1[0], 0, 0, FD_CLOEXEC, FD_CLOEXEC) ||
1190 fdflags(pe[0], 0, 0, FD_CLOEXEC, FD_CLOEXEC)) {
1191 tvec_error(tv, "pipe failed: %s", strerror(errno));
1199 tvec_error(tv, "fork failed: %s", strerror(errno));
1205 *infd_out = p0[0]; p0[0] = -1;
1206 *outfd_out = p1[1]; p1[1] = -1;
1207 if (pe[1] != STDERR_FILENO && dup2(pe[1], STDERR_FILENO) < 0) {
1208 fprintf(stderr, "failed to establish child stderr: %s",
1213 *kid_out = kid; kid = -1;
1214 *infd_out = p1[0]; p1[0] = -1;
1215 *outfd_out = p0[1]; p0[1] = -1;
1216 *errfd_out = pe[0]; pe[0] = -1;
1221 if (p0[0] >= 0) close(p0[0]);
1222 if (p0[1] >= 0) close(p0[1]);
1223 if (p1[0] >= 0) close(p1[0]);
1224 if (p1[1] >= 0) close(p1[1]);
1225 if (pe[0] >= 0) close(pe[0]);
1226 if (pe[1] >= 0) close(pe[1]);
1230 int tvec_fork(pid_t *kid_out, int *infd_out, int *outfd_out, int *errfd_out,
1231 struct tvec_state *tv, const struct tvec_remoteenv *env)
1233 struct tvec_config config;
1234 const struct tvec_remotefork *rf = (const struct tvec_remotefork *)env;
1236 int infd = -1, outfd = -1, errfd = -1;
1239 if (fork_common(&kid, &infd, &outfd, &errfd, tv)) { rc = -1; goto end; }
1241 config.tests = rf->f.tests ? rf->f.tests : tv->tests;
1242 config.nrout = tv->nrout; config.nreg = tv->nreg;
1243 config.regsz = tv->regsz;
1244 _exit(tvec_remoteserver(infd, outfd, &config));
1247 *kid_out = kid; *infd_out = infd; *outfd_out = outfd; *errfd_out = errfd;
1253 int tvec_exec(pid_t *kid_out, int *infd_out, int *outfd_out, int *errfd_out,
1254 struct tvec_state *tv, const struct tvec_remoteenv *env)
1256 const struct tvec_remoteexec *rx = (const struct tvec_remoteexec *)env;
1258 int infd = -1, outfd = -1, errfd = -1;
1262 if (fork_common(&kid, &infd, &outfd, &errfd, tv)) { rc = -1; goto end; }
1264 v[0].cur = infd; v[0].want = STDIN_FILENO;
1265 v[1].cur = outfd; v[1].want = STDOUT_FILENO;
1267 fprintf(stderr, "failed to establish standard file descriptors: %s",
1271 execvp(rx->x.args[0], (/*uncosnt*/ char *const *)rx->x.args);
1272 fprintf(stderr, "failed to invoke test runner: %s", strerror(errno));
1276 *kid_out = kid; *infd_out = infd; *outfd_out = outfd; *errfd_out = errfd;
1282 /*----- That's all, folks -------------------------------------------------*/