| | varnish-cache/bin/varnishtest/vtc_process.c |
0 |
|
/*- |
1 |
|
* Copyright (c) 2015 Varnish Software AS |
2 |
|
* All rights reserved. |
3 |
|
* |
4 |
|
* Author: Dridi Boukelmoune <dridi@varnish-software.com> |
5 |
|
* |
6 |
|
* SPDX-License-Identifier: BSD-2-Clause |
7 |
|
* |
8 |
|
* Redistribution and use in source and binary forms, with or without |
9 |
|
* modification, are permitted provided that the following conditions |
10 |
|
* are met: |
11 |
|
* 1. Redistributions of source code must retain the above copyright |
12 |
|
* notice, this list of conditions and the following disclaimer. |
13 |
|
* 2. Redistributions in binary form must reproduce the above copyright |
14 |
|
* notice, this list of conditions and the following disclaimer in the |
15 |
|
* documentation and/or other materials provided with the distribution. |
16 |
|
* |
17 |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
18 |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
19 |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
20 |
|
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE |
21 |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
22 |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
23 |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
24 |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
25 |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
26 |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
27 |
|
* SUCH DAMAGE. |
28 |
|
* |
29 |
|
* XXX: |
30 |
|
* -ignore-stderr (otherwise output to stderr is fail) |
31 |
|
*/ |
32 |
|
|
33 |
|
#include "config.h" |
34 |
|
|
35 |
|
#include <sys/ioctl.h> // Linux: struct winsize |
36 |
|
|
37 |
|
#include <ctype.h> |
38 |
|
#include <fcntl.h> |
39 |
|
#include <inttypes.h> |
40 |
|
#include <poll.h> |
41 |
|
#include <stdio.h> |
42 |
|
#include <stdlib.h> |
43 |
|
#include <string.h> |
44 |
|
#ifdef __sun |
45 |
|
# include <stropts.h> |
46 |
|
#endif |
47 |
|
#include <termios.h> |
48 |
|
#include <unistd.h> |
49 |
|
|
50 |
|
#include "vtc.h" |
51 |
|
|
52 |
|
#include "vre.h" |
53 |
|
#include "vev.h" |
54 |
|
#include "vlu.h" |
55 |
|
#include "vsb.h" |
56 |
|
#include "vsub.h" |
57 |
|
|
58 |
|
#include "teken.h" |
59 |
|
|
60 |
|
struct process { |
61 |
|
unsigned magic; |
62 |
|
#define PROCESS_MAGIC 0x1617b43e |
63 |
|
char *name; |
64 |
|
struct vtclog *vl; |
65 |
|
VTAILQ_ENTRY(process) list; |
66 |
|
|
67 |
|
char *spec; |
68 |
|
char *dir; |
69 |
|
char *out; |
70 |
|
char *err; |
71 |
|
int fd_term; |
72 |
|
int fd_stderr; |
73 |
|
int f_stdout; |
74 |
|
int f_stderr; |
75 |
|
struct vlu *vlu_stdout; |
76 |
|
struct vlu *vlu_stderr; |
77 |
|
int log; |
78 |
|
pid_t pid; |
79 |
|
int expect_exit; |
80 |
|
int expect_signal; |
81 |
|
int allow_core; |
82 |
|
|
83 |
|
uintmax_t stdout_bytes; |
84 |
|
uintmax_t stderr_bytes; |
85 |
|
|
86 |
|
pthread_mutex_t mtx; |
87 |
|
pthread_t tp; |
88 |
|
unsigned hasthread; |
89 |
|
|
90 |
|
int nlin; |
91 |
|
int ncol; |
92 |
|
int ansi_response; |
93 |
|
char **vram; |
94 |
|
teken_t tek[1]; |
95 |
|
}; |
96 |
|
|
97 |
|
static VTAILQ_HEAD(, process) processes = |
98 |
|
VTAILQ_HEAD_INITIALIZER(processes); |
99 |
|
|
100 |
|
static void term_resize(struct process *pp, int lin, int col); |
101 |
|
|
102 |
|
/********************************************************************** |
103 |
|
* Terminal emulation |
104 |
|
*/ |
105 |
|
|
106 |
|
static void |
107 |
5541885 |
term_cursor(void *priv, const teken_pos_t *pos) |
108 |
|
{ |
109 |
5541885 |
(void)priv; |
110 |
5541885 |
(void)pos; |
111 |
5541885 |
} |
112 |
|
|
113 |
|
static void |
114 |
35017924 |
term_putchar(void *priv, const teken_pos_t *pos, teken_char_t ch, |
115 |
|
const teken_attr_t *at) |
116 |
|
{ |
117 |
|
struct process *pp; |
118 |
|
|
119 |
35017924 |
CAST_OBJ_NOTNULL(pp, priv, PROCESS_MAGIC); |
120 |
35017905 |
(void)at; |
121 |
35017905 |
if (ch > 126 || ch < 32) |
122 |
22480 |
ch = '?'; |
123 |
35017914 |
assert(pos->tp_row < pp->nlin); |
124 |
35017914 |
assert(pos->tp_col < pp->ncol); |
125 |
35017912 |
pp->vram[pos->tp_row][pos->tp_col] = ch; |
126 |
35017912 |
} |
127 |
|
|
128 |
|
static void |
129 |
72020 |
term_fill(void *priv, const teken_rect_t *r, teken_char_t c, |
130 |
|
const teken_attr_t *a) |
131 |
|
{ |
132 |
|
teken_pos_t p; |
133 |
|
|
134 |
|
/* Braindead implementation of fill() - just call putchar(). */ |
135 |
399520 |
for (p.tp_row = r->tr_begin.tp_row; |
136 |
399520 |
p.tp_row < r->tr_end.tp_row; p.tp_row++) |
137 |
30329994 |
for (p.tp_col = r->tr_begin.tp_col; |
138 |
30329994 |
p.tp_col < r->tr_end.tp_col; p.tp_col++) |
139 |
30329994 |
term_putchar(priv, &p, c, a); |
140 |
72020 |
} |
141 |
|
|
142 |
|
static void |
143 |
69976 |
term_copy(void *priv, const teken_rect_t *r, const teken_pos_t *p) |
144 |
|
{ |
145 |
|
struct process *pp; |
146 |
|
int nrow, ncol, y; /* Has to be signed - >= 0 comparison */ |
147 |
|
|
148 |
|
/* |
149 |
|
* Copying is a little tricky. We must make sure we do it in |
150 |
|
* correct order, to make sure we don't overwrite our own data. |
151 |
|
*/ |
152 |
69976 |
CAST_OBJ_NOTNULL(pp, priv, PROCESS_MAGIC); |
153 |
|
|
154 |
69976 |
nrow = r->tr_end.tp_row - r->tr_begin.tp_row; |
155 |
69976 |
ncol = r->tr_end.tp_col - r->tr_begin.tp_col; |
156 |
|
|
157 |
69976 |
if (p->tp_row < r->tr_begin.tp_row) { |
158 |
|
/* Copy from top to bottom. */ |
159 |
984056 |
for (y = 0; y < nrow; y++) |
160 |
1912880 |
memmove(&pp->vram[p->tp_row + y][p->tp_col], |
161 |
956440 |
&pp->vram[r->tr_begin.tp_row + y][r->tr_begin.tp_col], ncol); |
162 |
27616 |
} else { |
163 |
|
/* Copy from bottom to top. */ |
164 |
236600 |
for (y = nrow - 1; y >= 0; y--) |
165 |
388480 |
memmove(&pp->vram[p->tp_row + y][p->tp_col], |
166 |
194240 |
&pp->vram[r->tr_begin.tp_row + y][r->tr_begin.tp_col], ncol); |
167 |
|
} |
168 |
69976 |
} |
169 |
|
|
170 |
|
static void |
171 |
640 |
term_respond(void *priv, const void *p, size_t l) |
172 |
|
{ |
173 |
|
struct process *pp; |
174 |
|
int r; |
175 |
|
|
176 |
640 |
CAST_OBJ_NOTNULL(pp, priv, PROCESS_MAGIC); |
177 |
|
|
178 |
640 |
vtc_dump(pp->vl, 4, "term_response", p, l); |
179 |
640 |
if (pp->ansi_response) { |
180 |
80 |
r = write(pp->fd_term, p, l); |
181 |
80 |
if (r != l) |
182 |
0 |
vtc_fatal(pp->vl, "Could not write to process: %s", |
183 |
0 |
strerror(errno)); |
184 |
80 |
} |
185 |
640 |
} |
186 |
|
|
187 |
|
static void |
188 |
5400 |
term_param(void *priv, int p, unsigned int v) |
189 |
|
{ |
190 |
|
struct process *pp; |
191 |
|
|
192 |
5400 |
CAST_OBJ_NOTNULL(pp, priv, PROCESS_MAGIC); |
193 |
5400 |
if (p == TP_132COLS && v) |
194 |
480 |
term_resize(pp, pp->nlin, 132); |
195 |
1440 |
if (p == TP_132COLS && !v) |
196 |
960 |
term_resize(pp, pp->nlin, 80); |
197 |
5400 |
} |
198 |
|
|
199 |
|
static const teken_funcs_t process_teken_func = { |
200 |
|
.tf_cursor = term_cursor, |
201 |
|
.tf_putchar = term_putchar, |
202 |
|
.tf_fill = term_fill, |
203 |
|
.tf_copy = term_copy, |
204 |
|
.tf_respond = term_respond, |
205 |
|
.tf_param = term_param, |
206 |
|
}; |
207 |
|
|
208 |
|
static void |
209 |
6240 |
term_screen_dump(const struct process *pp) |
210 |
|
{ |
211 |
|
int i; |
212 |
|
const teken_pos_t *pos; |
213 |
|
|
214 |
163040 |
for (i = 0; i < pp->nlin; i++) |
215 |
156800 |
vtc_dump(pp->vl, 3, "screen", pp->vram[i], pp->ncol); |
216 |
6240 |
pos = teken_get_cursor(pp->tek); |
217 |
12480 |
vtc_log(pp->vl, 3, "Cursor at line %d column %d", |
218 |
6240 |
pos->tp_row + 1, pos->tp_col + 1); |
219 |
6240 |
} |
220 |
|
|
221 |
|
static void |
222 |
4000 |
term_resize(struct process *pp, int lin, int col) |
223 |
|
{ |
224 |
|
teken_pos_t pos; |
225 |
|
char **vram; |
226 |
|
int i, j; |
227 |
|
|
228 |
4000 |
vram = calloc(lin, sizeof *pp->vram); |
229 |
4000 |
AN(vram); |
230 |
103840 |
for (i = 0; i < lin; i++) { |
231 |
99840 |
vram[i] = calloc(col + 1L, 1); |
232 |
99840 |
AN(vram[i]); |
233 |
99840 |
memset(vram[i], ' ', col); |
234 |
99840 |
vram[i][col] = '\0'; |
235 |
99840 |
} |
236 |
4000 |
if (pp->vram != NULL) { |
237 |
43960 |
for (i = 0; i < lin; i++) { |
238 |
42480 |
if (i >= pp->nlin) |
239 |
280 |
break; |
240 |
42200 |
j = col; |
241 |
42200 |
if (j > pp->ncol) |
242 |
15360 |
j = pp->ncol; |
243 |
42200 |
memcpy(vram[i], pp->vram[i], j); |
244 |
42200 |
} |
245 |
44000 |
for (i = 0; i < pp->nlin; i++) |
246 |
42240 |
free(pp->vram[i]); |
247 |
1760 |
free(pp->vram); |
248 |
1760 |
} |
249 |
4000 |
pp->vram = vram; |
250 |
4000 |
pp->nlin = lin; |
251 |
4000 |
pp->ncol = col; |
252 |
|
|
253 |
4000 |
pos.tp_row = lin; |
254 |
4000 |
pos.tp_col = col; |
255 |
4000 |
teken_set_winsize(pp->tek, &pos); |
256 |
4000 |
} |
257 |
|
|
258 |
|
static int |
259 |
200111 |
term_find_textline(const struct process *pp, int *x, int y, const char *pat) |
260 |
|
{ |
261 |
|
const char *t; |
262 |
|
int l; |
263 |
|
|
264 |
200111 |
if (*x == 0) { |
265 |
158561 |
t = strstr(pp->vram[y], pat); |
266 |
158561 |
if (t != NULL) { |
267 |
2760 |
*x = 1 + (t - pp->vram[y]); |
268 |
2760 |
return (1); |
269 |
|
} |
270 |
197351 |
} else if (*x <= pp->ncol) { |
271 |
41550 |
t = pp->vram[y] + *x - 1; |
272 |
41550 |
l = strlen(pat); |
273 |
41550 |
assert((*x - 1) + (l - 1) < pp->ncol); |
274 |
41550 |
if (!memcmp(t, pat, l)) |
275 |
7280 |
return (1); |
276 |
34270 |
} |
277 |
190071 |
return (0); |
278 |
200111 |
} |
279 |
|
|
280 |
|
static int |
281 |
20507 |
term_find_text(const struct process *pp, int *x, int *y, const char *pat) |
282 |
|
{ |
283 |
|
int yy; |
284 |
|
|
285 |
20507 |
if (*y == 0) { |
286 |
192196 |
for (yy = 0; yy < pp->nlin; yy++) { |
287 |
187420 |
if (term_find_textline(pp, x, yy, pat)) { |
288 |
3040 |
*y = yy + 1; |
289 |
3040 |
return (1); |
290 |
|
} |
291 |
184380 |
} |
292 |
17467 |
} else if (*y <= pp->nlin) { |
293 |
12691 |
if (term_find_textline(pp, x, *y - 1, pat)) |
294 |
7000 |
return (1); |
295 |
5691 |
} |
296 |
10467 |
return (0); |
297 |
20507 |
} |
298 |
|
|
299 |
|
static void |
300 |
10040 |
term_expect_text(struct process *pp, |
301 |
|
const char *lin, const char *col, const char *pat) |
302 |
|
{ |
303 |
10040 |
int x, y, l, d = 10000; |
304 |
|
char *t; |
305 |
|
|
306 |
10040 |
y = strtoul(lin, NULL, 0); |
307 |
10040 |
if (y < 0 || y > pp->nlin) |
308 |
0 |
vtc_fatal(pp->vl, "YYY %d nlin %d", y, pp->nlin); |
309 |
10040 |
x = strtoul(col, NULL, 0); |
310 |
10200 |
for(l = 0; l <= 10 && x > pp->ncol; l++) // wait for screen change |
311 |
160 |
usleep(100000); |
312 |
10040 |
if (x < 0 || x > pp->ncol) |
313 |
0 |
vtc_fatal(pp->vl, "XXX %d ncol %d", x, pp->ncol); |
314 |
10040 |
l = strlen(pat); |
315 |
10040 |
if (x + l - 1 > pp->ncol) |
316 |
0 |
vtc_fatal(pp->vl, "XXX %d ncol %d", x + l - 1, pp->ncol); |
317 |
10040 |
PTOK(pthread_mutex_lock(&pp->mtx)); |
318 |
20507 |
while (!term_find_text(pp, &x, &y, pat)) { |
319 |
10467 |
if (x != 0 && y != 0) { |
320 |
4950 |
t = pp->vram[y - 1] + x - 1; |
321 |
9900 |
vtc_log(pp->vl, 4, |
322 |
4950 |
"text at %d,%d: '%.*s'", y, x, l, t); |
323 |
4950 |
} |
324 |
10467 |
PTOK(pthread_mutex_unlock(&pp->mtx)); |
325 |
10467 |
usleep(d); |
326 |
10467 |
PTOK(pthread_mutex_lock(&pp->mtx)); |
327 |
10467 |
if (d < 3000000) |
328 |
10467 |
d += d; |
329 |
|
} |
330 |
10040 |
PTOK(pthread_mutex_unlock(&pp->mtx)); |
331 |
10040 |
vtc_log(pp->vl, 4, "found expected text at %d,%d: '%s'", y, x, pat); |
332 |
10040 |
} |
333 |
|
|
334 |
|
static void |
335 |
320 |
term_expect_cursor(const struct process *pp, const char *lin, const char *col) |
336 |
|
{ |
337 |
|
int x, y, l; |
338 |
|
const teken_pos_t *pos; |
339 |
|
|
340 |
320 |
pos = teken_get_cursor(pp->tek); |
341 |
320 |
y = strtoul(lin, NULL, 0); |
342 |
320 |
if (y < 0 || y > pp->nlin) |
343 |
0 |
vtc_fatal(pp->vl, "YYY %d nlin %d", y, pp->nlin); |
344 |
320 |
x = strtoul(col, NULL, 0); |
345 |
320 |
for(l = 0; l < 10 && x > pp->ncol; l++) // wait for screen change |
346 |
0 |
usleep(100000); |
347 |
320 |
if (x < 0 || x > pp->ncol) |
348 |
0 |
vtc_fatal(pp->vl, "XXX %d ncol %d", x, pp->ncol); |
349 |
320 |
if (y != 0 && (y-1) != pos->tp_row) |
350 |
0 |
vtc_fatal(pp->vl, "Cursor on line %d (expected %d)", |
351 |
0 |
pos->tp_row + 1, y); |
352 |
240 |
if (x != 0 && (x-1) != pos->tp_col) |
353 |
0 |
vtc_fatal(pp->vl, "Cursor in column %d (expected %d)", |
354 |
0 |
pos->tp_col + 1, y); |
355 |
320 |
} |
356 |
|
|
357 |
|
static void |
358 |
1360 |
term_match_text(struct process *pp, |
359 |
|
const char *lin, const char *col, const char *re) |
360 |
|
{ |
361 |
|
int i, l, err, erroff; |
362 |
|
struct vsb *vsb, re_vsb[1]; |
363 |
|
size_t len; |
364 |
|
ssize_t x, y; |
365 |
|
vre_t *vre; |
366 |
|
char errbuf[VRE_ERROR_LEN]; |
367 |
|
|
368 |
1360 |
vsb = VSB_new_auto(); |
369 |
1360 |
AN(vsb); |
370 |
|
|
371 |
1360 |
y = strtoul(lin, NULL, 0); |
372 |
1360 |
if (y < 0 || y > pp->nlin) |
373 |
0 |
vtc_fatal(pp->vl, "YYY %zd nlin %d", y, pp->nlin); |
374 |
1360 |
x = strtoul(col, NULL, 0); |
375 |
1360 |
for(l = 0; l < 10 && x > pp->ncol; l++) // wait for screen change |
376 |
0 |
usleep(100000); |
377 |
1360 |
if (x < 0 || x > pp->ncol) |
378 |
0 |
vtc_fatal(pp->vl, "XXX %zd ncol %d", x, pp->ncol); |
379 |
|
|
380 |
1360 |
if (x) |
381 |
640 |
x--; |
382 |
|
|
383 |
720 |
if (y) |
384 |
720 |
y--; |
385 |
|
|
386 |
1360 |
vre = VRE_compile(re, 0, &err, &erroff, 1); |
387 |
1360 |
if (vre == NULL) { |
388 |
0 |
AN(VSB_init(re_vsb, errbuf, sizeof errbuf)); |
389 |
0 |
AZ(VRE_error(re_vsb, err)); |
390 |
0 |
AZ(VSB_finish(re_vsb)); |
391 |
0 |
VSB_fini(re_vsb); |
392 |
0 |
vtc_fatal(pp->vl, "invalid regexp \"%s\" at %d (%s)", |
393 |
0 |
re, erroff, errbuf); |
394 |
|
} |
395 |
|
|
396 |
1360 |
PTOK(pthread_mutex_lock(&pp->mtx)); |
397 |
|
|
398 |
1360 |
len = (pp->nlin - y) * (pp->ncol - x); |
399 |
32960 |
for (i = y; i < pp->nlin; i++) { |
400 |
31600 |
VSB_bcat(vsb, &pp->vram[i][x], pp->ncol - x); |
401 |
31600 |
VSB_putc(vsb, '\n'); |
402 |
31600 |
} |
403 |
|
|
404 |
1360 |
AZ(VSB_finish(vsb)); |
405 |
|
|
406 |
1360 |
if (VRE_match(vre, VSB_data(vsb), len, 0, NULL) < 1) |
407 |
0 |
vtc_fatal(pp->vl, "match failed: (\"%s\")", re); |
408 |
|
else |
409 |
1360 |
vtc_log(pp->vl, 4, "match succeeded"); |
410 |
|
|
411 |
1360 |
PTOK(pthread_mutex_unlock(&pp->mtx)); |
412 |
1360 |
VSB_destroy(&vsb); |
413 |
1360 |
VRE_free(&vre); |
414 |
1360 |
} |
415 |
|
|
416 |
|
/********************************************************************** |
417 |
|
* Allocate and initialize a process |
418 |
|
*/ |
419 |
|
|
420 |
|
#define PROCESS_EXPAND(field, format, ...) \ |
421 |
|
do { \ |
422 |
|
vsb = macro_expandf(p->vl, format, __VA_ARGS__); \ |
423 |
|
AN(vsb); \ |
424 |
|
p->field = strdup(VSB_data(vsb)); \ |
425 |
|
AN(p->field); \ |
426 |
|
VSB_destroy(&vsb); \ |
427 |
|
} while (0) |
428 |
|
|
429 |
|
static void |
430 |
2240 |
process_coverage(struct process *p) |
431 |
|
{ |
432 |
|
const teken_attr_t *a; |
433 |
|
teken_pos_t pos; |
434 |
|
int fg, bg; |
435 |
|
|
436 |
|
// Code-Coverage of Teken |
437 |
|
|
438 |
2240 |
(void)teken_get_sequence(p->tek, TKEY_UP); |
439 |
2240 |
(void)teken_get_sequence(p->tek, TKEY_F1); |
440 |
2240 |
(void)teken_256to8(0); |
441 |
2240 |
(void)teken_256to16(0); |
442 |
2240 |
a = teken_get_defattr(p->tek); |
443 |
2240 |
teken_set_defattr(p->tek, a); |
444 |
2240 |
a = teken_get_curattr(p->tek); |
445 |
2240 |
teken_set_curattr(p->tek, a); |
446 |
2240 |
(void)teken_get_winsize(p->tek); |
447 |
2240 |
pos.tp_row = 0; |
448 |
2240 |
pos.tp_col = 8; |
449 |
2240 |
teken_set_cursor(p->tek, &pos); |
450 |
2240 |
teken_get_defattr_cons25(p->tek, &fg, &bg); |
451 |
2240 |
} |
452 |
|
|
453 |
|
static struct process * |
454 |
2240 |
process_new(const char *name) |
455 |
|
{ |
456 |
|
struct process *p; |
457 |
|
struct vsb *vsb; |
458 |
|
char buf[1024]; |
459 |
|
|
460 |
2240 |
ALLOC_OBJ(p, PROCESS_MAGIC); |
461 |
2240 |
AN(p); |
462 |
2240 |
REPLACE(p->name, name); |
463 |
2240 |
PTOK(pthread_mutex_init(&p->mtx, NULL)); |
464 |
|
|
465 |
2240 |
p->vl = vtc_logopen("%s", name); |
466 |
2240 |
AN(p->vl); |
467 |
|
|
468 |
2240 |
PROCESS_EXPAND(dir, "${tmpdir}/%s", name); |
469 |
2240 |
PROCESS_EXPAND(out, "${tmpdir}/%s/term", name); |
470 |
2240 |
PROCESS_EXPAND(err, "${tmpdir}/%s/stderr", name); |
471 |
|
|
472 |
2240 |
bprintf(buf, "rm -rf %s ; mkdir -p %s ; touch %s %s", |
473 |
|
p->dir, p->dir, p->out, p->err); |
474 |
2240 |
AZ(system(buf)); |
475 |
|
|
476 |
2240 |
p->fd_term = -1; |
477 |
|
|
478 |
2240 |
VTAILQ_INSERT_TAIL(&processes, p, list); |
479 |
2240 |
teken_init(p->tek, &process_teken_func, p); |
480 |
2240 |
term_resize(p, 24, 80); |
481 |
2240 |
process_coverage(p); |
482 |
2240 |
return (p); |
483 |
|
} |
484 |
|
|
485 |
|
#undef PROCESS_EXPAND |
486 |
|
|
487 |
|
/********************************************************************** |
488 |
|
* Clean up process |
489 |
|
*/ |
490 |
|
|
491 |
|
static void |
492 |
2240 |
process_delete(struct process *p) |
493 |
|
{ |
494 |
|
int i; |
495 |
|
|
496 |
2240 |
CHECK_OBJ_NOTNULL(p, PROCESS_MAGIC); |
497 |
2240 |
PTOK(pthread_mutex_destroy(&p->mtx)); |
498 |
2240 |
vtc_logclose(p->vl); |
499 |
2240 |
free(p->name); |
500 |
2240 |
free(p->dir); |
501 |
2240 |
free(p->out); |
502 |
2240 |
free(p->err); |
503 |
|
|
504 |
59840 |
for (i = 0; i < p->nlin; i++) |
505 |
57600 |
free(p->vram[i]); |
506 |
2240 |
free(p->vram); |
507 |
|
|
508 |
|
/* |
509 |
|
* We do not delete the directory, it may contain useful stdout |
510 |
|
* and stderr files. They will be deleted on account of belonging |
511 |
|
* to the test's tmpdir. |
512 |
|
*/ |
513 |
|
|
514 |
|
/* XXX: MEMLEAK (?) */ |
515 |
2240 |
FREE_OBJ(p); |
516 |
2240 |
} |
517 |
|
|
518 |
|
static void |
519 |
2240 |
process_undef(const struct process *p) |
520 |
|
{ |
521 |
2240 |
CHECK_OBJ_NOTNULL(p, PROCESS_MAGIC); |
522 |
|
|
523 |
2240 |
macro_undef(p->vl, p->name, "dir"); |
524 |
2240 |
macro_undef(p->vl, p->name, "out"); |
525 |
2240 |
macro_undef(p->vl, p->name, "err"); |
526 |
2240 |
} |
527 |
|
|
528 |
|
/********************************************************************** |
529 |
|
* Data stream handling |
530 |
|
*/ |
531 |
|
|
532 |
|
static int |
533 |
7480 |
process_vlu_func(void *priv, const char *l) |
534 |
|
{ |
535 |
|
struct process *p; |
536 |
|
|
537 |
7480 |
CAST_OBJ_NOTNULL(p, priv, PROCESS_MAGIC); |
538 |
7480 |
vtc_dump(p->vl, 4, "output", l, -1); |
539 |
7480 |
return (0); |
540 |
|
} |
541 |
|
|
542 |
|
static int v_matchproto_(vev_cb_f) |
543 |
160924 |
process_stdout(const struct vev *ev, int what) |
544 |
|
{ |
545 |
|
struct process *p; |
546 |
|
char buf[BUFSIZ]; |
547 |
|
int i; |
548 |
|
|
549 |
160924 |
CAST_OBJ_NOTNULL(p, ev->priv, PROCESS_MAGIC); |
550 |
160924 |
(void)what; |
551 |
160924 |
i = read(p->fd_term, buf, sizeof buf); |
552 |
160924 |
if (i <= 0) { |
553 |
2720 |
vtc_log(p->vl, 4, "stdout read %d", i); |
554 |
2720 |
return (1); |
555 |
|
} |
556 |
158206 |
PTOK(pthread_mutex_lock(&p->mtx)); |
557 |
158206 |
p->stdout_bytes += i; |
558 |
158206 |
PTOK(pthread_mutex_unlock(&p->mtx)); |
559 |
158206 |
if (p->log == 1) |
560 |
2155 |
(void)VLU_Feed(p->vlu_stdout, buf, i); |
561 |
156051 |
else if (p->log == 2) |
562 |
8371 |
vtc_dump(p->vl, 4, "stdout", buf, i); |
563 |
147680 |
else if (p->log == 3) |
564 |
1907 |
vtc_hexdump(p->vl, 4, "stdout", buf, i); |
565 |
158206 |
assert(write(p->f_stdout, buf, i) == i); |
566 |
158206 |
PTOK(pthread_mutex_lock(&p->mtx)); |
567 |
158206 |
teken_input(p->tek, buf, i); |
568 |
158206 |
PTOK(pthread_mutex_unlock(&p->mtx)); |
569 |
158206 |
return (0); |
570 |
160926 |
} |
571 |
|
|
572 |
|
static int v_matchproto_(vev_cb_f) |
573 |
3355 |
process_stderr(const struct vev *ev, int what) |
574 |
|
{ |
575 |
|
struct process *p; |
576 |
|
char buf[BUFSIZ]; |
577 |
|
int i; |
578 |
|
|
579 |
3355 |
CAST_OBJ_NOTNULL(p, ev->priv, PROCESS_MAGIC); |
580 |
3355 |
(void)what; |
581 |
3355 |
i = read(p->fd_stderr, buf, sizeof buf); |
582 |
3355 |
if (i <= 0) { |
583 |
2720 |
vtc_log(p->vl, 4, "stderr read %d", i); |
584 |
2720 |
return (1); |
585 |
|
} |
586 |
635 |
PTOK(pthread_mutex_lock(&p->mtx)); |
587 |
635 |
p->stderr_bytes += i; |
588 |
635 |
PTOK(pthread_mutex_unlock(&p->mtx)); |
589 |
635 |
vtc_dump(p->vl, 4, "stderr", buf, i); |
590 |
635 |
assert(write(p->f_stderr, buf, i) == i); |
591 |
635 |
return (0); |
592 |
3355 |
} |
593 |
|
|
594 |
|
static void |
595 |
0 |
process_cleanup(void *priv) |
596 |
|
{ |
597 |
0 |
struct vev_root *evb = priv; |
598 |
0 |
VEV_Destroy(&evb); |
599 |
0 |
} |
600 |
|
|
601 |
|
static void * |
602 |
2720 |
process_thread(void *priv) |
603 |
|
{ |
604 |
|
struct process *p; |
605 |
|
struct vev_root *evb; |
606 |
|
struct vev *ev; |
607 |
|
int r; |
608 |
|
|
609 |
2720 |
CAST_OBJ_NOTNULL(p, priv, PROCESS_MAGIC); |
610 |
|
|
611 |
2720 |
p->f_stdout = open(p->out, O_WRONLY|O_APPEND); |
612 |
2720 |
assert(p->f_stdout >= 0); |
613 |
2720 |
p->f_stderr = open(p->err, O_WRONLY|O_APPEND); |
614 |
2720 |
assert(p->f_stderr >= 0); |
615 |
|
|
616 |
2720 |
evb = VEV_New(); |
617 |
2720 |
AN(evb); |
618 |
2720 |
pthread_cleanup_push(process_cleanup, evb); |
619 |
|
|
620 |
2720 |
ev = VEV_Alloc(); |
621 |
2720 |
AN(ev); |
622 |
2720 |
ev->fd = p->fd_term; |
623 |
2720 |
ev->fd_flags = VEV__RD | VEV__HUP | VEV__ERR; |
624 |
2720 |
ev->callback = process_stdout; |
625 |
2720 |
ev->priv = p; |
626 |
2720 |
AZ(VEV_Start(evb, ev)); |
627 |
|
|
628 |
2720 |
ev = VEV_Alloc(); |
629 |
2720 |
AN(ev); |
630 |
2720 |
ev->fd = p->fd_stderr; |
631 |
2720 |
ev->fd_flags = VEV__RD | VEV__HUP | VEV__ERR; |
632 |
2720 |
ev->callback = process_stderr; |
633 |
2720 |
ev->priv = p; |
634 |
2720 |
AZ(VEV_Start(evb, ev)); |
635 |
|
|
636 |
2720 |
if (p->log == 1) { |
637 |
360 |
p->vlu_stdout = VLU_New(process_vlu_func, p, 1024); |
638 |
360 |
AN(p->vlu_stdout); |
639 |
360 |
p->vlu_stderr = VLU_New(process_vlu_func, p, 1024); |
640 |
360 |
AN(p->vlu_stderr); |
641 |
360 |
} |
642 |
|
|
643 |
2720 |
do { |
644 |
2720 |
r = VEV_Once(evb); |
645 |
2720 |
} while (r == 1); |
646 |
|
|
647 |
2720 |
if (r < 0) |
648 |
0 |
vtc_fatal(p->vl, "VEV_Once() = %d, error %s", r, |
649 |
0 |
strerror(errno)); |
650 |
|
|
651 |
5440 |
vtc_wait4(p->vl, p->pid, |
652 |
2720 |
p->expect_exit, p->expect_signal, p->allow_core); |
653 |
2720 |
closefd(&p->f_stdout); |
654 |
2720 |
closefd(&p->f_stderr); |
655 |
|
|
656 |
2720 |
PTOK(pthread_mutex_lock(&p->mtx)); |
657 |
|
|
658 |
|
/* NB: We keep the other macros around */ |
659 |
2720 |
macro_undef(p->vl, p->name, "pid"); |
660 |
2720 |
p->pid = -1; |
661 |
|
|
662 |
2720 |
PTOK(pthread_mutex_unlock(&p->mtx)); |
663 |
|
|
664 |
2720 |
pthread_cleanup_pop(0); |
665 |
2720 |
VEV_Destroy(&evb); |
666 |
2720 |
if (p->log == 1) { |
667 |
360 |
VLU_Destroy(&p->vlu_stdout); |
668 |
360 |
VLU_Destroy(&p->vlu_stderr); |
669 |
360 |
} |
670 |
2720 |
return (NULL); |
671 |
|
} |
672 |
|
|
673 |
|
static void |
674 |
3040 |
process_winsz(struct process *p, int fd) |
675 |
|
{ |
676 |
|
struct winsize ws; |
677 |
|
int i; |
678 |
|
|
679 |
3040 |
memset(&ws, 0, sizeof ws); |
680 |
3040 |
ws.ws_row = (short)p->nlin; |
681 |
3040 |
ws.ws_col = (short)p->ncol; |
682 |
3040 |
i = ioctl(fd, TIOCSWINSZ, &ws); |
683 |
3040 |
if (i) |
684 |
120 |
vtc_log(p->vl, 4, "TIOCWINSZ %d %s", i, strerror(errno)); |
685 |
3040 |
} |
686 |
|
|
687 |
|
static void |
688 |
2720 |
process_init_term(struct process *p, int fd) |
689 |
|
{ |
690 |
|
struct termios tt; |
691 |
|
int i; |
692 |
|
|
693 |
2720 |
process_winsz(p, fd); |
694 |
|
|
695 |
2720 |
memset(&tt, 0, sizeof tt); |
696 |
2720 |
tt.c_cflag = CREAD | CS8 | HUPCL; |
697 |
2720 |
tt.c_iflag = BRKINT | ICRNL | IMAXBEL | IXON | IXANY; |
698 |
2720 |
tt.c_lflag = ICANON | ISIG | IEXTEN | ECHO | ECHOE | ECHOKE | ECHOCTL; |
699 |
2720 |
tt.c_oflag = OPOST | ONLCR; |
700 |
2720 |
i = cfsetispeed(&tt, B9600); |
701 |
2720 |
if (i) |
702 |
0 |
vtc_log(p->vl, 4, "cfsetispeed %d %s", i, strerror(errno)); |
703 |
0 |
i = cfsetospeed(&tt, B9600); |
704 |
0 |
if (i) |
705 |
0 |
vtc_log(p->vl, 4, "cfsetospeed %d %s", i, strerror(errno)); |
706 |
0 |
tt.c_cc[VEOF] = '\x04'; // CTRL-D |
707 |
0 |
tt.c_cc[VERASE] = '\x08'; // CTRL-H (Backspace) |
708 |
0 |
tt.c_cc[VKILL] = '\x15'; // CTRL-U |
709 |
0 |
tt.c_cc[VINTR] = '\x03'; // CTRL-C |
710 |
0 |
tt.c_cc[VQUIT] = '\x1c'; // CTRL-backslash |
711 |
|
|
712 |
0 |
i = tcsetattr(fd, TCSAFLUSH, &tt); |
713 |
0 |
if (i) |
714 |
0 |
vtc_log(p->vl, 4, "TCSAFLUSH %d %s", i, strerror(errno)); |
715 |
2720 |
} |
716 |
|
|
717 |
|
/********************************************************************** |
718 |
|
* Start the process thread |
719 |
|
*/ |
720 |
|
|
721 |
|
static void |
722 |
2720 |
process_start(struct process *p) |
723 |
|
{ |
724 |
|
struct vsb *cl; |
725 |
|
int fd2[2]; |
726 |
|
int master, slave; |
727 |
|
const char *slavename; |
728 |
|
char c; |
729 |
|
|
730 |
2720 |
CHECK_OBJ_NOTNULL(p, PROCESS_MAGIC); |
731 |
2720 |
if (p->hasthread) |
732 |
0 |
vtc_fatal(p->vl, "Already running, -wait first"); |
733 |
|
|
734 |
2720 |
vtc_log(p->vl, 4, "CMD: %s", p->spec); |
735 |
|
|
736 |
2720 |
cl = macro_expand(p->vl, p->spec); |
737 |
2720 |
AN(cl); |
738 |
|
|
739 |
2720 |
master = posix_openpt(O_RDWR|O_NOCTTY); |
740 |
2720 |
assert(master >= 0); |
741 |
2720 |
AZ(grantpt(master)); |
742 |
2720 |
AZ(unlockpt(master)); |
743 |
2720 |
slavename = ptsname(master); |
744 |
2720 |
AN(slavename); |
745 |
|
|
746 |
2720 |
AZ(pipe(fd2)); |
747 |
|
|
748 |
2720 |
p->pid = fork(); |
749 |
2720 |
assert(p->pid >= 0); |
750 |
5440 |
if (p->pid == 0) { |
751 |
2720 |
assert(setsid() == getpid()); |
752 |
2720 |
assert(dup2(fd2[1], STDERR_FILENO) == STDERR_FILENO); |
753 |
2720 |
AZ(close(STDIN_FILENO)); |
754 |
2720 |
slave = open(slavename, O_RDWR); |
755 |
2720 |
assert(slave == STDIN_FILENO); |
756 |
|
#ifdef __sun |
757 |
|
if (ioctl(slave, I_PUSH, "ptem")) |
758 |
|
vtc_log(p->vl, 4, "PUSH ptem: %s", strerror(errno)); |
759 |
|
if (ioctl(slave, I_PUSH, "ldterm")) |
760 |
|
vtc_log(p->vl, 4, "PUSH ldterm: %s", strerror(errno)); |
761 |
|
(void)ioctl(STDIN_FILENO, TIOCSCTTY, NULL); |
762 |
|
#else |
763 |
2720 |
AZ(ioctl(STDIN_FILENO, TIOCSCTTY, NULL)); |
764 |
|
#endif |
765 |
2720 |
AZ(close(STDOUT_FILENO)); |
766 |
2720 |
assert(dup2(slave, STDOUT_FILENO) == STDOUT_FILENO); |
767 |
2720 |
VSUB_closefrom(STDERR_FILENO + 1); |
768 |
2720 |
process_init_term(p, slave); |
769 |
|
|
770 |
2720 |
AZ(setenv("TERM", "xterm", 1)); |
771 |
2720 |
AZ(unsetenv("TERMCAP")); |
772 |
|
// Not using NULL because GCC is now even more demented... |
773 |
2720 |
assert(write(STDERR_FILENO, "+", 1) == 1); |
774 |
2720 |
AZ(execl("/bin/sh", "/bin/sh", "-c", VSB_data(cl), (char*)0)); |
775 |
0 |
exit(1); |
776 |
|
} |
777 |
2720 |
vtc_log(p->vl, 3, "PID: %ld", (long)p->pid); |
778 |
2720 |
VSB_destroy(&cl); |
779 |
|
|
780 |
2720 |
assert(read(fd2[0], &c, 1) == 1); |
781 |
2720 |
p->fd_term = master; |
782 |
2720 |
closefd(&fd2[1]); |
783 |
2720 |
p->fd_stderr = fd2[0]; |
784 |
2720 |
macro_def(p->vl, p->name, "pid", "%ld", (long)p->pid); |
785 |
2720 |
macro_def(p->vl, p->name, "dir", "%s", p->dir); |
786 |
2720 |
macro_def(p->vl, p->name, "out", "%s", p->out); |
787 |
2720 |
macro_def(p->vl, p->name, "err", "%s", p->err); |
788 |
2720 |
p->hasthread = 1; |
789 |
2720 |
PTOK(pthread_create(&p->tp, NULL, process_thread, p)); |
790 |
2720 |
} |
791 |
|
|
792 |
|
/********************************************************************** |
793 |
|
* Wait for process thread to stop |
794 |
|
*/ |
795 |
|
|
796 |
|
static void |
797 |
2720 |
process_wait(struct process *p) |
798 |
|
{ |
799 |
|
void *v; |
800 |
|
|
801 |
2720 |
if (p->hasthread) { |
802 |
2720 |
PTOK(pthread_join(p->tp, &v)); |
803 |
2720 |
p->hasthread = 0; |
804 |
2720 |
} |
805 |
5440 |
vtc_log(p->vl, 4, "stdout %ju bytes, stderr %ju bytes", |
806 |
2720 |
p->stdout_bytes, p->stderr_bytes); |
807 |
2720 |
} |
808 |
|
|
809 |
|
/********************************************************************** |
810 |
|
* Send a signal to a process |
811 |
|
*/ |
812 |
|
|
813 |
|
static void |
814 |
1080 |
process_kill(struct process *p, const char *sig) |
815 |
|
{ |
816 |
1080 |
int j = 0; |
817 |
|
pid_t pid; |
818 |
|
|
819 |
1080 |
CHECK_OBJ_NOTNULL(p, PROCESS_MAGIC); |
820 |
1080 |
AN(sig); |
821 |
|
|
822 |
1080 |
PTOK(pthread_mutex_lock(&p->mtx)); |
823 |
1080 |
pid = p->pid; |
824 |
1080 |
PTOK(pthread_mutex_unlock(&p->mtx)); |
825 |
|
|
826 |
1080 |
if (pid <= 0) |
827 |
0 |
vtc_fatal(p->vl, "Cannot signal a non-running process"); |
828 |
|
|
829 |
1080 |
if (!strcmp(sig, "TERM")) |
830 |
840 |
j = SIGTERM; |
831 |
240 |
else if (!strcmp(sig, "INT")) |
832 |
40 |
j = SIGINT; |
833 |
200 |
else if (!strcmp(sig, "KILL")) |
834 |
80 |
j = SIGKILL; |
835 |
120 |
else if (!strcmp(sig, "HUP")) |
836 |
120 |
j = SIGHUP; |
837 |
0 |
else if (*sig == '-') |
838 |
0 |
j = strtoul(sig + 1, NULL, 10); |
839 |
|
else |
840 |
0 |
vtc_fatal(p->vl, "Could not grok signal (%s)", sig); |
841 |
|
|
842 |
1080 |
if (p->expect_signal == 0) |
843 |
1040 |
p->expect_signal = -j; |
844 |
1080 |
if (kill(-pid, j) < 0) |
845 |
0 |
vtc_fatal(p->vl, "Failed to send signal %d (%s)", |
846 |
0 |
j, strerror(errno)); |
847 |
|
else |
848 |
1080 |
vtc_log(p->vl, 4, "Sent signal %d", j); |
849 |
1080 |
} |
850 |
|
|
851 |
|
/********************************************************************** |
852 |
|
* Write to a process' stdin |
853 |
|
*/ |
854 |
|
|
855 |
|
static void |
856 |
5240 |
process_write(const struct process *p, const char *text) |
857 |
|
{ |
858 |
|
int r, len; |
859 |
|
|
860 |
5240 |
if (!p->hasthread) |
861 |
0 |
vtc_fatal(p->vl, "Cannot write to a non-running process"); |
862 |
|
|
863 |
5240 |
len = strlen(text); |
864 |
5240 |
vtc_log(p->vl, 4, "Writing %d bytes", len); |
865 |
5240 |
r = write(p->fd_term, text, len); |
866 |
5240 |
if (r != len) |
867 |
0 |
vtc_fatal(p->vl, "Failed to write: len=%d %s (%d)", |
868 |
0 |
len, strerror(errno), errno); |
869 |
5240 |
} |
870 |
|
|
871 |
|
static void |
872 |
5000 |
process_write_hex(const struct process *p, const char *text) |
873 |
|
{ |
874 |
|
struct vsb *vsb; |
875 |
|
|
876 |
5000 |
if (!p->hasthread) |
877 |
0 |
vtc_fatal(p->vl, "Cannot write to a non-running process"); |
878 |
|
|
879 |
5000 |
vsb = vtc_hex_to_bin(p->vl, text); |
880 |
5000 |
assert(VSB_len(vsb) >= 0); |
881 |
5000 |
vtc_hexdump(p->vl, 4, "sendhex", VSB_data(vsb), VSB_len(vsb)); |
882 |
5000 |
AZ(VSB_tofile(vsb, p->fd_term)); |
883 |
5000 |
VSB_destroy(&vsb); |
884 |
5000 |
} |
885 |
|
|
886 |
|
static void |
887 |
0 |
process_close(struct process *p) |
888 |
|
{ |
889 |
|
|
890 |
0 |
if (!p->hasthread) |
891 |
0 |
vtc_fatal(p->vl, "Cannot close a non-running process"); |
892 |
|
|
893 |
0 |
process_kill(p, "HUP"); |
894 |
0 |
} |
895 |
|
|
896 |
|
/* SECTION: process process |
897 |
|
* |
898 |
|
* Run a process with stdin+stdout on a pseudo-terminal and stderr on a pipe. |
899 |
|
* |
900 |
|
* Output from the pseudo-terminal is copied verbatim to ${pNAME_out}, |
901 |
|
* and the -log/-dump/-hexdump flags will also put it in the vtc-log. |
902 |
|
* |
903 |
|
* The pseudo-terminal is not in ECHO mode, but if the programs run set |
904 |
|
* it to ECHO mode ("stty sane") any input sent to the process will also |
905 |
|
* appear in this stream because of the ECHO. |
906 |
|
* |
907 |
|
* Output from the stderr-pipe is copied verbatim to ${pNAME_err}, and |
908 |
|
* is always included in the vtc_log. |
909 |
|
* |
910 |
|
* process pNAME SPEC [-allow-core] [-expect-exit N] [-expect-signal N] |
911 |
|
* [-dump] [-hexdump] [-log] |
912 |
|
* [-run] [-close] [-kill SIGNAL] [-start] [-stop] [-wait] |
913 |
|
* [-write STRING] [-writeln STRING] [-writehex HEXSTRING] |
914 |
|
* [-need-bytes [+]NUMBER] |
915 |
|
* [-screen-dump] [-winsz LINES COLUMNS] [-ansi-response] |
916 |
|
* [-expect-cursor LINE COLUMN] [-expect-text LINE COLUMN TEXT] |
917 |
|
* [-match-text LINE COLUMN REGEXP] |
918 |
|
* |
919 |
|
* pNAME |
920 |
|
* Name of the process. It must start with 'p'. |
921 |
|
* |
922 |
|
* SPEC |
923 |
|
* The command(s) to run in this process. |
924 |
|
* |
925 |
|
* \-hexdump |
926 |
|
* Log output with vtc_hexdump(). Must be before -start/-run. |
927 |
|
* |
928 |
|
* \-dump |
929 |
|
* Log output with vtc_dump(). Must be before -start/-run. |
930 |
|
* |
931 |
|
* \-log |
932 |
|
* Log output with VLU/vtc_log(). Must be before -start/-run. |
933 |
|
* |
934 |
|
* \-start |
935 |
|
* Start the process. |
936 |
|
* |
937 |
|
* \-expect-exit N |
938 |
|
* Expect exit status N |
939 |
|
* |
940 |
|
* \-expect-signal N |
941 |
|
* Expect signal in exit status N |
942 |
|
* |
943 |
|
* \-allow-core |
944 |
|
* Core dump in exit status is OK |
945 |
|
* |
946 |
|
* \-wait |
947 |
|
* Wait for the process to finish. |
948 |
|
* |
949 |
|
* \-run |
950 |
|
* Shorthand for -start -wait. |
951 |
|
* |
952 |
|
* In most cases, if you just want to start a process and wait for it |
953 |
|
* to finish, you can use the ``shell`` command instead. |
954 |
|
* The following commands are equivalent:: |
955 |
|
* |
956 |
|
* shell "do --something" |
957 |
|
* |
958 |
|
* process p1 "do --something" -run |
959 |
|
* |
960 |
|
* However, you may use the ``process`` variant to conveniently |
961 |
|
* collect the standard input and output without dealing with shell |
962 |
|
* redirections yourself. The ``shell`` command can also expect an |
963 |
|
* expression from either output, consider using it if you only need |
964 |
|
* to match one. |
965 |
|
* |
966 |
|
* \-key KEYSYM |
967 |
|
* Send emulated key-press. |
968 |
|
* KEYSYM can be one of (NPAGE, PPAGE, HOME, END) |
969 |
|
* |
970 |
|
* |
971 |
|
* \-kill SIGNAL |
972 |
|
* Send a signal to the process. The argument can be either |
973 |
|
* the string "TERM", "INT", or "KILL" for SIGTERM, SIGINT or SIGKILL |
974 |
|
* signals, respectively, or a hyphen (-) followed by the signal |
975 |
|
* number. |
976 |
|
* |
977 |
|
* If you need to use other signal names, you can use the ``kill``\(1) |
978 |
|
* command directly:: |
979 |
|
* |
980 |
|
* shell "kill -USR1 ${pNAME_pid}" |
981 |
|
* |
982 |
|
* Note that SIGHUP usage is discouraged in test cases. |
983 |
|
* |
984 |
|
* \-stop |
985 |
|
* Shorthand for -kill TERM. |
986 |
|
* |
987 |
|
* \-close |
988 |
|
* Alias for "-kill HUP" |
989 |
|
* |
990 |
|
* \-winsz LINES COLUMNS |
991 |
|
* Change the terminal window size to LIN lines and COL columns. |
992 |
|
* |
993 |
|
* \-write STRING |
994 |
|
* Write a string to the process' stdin. |
995 |
|
* |
996 |
|
* \-writeln STRING |
997 |
|
* Same as -write followed by a newline (\\n). |
998 |
|
* |
999 |
|
* \-writehex HEXSTRING |
1000 |
|
* Same as -write but interpreted as hexadecimal bytes. |
1001 |
|
* |
1002 |
|
* \-need-bytes [+]NUMBER |
1003 |
|
* Wait until at least NUMBER bytes have been received in total. |
1004 |
|
* If '+' is prefixed, NUMBER new bytes must be received. |
1005 |
|
* |
1006 |
|
* \-ansi-response |
1007 |
|
* Respond to terminal respond-back sequences |
1008 |
|
* |
1009 |
|
* \-expect-cursor LINE COLUMN |
1010 |
|
* Expect cursors location |
1011 |
|
* |
1012 |
|
* \-expect-text LINE COLUMNS TEXT |
1013 |
|
* Wait for TEXT to appear at LIN,COL on the virtual screen. |
1014 |
|
* Lines and columns are numbered 1...N |
1015 |
|
* LIN==0 means "on any line" |
1016 |
|
* COL==0 means "anywhere on the line" |
1017 |
|
* |
1018 |
|
* \-match-text LINE COLUMN REGEXP |
1019 |
|
* Wait for the PAT regular expression to match the text at LIN,COL on the virtual screen. |
1020 |
|
* Lines and columns are numbered 1...N |
1021 |
|
* LIN==0 means "on any line" |
1022 |
|
* COL==0 means "anywhere on the line" |
1023 |
|
* |
1024 |
|
* |
1025 |
|
* \-screen-dump |
1026 |
|
* Dump the virtual screen into vtc_log |
1027 |
|
* |
1028 |
|
*/ |
1029 |
|
|
1030 |
|
void |
1031 |
71280 |
cmd_process(CMD_ARGS) |
1032 |
|
{ |
1033 |
|
struct process *p, *p2; |
1034 |
|
uintmax_t u, v, bsnap; |
1035 |
|
unsigned lin,col; |
1036 |
71280 |
int spec_set = 0; |
1037 |
|
|
1038 |
71280 |
(void)priv; |
1039 |
|
|
1040 |
71280 |
if (av == NULL) { |
1041 |
|
/* Reset and free */ |
1042 |
42400 |
VTAILQ_FOREACH_SAFE(p, &processes, list, p2) { |
1043 |
2240 |
if (p->pid > 0) { |
1044 |
200 |
process_kill(p, "TERM"); |
1045 |
200 |
sleep(1); |
1046 |
200 |
if (p->pid > 0) |
1047 |
0 |
process_kill(p, "KILL"); |
1048 |
200 |
} |
1049 |
360 |
if (p->hasthread) |
1050 |
360 |
process_wait(p); |
1051 |
2240 |
VTAILQ_REMOVE(&processes, p, list); |
1052 |
2240 |
process_undef(p); |
1053 |
2240 |
process_delete(p); |
1054 |
2240 |
} |
1055 |
40160 |
return; |
1056 |
|
} |
1057 |
|
|
1058 |
31120 |
AZ(strcmp(av[0], "process")); |
1059 |
31120 |
av++; |
1060 |
|
|
1061 |
31120 |
VTC_CHECK_NAME(vl, av[0], "Process", 'p'); |
1062 |
72400 |
VTAILQ_FOREACH(p, &processes, list) |
1063 |
70160 |
if (!strcmp(p->name, av[0])) |
1064 |
28880 |
break; |
1065 |
55520 |
if (p == NULL) |
1066 |
2240 |
p = process_new(av[0]); |
1067 |
31120 |
av++; |
1068 |
|
|
1069 |
31120 |
PTOK(pthread_mutex_lock(&p->mtx)); |
1070 |
31120 |
bsnap = p->stdout_bytes; |
1071 |
31120 |
PTOK(pthread_mutex_unlock(&p->mtx)); |
1072 |
|
|
1073 |
70240 |
for (; *av != NULL; av++) { |
1074 |
39120 |
if (vtc_error) |
1075 |
0 |
break; |
1076 |
|
|
1077 |
39120 |
if (!strcmp(*av, "-allow-core")) { |
1078 |
0 |
p->allow_core = 1; |
1079 |
0 |
continue; |
1080 |
|
} |
1081 |
39120 |
if (!strcmp(*av, "-close")) { |
1082 |
0 |
process_close(p); |
1083 |
0 |
continue; |
1084 |
|
} |
1085 |
39120 |
if (!strcmp(*av, "-dump")) { |
1086 |
760 |
if (p->hasthread) |
1087 |
0 |
vtc_fatal(p->vl, |
1088 |
|
"Cannot dump a running process"); |
1089 |
760 |
p->log = 2; |
1090 |
760 |
continue; |
1091 |
|
} |
1092 |
38360 |
if (!strcmp(*av, "-expect-exit")) { |
1093 |
640 |
p->expect_exit = strtoul(av[1], NULL, 0); |
1094 |
640 |
av++; |
1095 |
640 |
continue; |
1096 |
|
} |
1097 |
37720 |
if (!strcmp(*av, "-expect-signal")) { |
1098 |
0 |
p->expect_signal = strtoul(av[1], NULL, 0); |
1099 |
0 |
av++; |
1100 |
0 |
continue; |
1101 |
|
} |
1102 |
37720 |
if (!strcmp(*av, "-hexdump")) { |
1103 |
160 |
if (p->hasthread) |
1104 |
0 |
vtc_fatal(p->vl, |
1105 |
|
"Cannot dump a running process"); |
1106 |
160 |
p->log = 3; |
1107 |
160 |
continue; |
1108 |
|
} |
1109 |
37560 |
if (!strcmp(*av, "-key")) { |
1110 |
160 |
if (!strcmp(av[1], "NPAGE")) |
1111 |
40 |
process_write(p, "\x1b\x5b\x36\x7e"); |
1112 |
120 |
else if (!strcmp(av[1], "PPAGE")) |
1113 |
40 |
process_write(p, "\x1b\x5b\x35\x7e"); |
1114 |
80 |
else if (!strcmp(av[1], "HOME")) |
1115 |
40 |
process_write(p, "\x1b\x4f\x48"); |
1116 |
40 |
else if (!strcmp(av[1], "END")) |
1117 |
40 |
process_write(p, "\x1b\x4f\x46"); |
1118 |
|
else |
1119 |
0 |
vtc_fatal(p->vl, "Unknown key %s", av[1]); |
1120 |
160 |
continue; |
1121 |
|
} |
1122 |
37400 |
if (!strcmp(*av, "-kill")) { |
1123 |
480 |
process_kill(p, av[1]); |
1124 |
480 |
av++; |
1125 |
480 |
continue; |
1126 |
|
} |
1127 |
36920 |
if (!strcmp(*av, "-log")) { |
1128 |
360 |
if (p->hasthread) |
1129 |
0 |
vtc_fatal(p->vl, |
1130 |
|
"Cannot log a running process"); |
1131 |
360 |
p->log = 1; |
1132 |
360 |
continue; |
1133 |
|
} |
1134 |
36560 |
if (!strcmp(*av, "-need-bytes")) { |
1135 |
360 |
u = strtoumax(av[1], NULL, 0); |
1136 |
360 |
if (av[1][0] == '+') |
1137 |
280 |
u += bsnap; |
1138 |
360 |
av++; |
1139 |
360 |
do { |
1140 |
360 |
PTOK(pthread_mutex_lock(&p->mtx)); |
1141 |
711 |
v = p->stdout_bytes; |
1142 |
711 |
PTOK(pthread_mutex_unlock(&p->mtx)); |
1143 |
711 |
vtc_log(p->vl, 4, "Have %ju bytes", v); |
1144 |
711 |
usleep(500000); |
1145 |
711 |
} while(v < u); |
1146 |
360 |
continue; |
1147 |
|
} |
1148 |
36200 |
if (!strcmp(*av, "-run")) { |
1149 |
760 |
process_start(p); |
1150 |
760 |
process_wait(p); |
1151 |
760 |
continue; |
1152 |
|
} |
1153 |
35440 |
if (!strcmp(*av, "-ansi-response")) { |
1154 |
80 |
p->ansi_response = 1; |
1155 |
80 |
continue; |
1156 |
|
} |
1157 |
35360 |
if (!strcmp(*av, "-expect-text")) { |
1158 |
10040 |
AN(av[1]); |
1159 |
10040 |
AN(av[2]); |
1160 |
10040 |
AN(av[3]); |
1161 |
10040 |
term_expect_text(p, av[1], av[2], av[3]); |
1162 |
10040 |
av += 3; |
1163 |
10040 |
continue; |
1164 |
|
} |
1165 |
25320 |
if (!strcmp(*av, "-expect-cursor")) { |
1166 |
320 |
AN(av[1]); |
1167 |
320 |
AN(av[2]); |
1168 |
320 |
term_expect_cursor(p, av[1], av[2]); |
1169 |
320 |
av += 2; |
1170 |
320 |
continue; |
1171 |
|
} |
1172 |
25000 |
if (!strcmp(*av, "-match-text")) { |
1173 |
1360 |
AN(av[1]); |
1174 |
1360 |
AN(av[2]); |
1175 |
1360 |
AN(av[3]); |
1176 |
1360 |
term_match_text(p, av[1], av[2], av[3]); |
1177 |
1360 |
av += 3; |
1178 |
1360 |
continue; |
1179 |
|
} |
1180 |
23640 |
if (!strcmp(*av, "-screen_dump") || |
1181 |
17840 |
!strcmp(*av, "-screen-dump")) { |
1182 |
6240 |
term_screen_dump(p); |
1183 |
6240 |
continue; |
1184 |
|
} |
1185 |
17400 |
if (!strcmp(*av, "-start")) { |
1186 |
1960 |
process_start(p); |
1187 |
1960 |
continue; |
1188 |
|
} |
1189 |
15440 |
if (!strcmp(*av, "-stop")) { |
1190 |
400 |
process_kill(p, "TERM"); |
1191 |
400 |
sleep(1); |
1192 |
400 |
continue; |
1193 |
|
} |
1194 |
15040 |
if (!strcmp(*av, "-wait")) { |
1195 |
1600 |
process_wait(p); |
1196 |
1600 |
continue; |
1197 |
|
} |
1198 |
13440 |
if (!strcmp(*av, "-winsz")) { |
1199 |
320 |
lin = atoi(av[1]); |
1200 |
320 |
assert(lin > 1); |
1201 |
320 |
col = atoi(av[2]); |
1202 |
320 |
assert(col > 1); |
1203 |
320 |
av += 2; |
1204 |
320 |
PTOK(pthread_mutex_lock(&p->mtx)); |
1205 |
320 |
term_resize(p, lin, col); |
1206 |
320 |
PTOK(pthread_mutex_unlock(&p->mtx)); |
1207 |
320 |
process_winsz(p, p->fd_term); |
1208 |
320 |
continue; |
1209 |
|
} |
1210 |
13120 |
if (!strcmp(*av, "-write")) { |
1211 |
4440 |
process_write(p, av[1]); |
1212 |
4440 |
av++; |
1213 |
4440 |
continue; |
1214 |
|
} |
1215 |
8680 |
if (!strcmp(*av, "-writehex")) { |
1216 |
5000 |
process_write_hex(p, av[1]); |
1217 |
5000 |
av++; |
1218 |
5000 |
continue; |
1219 |
|
} |
1220 |
3680 |
if (!strcmp(*av, "-writeln")) { |
1221 |
320 |
process_write(p, av[1]); |
1222 |
320 |
process_write(p, "\n"); |
1223 |
320 |
av++; |
1224 |
320 |
continue; |
1225 |
|
} |
1226 |
3360 |
if (**av == '-' || spec_set) |
1227 |
0 |
vtc_fatal(p->vl, "Unknown process argument: %s", *av); |
1228 |
3360 |
REPLACE(p->spec, *av); |
1229 |
3360 |
spec_set = 1; |
1230 |
3360 |
} |
1231 |
71280 |
} |