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 ------------------------------------------------------*/
36 #include <sys/types.h>
45 /*----- Data structures ---------------------------------------------------*/
51 #define TVRF_BROKEN 1u
54 struct tvec_remotectx
{
59 struct remote_output
{
60 struct tvec_output _o
;
64 /*----- Basic I/O ---------------------------------------------------------*/
66 static int PRINTF_LIKE(3, 4)
67 ioerr(struct tvec_state
*tv
, struct tvec_remote
*r
, const char *msg
, ...)
73 tvec_write(tv
, msg
, &ap
);
78 static int send_all(struct tvec_state
*tv
, struct tvec_remote
*r
,
79 const unsigned char *p
, size_t sz
)
84 n
= write(r
->outfd
, p
, sz
);
88 return (ioerr(tv
, r
, "failed to send: %s",
89 n ?
strerror(errno
) : "empty write"));
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
)
103 n
= read(r
->infd
, p
, sz
);
105 { p
+= n
; sz
-= n
; ff
|= f_any
; }
106 else if (!n
&& (f
&RCVF_ALLOWEOF
) && !(ff
&f_any
))
109 return (ioerr(tv
, r
, "failed to receive: %s",
110 n ?
strerror(errno
) : "unexpected end-of-file"));
117 int tvec_send(struct tvec_state
*tv
, struct tvec_reomte
*r
)
119 kludge64 k
; unsigned char lenbuf
[8];
120 const char *p
; size_t sz
;
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");
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);
134 int tvec_recv(struct tvec_state
*tv
, struct tvec_reomte
*r
, buf
*b_out
)
136 kludge64 k
, szmax
; unsigned char lenbuf
[8];
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
);
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
)));
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);
156 /*----- Data formatting primitives ----------------------------------------*/
159 /*----- Packet types ------------------------------------------------------*/
161 #define TVPK_ERROR 0x0001 /* msg: string */
162 #define TVPK_NOTICE 0x0002 /* msg: string */
163 #define TVPK_STATUS 0x0003 /* st: char */
165 #define TVPK_BGROUP 0x0101 /* name: string */
166 #define TVPK_TEST 0x0102 /* in: regs */
167 #define TVPK_EGROUP 0x0103 /* --- */
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 */
176 /*----- The output driver -------------------------------------------------*/
178 #define SENDPK(ro, pk) \
180 { buf_reset(&(ro)->r.bout); \
181 buf_putu16l(&(ro)->r.bout.b, (pk)); }) \
183 { tvec_send(&ro->_o.tv, &ro->r); })
185 static int sendstr(struct tvec_output
*o
, unsigned pk
,
186 const char *p
, va_list *ap
)
188 struct remote_output
*ro
= (struct remote_output
*)o
;
190 if (ro
->r
.f
&TVRF_BROKEN
) return (-1);
191 dbuf_reset(&ro
->r
.bout
);
192 buf_putu16l(&ro
->r
.bout
.b
, TVPK_ERROR
);
193 buf_vputstrf16l(&ro
->r
.bout
.b
, msg
, ap
);
194 return (tvec_send(ro
->_o
.tv
, &ro
->r
));
197 static void report(struct tvec_output
*o
, unsigned pk
,
198 const char *msg
, va_list *ap
)
200 if (sendstr(o
, pk
, msg
, ap
)) {
201 fprintf(stderr
, "%s: ", QUIS
);
202 vfprintf(stderr
, msg
, *ap
);
207 static void remote_error(struct tvec_output
*o
, const char *msg
, va_list *ap
)
208 { report(o
, TVPK_ERROR
, msg
, ap
); }
210 static void remote_notice(struct tvec_output
*o
,
211 const char *msg
, va_list *ap
)
212 { report(o
, TVPK_NOTICE
, msg
, ap
); }
214 static void remote_setstatus(struct tvec_ouptut
*o
, int st
)
216 struct remote_output
*ro
= (struct remote_output
*)o
;
217 SENDPK(ro
, TVPK_STATUS
) buf_putbyte(&ro
->r
.bout
.b
, st
);
220 static void remote_skipgroup(struct tvec_output
*o
,
221 const char *excuse
, va_list *ap
)
222 { sendstr(o
, TVPK_SKIPGRP
, excuse
, ap
); }
224 static void remote_skip(struct tvec_output
*o
,
225 const char *excuse
, va_list *ap
)
226 { sendstr(o
, TVPK_SKIP
, excuse
, ap
); }
228 static void remote_fail(struct tvec_output
*o
,
229 const char *detail
, va_list *ap
)
230 { sendstr(o
, TVPK_FAIL
, detail
, ap
); }
232 static void remote_mismatch(struct tvec_output
*o
)
234 struct remote_output
*ro
= (struct remote_output
*)o
;
235 struct tvec_state
*rv
= ro
->_o
.tv
;
237 SENDPK(ro
, TVPK_MISMATCH
) {
238 tvec_serialize(tv
, &ro
->r
.bout
.b
, tv
->in
, tv
->nreg
, tv
->regsz
);
239 tvec_serialize(tv
, &ro
->r
.bout
.b
, tv
->out
, tv
->nrout
, tv
->regsz
);
243 static void remote_bbench(struct tvec_output
*o
)
245 struct remote_output
*ro
= (struct remote_output
*)o
;
246 struct tvec_state
*rv
= ro
->_o
.tv
;
248 SENDPK(ro
, TVPK_BBENCH
)
249 tvec_serialize(tv
, &ro
->r
.bout
.b
, tv
->in
, tv
->nreg
, tv
->regsz
);
252 static void remote_ebench(struct tvec_output
*o
,
253 const struct bench_timing
*t
)
255 struct remote_output
*ro
= (struct remote_output
*)o
;
258 SENDPK(ro
, TVPK_EBENCH
) {
259 buf_putu16l(&ro
->r
.bout
.b
, t
->f
);
260 ASSIGN64(k
, t
->n
); buf_putk64l(&ro
->r
.bout
.b
, k
);
261 if (t
->f
&BTF_TIMEOK
) buf_putf64l(&ro
->r
.bout
.b
, t
->t
);
262 if (t
->f
&BTF_CYOK
) buf_putf64l(&ro
->r
.bout
.b
, t
->cy
);
266 static void remote_write(struct tvec_output
*o
, const char *p
, size_t sz
)
267 { assert(!"remote_write"); }
268 static void remote_bsession(struct tvec_output
*o
)
269 { assert(!"remote_bsession"); }
270 static int remote_esession(struct tvec_output
*o
)
271 { assert(!"remote_esession"); return (-1); }
272 static void remote_bgroup(struct tvec_output
*o
)
273 { assert(!"remote_bgroup"); }
274 static void remote_btest(struct tvec_output
*o
)
275 { assert(!"remote_btest"); }
276 static void remote_egroup(struct tvec_output
*o
, unsigned outcome
)
277 { assert(!"remote_egroup"); }
278 static void remote_etest(struct tvec_output
*o
, unsigned outcome
)
279 { assert(!"remote_etest"); }
281 static void remote_destroy(struct tvec_output
*o
)
285 static const struct tvec_outops remote_ops
= {
286 remote_error
, remote_notice
, remote_setstatus
, remote_write
,
287 remote_bsession
, remote_esession
,
288 remote_bgroup
, remote_egroup
, remote_skip
,
289 remote_btest
, remote_skip
, remote_fail
, remote_mismatch
, remote_etest
,
290 remote_bbench
, remote_ebench
,
293 /*----- Main code ---------------------------------------------------------*/
297 /*----- That's all, folks -------------------------------------------------*/