return test_run_spawned(t);
}
-static int check_activity(int fd, bool activity, const char *path,
- const char *stream)
+#define BUFSZ 4096
+
+enum fd_cmp_type {
+ FD_CMP_MONITOR,
+ FD_CMP_OUT,
+ FD_CMP_ERR,
+ FD_CMP_MAX = FD_CMP_ERR,
+};
+
+struct fd_cmp {
+ enum fd_cmp_type type;
+ int fd;
+ int fd_match;
+ bool activity;
+ const char *path;
+ const char *name;
+ char buf[BUFSZ];
+ char buf_match[BUFSZ];
+ unsigned int head;
+ unsigned int head_match;
+};
+
+static int fd_cmp_check_activity(struct fd_cmp *fd_cmp)
{
struct stat st;
/* not monitoring or monitoring and it has activity */
- if (fd < 0 || activity)
+ if (fd_cmp == NULL || fd_cmp->fd < 0 || fd_cmp->activity)
return 0;
/* monitoring, there was no activity and size matches */
- if (stat(path, &st) == 0 && st.st_size == 0)
+ if (stat(fd_cmp->path, &st) == 0 && st.st_size == 0)
return 0;
- ERR("Expecting output on %s, but test didn't produce any\n", stream);
+ ERR("Expecting output on %s, but test didn't produce any\n",
+ fd_cmp->name);
return -1;
}
-#define BUFSZ 4096
-struct buffer {
- char buf[BUFSZ];
- unsigned int head;
-};
+static bool fd_cmp_is_active(struct fd_cmp *fd_cmp)
+{
+ return fd_cmp->fd != -1;
+}
+
+static int fd_cmp_open_monitor(struct fd_cmp *fd_cmp, int fd, int fd_ep)
+{
+ struct epoll_event ep = {};
+
+ ep.events = EPOLLHUP;
+ ep.data.ptr = fd_cmp;
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd, &ep) < 0) {
+ ERR("could not add monitor fd to epoll: %m\n");
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int fd_cmp_open_std(struct fd_cmp *fd_cmp,
+ const char *fn, int fd, int fd_ep)
+{
+ struct epoll_event ep = {};
+ int fd_match;
+
+ fd_match = open(fn, O_RDONLY);
+ if (fd_match < 0) {
+ ERR("could not open %s for read: %m\n", fn);
+ return -errno;
+ }
+ ep.events = EPOLLIN;
+ ep.data.ptr = fd_cmp;
+ if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd, &ep) < 0) {
+ ERR("could not add fd to epoll: %m\n");
+ close(fd_match);
+ return -errno;
+ }
+
+ return fd_match;
+}
+
+/* opens output file AND adds descriptor to epoll */
+static int fd_cmp_open(struct fd_cmp **fd_cmp_out,
+ enum fd_cmp_type type, const char *fn, int fd,
+ int fd_ep)
+{
+ int err = 0;
+ struct fd_cmp *fd_cmp;
+
+ fd_cmp = calloc(1, sizeof(*fd_cmp));
+ if (fd_cmp == NULL) {
+ ERR("could not allocate fd_cmp\n");
+ return -ENOMEM;
+ }
+
+ switch (type) {
+ case FD_CMP_MONITOR:
+ err = fd_cmp_open_monitor(fd_cmp, fd, fd_ep);
+ break;
+ case FD_CMP_OUT:
+ fd_cmp->name = "STDOUT";
+ err = fd_cmp_open_std(fd_cmp, fn, fd, fd_ep);
+ break;
+ case FD_CMP_ERR:
+ fd_cmp->name = "STDERR";
+ err = fd_cmp_open_std(fd_cmp, fn, fd, fd_ep);
+ break;
+ default:
+ ERR("unknown fd type %d\n", type);
+ err = -1;
+ }
+
+ if (err < 0) {
+ free(fd_cmp);
+ return err;
+ }
+
+ fd_cmp->fd_match = err;
+ fd_cmp->fd = fd;
+ fd_cmp->type = type;
+ fd_cmp->path = fn;
+
+ *fd_cmp_out = fd_cmp;
+ return 0;
+}
+
+static int fd_cmp_check_ev_in(struct fd_cmp *fd_cmp)
+{
+ if (fd_cmp->type == FD_CMP_MONITOR) {
+ ERR("Unexpected activity on monitor pipe\n");
+ return -EINVAL;
+ }
+ fd_cmp->activity = true;
+
+ return 0;
+}
+
+static void fd_cmp_delete_ep(struct fd_cmp *fd_cmp, int fd_ep)
+{
+ if (epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd_cmp->fd, NULL) < 0) {
+ ERR("could not remove fd %d from epoll: %m\n", fd_cmp->fd);
+ }
+ fd_cmp->fd = -1;
+}
+static void fd_cmp_close(struct fd_cmp *fd_cmp)
+{
+ if (fd_cmp == NULL)
+ return;
-static bool cmpbuf_regex_one(const char *pattern, const char *s)
+ if (fd_cmp->fd >= 0)
+ close(fd_cmp->fd);
+ free(fd_cmp);
+}
+
+static bool fd_cmp_regex_one(const char *pattern, const char *s)
{
_cleanup_(regfree) regex_t re = { };
* read fd and fd_match, checking the first matches the regex of the second,
* line by line
*/
-static bool cmpbuf_regex(const struct test *t, const char *prefix,
- int fd, int fd_match, struct buffer *buf,
- struct buffer *buf_match)
+static bool fd_cmp_regex(struct fd_cmp *fd_cmp, const struct test *t)
{
char *p, *p_match;
int done = 0, done_match = 0, r;
- if (buf->head >= sizeof(buf->buf)) {
- ERR("Read %zu bytes without a newline\n", sizeof(buf->buf));
- ERR("output: %.*s", (int)sizeof(buf->buf), buf->buf);
+ if (fd_cmp->head >= sizeof(fd_cmp->buf)) {
+ ERR("Read %zu bytes without a newline\n", sizeof(fd_cmp->buf));
+ ERR("output: %.*s", (int)sizeof(fd_cmp->buf), fd_cmp->buf);
return false;
}
- r = read(fd, buf->buf + buf->head, sizeof(buf->buf) - buf->head);
+ r = read(fd_cmp->fd, fd_cmp->buf + fd_cmp->head,
+ sizeof(fd_cmp->buf) - fd_cmp->head);
if (r <= 0)
return true;
- buf->head += r;
+ fd_cmp->head += r;
/*
* Process as many lines as read from fd and that fits in the buffer -
* get the same amount from fd_match
*/
for (;;) {
- p = memchr(buf->buf + done, '\n', buf->head - done);
+ p = memchr(fd_cmp->buf + done, '\n', fd_cmp->head - done);
if (!p)
break;
*p = '\0';
- p_match = memchr(buf_match->buf + done_match, '\n',
- buf_match->head - done_match);
+ p_match = memchr(fd_cmp->buf_match + done_match, '\n',
+ fd_cmp->head_match - done_match);
if (!p_match) {
- if (buf_match->head >= sizeof(buf_match->buf)) {
- ERR("Read %zu bytes without a match\n", sizeof(buf_match->buf));
- ERR("output: %.*s", (int)sizeof(buf_match->buf), buf_match->buf);
+ if (fd_cmp->head_match >= sizeof(fd_cmp->buf_match)) {
+ ERR("Read %zu bytes without a match\n", sizeof(fd_cmp->buf_match));
+ ERR("output: %.*s", (int)sizeof(fd_cmp->buf_match), fd_cmp->buf_match);
return false;
}
/* pump more data from file */
- r = read(fd_match, buf_match->buf + buf_match->head,
- sizeof(buf_match->buf) - buf_match->head);
+ r = read(fd_cmp->fd_match, fd_cmp->buf_match + fd_cmp->head_match,
+ sizeof(fd_cmp->buf_match) - fd_cmp->head_match);
if (r <= 0) {
- ERR("could not read match fd %d\n", fd_match);
+ ERR("could not read match fd %d\n", fd_cmp->fd_match);
return false;
}
- buf_match->head += r;
- p_match = memchr(buf_match->buf + done_match, '\n',
- buf_match->head - done_match);
+ fd_cmp->head_match += r;
+ p_match = memchr(fd_cmp->buf_match + done_match, '\n',
+ fd_cmp->head_match - done_match);
if (!p_match) {
- ERR("could not find match line from fd %d\n", fd_match);
+ ERR("could not find match line from fd %d\n", fd_cmp->fd_match);
return false;
}
}
*p_match = '\0';
- if (!cmpbuf_regex_one(buf_match->buf + done_match, buf->buf + done)) {
- ERR("Output does not match pattern on %s:\n", prefix);
- ERR("pattern: %s\n", buf_match->buf + done_match);
- ERR("output : %s\n", buf->buf + done);
+ if (!fd_cmp_regex_one(fd_cmp->buf_match + done_match, fd_cmp->buf + done)) {
+ ERR("Output does not match pattern on %s:\n", fd_cmp->name);
+ ERR("pattern: %s\n", fd_cmp->buf_match + done_match);
+ ERR("output : %s\n", fd_cmp->buf + done);
return false;
}
- done = p - buf->buf + 1;
- done_match = p_match - buf_match->buf + 1;
+ done = p - fd_cmp->buf + 1;
+ done_match = p_match - fd_cmp->buf_match + 1;
}
/*
* buffer by memmoving the remaining bytes up to the beginning
*/
if (done) {
- if (buf->head - done)
- memmove(buf->buf, buf->buf + done, buf->head - done);
- buf->head -= done;
+ if (fd_cmp->head - done)
+ memmove(fd_cmp->buf, fd_cmp->buf + done, fd_cmp->head - done);
+ fd_cmp->head -= done;
}
if (done_match) {
- if (buf_match->head - done_match)
- memmove(buf_match->buf, buf_match->buf + done_match,
- buf_match->head - done_match);
- buf_match->head -= done_match;
+ if (fd_cmp->head_match - done_match)
+ memmove(fd_cmp->buf_match, fd_cmp->buf_match + done_match,
+ fd_cmp->head_match - done_match);
+ fd_cmp->head_match -= done_match;
}
return true;
}
/* read fd and fd_match, checking they match exactly */
-static bool cmpbuf_exact(const struct test *t, const char *prefix,
- int fd, int fd_match, struct buffer *buf,
- struct buffer *buf_match)
+static bool fd_cmp_exact(struct fd_cmp *fd_cmp, const struct test *t)
{
int r, rmatch, done = 0;
- r = read(fd, buf->buf, sizeof(buf->buf) - 1);
+ r = read(fd_cmp->fd, fd_cmp->buf, sizeof(fd_cmp->buf) - 1);
if (r <= 0)
/* try again later */
return true;
/* read as much data from fd_match as we read from fd */
for (;;) {
- rmatch = read(fd_match, buf_match->buf + done, r - done);
+ rmatch = read(fd_cmp->fd_match, fd_cmp->buf_match + done, r - done);
if (rmatch == 0)
break;
if (rmatch < 0) {
if (errno == EINTR)
continue;
- ERR("could not read match fd %d\n", fd_match);
+ ERR("could not read match fd %d\n", fd_cmp->fd_match);
return false;
}
done += rmatch;
}
- buf->buf[r] = '\0';
- buf_match->buf[r] = '\0';
+ fd_cmp->buf[r] = '\0';
+ fd_cmp->buf_match[r] = '\0';
if (t->print_outputs)
- printf("%s: %s\n", prefix, buf->buf);
+ printf("%s: %s\n", fd_cmp->name, fd_cmp->buf);
- if (!streq(buf->buf, buf_match->buf)) {
- ERR("Outputs do not match on %s:\n", prefix);
- ERR("correct:\n%s\n", buf_match->buf);
- ERR("wrong:\n%s\n", buf->buf);
+ if (!streq(fd_cmp->buf, fd_cmp->buf_match)) {
+ ERR("Outputs do not match on %s:\n", fd_cmp->name);
+ ERR("correct:\n%s\n", fd_cmp->buf_match);
+ ERR("wrong:\n%s\n", fd_cmp->buf);
return false;
}
int fdout, int fderr, int fdmonitor,
pid_t child)
{
- struct epoll_event ep_outpipe, ep_errpipe, ep_monitor;
- int err, fd_ep, fd_matchout = -1, fd_matcherr = -1;
- bool fd_activityout = false, fd_activityerr = false;
+ int err, fd_ep;
unsigned long long end_usec, start_usec;
- _cleanup_free_ struct buffer *buf_out = NULL, *buf_err = NULL;
+ struct fd_cmp *fd_cmp_out = NULL;
+ struct fd_cmp *fd_cmp_err = NULL;
+ struct fd_cmp *fd_cmp_monitor = NULL;
int n_fd = 0;
fd_ep = epoll_create1(EPOLL_CLOEXEC);
}
if (t->output.out != NULL) {
- fd_matchout = open(t->output.out, O_RDONLY);
- if (fd_matchout < 0) {
- err = -errno;
- ERR("could not open %s for read: %m\n",
- t->output.out);
+ err = fd_cmp_open(&fd_cmp_out,
+ FD_CMP_OUT, t->output.out, fdout, fd_ep);
+ if (err < 0)
goto out;
- }
- memset(&ep_outpipe, 0, sizeof(struct epoll_event));
- ep_outpipe.events = EPOLLIN;
- ep_outpipe.data.ptr = &fdout;
- if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fdout, &ep_outpipe) < 0) {
- err = -errno;
- ERR("could not add fd to epoll: %m\n");
- goto out;
- }
- buf_out = calloc(2, sizeof(*buf_out));
n_fd++;
- } else
- fdout = -1;
+ }
if (t->output.err != NULL) {
- fd_matcherr = open(t->output.err, O_RDONLY);
- if (fd_matcherr < 0) {
- err = -errno;
- ERR("could not open %s for read: %m\n",
- t->output.err);
- goto out;
-
- }
- memset(&ep_errpipe, 0, sizeof(struct epoll_event));
- ep_errpipe.events = EPOLLIN;
- ep_errpipe.data.ptr = &fderr;
- if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fderr, &ep_errpipe) < 0) {
- err = -errno;
- ERR("could not add fd to epoll: %m\n");
+ err = fd_cmp_open(&fd_cmp_err,
+ FD_CMP_ERR, t->output.err, fderr, fd_ep);
+ if (err < 0)
goto out;
- }
- buf_err = calloc(2, sizeof(*buf_err));
n_fd++;
- } else
- fderr = -1;
-
- memset(&ep_monitor, 0, sizeof(struct epoll_event));
- ep_monitor.events = EPOLLHUP;
- ep_monitor.data.ptr = &fdmonitor;
- if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fdmonitor, &ep_monitor) < 0) {
- err = -errno;
- ERR("could not add monitor fd to epoll: %m\n");
- goto out;
}
+
+ err = fd_cmp_open(&fd_cmp_monitor, FD_CMP_MONITOR, NULL, fdmonitor, fd_ep);
+ if (err < 0)
+ goto out;
n_fd++;
start_usec = now_usec();
}
for (i = 0; i < fdcount; i++) {
- int fd = *(int *)ev[i].data.ptr;
+ struct fd_cmp *fd_cmp = ev[i].data.ptr;
bool ret;
if (ev[i].events & EPOLLIN) {
- int fd_match;
- struct buffer *buf, *buf_match;
- const char *prefix;
-
- if (fd == fdout) {
- fd_match = fd_matchout;
- buf = buf_out;
- fd_activityout = true;
- prefix = "STDOUT";
- } else if (fd == fderr) {
- fd_match = fd_matcherr;
- buf = buf_err;
- fd_activityerr = true;
- prefix = "STDERR";
- } else {
- ERR("Unexpected activity on monitor pipe\n");
- err = -EINVAL;
+ err = fd_cmp_check_ev_in(fd_cmp);
+ if (err < 0)
goto out;
- }
-
- buf_match = buf + 1;
if (t->output.regex)
- ret = cmpbuf_regex(t, prefix, fd, fd_match,
- buf, buf_match);
+ ret = fd_cmp_regex(fd_cmp, t);
else
- ret = cmpbuf_exact(t, prefix, fd, fd_match,
- buf, buf_match);
+ ret = fd_cmp_exact(fd_cmp, t);
if (!ret) {
err = -1;
goto out;
}
} else if (ev[i].events & EPOLLHUP) {
- if (epoll_ctl(fd_ep, EPOLL_CTL_DEL, fd, NULL) < 0) {
- ERR("could not remove fd %d from epoll: %m\n", fd);
- }
- *(int *)ev[i].data.ptr = -1;
+ fd_cmp_delete_ep(fd_cmp, fd_ep);
n_fd--;
}
}
}
- err = check_activity(fd_matchout, fd_activityout, t->output.out, "stdout");
- err |= check_activity(fd_matcherr, fd_activityerr, t->output.err, "stderr");
+ err = fd_cmp_check_activity(fd_cmp_out);
+ err |= fd_cmp_check_activity(fd_cmp_err);
- if (err == 0 && fdmonitor >= 0) {
+ if (err == 0 && fd_cmp_is_active(fd_cmp_monitor)) {
err = -EINVAL;
ERR("Test '%s' timed out, killing %d\n", t->name, child);
kill(child, SIGKILL);
}
out:
- if (fd_matchout >= 0)
- close(fd_matchout);
- if (fd_matcherr >= 0)
- close(fd_matcherr);
- if (fd_ep >= 0)
- close(fd_ep);
+ fd_cmp_close(fd_cmp_out);
+ fd_cmp_close(fd_cmp_err);
+ fd_cmp_close(fd_cmp_monitor);
+ close(fd_ep);
+
return err == 0;
}