@@@ wip
[mLib] / test / tvec-remote.c
1 /* -*-c-*-
2 *
3 * Remote testing
4 *
5 * (c) 2023 Straylight/Edgeware
6 */
7
8 /*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of the mLib utilities library.
11 *
12 * mLib is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU Library General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or (at
15 * your option) any later version.
16 *
17 * mLib is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
20 * License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib. If not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
25 * USA.
26 */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 #include <errno.h>
31 #include <stdarg.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include <sys/types.h>
37 #include <sys/uio.h>
38 #include <fcntl.h>
39 #include <unistd.h>
40
41 #include "alloc.h"
42 #include "buf.h"
43 #include "tvec.h"
44
45 /*----- Data structures ---------------------------------------------------*/
46
47 struct tvec_remote {
48 int infd, outfd;
49 dbuf bin, bout;
50 unsigned f;
51 #define TVRF_BROKEN 1u
52 };
53
54 struct tvec_remotectx {
55 struct tvec_remote r;
56 pid_t kid;
57 };
58
59 struct remote_output {
60 struct tvec_output _o;
61 struct tvec_remote r;
62 };
63
64 /*----- Basic I/O ---------------------------------------------------------*/
65
66 static int PRINTF_LIKE(3, 4)
67 ioerr(struct tvec_state *tv, struct tvec_remote *r, const char *msg, ...)
68 {
69 va_list ap;
70
71 va_start(ap, msg);
72 r->f |= TVRF_BROKEN;
73 tvec_write(tv, msg, &ap);
74 va_end(ap);
75 return (-1);
76 }
77
78 static int send_all(struct tvec_state *tv, struct tvec_remote *r,
79 const unsigned char *p, size_t sz)
80 {
81 ssize_t n;
82
83 while (sz) {
84 n = write(r->outfd, p, sz);
85 if (n > 0)
86 { p += n; sz -= n; }
87 else
88 return (ioerr(tv, r, "failed to send: %s",
89 n ? strerror(errno) : "empty write"));
90 }
91 return (0);
92 }
93
94 #define RCVF_ALLOWEOF 1u
95 static int recv_all(struct tvec_state *tv, struct tvec_remote *r,
96 unsigned char *p, size_t sz, unsigned f)
97 {
98 ssize_t n;
99 unsigned ff = 0;
100 #define f_any 1u
101
102 while (sz) {
103 n = read(r->infd, p, sz);
104 if (n > 0)
105 { p += n; sz -= n; ff |= f_any; }
106 else if (!n && (f&RCVF_ALLOWEOF) && !(ff&f_any))
107 return (1);
108 else
109 return (ioerr(tv, r, "failed to receive: %s",
110 n ? strerror(errno) : "unexpected end-of-file"));
111 }
112 return (0);
113
114 #undef f_any
115 }
116
117 int tvec_send(struct tvec_state *tv, struct tvec_remote *r)
118 {
119 kludge64 k; unsigned char lenbuf[8];
120 const char *p; size_t sz;
121
122 if (r->f&TVRF_BROKEN) return (-1);
123 if (BBAD(&r->bout.b))
124 return (ioerr(tv, r, "failed to build output packet buffer");
125
126 p = BBASE(r->bout.b); sz = BLEN(&r->bout.b);
127 ASSIGN64(k, sz); STORE64_L_(lenbuf, k);
128 if (send_all(tv, r, lenbuf, sizeof(lenbuf))) return (-1);
129 if (send_all(tv, r, p, sz)) return (-1);
130
131 return (0);
132 }
133
134 int tvec_recv(struct tvec_state *tv, struct tvec_remote *r, buf *b_out)
135 {
136 kludge64 k, szmax; unsigned char lenbuf[8];
137 unsigned char *p;
138 size_t sz;
139 int rc;
140
141 if (r->f&TVRF_BROKEN) return (-1);
142 ASSIGN64(k, (size_t)-1);
143 rc = recv_all(tv, r, lenbuf, sizeof(lenbuf), RCVF_ALLOWEOF);
144 if (rc) return (rc);
145 LOAD64_L_(k, lenbuf);
146 if (CMP64(k, >, szmax))
147 return (ioerr(tv, r, "packet size 0x%08lx%08lx out of range",
148 (unsigned long)HI64(k), (unsigned long)LO64(k)));
149
150 sz = GET64(size_t, k); buf_reset(&r->bin); p = buf_get(&r->bin.b, sz);
151 if (!p) return (ioerr(tv, r, "failed to allocate receive buffer"));
152 if (recv_all(tv, r, p, sz, 0)) return (-1);
153 buf_init(b_out, p, sz); return (0);
154 }
155
156 /*----- Data formatting primitives ----------------------------------------*/
157
158
159 /*----- Packet types ------------------------------------------------------*/
160
161 #define TVPK_ERROR 0x0001 /* msg: string */
162 #define TVPK_NOTICE 0x0002 /* msg: string */
163 #define TVPK_STATUS 0x0003 /* st: char */
164
165 #define TVPK_BGROUP 0x0101 /* name: string */
166 #define TVPK_TEST 0x0102 /* in: regs */
167 #define TVPK_EGROUP 0x0103 /* --- */
168
169 #define TVPK_SKIPGRP 0x0201 /* excuse: string */
170 #define TVPK_SKIP 0x0202 /* excuse: string */
171 #define TVPK_FAIL 0x0203 /* detail: string */
172 #define TVPK_MISMATCH 0x0204 /* in, out: regs */
173 #define TVPK_BBENCH 0x0205 /* in: regs */
174 #define TVPK_EBENCH 0x0206 /* flags: u16; n: u64; t, cy: float */
175
176 /*----- The output driver -------------------------------------------------*/
177
178 #define SENDPK(ro, pk) \
179 if ((ro)->r.f&TVRF_BROKEN) /* do nothing */; else \
180 MC_BEFORE(setpk, \
181 { buf_reset(&(ro)->r.bout); \
182 buf_putu16l(&(ro)->r.bout.b, (pk)); }) \
183 MC_AFTER(send, \
184 { tvec_send(&ro->_o.tv, &ro->r); })
185
186 static int sendstr(struct tvec_output *o, unsigned pk,
187 const char *p, va_list *ap)
188 {
189 struct remote_output *ro = (struct remote_output *)o;
190
191 SENDPK(ro, pk) buf_vputstrf16l(&ro->r.bout.b, msg, ap);
192 return (ro->r.f&TVRF_BROKEN ? -1 : 0);
193 }
194
195 static void report(struct tvec_output *o, unsigned pk, const char *what,
196 const char *msg, va_list *ap)
197 {
198 if (sendstr(o, pk, msg, ap)) {
199 fprintf(stderr, "%s %s: ", QUIS, what);
200 vfprintf(stderr, msg, *ap);
201 fputc('\n', stderr);
202 }
203 }
204
205 static void remote_error(struct tvec_output *o, const char *msg, va_list *ap)
206 { report(o, TVPK_ERROR, "ERROR", msg, ap); }
207
208 static void remote_notice(struct tvec_output *o,
209 const char *msg, va_list *ap)
210 { report(o, TVPK_NOTICE, "notice", msg, ap); }
211
212 static void remote_setstatus(struct tvec_ouptut *o, int st)
213 {
214 struct remote_output *ro = (struct remote_output *)o;
215 SENDPK(ro, TVPK_STATUS) buf_putbyte(&ro->r.bout.b, st);
216 }
217
218 static void remote_skipgroup(struct tvec_output *o,
219 const char *excuse, va_list *ap)
220 { sendstr(o, TVPK_SKIPGRP, excuse, ap); }
221
222 static void remote_skip(struct tvec_output *o,
223 const char *excuse, va_list *ap)
224 { sendstr(o, TVPK_SKIP, excuse, ap); }
225
226 static void remote_fail(struct tvec_output *o,
227 const char *detail, va_list *ap)
228 { sendstr(o, TVPK_FAIL, detail, ap); }
229
230 static void remote_mismatch(struct tvec_output *o)
231 {
232 struct remote_output *ro = (struct remote_output *)o;
233 struct tvec_state *rv = ro->_o.tv;
234
235 SENDPK(ro, TVPK_MISMATCH) {
236 tvec_serialize(tv, &ro->r.bout.b, tv->in, tv->nreg, tv->regsz);
237 tvec_serialize(tv, &ro->r.bout.b, tv->out, tv->nrout, tv->regsz);
238 }
239 }
240
241 static void remote_bbench(struct tvec_output *o)
242 {
243 struct remote_output *ro = (struct remote_output *)o;
244 struct tvec_state *rv = ro->_o.tv;
245
246 SENDPK(ro, TVPK_BBENCH)
247 tvec_serialize(tv, &ro->r.bout.b, tv->in, tv->nreg, tv->regsz);
248 }
249
250 static void remote_ebench(struct tvec_output *o,
251 const struct bench_timing *t)
252 {
253 struct remote_output *ro = (struct remote_output *)o;
254 kludge64 k;
255
256 SENDPK(ro, TVPK_EBENCH) {
257 buf_putu16l(&ro->r.bout.b, t->f);
258 ASSIGN64(k, t->n); buf_putk64l(&ro->r.bout.b, k);
259 if (t->f&BTF_TIMEOK) buf_putf64l(&ro->r.bout.b, t->t);
260 if (t->f&BTF_CYOK) buf_putf64l(&ro->r.bout.b, t->cy);
261 }
262 }
263
264 static void remote_write(struct tvec_output *o, const char *p, size_t sz)
265 { assert(!"remote_write"); }
266 static void remote_bsession(struct tvec_output *o)
267 { assert(!"remote_bsession"); }
268 static int remote_esession(struct tvec_output *o)
269 { assert(!"remote_esession"); return (-1); }
270 static void remote_bgroup(struct tvec_output *o)
271 { assert(!"remote_bgroup"); }
272 static void remote_btest(struct tvec_output *o)
273 { assert(!"remote_btest"); }
274 static void remote_egroup(struct tvec_output *o, unsigned outcome)
275 { assert(!"remote_egroup"); }
276 static void remote_etest(struct tvec_output *o, unsigned outcome)
277 { assert(!"remote_etest"); }
278
279 static void remote_destroy(struct tvec_output *o)
280 {
281 }
282
283 static const struct tvec_outops remote_ops = {
284 remote_error, remote_notice, remote_setstatus, remote_write,
285 remote_bsession, remote_esession,
286 remote_bgroup, remote_egroup, remote_skip,
287 remote_btest, remote_skip, remote_fail, remote_mismatch, remote_etest,
288 remote_bbench, remote_ebench,
289 remote_destroy
290
291 /*----- Main code ---------------------------------------------------------*/
292
293
294
295 /*----- That's all, folks -------------------------------------------------*/