--- /dev/null
+/* -*-c-*-
+ *
+ * Test-vector framework remote testing extension
+ *
+ * (c) 2024 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Library General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with mLib. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef MLIB_TVEC_REMOTE_H
+#define MLIB_TVEC_REMOTE_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stddef.h>
+
+#include <sys/types.h>
+
+#ifndef MLIB_BUF_H
+# include "buf.h"
+#endif
+
+#ifndef MLIB_DSTR_H
+# include "dstr.h"
+#endif
+
+#ifndef MLIB_LBUF_H
+# include "lbuf.h"
+#endif
+
+#ifndef MLIB_TVEC_H
+# include "tvec.h"
+#endif
+
+/*----- Test environment --------------------------------------------------*/
+
+struct tvec_remoteenv;
+
+typedef int tvec_connectfn(pid_t */*kid_out*/, int */*infd_out*/,
+ int */*outfd_out*/, int */*errfd_out*/,
+ struct tvec_state */*tv*/,
+ const struct tvec_remoteenv */*env*/);
+ /* A connection function. On entry, @tv@ holds the test-vector state, and
+ * @env@ is the test group's remote environment structure, which will
+ * typically really be some subclass of @struct tvec_remoteenv@ containing
+ * additional parameters for establishing the child process.
+ *
+ * On successful completion, the function stores input and output
+ * descriptors (which need not be distinct) in @*infd_out@ and
+ * @*outfd_out@, and returns zero; if it creates a child process, it should
+ * additionally store the child's process-id in @*kid_out@ and store in
+ * @*errfd_out@ a descriptor from which the child's error output can be
+ * read. On error, the function should report an appropriate message via
+ * @tvec_error@ and return %$-1$%.
+ */
+
+struct tvec_remoteenv_slots {
+ /* Additional slots for the remote-environment base class. */
+
+ tvec_connectfn *connect; /* connection function */
+ const struct tvec_env *env; /* subordinate environment */
+ unsigned dflt_reconn; /* default reconnection */
+};
+
+struct tvec_remoteenv {
+ /* Remote environment base class instance structure. */
+
+ struct tvec_env _env;
+ struct tvec_remoteenv_slots r;
+};
+
+struct tvec_remotefork_slots {
+ /* Additional slots for the remote-environment forking class. */
+
+ const struct tvec_test **tests; /* child tests (or null) */
+};
+
+struct tvec_remotefork {
+ /* Remote environment forking class instance structure. */
+
+ struct tvec_env _env;
+ struct tvec_remoteenv_slots r;
+ struct tvec_remotefork_slots f;
+};
+
+struct tvec_remoteexec_slots {
+ /* Additional slots for the remote-environment exec class. */
+
+ const char *const *args; /* command line to execute */
+};
+
+struct tvec_remoteexec {
+ /* Remote environment exec class instance structure. */
+
+ struct tvec_env _env;
+ struct tvec_remoteenv_slots r;
+ struct tvec_remoteexec_slots x;
+};
+
+union tvec_remoteenv_subclass_kludge {
+ /* Pointless union to engage the common-prefix rule. */
+
+ struct tvec_env _env;
+ struct tvec_remoteenv renv;
+ struct tvec_remotefork fork;
+ struct tvec_remoteexec exec;
+};
+
+struct tvec_remotecomms {
+ /* Remote communications state; private. */
+
+ int infd, outfd; /* input and output descriptors */
+ dbuf bout; /* output buffer */
+ unsigned char *bin; /* input buffer */
+ size_t binoff, binlen, binsz; /* input offset, length, and size */
+ size_t t; /* temporary offset */
+ unsigned f; /* flags */
+#define TVRF_BROKEN 0x0001u /* communications have failed */
+};
+#define TVEC_REMOTECOMMS_INIT { -1, -1, DBUF_INIT, 0, 0, 0, 0, 0, 0 }
+
+struct tvec_remotectx {
+ /* Remote environment context; private. */
+
+ struct tvec_state *tv; /* test vector state */
+ struct tvec_remotecomms rc; /* communication state */
+ const struct tvec_remoteenv *re; /* environment configuration */
+ void *subctx; /* subenvironment context */
+ struct tvec_vardef vd; /* temporary variable definition */
+ unsigned ver; /* protocol version */
+ pid_t kid; /* child process id */
+ int errfd; /* child stderr descriptor */
+ lbuf errbuf; /* child stderr line buffer */
+ dstr prgwant, progress; /* progress: wanted/reported */
+ unsigned exwant, exit; /* exit status wanted/reported */
+#define TVRF_RCNMASK 0x0300u /* reconnection behaviour: */
+#define TVRCN_DEMAND 0x0000u /* connect on demand */
+#define TVRCN_SKIP 0x0100u /* skip unless connected */
+#define TVRCN_FORCE 0x0200u /* force reconnection */
+#define TVRF_MUFFLE 0x0400u /* muffle child stderr */
+#define TVRF_SETEXIT 0x0800u /* set `@exit' */
+#define TVRF_SETPRG 0x1000u /* set `@progress' */
+#define TVRF_SETRCN 0x2000u /* set `@reconnect' */
+#define TVRF_SETMASK (TVRF_SETEXIT | TVRF_SETPRG | TVRF_SETRCN)
+ /* mask of @TVTF_SET...@ */
+};
+
+/* Exit status.
+ *
+ * We don't use the conventional encoding returned by the @wait@(2) family of
+ * system calls because it's too hard for our flags type to decode. Instead,
+ * we use our own encoding.
+ *
+ * The exit code or signal number ends up in the `value' field in the low 12
+ * bits; bit 12 is set if the value field holds a signal, and it if holds an
+ * exit code. Bits 13--15 hold a code which describes the status of a child
+ * process or connection.
+ */
+#define TVXF_VALMASK 0x0fffu /* value (exit code or signal) */
+#define TVXF_SIG 0x1000u /* value is signal, not exit code */
+#define TVXF_CAUSEMASK 0xe000u /* mask for cause bits */
+#define TVXST_RUN 0x0000u /* still running */
+#define TVXST_EXIT 0x2000u /* child exited */
+#define TVXST_KILL 0x4000u /* child killed by signal */
+#define TVXST_CONT 0x6000u /* child continued (?) */
+#define TVXST_STOP 0x8000u /* child stopped (?) */
+#define TVXST_DISCONN 0xa000u /* disconnected */
+#define TVXST_UNK 0xc000u /* unknown */
+#define TVXST_ERR 0xe000u /* local error prevented diagnosis */
+
+/* --- Environment implementation --- *
+ *
+ * The following special variables are supported.
+ *
+ * * %|@exit|% is the expected exit status; see @TVXF_...@ and @TVXST_...@.
+ *
+ * * %|progress|% is the expected progress token when the test completes.
+ * On successful completion, this will be %|%DONE|%; it's %|%RUN|% on
+ * entry to the test function, but that can call @tvec_setprogress@ to
+ * change it.
+ *
+ * * %|reconnect|% is a reconnection policy; see @TVRCN_...@.
+ *
+ * Unrecognized variables are passed to the remote server, as are the
+ * environment events. As a convenience for @fork@-based remote execution,
+ * If the outermost environment in the server's test definition has
+ * @tvec_remotesetup@ as its @setup@ function, then its subordinate
+ * environment is used instead.
+ */
+
+extern tvec_envsetupfn tvec_remotesetup;
+extern tvec_envfindvarfn tvec_remotefindvar;
+extern tvec_envbeforefn tvec_remotebefore;
+extern tvec_envrunfn tvec_remoterun;
+extern tvec_envafterfn tvec_remoteafter;
+extern tvec_envteardownfn tvec_remoteteardown;
+
+#define TVEC_REMOTEENV \
+ { sizeof(struct tvec_remotectx), \
+ tvec_remotesetup, \
+ tvec_remotefindvar, \
+ tvec_remotebefore, \
+ tvec_remoterun, \
+ tvec_remoteafter, \
+ tvec_remoteteardown }
+
+extern tvec_connectfn tvec_fork, tvec_exec;
+
+#define TVEC_REMOTEFORK(subenv, tests) \
+ TVEC_REMOTEENV, { tvec_fork, subenv }, { tests }
+
+#define TVEC_REMOTEEXEC(subenv, args) \
+ TVEC_REMOTEENV, { tvec_exec, subenv }, { args }
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @tvec_setprogress@, @tvec_setprogress_v@ --- *
+ *
+ * Arguments: @const char *status@ = progress status token format
+ * @va_list ap@ = argument tail
+ *
+ * Returns: ---
+ *
+ * Use: Reports the progress of a test execution to the client.
+ *
+ * The framework makes use of tokens beginning with %|%|%:
+ *
+ * * %|%IDLE|%: during the top-level server code;
+ *
+ * * %|%SETUP|%: during the enclosing environment's @before@
+ * function;
+ *
+ * * %|%RUN|%: during the environment's @run@ function, or the
+ * test function; and
+ *
+ * * %|%DONE|%: during the enclosing environment's @after@
+ * function.
+ *
+ * The intent is that a test can use the progress token to check
+ * that a function which is expected to crash does so at the
+ * correct point, so it's expected that more complex test
+ * functions and/or environments will set their own progress
+ * tokens to reflect what's going on.
+ */
+
+extern PRINTF_LIKE(1, 2) int tvec_setprogress(const char */*status*/, ...);
+extern int tvec_setprogress_v(const char */*status*/, va_list */*ap*/);
+
+/* --- @tvec_remoteserver@ --- *
+ *
+ * Arguments: @int infd@, @int outfd@ = input and output file descriptors
+ * @const struct tvec_config *config@ = test configuration
+ *
+ * Returns: Suggested exit code.
+ *
+ * Use: Run a test server, reading packets from @infd@ and writing
+ * responses and notifications to @outfd@, and invoking tests as
+ * described by @config@.
+ *
+ * This function is not particularly general purpose. It
+ * expects to `take over' the process, and makes use of private
+ * global variables.
+ */
+
+extern int tvec_remoteserver(int /*infd*/, int /*outfd*/,
+ const struct tvec_config */*config*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif