aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runcap.c4
-rw-r--r--t/rt.c227
2 files changed, 230 insertions, 1 deletions
diff --git a/runcap.c b/runcap.c
index 648fd84..9dcefa1 100644
--- a/runcap.c
+++ b/runcap.c
@@ -194,7 +194,7 @@ timeval_after(struct timeval const *a, struct timeval const *b)
if (a->tv_sec == b->tv_sec)
return a->tv_usec < b->tv_usec;
else
- return a->tv_sec < b->tv_usec;
+ return a->tv_sec < b->tv_sec;
}
static inline struct timeval
@@ -343,6 +343,8 @@ runcap_loop(struct runcap *rc)
gettimeofday(&now, NULL);
tv = timeval_diff(&finish, &now);
if (!timeval_after(&now, &finish)) {
+ if (rc->rc_pid == (time_t) -1)
+ break;
kill(rc->rc_pid, SIGKILL);
continue;
}
diff --git a/t/rt.c b/t/rt.c
new file mode 100644
index 0000000..345ab8b
--- /dev/null
+++ b/t/rt.c
@@ -0,0 +1,227 @@
+/* runcap - run program and capture its output
+ Copyright (C) 2017 Sergey Poznyakoff
+
+ Runcap is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the
+ Free Software Foundation; either version 3 of the License, or (at your
+ option) any later version.
+
+ Runcap is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with Runcap. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include "runcap.h"
+
+static char *progname;
+
+void
+error(char const *fmt, ...)
+{
+ va_list ap;
+
+ fprintf(stderr, "%s: ", progname);
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+}
+
+void
+usage(int code)
+{
+ FILE *fp = code ? stderr : stdout;
+ fprintf(fp, "%s [OPTIONS] COMMAND [ARG...]\n", progname);
+ fprintf(fp, "OPTIONS are:\n\n");
+ fprintf(fp, " -S all|stderr|stdout selects capture for the next -m or -s option\n");
+ fprintf(fp, " -f FILE reads stdin from FILE\n");
+ fprintf(fp, " -i inline read (use before -f)\n");
+ fprintf(fp, " -m monitors each line recevied from the program (see -S)\n");
+ fprintf(fp, " -p PROGNAME sets program name to use instead of COMMAND\n");
+ fprintf(fp, " -s SIZE sets capture size (see -S)\n");
+ fprintf(fp, " -t SECONDS sets execution timeout\n");
+ fputc('\n', fp);
+ exit(code);
+}
+
+#define WA_STDOUT 0x01
+#define WA_STDERR 0x02
+#define WA_ALL (WA_STDOUT|WA_STDERR)
+
+static int
+whatarg(char const *arg)
+{
+ if (strcmp(arg, "all") == 0)
+ return WA_ALL;
+ else if (strcmp(arg, "stdout") == 0)
+ return WA_STDOUT;
+ else if (strcmp(arg, "stderr") == 0)
+ return WA_STDERR;
+ error("unreconginzed option argument: %s", arg);
+ exit(1);
+}
+
+static void
+linemon(const char *ptr, size_t len, void *data)
+{
+ fprintf(stdout, "[%s]: ", (char*) data);
+ fwrite(ptr, len-1, 1, stdout);
+ fputc('\n', stdout);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct runcap rc;
+ int rcf = 0;
+ int what = WA_ALL;
+ int inopt = 0;
+
+ int c;
+ int fd;
+ unsigned long size;
+
+ progname = strrchr(argv[0], '/');
+ if (progname)
+ progname++;
+ else
+ progname = argv[0];
+ while ((c = getopt(argc, argv, "?f:imp:S:s:t:")) != EOF) {
+ switch (c) {
+ case 'f':
+ fd = open(optarg, O_RDONLY);
+ if (fd == -1) {
+ error("can't open \"%s\": %s",
+ optarg, strerror(errno));
+ exit(1);
+ }
+ if (inopt) {
+ struct stat st;
+ char *buffer;
+ if (fstat(fd, &st)) {
+ error("can't fstat \"%s\": %s",
+ optarg, strerror(errno));
+ exit(1);
+ }
+ size = st.st_size;
+ buffer = malloc(size + 1);
+ if (!buffer) {
+ error("not enough memory");
+ exit(1);
+ }
+
+ rc.rc_cap[RUNCAP_STDIN].fc_size = size;
+ rc.rc_cap[RUNCAP_STDIN].fc_base = buffer;
+ while (size) {
+ ssize_t n = read(fd, buffer, size);
+ if (n < 0) {
+ error("error reading from \"%s\": %s",
+ optarg, strerror(errno));
+ exit(1);
+ }
+ if (n == 0) {
+ error("unexpected eof on \"%s\"",
+ optarg);
+ exit(1);
+ }
+ size -= n;
+ buffer += n;
+ }
+ close(fd);
+ rc.rc_cap[RUNCAP_STDIN].fc_fd = -1;
+ } else {
+ rc.rc_cap[RUNCAP_STDIN].fc_fd = fd;
+ rc.rc_cap[RUNCAP_STDIN].fc_size = 0;
+ }
+ rcf |= RCF_STDIN;
+ break;
+ case 'i':
+ inopt = 1;
+ break;
+ case 'S':
+ what = whatarg(optarg);
+ break;
+ case 'm':
+ if (what & WA_STDOUT) {
+ rc.rc_cap[RUNCAP_STDOUT].fc_linemon = linemon;
+ rc.rc_cap[RUNCAP_STDOUT].fc_monarg = "stdout";
+ rcf |= RCF_STDOUT_LINEMON;
+ }
+ if (what & WA_STDERR) {
+ rc.rc_cap[RUNCAP_STDERR].fc_linemon = linemon;
+ rc.rc_cap[RUNCAP_STDERR].fc_monarg = "stderr";
+ rcf |= RCF_STDERR_LINEMON;
+ }
+ break;
+ case 'p':
+ rc.rc_program = optarg;
+ rcf |= RCF_PROGRAM;
+ break;
+ case 's':
+ size = strtoul(optarg, NULL, 10);
+ if (what & WA_STDOUT) {
+ rc.rc_cap[RUNCAP_STDOUT].fc_size = size;
+ rcf |= RCF_STDOUT_SIZE;
+ }
+ if (what & WA_STDERR) {
+ rc.rc_cap[RUNCAP_STDERR].fc_size = size;
+ rcf |= RCF_STDERR_SIZE;
+ }
+ break;
+ case 't':
+ rc.rc_timeout = strtoul(optarg, NULL, 10);
+ rcf |= RCF_TIMEOUT;
+ break;
+ default:
+ usage(optopt != '?');
+ }
+ }
+
+ if (argc == optind) {
+ static char *xargv[2];
+ if (rcf & RCF_PROGRAM) {
+ xargv[0] = rc.rc_program;
+ xargv[1] = NULL;
+ rc.rc_argv = xargv;
+ } else
+ usage(1);
+ } else
+ rc.rc_argv = argv + optind;
+
+ c = runcap(&rc, rcf);
+
+ printf("res=%d\n", c);
+ if (c) {
+ error("system error: %s", strerror(rc.rc_errno));
+ exit(1);
+ }
+
+ if (WIFEXITED(rc.rc_status)) {
+ printf("exit code: %d\n", WEXITSTATUS(rc.rc_status));
+ } else if (WIFSIGNALED(rc.rc_status)) {
+ printf("got signal: %d\n", WTERMSIG(rc.rc_status));
+ } else if (WIFSTOPPED(rc.rc_status)) {
+ printf("stopped by signal %d\n", WSTOPSIG(rc.rc_status));
+ } else
+ printf("unrecognized status: %d\n", rc.rc_status);
+
+ printf("stdout: %zu lines, %zu bytes\n",
+ rc.rc_cap[RUNCAP_STDOUT].fc_nlines,
+ rc.rc_cap[RUNCAP_STDOUT].fc_leng);
+ printf("stderr: %zu lines, %zu bytes\n",
+ rc.rc_cap[RUNCAP_STDERR].fc_nlines,
+ rc.rc_cap[RUNCAP_STDERR].fc_leng);
+
+ return 0;
+}

Return to:

Send suggestions and report system problems to the System administrator.