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