| | 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 |
138793 |
term_cursor(void *priv, const teken_pos_t *pos) |
| 109 |
|
{ |
| 110 |
138793 |
(void)priv; |
| 111 |
138793 |
(void)pos; |
| 112 |
138793 |
} |
| 113 |
|
|
| 114 |
|
static void |
| 115 |
875299 |
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 |
875299 |
CAST_OBJ_NOTNULL(pp, priv, PROCESS_MAGIC); |
| 121 |
875298 |
(void)at; |
| 122 |
875298 |
if (ch > 126 || ch < 32) |
| 123 |
562 |
ch = '?'; |
| 124 |
875298 |
assert(pos->tp_row < pp->nlin); |
| 125 |
875299 |
assert(pos->tp_col < pp->ncol); |
| 126 |
875302 |
pp->vram[pos->tp_row][pos->tp_col] = ch; |
| 127 |
875302 |
} |
| 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 |
757962 |
for (p.tp_col = r->tr_begin.tp_col; |
| 139 |
757962 |
p.tp_col < r->tr_end.tp_col; p.tp_col++) |
| 140 |
757962 |
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 |
23910 |
for (y = 0; y < nrow; y++) |
| 161 |
46506 |
memmove(&pp->vram[p->tp_row + y][p->tp_col], |
| 162 |
23253 |
&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 |
4844 |
term_find_textline(const struct process *pp, int *x, int y, const char *pat) |
| 261 |
|
{ |
| 262 |
|
const char *t; |
| 263 |
|
int l; |
| 264 |
|
|
| 265 |
4844 |
if (*x == 0) { |
| 266 |
3802 |
t = strstr(pp->vram[y], pat); |
| 267 |
3802 |
if (t != NULL) { |
| 268 |
70 |
*x = 1 + (t - pp->vram[y]); |
| 269 |
70 |
return (1); |
| 270 |
|
} |
| 271 |
4774 |
} 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 |
4592 |
return (0); |
| 279 |
4844 |
} |
| 280 |
|
|
| 281 |
|
static int |
| 282 |
511 |
term_find_text(const struct process *pp, int *x, int *y, const char *pat) |
| 283 |
|
{ |
| 284 |
|
int yy; |
| 285 |
|
|
| 286 |
511 |
if (*y == 0) { |
| 287 |
4640 |
for (yy = 0; yy < pp->nlin; yy++) { |
| 288 |
4525 |
if (term_find_textline(pp, x, yy, pat)) { |
| 289 |
77 |
*y = yy + 1; |
| 290 |
77 |
return (1); |
| 291 |
|
} |
| 292 |
4448 |
} |
| 293 |
434 |
} else if (*y <= pp->nlin) { |
| 294 |
319 |
if (term_find_textline(pp, x, *y - 1, pat)) |
| 295 |
175 |
return (1); |
| 296 |
144 |
} |
| 297 |
259 |
return (0); |
| 298 |
511 |
} |
| 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 |
511 |
while (!term_find_text(pp, &x, &y, pat)) { |
| 320 |
259 |
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 |
259 |
PTOK(pthread_mutex_unlock(&pp->mtx)); |
| 326 |
259 |
usleep(d); |
| 327 |
259 |
PTOK(pthread_mutex_lock(&pp->mtx)); |
| 328 |
259 |
if (d < 3000000) |
| 329 |
259 |
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 |
4033 |
process_stdout(const struct vev *ev, int what) |
| 545 |
|
{ |
| 546 |
|
struct process *p; |
| 547 |
|
char buf[BUFSIZ]; |
| 548 |
|
int i; |
| 549 |
|
|
| 550 |
4033 |
CAST_OBJ_NOTNULL(p, ev->priv, PROCESS_MAGIC); |
| 551 |
4033 |
(void)what; |
| 552 |
4033 |
i = read(p->fd_term, buf, sizeof buf); |
| 553 |
4033 |
if (i <= 0) { |
| 554 |
69 |
vtc_log(p->vl, 4, "stdout read %d", i); |
| 555 |
69 |
return (1); |
| 556 |
|
} |
| 557 |
3964 |
PTOK(pthread_mutex_lock(&p->mtx)); |
| 558 |
3964 |
p->stdout_bytes += i; |
| 559 |
3964 |
PTOK(pthread_mutex_unlock(&p->mtx)); |
| 560 |
3964 |
if (p->log == 1) |
| 561 |
53 |
(void)VLU_Feed(p->vlu_stdout, buf, i); |
| 562 |
3911 |
else if (p->log == 2) |
| 563 |
271 |
vtc_dump(p->vl, 4, "stdout", buf, i); |
| 564 |
3640 |
else if (p->log == 3) |
| 565 |
50 |
vtc_hexdump(p->vl, 4, "stdout", buf, i); |
| 566 |
3964 |
assert(write(p->f_stdout, buf, i) == i); |
| 567 |
3964 |
PTOK(pthread_mutex_lock(&p->mtx)); |
| 568 |
3964 |
teken_input(p->tek, buf, i); |
| 569 |
3964 |
PTOK(pthread_mutex_unlock(&p->mtx)); |
| 570 |
3964 |
return (0); |
| 571 |
4033 |
} |
| 572 |
|
|
| 573 |
|
static int v_matchproto_(vev_cb_f) |
| 574 |
85 |
process_stderr(const struct vev *ev, int what) |
| 575 |
|
{ |
| 576 |
|
struct process *p; |
| 577 |
|
char buf[BUFSIZ]; |
| 578 |
|
int i; |
| 579 |
|
|
| 580 |
85 |
CAST_OBJ_NOTNULL(p, ev->priv, PROCESS_MAGIC); |
| 581 |
85 |
(void)what; |
| 582 |
85 |
i = read(p->fd_stderr, buf, sizeof buf); |
| 583 |
85 |
if (i <= 0) { |
| 584 |
69 |
vtc_log(p->vl, 4, "stderr read %d", i); |
| 585 |
69 |
return (1); |
| 586 |
|
} |
| 587 |
16 |
PTOK(pthread_mutex_lock(&p->mtx)); |
| 588 |
16 |
p->stderr_bytes += i; |
| 589 |
16 |
PTOK(pthread_mutex_unlock(&p->mtx)); |
| 590 |
16 |
vtc_dump(p->vl, 4, "stderr", buf, i); |
| 591 |
16 |
assert(write(p->f_stderr, buf, i) == i); |
| 592 |
16 |
return (0); |
| 593 |
85 |
} |
| 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 |
1806 |
cmd_process(CMD_ARGS) |
| 1033 |
|
{ |
| 1034 |
|
struct process *p, *p2; |
| 1035 |
|
uintmax_t u, v, bsnap; |
| 1036 |
|
unsigned lin,col; |
| 1037 |
1806 |
int spec_set = 0; |
| 1038 |
|
|
| 1039 |
1806 |
(void)priv; |
| 1040 |
|
|
| 1041 |
1806 |
if (av == NULL) { |
| 1042 |
|
/* Reset and free */ |
| 1043 |
1080 |
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 |
1023 |
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 |
18 |
v = p->stdout_bytes; |
| 1143 |
18 |
PTOK(pthread_mutex_unlock(&p->mtx)); |
| 1144 |
18 |
vtc_log(p->vl, 4, "Have %ju bytes", v); |
| 1145 |
18 |
VTIM_sleep(0.5); |
| 1146 |
18 |
} 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 |
1806 |
} |