aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runcap.325
-rw-r--r--runcap.c7
-rw-r--r--runcap.h2
-rw-r--r--t/Makefile.am3
-rw-r--r--t/env.at28
-rw-r--r--t/rt.c142
-rw-r--r--t/testsuite.at2
7 files changed, 188 insertions, 21 deletions
diff --git a/runcap.3 b/runcap.3
index 76dc6cc..2bd3648 100644
--- a/runcap.3
+++ b/runcap.3
@@ -13,7 +13,7 @@
.\"
.\" You should have received a copy of the GNU General Public License
.\" along with runcap. If not, see <http://www.gnu.org/licenses/>.
-.TH RUNCAP 2 "January 30, 2020" "RUNCAP" "User Commands"
+.TH RUNCAP 2 "March 14, 2024" "RUNCAP" "User Commands"
.SH NAME
runcap \- run external process and capture its stdout and stderr
.SH SYNOPSIS
@@ -67,13 +67,14 @@ The \fBstruct runcap\fR is defined as follows:
.nf
struct runcap
{
- char *rc_program; /* [\fIIN\fR] (Path)name of the program to run */
- char **rc_argv; /* [\fIIN\fR] Argument vector */
- unsigned rc_timeout; /* [\fIIN\fR] Execution timeout */
+ char *rc_program; /* [\fIIN\fR] (Path)name of the program to run. */
+ char **rc_argv; /* [\fIIN\fR] Argument vector. */
+ char **rc_env; /* [\fIIN\fR] Environment variables. */
+ unsigned rc_timeout; /* [\fIIN\fR] Execution timeout. */
struct stream_capture rc_cap[3];
- pid_t rc_pid; /* [\fIOUT\fR] PID of the process */
- int rc_status; /* [\fIOUT\fR] Termination status */
- int rc_errno; /* [\fIOUT\fR] System error code */
+ pid_t rc_pid; /* [\fIOUT\fR] PID of the process. */
+ int rc_status; /* [\fIOUT\fR] Termination status. */
+ int rc_errno; /* [\fIOUT\fR] System error code. */
};
.fi
.in
@@ -96,6 +97,14 @@ Time to wait for the program termination, in seconds. If initialized,
the \fBRCF_TIMEOUT\fR bit must be set in \fIflags\fR. If not set,
.B runcap
will wait indefinitely.
+.TP
+.B rc_env
+A
+.BR NULL -terminated
+array of environment variables. Each element (except the last
+.BR NULL )
+has the form "\fIname\fR=\fIvalue\fR". If initialized, the
+\fBRCF_ENV\fR bit must be set in \fIflags\fR.
.PP
The three streams associated with the running command are described by
the
@@ -476,7 +485,7 @@ archive(void)
.SH AUTHORS
Sergey Poznyakoff
.SH COPYRIGHT
-Copyright \(co 2017--2020 Sergey Poznyakoff
+Copyright \(co 2017--2024 Sergey Poznyakoff
.br
.na
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
diff --git a/runcap.c b/runcap.c
index de64f70..56f6974 100644
--- a/runcap.c
+++ b/runcap.c
@@ -260,6 +260,8 @@ timeval_diff(struct timeval const *a, struct timeval const *b)
return res;
}
+extern char **environ;
+
static int
runcap_start(struct runcap *rc)
{
@@ -306,6 +308,8 @@ runcap_start(struct runcap *rc)
i--;
}
+ if (rc->rc_env)
+ environ = rc->rc_env;
execvp(rc->rc_program ? rc->rc_program : rc->rc_argv[0],
rc->rc_argv);
_exit(127);
@@ -479,7 +483,8 @@ runcap_init(struct runcap *rc, int flags)
rc->rc_program = NULL;
if (!(flags & RCF_TIMEOUT))
rc->rc_timeout = 0;
-
+ if (!(flags & RCF_ENV))
+ rc->rc_env = NULL;
if (flags & RCF_STDIN) {
if (rc->rc_cap[RUNCAP_STDIN].sc_size > 0
&& rc->rc_cap[RUNCAP_STDIN].sc_fd != -1) {
diff --git a/runcap.h b/runcap.h
index 1fbb8f7..011cb06 100644
--- a/runcap.h
+++ b/runcap.h
@@ -47,6 +47,7 @@ struct runcap
{
char *rc_program; /* [IN] (Path)name of the program to run */
char **rc_argv; /* [IN] Argument vector */
+ char **rc_env; /* [IN] Environment variables */
unsigned rc_timeout; /* [IN] Execution timeout */
struct stream_capture rc_cap[RUNCAP_NBUF];
/* rc_cap[RUNCAP_STDIN] - [IN], rest - [OUT] */
@@ -58,6 +59,7 @@ struct runcap
#define RCF_PROGRAM 0x0001 /* rc_program is set */
#define RCF_TIMEOUT 0x0002 /* rc_timeout is set */
#define RCF_STDIN 0x0004 /* rc_cap[RUNCAP_STDIN] is set */
+#define RCF_ENV 0x0008 /* rc_env is set */
#define RCF_SC_SIZE 0x1 /* sc_size is set */
#define RCF_SC_LINEMON 0x2 /* sc_linemon is set*/
diff --git a/t/Makefile.am b/t/Makefile.am
index 9e958ad..1905212 100644
--- a/t/Makefile.am
+++ b/t/Makefile.am
@@ -55,7 +55,8 @@ TESTSUITE_AT = \
nocap.at\
redirect.at\
seek00.at\
- seek01.at
+ seek01.at\
+ env.at
# Add more files here
TESTSUITE = $(srcdir)/testsuite
diff --git a/t/env.at b/t/env.at
new file mode 100644
index 0000000..42883a1
--- /dev/null
+++ b/t/env.at
@@ -0,0 +1,28 @@
+# Testsuite for runcap - run program and capture its output -*- autotest -*-
+# Copyright (C) 2024 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/>.
+AT_SETUP([environment modifications])
+AT_KEYWORDS([env environ])
+AT_CHECK([rt -n stdout -e FOO=bar -- /bin/sh -c 'echo $FOO'],
+[0],
+[res=0
+exit code: 0
+stdout: 1 lines, 4 bytes
+stderr: 0 lines, 0 bytes
+stdout listing:
+ 1: bar
+stdout listing ends
+])
+AT_CLEANUP
diff --git a/t/rt.c b/t/rt.c
index 1ecdc3a..bcb49a4 100644
--- a/t/rt.c
+++ b/t/rt.c
@@ -25,6 +25,7 @@
#include <sys/wait.h>
#include <errno.h>
#include <inttypes.h>
+#include <assert.h>
#include "runcap.h"
static char *progname;
@@ -49,6 +50,10 @@ usage(int code)
fprintf(fp, "tests the runcap library\n");
fprintf(fp, "OPTIONS are:\n\n");
fprintf(fp, " -S all|stderr|stdout selects capture for the next -m, -N, or -s option\n");
+ fprintf(fp, " -e VAR=NAME set environment variable\n");
+ fprintf(fp, " -e -VAR unset environment variable\n");
+ fprintf(fp, " -e - clear environment (except for PATH,\n");
+ fprintf(fp, " HOME, and LOGNAME)\n");
fprintf(fp, " -f FILE reads stdin from FILE\n");
fprintf(fp, " -i inline read (use before -f)\n");
fprintf(fp, " -N disable capturing\n");
@@ -234,11 +239,9 @@ readreq_do(struct runcap *rc, struct readreq *req)
if (req->full) {
char *buf = malloc(req->count);
ssize_t n;
-
- if (!buf) {
- perror("malloc");
- abort();
- }
+
+ assert(buf != NULL);
+
n = runcap_read(rc, req->what, buf, req->count);
if (n < 0) {
perror("runcap_read");
@@ -279,6 +282,123 @@ open_outfile(char *file, int stream, struct runcap *rc, int *flags)
*flags |= RCF_SC_TO_FLAG(RCF_SC_STORFD, stream);
}
+static int
+getenvind(char **env, char const *name)
+{
+ size_t i;
+ for (i = 0; env[i]; i++) {
+ char const *p;
+ char *q;
+
+ for (p = name, q = env[i]; *p == *q; p++, q++)
+ ;
+ if (*p == 0 && *q == '=') {
+ return i;
+ }
+ }
+ return -1;
+}
+
+char **
+envdup(char **env)
+{
+ int i;
+ char **new_env;
+
+ for (i = 0; env[i]; i++)
+ ;
+
+ new_env = calloc(i+1, sizeof(env[0]));
+ assert(new_env != NULL);
+
+ for (i = 0; env[i]; i++) {
+ new_env[i] = strdup(env[i]);
+ assert(new_env[i] != NULL);
+ }
+ new_env[i] = NULL;
+
+ return new_env;
+}
+
+char **envupdate(char **, char *);
+
+char **
+envclear(char **env)
+{
+ int i, j;
+ static char *keep[] = {
+ "PATH",
+ "HOME",
+ "LOGNAME",
+ NULL
+ };
+ char **new_env = calloc(1, sizeof(new_env[0]));
+ assert(new_env != NULL);
+ for (i = 0; keep[i]; i++) {
+ j = getenvind(env, keep[i]);
+ if (j != -1)
+ new_env = envupdate(new_env, env[j]);
+ }
+ for (i = 0; env[i]; i++)
+ free(env[i]);
+ free(env);
+ return new_env;
+}
+
+char **
+envappend(char **env, char *arg)
+{
+ int i;
+
+ for (i = 0; env[i]; i++)
+ ;
+ env = realloc(env, (i+2) * sizeof(env[0]));
+ assert(env != NULL);
+ env[i] = strdup(arg);
+ assert(env[i] != NULL);
+ env[i+1] = NULL;
+ return env;
+}
+
+char **
+envdelete(char **env, char *arg)
+{
+ int i = getenvind(env, arg);
+ if (i != -1) {
+ int n;
+ for (n = 0; env[n]; n++)
+ ;
+ free(env[i]);
+ memmove(env + i, env + i + 1, (n - i) * sizeof(env[0]));
+ }
+ return env;
+}
+
+extern char **environ;
+
+char **
+envupdate(char **env, char *arg)
+{
+ if (env == NULL)
+ env = envdup(environ);
+ if (*arg == '-') {
+ if (arg[1] == 0)
+ env = envclear(env);
+ else
+ env = envdelete(env, arg+1);
+ } else {
+ int i = getenvind(env, arg);
+ if (i == -1)
+ env = envappend(env, arg);
+ else {
+ free(env[i]);
+ env[i] = strdup(arg);
+ assert(env[i] != NULL);
+ }
+ }
+ return env;
+}
+
int
main(int argc, char **argv)
{
@@ -306,8 +426,13 @@ main(int argc, char **argv)
progname++;
else
progname = argv[0];
- while ((c = getopt(argc, argv, "?f:iNn:mo:p:r:S:s:t:")) != EOF) {
+ memset(&rc, 0, sizeof(rc));
+ while ((c = getopt(argc, argv, "?e:f:iNn:mo:p:r:S:s:t:")) != EOF) {
switch (c) {
+ case 'e':
+ rc.rc_env = envupdate(rc.rc_env, optarg);
+ rcf |= RCF_ENV;
+ break;
case 'f':
fd = open(optarg, O_RDONLY);
if (fd == -1) {
@@ -325,10 +450,7 @@ main(int argc, char **argv)
}
size = st.st_size;
buffer = malloc(size + 1);
- if (!buffer) {
- error("not enough memory");
- exit(1);
- }
+ assert(buffer != NULL);
rc.rc_cap[RUNCAP_STDIN].sc_size = size;
rc.rc_cap[RUNCAP_STDIN].sc_base = buffer;
diff --git a/t/testsuite.at b/t/testsuite.at
index a3dd7c9..bbce1ff 100644
--- a/t/testsuite.at
+++ b/t/testsuite.at
@@ -32,4 +32,4 @@ m4_include([seek00.at])
m4_include([seek01.at])
m4_include([read.at])
m4_include([redirect.at])
-
+m4_include([env.at])

Return to:

Send suggestions and report system problems to the System administrator.