Commit 70c9a264 authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

Restructure 'process' a bit and teach it a few new tricks.

parent ccfa03fc
...@@ -42,7 +42,7 @@ shell -exit 1 -expect "Command failed with error code 101" { ...@@ -42,7 +42,7 @@ shell -exit 1 -expect "Command failed with error code 101" {
shell "varnishadm -n ${tmpdir}/v1 param.set cli_limit 128" shell "varnishadm -n ${tmpdir}/v1 param.set cli_limit 128"
shell -expect "[response was truncated]" "varnishadm -n ${tmpdir}/v1 help" shell -expect "[response was truncated]" "varnishadm -n ${tmpdir}/v1 help"
process p1 -stop -wait process p1 -expect-exit 64 -stop -wait
# Test multiple -f options # Test multiple -f options
......
...@@ -2,13 +2,15 @@ varnishtest "Simple process tests" ...@@ -2,13 +2,15 @@ varnishtest "Simple process tests"
# new & start # new & start
process p1 "cat" -start process p1 "cat" -start
process p2 "cat" -start process p2 -log "cat" -start
process p3 "cat" -start process p3 -dump "cat" -start
process p4 -hexdump "cat" -start
# write # write
process p1 -writeln "foo" process p1 -writeln "foo"
process p2 -writeln "bar" process p2 -writeln "bar"
process p3 -writeln "baz" process p3 -writeln "baz"
process p4 -writeln "b\001z"
# give enough time for the writes # give enough time for the writes
delay 0.5 delay 0.5
...@@ -17,11 +19,13 @@ delay 0.5 ...@@ -17,11 +19,13 @@ delay 0.5
process p1 -stop process p1 -stop
process p2 -close process p2 -close
process p3 -kill KILL process p3 -kill KILL
process p4 -kill TERM
# wait # wait
process p1 -wait process p1 -wait
process p2 -wait process p2 -wait
process p3 -wait process p3 -wait
process p4 -wait
# check stdout # check stdout
shell "grep -q foo ${p1_out}" shell "grep -q foo ${p1_out}"
......
...@@ -33,13 +33,17 @@ ...@@ -33,13 +33,17 @@
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <poll.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include "vtc.h" #include "vtc.h"
#include "vev.h"
#include "vlu.h" #include "vlu.h"
#include "vsb.h"
#include "vsub.h" #include "vsub.h"
struct process { struct process {
...@@ -53,10 +57,17 @@ struct process { ...@@ -53,10 +57,17 @@ struct process {
char *dir; char *dir;
char *out; char *out;
char *err; char *err;
int fd_to; int fd_stdin;
int fd_stdout;
int fd_stderr;
int f_stdout;
int f_stderr;
struct vlu *vlu_stdout;
struct vlu *vlu_stderr;
const char *cur_fd;
int log; int log;
int fd_from;
pid_t pid; pid_t pid;
int expect_exit;
pthread_mutex_t mtx; pthread_mutex_t mtx;
pthread_t tp; pthread_t tp;
...@@ -103,8 +114,8 @@ process_new(const char *name) ...@@ -103,8 +114,8 @@ process_new(const char *name)
p->dir, p->dir, p->out, p->err); p->dir, p->dir, p->out, p->err);
AZ(system(buf)); AZ(system(buf));
p->fd_to = -1; p->fd_stdin = -1;
p->fd_from = -1; p->fd_stdout = -1;
VTAILQ_INSERT_TAIL(&processes, p, list); VTAILQ_INSERT_TAIL(&processes, p, list);
return (p); return (p);
...@@ -158,7 +169,66 @@ process_vlu_func(void *priv, const char *l) ...@@ -158,7 +169,66 @@ process_vlu_func(void *priv, const char *l)
struct process *p; struct process *p;
CAST_OBJ_NOTNULL(p, priv, PROCESS_MAGIC); CAST_OBJ_NOTNULL(p, priv, PROCESS_MAGIC);
vtc_dump(p->vl, 4, "output", l, -1); vtc_dump(p->vl, 4, p->cur_fd, l, -1);
return (0);
}
static int v_matchproto_(vev_cb_f)
process_stdin(const struct vev *ev, int what)
{
struct process *p;
CAST_OBJ_NOTNULL(p, ev->priv, PROCESS_MAGIC);
vtc_log(p->vl, 4, "stdin event 0x%x", what);
return (1);
}
static int v_matchproto_(vev_cb_f)
process_stdout(const struct vev *ev, int what)
{
struct process *p;
char buf[BUFSIZ];
int i;
CAST_OBJ_NOTNULL(p, ev->priv, PROCESS_MAGIC);
(void)what;
p->cur_fd = "stdout";
i = read(p->fd_stdout, buf, sizeof buf);
if (i <= 0) {
vtc_log(p->vl, 4, "stdout read %d", i);
return (1);
}
if (p->log == 1)
(void)VLU_Feed(p->vlu_stdout, buf, i);
else if (p->log == 2)
vtc_dump(p->vl, 4, "stdout", buf, i);
else if (p->log == 3)
vtc_hexdump(p->vl, 4, "stdout", buf, i);
(void)write(p->f_stdout, buf, i);
return (0);
}
static int v_matchproto_(vev_cb_f)
process_stderr(const struct vev *ev, int what)
{
struct process *p;
char buf[BUFSIZ];
int i;
CAST_OBJ_NOTNULL(p, ev->priv, PROCESS_MAGIC);
(void)what;
p->cur_fd = "stderr";
i = read(p->fd_stderr, buf, sizeof buf);
if (i <= 0) {
vtc_log(p->vl, 4, "stderr read %d", i);
return (1);
}
if (p->log == 1)
(void)VLU_Feed(p->vlu_stderr, buf, i);
else if (p->log == 2)
vtc_dump(p->vl, 4, "stderr", buf, i);
else if (p->log == 3)
vtc_hexdump(p->vl, 4, "stderr", buf, i);
(void)write(p->f_stderr, buf, i);
return (0); return (0);
} }
...@@ -167,17 +237,65 @@ process_thread(void *priv) ...@@ -167,17 +237,65 @@ process_thread(void *priv)
{ {
struct process *p; struct process *p;
struct rusage ru; struct rusage ru;
struct vev_root *evb;
struct vev *ev;
int r; int r;
CAST_OBJ_NOTNULL(p, priv, PROCESS_MAGIC); CAST_OBJ_NOTNULL(p, priv, PROCESS_MAGIC);
if (p->fd_from > 0)
(void)VLU_File(p->fd_from, process_vlu_func, p, 1024); p->f_stdout = open(p->out, O_WRONLY|O_APPEND);
assert(p->f_stdout >= 0);
p->f_stderr = open(p->err, O_WRONLY|O_APPEND);
assert(p->f_stderr >= 0);
evb = VEV_New();
AN(evb);
ev = VEV_Alloc();
AN(ev);
ev->fd = p->fd_stdin;
ev->fd_flags = VEV__HUP | VEV__ERR;
ev->priv = p;
ev->callback = process_stdin;
AZ(VEV_Start(evb, ev));
ev = VEV_Alloc();
AN(ev);
ev->fd = p->fd_stdout;
ev->fd_flags = VEV__RD | VEV__HUP | VEV__ERR;
ev->callback = process_stdout;
ev->priv = p;
AZ(VEV_Start(evb, ev));
ev = VEV_Alloc();
AN(ev);
ev->fd = p->fd_stderr;
ev->fd_flags = VEV__RD | VEV__HUP | VEV__ERR;
ev->callback = process_stderr;
ev->priv = p;
AZ(VEV_Start(evb, ev));
if (p->log == 1) {
p->vlu_stdout = VLU_New(process_vlu_func, p, 1024);
AN(p->vlu_stdout);
p->vlu_stderr = VLU_New(process_vlu_func, p, 1024);
AN(p->vlu_stderr);
}
do {
r = VEV_Once(evb);
} while (r == 1);
if (r < 0)
vtc_fatal(p->vl, "VEV_Once() = %d, error %s", r,
strerror(errno));
r = wait4(p->pid, &p->status, 0, &ru); r = wait4(p->pid, &p->status, 0, &ru);
AZ(pthread_mutex_lock(&p->mtx)); closefd(&p->f_stdout);
closefd(&p->f_stderr);
if (p->fd_to >= 0) AZ(pthread_mutex_lock(&p->mtx));
closefd(&p->fd_to);
/* NB: We keep the other macros around */ /* NB: We keep the other macros around */
macro_undef(p->vl, p->name, "pid"); macro_undef(p->vl, p->name, "pid");
...@@ -191,17 +309,23 @@ process_thread(void *priv) ...@@ -191,17 +309,23 @@ process_thread(void *priv)
AZ(pthread_mutex_unlock(&p->mtx)); AZ(pthread_mutex_unlock(&p->mtx));
if (WIFEXITED(p->status) && WEXITSTATUS(p->status) == 0)
return (NULL);
#ifdef WCOREDUMP #ifdef WCOREDUMP
vtc_log(p->vl, 2, "Bad exit code: %04x sig %d exit %d core %d", vtc_log(p->vl, 2, "Exit code: %04x sig %d exit %d core %d",
p->status, WTERMSIG(p->status), WEXITSTATUS(p->status), p->status, WTERMSIG(p->status), WEXITSTATUS(p->status),
WCOREDUMP(p->status)); WCOREDUMP(p->status));
#else #else
vtc_log(p->vl, 2, "Bad exit code: %04x sig %d exit %d", vtc_log(p->vl, 2, "Exit code: %04x sig %d exit %d",
p->status, WTERMSIG(p->status), WEXITSTATUS(p->status)); p->status, WTERMSIG(p->status), WEXITSTATUS(p->status));
#endif #endif
if (WEXITSTATUS(p->status) != p->expect_exit)
vtc_fatal(p->vl, "Expected exit %d got %d",
p->expect_exit, WEXITSTATUS(p->status));
VEV_Destroy(&evb);
if (p->log == 1) {
VLU_Destroy(&p->vlu_stdout);
VLU_Destroy(&p->vlu_stderr);
}
return (NULL); return (NULL);
} }
...@@ -209,9 +333,7 @@ static void ...@@ -209,9 +333,7 @@ static void
process_start(struct process *p) process_start(struct process *p)
{ {
struct vsb *cl; struct vsb *cl;
int out_fd, err_fd; int fd0[2], fd1[2], fd2[2];
int fds[2];
int fdt[2] = { -1, -1 };
CHECK_OBJ_NOTNULL(p, PROCESS_MAGIC); CHECK_OBJ_NOTNULL(p, PROCESS_MAGIC);
if (p->hasthread) if (p->hasthread)
...@@ -221,44 +343,35 @@ process_start(struct process *p) ...@@ -221,44 +343,35 @@ process_start(struct process *p)
cl = macro_expand(p->vl, p->spec); cl = macro_expand(p->vl, p->spec);
AN(cl); AN(cl);
AZ(pipe(fds));
if (p->log) { AZ(pipe(fd0));
AZ(pipe(fdt)); AZ(pipe(fd1));
out_fd = fdt[1]; AZ(pipe(fd2));
err_fd = fdt[1];
} else {
out_fd = open(p->out, O_WRONLY|O_APPEND);
assert(out_fd >= 0);
err_fd = open(p->err, O_WRONLY|O_APPEND);
assert(err_fd >= 0);
}
p->pid = fork(); p->pid = fork();
assert(p->pid >= 0); assert(p->pid >= 0);
if (p->pid == 0) { if (p->pid == 0) {
assert(dup2(fds[0], 0) == 0); assert(dup2(fd0[0], STDIN_FILENO) == STDIN_FILENO);
assert(dup2(out_fd, 1) == 1); assert(dup2(fd1[1], STDOUT_FILENO) == STDOUT_FILENO);
assert(dup2(err_fd, 2) == 2); assert(dup2(fd2[1], STDERR_FILENO) == STDERR_FILENO);
VSUB_closefrom(STDERR_FILENO + 1); VSUB_closefrom(STDERR_FILENO + 1);
AZ(setpgid(0, 0)); AZ(setpgid(0, 0));
AZ(execl("/bin/sh", "/bin/sh", "-c", VSB_data(cl), AZ(execl("/bin/sh", "/bin/sh", "-c", VSB_data(cl), NULL));
(char *)NULL));
exit(1); exit(1);
} }
vtc_log(p->vl, 3, "PID: %ld", (long)p->pid); vtc_log(p->vl, 3, "PID: %ld", (long)p->pid);
VSB_destroy(&cl);
closefd(&fd0[0]);
closefd(&fd1[1]);
closefd(&fd2[1]);
p->fd_stdin = fd0[1];
p->fd_stdout = fd1[0];
p->fd_stderr = fd2[0];
macro_def(p->vl, p->name, "pid", "%ld", (long)p->pid); macro_def(p->vl, p->name, "pid", "%ld", (long)p->pid);
macro_def(p->vl, p->name, "dir", "%s", p->dir); macro_def(p->vl, p->name, "dir", "%s", p->dir);
macro_def(p->vl, p->name, "out", "%s", p->out); macro_def(p->vl, p->name, "out", "%s", p->out);
macro_def(p->vl, p->name, "err", "%s", p->err); macro_def(p->vl, p->name, "err", "%s", p->err);
closefd(&fds[0]);
p->fd_to = fds[1];
if (p->log) {
closefd(&fdt[1]);
p->fd_from = fdt[0];
} else {
closefd(&out_fd);
closefd(&err_fd);
}
VSB_destroy(&cl);
p->hasthread = 1; p->hasthread = 1;
AZ(pthread_create(&p->tp, NULL, process_thread, p)); AZ(pthread_create(&p->tp, NULL, process_thread, p));
} }
...@@ -330,7 +443,7 @@ process_write(const struct process *p, const char *text) ...@@ -330,7 +443,7 @@ process_write(const struct process *p, const char *text)
len = strlen(text); len = strlen(text);
vtc_log(p->vl, 4, "Writing %d bytes", len); vtc_log(p->vl, 4, "Writing %d bytes", len);
r = write(p->fd_to, text, len); r = write(p->fd_stdin, text, len);
if (r < 0) if (r < 0)
vtc_fatal(p->vl, "Failed to write: %s (%d)", vtc_fatal(p->vl, "Failed to write: %s (%d)",
strerror(errno), errno); strerror(errno), errno);
...@@ -344,8 +457,8 @@ process_close(struct process *p) ...@@ -344,8 +457,8 @@ process_close(struct process *p)
vtc_fatal(p->vl, "Cannot close a non-running process"); vtc_fatal(p->vl, "Cannot close a non-running process");
AZ(pthread_mutex_lock(&p->mtx)); AZ(pthread_mutex_lock(&p->mtx));
if (p->fd_to >= 0) if (p->fd_stdin >= 0)
closefd(&p->fd_to); closefd(&p->fd_stdin);
AZ(pthread_mutex_unlock(&p->mtx)); AZ(pthread_mutex_unlock(&p->mtx));
} }
...@@ -354,8 +467,10 @@ process_close(struct process *p) ...@@ -354,8 +467,10 @@ process_close(struct process *p)
* Run a process in the background with stdout and stderr redirected to * Run a process in the background with stdout and stderr redirected to
* ${pNAME_out} and ${pNAME_err}, both located in ${pNAME_dir}:: * ${pNAME_out} and ${pNAME_err}, both located in ${pNAME_dir}::
* *
* process pNAME SPEC [-log] [-start] [-wait] [-run] [-kill STRING] \ * process pNAME SPEC [-log] [-dump] [-hexdump] [-expect-exit N]
* [-stop] [-write STRING] [-writeln STRING] [-close] * [-start] [-run]
* [-write STRING] [-writeln STRING]
* [-kill STRING] [-stop] [-wait] [-close]
* *
* pNAME * pNAME
* Name of the process. It must start with 'p'. * Name of the process. It must start with 'p'.
...@@ -363,12 +478,21 @@ process_close(struct process *p) ...@@ -363,12 +478,21 @@ process_close(struct process *p)
* SPEC * SPEC
* The command(s) to run in this process. * The command(s) to run in this process.
* *
* \-log * \-hexdump
* Log stdout/stderr with vtc_hexdump(). Must be before -start/-run.
*
* \-dump
* Log stdout/stderr with vtc_dump(). Must be before -start/-run. * Log stdout/stderr with vtc_dump(). Must be before -start/-run.
* *
* \-log
* Log stdout/stderr with VLU/vtc_log(). Must be before -start/-run.
*
* \-start * \-start
* Start the process. * Start the process.
* *
* \-expect-exit N
* Expect exit status N
*
* \-wait * \-wait
* Wait for the process to finish. * Wait for the process to finish.
* *
...@@ -461,6 +585,20 @@ cmd_process(CMD_ARGS) ...@@ -461,6 +585,20 @@ cmd_process(CMD_ARGS)
process_start(p); process_start(p);
continue; continue;
} }
if (!strcmp(*av, "-hexdump")) {
if (p->hasthread)
vtc_fatal(p->vl,
"Cannot dump a running process");
p->log = 3;
continue;
}
if (!strcmp(*av, "-dump")) {
if (p->hasthread)
vtc_fatal(p->vl,
"Cannot dump a running process");
p->log = 2;
continue;
}
if (!strcmp(*av, "-log")) { if (!strcmp(*av, "-log")) {
if (p->hasthread) if (p->hasthread)
vtc_fatal(p->vl, vtc_fatal(p->vl,
...@@ -468,6 +606,11 @@ cmd_process(CMD_ARGS) ...@@ -468,6 +606,11 @@ cmd_process(CMD_ARGS)
p->log = 1; p->log = 1;
continue; continue;
} }
if (!strcmp(*av, "-expect-exit")) {
p->expect_exit = strtoul(av[1], NULL, 0);
av++;
continue;
}
if (!strcmp(*av, "-wait")) { if (!strcmp(*av, "-wait")) {
process_wait(p); process_wait(p);
continue; continue;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment