changeset 4549:dfe9f9f1c5f2 draft

Merge branch 'memstream'
author Roy Marples <roy@marples.name>
date Thu, 04 Jul 2019 12:22:46 +0100
parents c7df03794de3 (current diff) 331b92976059 (diff)
children 1b9e9a69b187
files configure src/arp.c src/auth.c src/common.c src/common.h src/dhcp-common.c src/dhcp-common.h src/dhcp.c src/dhcp.h src/dhcp6.c src/dhcp6.h src/dhcpcd.c src/dhcpcd.h src/ipv4.c src/ipv4ll.c src/ipv4ll.h src/ipv6.c src/ipv6.h src/ipv6nd.c src/ipv6nd.h src/script.c src/script.h
diffstat 22 files changed, 556 insertions(+), 963 deletions(-) [+]
line wrap: on
line diff
--- a/configure	Thu Jul 04 12:18:28 2019 +0100
+++ b/configure	Thu Jul 04 12:22:46 2019 +0100
@@ -15,6 +15,7 @@
 CLOSEFROM=
 RBTREE=
 CONSTTIME_MEMEQUAL=
+OPEN_MEMSTREAM=
 STRLCPY=
 UDEV=
 OS=
@@ -741,29 +742,25 @@
 	echo "#include			\"compat/arc4random_uniform.h\"" >>$CONFIG_H
 fi
 
-
-if [ -z "$STRLCPY" ]; then
-	printf "Testing for strlcpy ... "
-	cat <<EOF >_strlcpy.c
-#include <string.h>
+if [ -z "$OPEN_MEMSTREAM" ]; then
+	printf "Testing for open_memstream ... "
+	cat <<EOF >_open_memstream.c
+#include <stdio.h>
 int main(void) {
-	const char s1[] = "foo";
-	char s2[10];
-	strlcpy(s2, s1, sizeof(s2));
+	open_memstream(NULL, NULL);
 	return 0;
 }
 EOF
-	if $XCC _strlcpy.c -o _strlcpy 2>&3; then
-		STRLCPY=yes
+	if $XCC _open_memstream.c -o _open_memstream 2>&3; then
+		OPEN_MEMSTREAM=yes
 	else
-		STRLCPY=no
+		OPEN_MEMSTREAM=no
 	fi
-	echo "$STRLCPY"
-	rm -f _strlcpy.c _strlcpy
+	echo "$OPEN_MEMSTREAM"
+	rm -f _open_memstream.c _open_memstream
 fi
-if [ "$STRLCPY" = no ]; then
-	echo "COMPAT_SRCS+=	compat/strlcpy.c" >>$CONFIG_MK
-	echo "#include			\"compat/strlcpy.h\"" >>$CONFIG_H
+if [ "$OPEN_MEMSTREAM" = yes ]; then
+	echo "#define	HAVE_OPEN_MEMSTREAM" >>$CONFIG_H
 fi
 
 if [ -z "$PIDFILE_LOCK" ]; then
@@ -824,6 +821,30 @@
 	echo "#define	HAVE_SETPROCTITLE" >>$CONFIG_H
 fi
 
+if [ -z "$STRLCPY" ]; then
+	printf "Testing for strlcpy ... "
+	cat <<EOF >_strlcpy.c
+#include <string.h>
+int main(void) {
+	const char s1[] = "foo";
+	char s2[10];
+	strlcpy(s2, s1, sizeof(s2));
+	return 0;
+}
+EOF
+	if $XCC _strlcpy.c -o _strlcpy 2>&3; then
+		STRLCPY=yes
+	else
+		STRLCPY=no
+	fi
+	echo "$STRLCPY"
+	rm -f _strlcpy.c _strlcpy
+fi
+if [ "$STRLCPY" = no ]; then
+	echo "COMPAT_SRCS+=	compat/strlcpy.c" >>$CONFIG_MK
+	echo "#include			\"compat/strlcpy.h\"" >>$CONFIG_H
+fi
+
 if [ -z "$STRTOI" ]; then
 	printf "Testing for strtoi ... "
 	cat <<EOF >_strtoi.c
--- a/src/arp.c	Thu Jul 04 12:18:28 2019 +0100
+++ b/src/arp.c	Thu Jul 04 12:22:46 2019 +0100
@@ -37,6 +37,7 @@
 
 #include <errno.h>
 #include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 
--- a/src/auth.c	Thu Jul 04 12:18:28 2019 +0100
+++ b/src/auth.c	Thu Jul 04 12:22:46 2019 +0100
@@ -31,6 +31,7 @@
 #include <fcntl.h>
 #include <inttypes.h>
 #include <stddef.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
--- a/src/common.c	Thu Jul 04 12:18:28 2019 +0100
+++ b/src/common.c	Thu Jul 04 12:22:46 2019 +0100
@@ -57,54 +57,6 @@
 /* Most route(4) messages are less than 256 bytes. */
 #define IOVEC_BUFSIZ	256
 
-ssize_t
-setvar(char **e, const char *prefix, const char *var, const char *value)
-{
-	size_t len = strlen(var) + strlen(value) + 3;
-
-	if (prefix)
-		len += strlen(prefix) + 1;
-	if ((*e = malloc(len)) == NULL) {
-		logerr(__func__);
-		return -1;
-	}
-	if (prefix)
-		snprintf(*e, len, "%s_%s=%s", prefix, var, value);
-	else
-		snprintf(*e, len, "%s=%s", var, value);
-	return (ssize_t)len;
-}
-
-ssize_t
-setvard(char **e, const char *prefix, const char *var, size_t value)
-{
-
-	char buffer[32];
-
-	snprintf(buffer, sizeof(buffer), "%zu", value);
-	return setvar(e, prefix, var, buffer);
-}
-
-ssize_t
-addvar(char ***e, const char *prefix, const char *var, const char *value)
-{
-	ssize_t len;
-
-	len = setvar(*e, prefix, var, value);
-	if (len != -1)
-		(*e)++;
-	return (ssize_t)len;
-}
-
-ssize_t
-addvard(char ***e, const char *prefix, const char *var, size_t value)
-{
-	char buffer[32];
-
-	snprintf(buffer, sizeof(buffer), "%zu", value);
-	return addvar(e, prefix, var, buffer);
-}
-
 const char *
 hwaddr_ntoa(const void *hwaddr, size_t hwlen, char *buf, size_t buflen)
 {
--- a/src/common.h	Thu Jul 04 12:18:28 2019 +0100
+++ b/src/common.h	Thu Jul 04 12:22:46 2019 +0100
@@ -194,11 +194,6 @@
 extern int clock_monotonic;
 int get_monotonic(struct timespec *);
 
-ssize_t setvar(char **, const char *, const char *, const char *);
-ssize_t setvard(char **, const char *, const char *, size_t);
-ssize_t addvar(char ***, const char *, const char *, const char *);
-ssize_t addvard(char ***, const char *, const char *, size_t);
-
 const char *hwaddr_ntoa(const void *, size_t, char *, size_t);
 size_t hwaddr_aton(uint8_t *, const char *);
 size_t read_hwaddr_aton(uint8_t **, const char *);
--- a/src/dhcp-common.c	Thu Jul 04 12:18:28 2019 +0100
+++ b/src/dhcp-common.c	Thu Jul 04 12:22:46 2019 +0100
@@ -37,8 +37,6 @@
 #include <string.h>
 #include <unistd.h>
 
-#include <arpa/nameser.h> /* after normal includes for sunos */
-
 #include "config.h"
 
 #include "common.h"
@@ -47,13 +45,7 @@
 #include "if.h"
 #include "ipv6.h"
 #include "logerr.h"
-
-/* Support very old arpa/nameser.h as found in OpenBSD */
-#ifndef NS_MAXDNAME
-#define NS_MAXCDNAME MAXCDNAME
-#define NS_MAXDNAME MAXDNAME
-#define NS_MAXLABEL MAXLABEL
-#endif
+#include "script.h"
 
 const char *
 dhcp_get_hostname(char *buf, size_t buf_len, const struct if_options *ifo)
@@ -625,41 +617,9 @@
 	return (ssize_t)sz;
 }
 
-/* It's possible for DHCPv4 to contain an IPv6 address */
 static ssize_t
-ipv6_printaddr(char *s, size_t sl, const uint8_t *d, const char *ifname)
-{
-	char buf[INET6_ADDRSTRLEN];
-	const char *p;
-	size_t l;
-
-	p = inet_ntop(AF_INET6, d, buf, sizeof(buf));
-	if (p == NULL)
-		return -1;
-
-	l = strlen(p);
-	if (d[0] == 0xfe && (d[1] & 0xc0) == 0x80)
-		l += 1 + strlen(ifname);
-
-	if (s == NULL)
-		return (ssize_t)l;
-
-	if (sl < l) {
-		errno = ENOMEM;
-		return -1;
-	}
-
-	s += strlcpy(s, p, sl);
-	if (d[0] == 0xfe && (d[1] & 0xc0) == 0x80) {
-		*s++ = '%';
-		s += strlcpy(s, ifname, sl);
-	}
-	*s = '\0';
-	return (ssize_t)l;
-}
-
-static ssize_t
-print_option(char *s, size_t len, const struct dhcp_opt *opt,
+print_option(FILE *fp, const char *prefix, const struct dhcp_opt *opt,
+    int vname,
     const uint8_t *data, size_t dl, const char *ifname)
 {
 	const uint8_t *e, *t;
@@ -668,52 +628,58 @@
 	uint32_t u32;
 	int32_t s32;
 	struct in_addr addr;
-	ssize_t bytes = 0, sl;
+	ssize_t sl;
 	size_t l;
-#ifdef INET
-	char *tmp;
-#endif
+
+	/* Ensure a valid length */
+	dl = (size_t)dhcp_optlen(opt, dl);
+	if ((ssize_t)dl == -1)
+		return 0;
+
+	if (fprintf(fp, "%s", prefix) == -1)
+		return -1;
+	if (vname) {
+		if (fprintf(fp, "_%s", opt->var) == -1)
+			return -1;
+	}
+	if (fputc('=', fp) == EOF)
+		return -1;
+	if (dl == 0)
+		return 1;
 
 	if (opt->type & OT_RFC1035) {
-		sl = decode_rfc1035(s, len, data, dl);
+		char domain[NS_MAXDNAME];
+
+		sl = decode_rfc1035(domain, sizeof(domain), data, dl);
 		if (sl == 0 || sl == -1)
 			return sl;
-		if (s != NULL) {
-			if (valid_domainname(s, opt->type) == -1)
-				return -1;
-		}
-		return sl;
+		if (valid_domainname(domain, opt->type) == -1)
+			return -1;
+		return efprintf(fp, "%s", domain);
 	}
 
 #ifdef INET
-	if (opt->type & OT_RFC3361) {
-		if ((tmp = decode_rfc3361(data, dl)) == NULL)
-			return -1;
-		l = strlen(tmp);
-		sl = print_string(s, len, opt->type, (uint8_t *)tmp, l);
-		free(tmp);
-		return sl;
-	}
+	if (opt->type & OT_RFC3361)
+		return print_rfc3361(fp, data, dl);
 
 	if (opt->type & OT_RFC3442)
-		return decode_rfc3442(s, len, data, dl);
+		return print_rfc3442(fp, data, dl);
 #endif
 
-	if (opt->type & OT_STRING)
-		return print_string(s, len, opt->type, data, dl);
+	if (opt->type & OT_STRING) {
+		char buf[1024];
 
-	if (opt->type & OT_FLAG) {
-		if (s) {
-			*s++ = '1';
-			*s = '\0';
-		}
-		return 1;
+		if (print_string(buf, sizeof(buf), opt->type, data, dl) == -1)
+			return -1;
+		return efprintf(fp, "%s", buf);
 	}
 
+	if (opt->type & OT_FLAG)
+		return efprintf(fp, "1");
+
 	if (opt->type & OT_BITFLAG) {
 		/* bitflags are a string, MSB first, such as ABCDEFGH
 		 * where A is 10000000, B is 01000000, etc. */
-		bytes = 0;
 		for (l = 0, sl = sizeof(opt->bitflags) - 1;
 		    l < sizeof(opt->bitflags);
 		    l++, sl--)
@@ -723,109 +689,80 @@
 			    opt->bitflags[l] != '0' &&
 			    *data & (1 << sl))
 			{
-				if (s)
-					*s++ = opt->bitflags[l];
-				bytes++;
+				if (fputc(opt->bitflags[l], fp) == EOF)
+					return -1;
 			}
 		}
-		if (s)
-			*s = '\0';
-		return bytes;
-	}
-
-	if (!s) {
-		if (opt->type & OT_UINT8)
-			l = 3;
-		else if (opt->type & OT_INT8)
-			l = 4;
-		else if (opt->type & OT_UINT16) {
-			l = 5;
-			dl /= 2;
-		} else if (opt->type & OT_INT16) {
-			l = 6;
-			dl /= 2;
-		} else if (opt->type & OT_UINT32) {
-			l = 10;
-			dl /= 4;
-		} else if (opt->type & OT_INT32) {
-			l = 11;
-			dl /= 4;
-		} else if (opt->type & OT_ADDRIPV4) {
-			l = 16;
-			dl /= 4;
-		} else if (opt->type & OT_ADDRIPV6) {
-			e = data + dl;
-			l = 0;
-			while (data < e) {
-				if (l)
-					l++; /* space */
-				sl = ipv6_printaddr(NULL, 0, data, ifname);
-				if (sl == -1)
-					return l == 0 ? -1 : (ssize_t)l;
-				l += (size_t)sl;
-				data += 16;
-			}
-			return (ssize_t)l;
-		} else {
-			errno = EINVAL;
+		if (fputc('\0', fp) == EOF)
 			return -1;
-		}
-		return (ssize_t)(l * dl);
+		return 1;
 	}
 
 	t = data;
 	e = data + dl;
 	while (data < e) {
 		if (data != t) {
-			*s++ = ' ';
-			bytes++;
-			len--;
+			if (fputc(' ', fp) == EOF)
+				return -1;
 		}
 		if (opt->type & OT_UINT8) {
-			sl = snprintf(s, len, "%u", *data);
+			if (fprintf(fp, "%u", *data) == -1)
+				return -1;
 			data++;
 		} else if (opt->type & OT_INT8) {
-			sl = snprintf(s, len, "%d", *data);
+			if (fprintf(fp, "%d", *data) == -1)
+				return -1;
 			data++;
 		} else if (opt->type & OT_UINT16) {
 			memcpy(&u16, data, sizeof(u16));
 			u16 = ntohs(u16);
-			sl = snprintf(s, len, "%u", u16);
+			if (fprintf(fp, "%u", u16) == -1)
+				return -1;
 			data += sizeof(u16);
 		} else if (opt->type & OT_INT16) {
 			memcpy(&u16, data, sizeof(u16));
 			s16 = (int16_t)ntohs(u16);
-			sl = snprintf(s, len, "%d", s16);
+			if (fprintf(fp, "%d", s16) == -1)
+				return -1;
 			data += sizeof(u16);
 		} else if (opt->type & OT_UINT32) {
 			memcpy(&u32, data, sizeof(u32));
 			u32 = ntohl(u32);
-			sl = snprintf(s, len, "%u", u32);
+			if (fprintf(fp, "%u", u32) == -1)
+				return -1;
 			data += sizeof(u32);
 		} else if (opt->type & OT_INT32) {
 			memcpy(&u32, data, sizeof(u32));
 			s32 = (int32_t)ntohl(u32);
-			sl = snprintf(s, len, "%d", s32);
+			if (fprintf(fp, "%d", s32) == -1)
+				return -1;
 			data += sizeof(u32);
 		} else if (opt->type & OT_ADDRIPV4) {
 			memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
-			sl = snprintf(s, len, "%s", inet_ntoa(addr));
+			if (fprintf(fp, "%s", inet_ntoa(addr)) == -1)
+				return -1;
 			data += sizeof(addr.s_addr);
 		} else if (opt->type & OT_ADDRIPV6) {
-			sl = ipv6_printaddr(s, len, data, ifname);
+			char buf[INET6_ADDRSTRLEN];
+
+			if (inet_ntop(AF_INET6, data, buf, sizeof(buf)) == NULL)
+				return -1;
+			if (fprintf(fp, "%s", buf) == -1)
+				return -1;
+			if (data[0] == 0xfe && (data[1] & 0xc0) == 0x80) {
+				if (fprintf(fp,"%%%s", ifname) == -1)
+					return -1;
+			}
 			data += 16;
 		} else {
 			errno = EINVAL;
 			return -1;
 		}
-		if (sl == -1)
-			return bytes == 0 ? -1 : bytes;
-		len -= (size_t)sl;
-		bytes += sl;
-		s += sl;
 	}
 
-	return bytes;
+	if (fputc('\0', fp) == EOF)
+		return -1;
+	return 1;
 }
 
 int
@@ -860,61 +797,15 @@
 	    ifp->name, ssid);
 }
 
-static size_t
-dhcp_envoption1(char **env, const char *prefix,
-    const struct dhcp_opt *opt, int vname, const uint8_t *od, size_t ol,
-    const char *ifname)
-{
-	ssize_t len;
-	size_t e;
-	char *v, *val;
-	int r;
-
-	/* Ensure a valid length */
-	ol = (size_t)dhcp_optlen(opt, ol);
-	if ((ssize_t)ol == -1)
-		return 0;
-
-	len = print_option(NULL, 0, opt, od, ol, ifname);
-	if (len < 0)
-		return 0;
-	if (vname)
-		e = strlen(opt->var) + 1;
-	else
-		e = 0;
-	if (prefix)
-		e += strlen(prefix);
-	e += (size_t)len + 2;
-	if (env == NULL)
-		return e;
-	v = val = *env = malloc(e);
-	if (v == NULL)
-		return 0;
-	if (vname)
-		r = snprintf(val, e, "%s_%s=", prefix, opt->var);
-	else
-		r = snprintf(val, e, "%s=", prefix);
-	if (r != -1 && len != 0) {
-		v += r;
-		if (print_option(v, (size_t)len + 1, opt, od, ol, ifname) == -1)
-			r = -1;
-	}
-	if (r == -1) {
-		free(val);
-		return 0;
-	}
-	return e;
-}
-
-size_t
-dhcp_envoption(struct dhcpcd_ctx *ctx, char **env, const char *prefix,
+void
+dhcp_envoption(struct dhcpcd_ctx *ctx, FILE *fp, const char *prefix,
     const char *ifname, struct dhcp_opt *opt,
     const uint8_t *(*dgetopt)(struct dhcpcd_ctx *,
     size_t *, unsigned int *, size_t *,
     const uint8_t *, size_t, struct dhcp_opt **),
     const uint8_t *od, size_t ol)
 {
-	size_t e, i, n, eos, eol;
+	size_t i, eos, eol;
 	ssize_t eo;
 	unsigned int eoc;
 	const uint8_t *eod;
@@ -924,52 +815,36 @@
 
 	/* If no embedded or encapsulated options, it's easy */
 	if (opt->embopts_len == 0 && opt->encopts_len == 0) {
-		if (!(opt->type & OT_RESERVED)) {
-			if (dhcp_envoption1(env == NULL ? NULL : &env[0],
-			    prefix, opt, 1, od, ol, ifname))
-				return 1;
-			else
-				logerr("%s: %s %d",
-				    ifname, __func__, opt->option);
-		}
-		return 0;
+		if (opt->type & OT_RESERVED)
+			return;
+		if (print_option(fp, prefix, opt, 1, od, ol, ifname) == -1)
+			logerr("%s: %s %d", ifname, __func__, opt->option);
+		return;
 	}
 
 	/* Create a new prefix based on the option */
-	if (env) {
-		if (opt->type & OT_INDEX) {
-			if (opt->index > 999) {
-				errno = ENOBUFS;
-				logerr(__func__);
-				return 0;
-			}
-		}
-		e = strlen(prefix) + strlen(opt->var) + 2 +
-		    (opt->type & OT_INDEX ? 3 : 0);
-		pfx = malloc(e);
-		if (pfx == NULL) {
-			logerr(__func__);
-			return 0;
-		}
-		if (opt->type & OT_INDEX)
-			snprintf(pfx, e, "%s_%s%d", prefix,
-			    opt->var, ++opt->index);
-		else
-			snprintf(pfx, e, "%s_%s", prefix, opt->var);
-	} else
-		pfx = NULL;
+	if (opt->type & OT_INDEX) {
+		if (asprintf(&pfx, "%s_%s%d",
+		    prefix, opt->var, ++opt->index) == -1)
+			pfx = NULL;
+	} else {
+		if (asprintf(&pfx, "%s_%s", prefix, opt->var) == -1)
+			pfx = NULL;
+	}
+	if (pfx == NULL) {
+		logerr(__func__);
+		return;
+	}
 
 	/* Embedded options are always processed first as that
 	 * is a fixed layout */
-	n = 0;
 	for (i = 0, eopt = opt->embopts; i < opt->embopts_len; i++, eopt++) {
 		eo = dhcp_optlen(eopt, ol);
 		if (eo == -1) {
-			if (env == NULL)
-				logerrx("%s: %s %d.%d/%zu: "
-				    "malformed embedded option",
-				    ifname, __func__, opt->option,
-				    eopt->option, i);
+			logerrx("%s: %s %d.%d/%zu: "
+			    "malformed embedded option",
+			    ifname, __func__, opt->option,
+			    eopt->option, i);
 			goto out;
 		}
 		if (eo == 0) {
@@ -978,9 +853,9 @@
 			 * This may not be an error as some options like
 			 * DHCP FQDN in RFC4702 have a string as the last
 			 * option which is optional. */
-			if (env == NULL &&
-			    (ol != 0 || !(eopt->type & OT_OPTIONAL)))
-				logerrx("%s: %s %d.%d/%zu: missing embedded option",
+			if (ol != 0 || !(eopt->type & OT_OPTIONAL))
+				logerrx("%s: %s %d.%d/%zu: "
+				    "missing embedded option",
 				    ifname, __func__, opt->option,
 				    eopt->option, i);
 			goto out;
@@ -990,10 +865,8 @@
 		 * This avoids new_fqdn_fqdn which would be silly. */
 		if (!(eopt->type & OT_RESERVED)) {
 			ov = strcmp(opt->var, eopt->var);
-			if (dhcp_envoption1(env == NULL ? NULL : &env[n],
-			    pfx, eopt, ov, od, (size_t)eo, ifname))
-				n++;
-			else if (env == NULL)
+			if (print_option(fp, pfx, eopt, ov, od, (size_t)eo,
+			    ifname) == -1)
 				logerr("%s: %s %d.%d/%zu",
 				    ifname, __func__,
 				    opt->option, eopt->option, i);
@@ -1024,19 +897,16 @@
 			    i < opt->encopts_len;
 			    i++, eopt++)
 			{
-				if (eopt->option == eoc) {
-					if (eopt->type & OT_OPTION) {
-						if (oopt == NULL)
-							/* Report error? */
-							continue;
-					}
-					n += dhcp_envoption(ctx,
-					    env == NULL ? NULL : &env[n], pfx,
-					    ifname,
-					    eopt->type & OT_OPTION ? oopt:eopt,
-					    dgetopt, eod, eol);
-					break;
+				if (eopt->option != eoc)
+					continue;
+				if (eopt->type & OT_OPTION) {
+					if (oopt == NULL)
+						/* Report error? */
+						continue;
 				}
+				dhcp_envoption(ctx, fp, pfx, ifname,
+				    eopt->type & OT_OPTION ? oopt:eopt,
+				    dgetopt, eod, eol);
 			}
 			od += eos + eol;
 			ol -= eos + eol;
@@ -1044,11 +914,7 @@
 	}
 
 out:
-	if (env)
-		free(pfx);
-
-	/* Return number of options found */
-	return n;
+	free(pfx);
 }
 
 void
@@ -1071,7 +937,7 @@
 	size_t sz;
 	void *buf;
 	ssize_t len;
-	
+
 	if (fstat(fd, &st) != 0)
 		goto out;
 	if (!S_ISREG(st.st_mode)) {
--- a/src/dhcp-common.h	Thu Jul 04 12:18:28 2019 +0100
+++ b/src/dhcp-common.h	Thu Jul 04 12:22:46 2019 +0100
@@ -34,9 +34,18 @@
 
 #include <stdint.h>
 
+#include <arpa/nameser.h> /* after normal includes for sunos */
+
 #include "common.h"
 #include "dhcpcd.h"
 
+/* Support very old arpa/nameser.h as found in OpenBSD */
+#ifndef NS_MAXDNAME
+#define NS_MAXCDNAME MAXCDNAME
+#define NS_MAXDNAME MAXDNAME
+#define NS_MAXLABEL MAXLABEL
+#endif
+
 /* Max MTU - defines dhcp option length */
 #define	IP_UDP_SIZE		  28
 #define	MTU_MAX			1500 - IP_UDP_SIZE
@@ -112,8 +121,8 @@
 ssize_t print_string(char *, size_t, int, const uint8_t *, size_t);
 int dhcp_set_leasefile(char *, size_t, int, const struct interface *);
 
-size_t dhcp_envoption(struct dhcpcd_ctx *,
-    char **, const char *, const char *, struct dhcp_opt *,
+void dhcp_envoption(struct dhcpcd_ctx *,
+    FILE *, const char *, const char *, struct dhcp_opt *,
     const uint8_t *(*dgetopt)(struct dhcpcd_ctx *,
     size_t *, unsigned int *, size_t *,
     const uint8_t *, size_t, struct dhcp_opt **),
--- a/src/dhcp.c	Thu Jul 04 12:18:28 2019 +0100
+++ b/src/dhcp.c	Thu Jul 04 12:22:46 2019 +0100
@@ -48,6 +48,7 @@
 #include <inttypes.h>
 #include <stdbool.h>
 #include <stddef.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -341,23 +342,25 @@
 }
 
 ssize_t
-decode_rfc3442(char *out, size_t len, const uint8_t *p, size_t pl)
+print_rfc3442(FILE *fp, const uint8_t *data, size_t data_len)
 {
-	const uint8_t *e;
-	size_t bytes = 0, ocets;
-	int b;
+	const uint8_t *p = data, *e;
+	size_t ocets;
 	uint8_t cidr;
 	struct in_addr addr;
-	char *o = out;
 
 	/* Minimum is 5 -first is CIDR and a router length of 4 */
-	if (pl < 5) {
+	if (data_len < 5) {
 		errno = EINVAL;
 		return -1;
 	}
 
-	e = p + pl;
+	e = p + data_len;
 	while (p < e) {
+		if (p != data) {
+			if (fputc(' ', fp) == EOF)
+				return -1;
+		}
 		cidr = *p++;
 		if (cidr > 32) {
 			errno = EINVAL;
@@ -368,41 +371,25 @@
 			errno = ERANGE;
 			return -1;
 		}
-		if (!out) {
-			p += 4 + ocets;
-			bytes += ((4 * 4) * 2) + 4;
-			continue;
-		}
-		if ((((4 * 4) * 2) + 4) > len) {
-			errno = ENOBUFS;
-			return -1;
+		/* If we have ocets then we have a destination and netmask */
+		addr.s_addr = 0;
+		if (ocets > 0) {
+			memcpy(&addr.s_addr, p, ocets);
+			p += ocets;
 		}
-		if (o != out) {
-			*o++ = ' ';
-			len--;
-		}
-		/* If we have ocets then we have a destination and netmask */
-		if (ocets > 0) {
-			addr.s_addr = 0;
-			memcpy(&addr.s_addr, p, ocets);
-			b = snprintf(o, len, "%s/%d", inet_ntoa(addr), cidr);
-			p += ocets;
-		} else
-			b = snprintf(o, len, "0.0.0.0/0");
-		o += b;
-		len -= (size_t)b;
+		if (fprintf(fp, "%s/%d", inet_ntoa(addr), cidr) == -1)
+			return -1;
 
 		/* Finally, snag the router */
 		memcpy(&addr.s_addr, p, 4);
 		p += 4;
-		b = snprintf(o, len, " %s", inet_ntoa(addr));
-		o += b;
-		len -= (size_t)b;
+		if (fprintf(fp, " %s", inet_ntoa(addr)) == -1)
+			return -1;
 	}
 
-	if (out)
-		return o - out;
-	return (ssize_t)bytes;
+	if (fputc('\0', fp) == EOF)
+		return -1;
+	return 1;
 }
 
 static int
@@ -475,15 +462,12 @@
 	return n;
 }
 
-char *
-decode_rfc3361(const uint8_t *data, size_t dl)
+ssize_t
+print_rfc3361(FILE *fp, const uint8_t *data, size_t dl)
 {
 	uint8_t enc;
-	size_t l;
-	ssize_t r;
-	char *sip = NULL;
+	char sip[NS_MAXDNAME];
 	struct in_addr addr;
-	char *p;
 
 	if (dl < 2) {
 		errno = EINVAL;
@@ -494,13 +478,10 @@
 	dl--;
 	switch (enc) {
 	case 0:
-		if ((r = decode_rfc1035(NULL, 0, data, dl)) > 0) {
-			l = (size_t)r + 1;
-			sip = malloc(l);
-			if (sip == NULL)
-				return 0;
-			decode_rfc1035(sip, l, data, dl);
-		}
+		if (decode_rfc1035(sip, sizeof(sip), data, dl) == -1)
+			return -1;
+		if (efprintf(fp, "%s", sip) == -1)
+			return -1;
 		break;
 	case 1:
 		if (dl == 0 || dl % 4 != 0) {
@@ -508,25 +489,27 @@
 			break;
 		}
 		addr.s_addr = INADDR_BROADCAST;
-		l = ((dl / sizeof(addr.s_addr)) * ((4 * 4) + 1)) + 1;
-		sip = p = malloc(l);
-		if (sip == NULL)
-			return 0;
-		while (dl != 0) {
+		for (;
+		    dl != 0;
+		    data += sizeof(addr.s_addr), dl -= sizeof(addr.s_addr))
+		{
 			memcpy(&addr.s_addr, data, sizeof(addr.s_addr));
-			data += sizeof(addr.s_addr);
-			p += snprintf(p, l - (size_t)(p - sip),
-			    "%s ", inet_ntoa(addr));
-			dl -= sizeof(addr.s_addr);
+			if (fprintf(fp, "%s", inet_ntoa(addr)) == -1)
+				return -1;
+			if (dl != 0) {
+				if (fputc(' ', fp) == EOF)
+					return -1;
+			}
 		}
-		*--p = '\0';
+		if (fputc('\0', fp) == EOF)
+			return -1;
 		break;
 	default:
 		errno = EINVAL;
 		return 0;
 	}
 
-	return sip;
+	return 1;
 }
 
 static char *
@@ -1299,9 +1282,8 @@
 }
 
 ssize_t
-dhcp_env(char **env, const char *prefix,
-    const struct bootp *bootp, size_t bootp_len,
-    const struct interface *ifp)
+dhcp_env(FILE *fenv, const char *prefix, const struct interface *ifp,
+    const struct bootp *bootp, size_t bootp_len)
 {
 	const struct if_options *ifo;
 	const uint8_t *p;
@@ -1309,109 +1291,73 @@
 	struct in_addr net;
 	struct in_addr brd;
 	struct dhcp_opt *opt, *vo;
-	size_t e, i, pl;
-	char **ep;
-	char cidr[4], safe[(BOOTP_FILE_LEN * 4) + 1];
+	size_t i, pl;
+	char safe[(BOOTP_FILE_LEN * 4) + 1];
 	uint8_t overl = 0;
 	uint32_t en;
 
-	e = 0;
 	ifo = ifp->options;
 	if (get_option_uint8(ifp->ctx, &overl, bootp, bootp_len,
 	    DHO_OPTSOVERLOADED) == -1)
 		overl = 0;
 
-	if (env == NULL) {
-		if (bootp->yiaddr || bootp->ciaddr)
-			e += 5;
-		if (*bootp->file && !(overl & 1))
-			e++;
-		if (*bootp->sname && !(overl & 2))
-			e++;
-		for (i = 0, opt = ifp->ctx->dhcp_opts;
-		    i < ifp->ctx->dhcp_opts_len;
-		    i++, opt++)
-		{
-			if (has_option_mask(ifo->nomask, opt->option))
-				continue;
-			if (dhcp_getoverride(ifo, opt->option))
-				continue;
-			p = get_option(ifp->ctx, bootp, bootp_len,
-			    opt->option, &pl);
-			if (!p)
-				continue;
-			e += dhcp_envoption(ifp->ctx, NULL, NULL, ifp->name,
-			    opt, dhcp_getoption, p, pl);
-		}
-		for (i = 0, opt = ifo->dhcp_override;
-		    i < ifo->dhcp_override_len;
-		    i++, opt++)
-		{
-			if (has_option_mask(ifo->nomask, opt->option))
-				continue;
-			p = get_option(ifp->ctx, bootp, bootp_len,
-			    opt->option, &pl);
-			if (!p)
-				continue;
-			e += dhcp_envoption(ifp->ctx, NULL, NULL, ifp->name,
-			    opt, dhcp_getoption, p, pl);
-		}
-		return (ssize_t)e;
-	}
-
-	ep = env;
 	if (bootp->yiaddr || bootp->ciaddr) {
 		/* Set some useful variables that we derive from the DHCP
 		 * message but are not necessarily in the options */
 		addr.s_addr = bootp->yiaddr ? bootp->yiaddr : bootp->ciaddr;
-		addvar(&ep, prefix, "ip_address", inet_ntoa(addr));
+		if (efprintf(fenv, "%s_ip_address=%s",
+		    prefix, inet_ntoa(addr)) == -1)
+			return -1;
 		if (get_option_addr(ifp->ctx, &net,
-		    bootp, bootp_len, DHO_SUBNETMASK) == -1)
-		{
+		    bootp, bootp_len, DHO_SUBNETMASK) == -1) {
 			net.s_addr = ipv4_getnetmask(addr.s_addr);
-			addvar(&ep, prefix,
-			    "subnet_mask", inet_ntoa(net));
+			if (efprintf(fenv, "%s_subnet_mask=%s",
+			    prefix, inet_ntoa(net)) == -1)
+				return -1;
 		}
-		snprintf(cidr, sizeof(cidr), "%d", inet_ntocidr(net));
-		addvar(&ep, prefix, "subnet_cidr", cidr);
+		if (efprintf(fenv, "%s_subnet_cidr=%d",
+		    prefix, inet_ntocidr(net))== -1)
+			return -1;
 		if (get_option_addr(ifp->ctx, &brd,
 		    bootp, bootp_len, DHO_BROADCAST) == -1)
 		{
 			brd.s_addr = addr.s_addr | ~net.s_addr;
-			addvar(&ep, prefix,
-			    "broadcast_address", inet_ntoa(brd));
+			if (efprintf(fenv, "%s_broadcast_address=%s",
+			    prefix, inet_ntoa(brd)) == -1)
+				return -1;
 		}
 		addr.s_addr = bootp->yiaddr & net.s_addr;
-		addvar(&ep, prefix,
-		    "network_number", inet_ntoa(addr));
+		if (efprintf(fenv, "%s_network_number=%s",
+		    prefix, inet_ntoa(addr)) == -1)
+			return -1;
 	}
 
 	if (*bootp->file && !(overl & 1)) {
 		print_string(safe, sizeof(safe), OT_STRING,
 		    bootp->file, sizeof(bootp->file));
-		addvar(&ep, prefix, "filename", safe);
+		if (efprintf(fenv, "%s_filename=%s", prefix, safe) == -1)
+			return -1;
 	}
 	if (*bootp->sname && !(overl & 2)) {
 		print_string(safe, sizeof(safe), OT_STRING | OT_DOMAIN,
 		    bootp->sname, sizeof(bootp->sname));
-		addvar(&ep, prefix, "server_name", safe);
+		if (efprintf(fenv, "%s_server_name=%s", prefix, safe) == -1)
+			return -1;
 	}
 
 	/* Zero our indexes */
-	if (env) {
-		for (i = 0, opt = ifp->ctx->dhcp_opts;
-		    i < ifp->ctx->dhcp_opts_len;
-		    i++, opt++)
-			dhcp_zero_index(opt);
-		for (i = 0, opt = ifp->options->dhcp_override;
-		    i < ifp->options->dhcp_override_len;
-		    i++, opt++)
-			dhcp_zero_index(opt);
-		for (i = 0, opt = ifp->ctx->vivso;
-		    i < ifp->ctx->vivso_len;
-		    i++, opt++)
-			dhcp_zero_index(opt);
-	}
+	for (i = 0, opt = ifp->ctx->dhcp_opts;
+	    i < ifp->ctx->dhcp_opts_len;
+	    i++, opt++)
+		dhcp_zero_index(opt);
+	for (i = 0, opt = ifp->options->dhcp_override;
+	    i < ifp->options->dhcp_override_len;
+	    i++, opt++)
+		dhcp_zero_index(opt);
+	for (i = 0, opt = ifp->ctx->vivso;
+	    i < ifp->ctx->vivso_len;
+	    i++, opt++)
+		dhcp_zero_index(opt);
 
 	for (i = 0, opt = ifp->ctx->dhcp_opts;
 	    i < ifp->ctx->dhcp_opts_len;
@@ -1424,7 +1370,7 @@
 		p = get_option(ifp->ctx, bootp, bootp_len, opt->option, &pl);
 		if (p == NULL)
 			continue;
-		ep += dhcp_envoption(ifp->ctx, ep, prefix, ifp->name,
+		dhcp_envoption(ifp->ctx, fenv, prefix, ifp->name,
 		    opt, dhcp_getoption, p, pl);
 
 		if (opt->option != DHO_VIVSO || pl <= (int)sizeof(uint32_t))
@@ -1437,7 +1383,7 @@
 		/* Skip over en + total size */
 		p += sizeof(en) + 1;
 		pl -= sizeof(en) + 1;
-		ep += dhcp_envoption(ifp->ctx, ep, prefix, ifp->name,
+		dhcp_envoption(ifp->ctx, fenv, prefix, ifp->name,
 		    vo, dhcp_getoption, p, pl);
 	}
 
@@ -1450,11 +1396,11 @@
 		p = get_option(ifp->ctx, bootp, bootp_len, opt->option, &pl);
 		if (p == NULL)
 			continue;
-		ep += dhcp_envoption(ifp->ctx, ep, prefix, ifp->name,
+		dhcp_envoption(ifp->ctx, fenv, prefix, ifp->name,
 		    opt, dhcp_getoption, p, pl);
 	}
 
-	return ep - env;
+	return 1;
 }
 
 static void
--- a/src/dhcp.h	Thu Jul 04 12:18:28 2019 +0100
+++ b/src/dhcp.h	Thu Jul 04 12:22:46 2019 +0100
@@ -246,15 +246,15 @@
 #include "dhcpcd.h"
 #include "if-options.h"
 
-char *decode_rfc3361(const uint8_t *, size_t);
-ssize_t decode_rfc3442(char *, size_t, const uint8_t *p, size_t);
+ssize_t print_rfc3361(FILE *, const uint8_t *, size_t);
+ssize_t print_rfc3442(FILE *, const uint8_t *, size_t);
 
 void dhcp_printoptions(const struct dhcpcd_ctx *,
     const struct dhcp_opt *, size_t);
 uint16_t dhcp_get_mtu(const struct interface *);
 int dhcp_get_routes(rb_tree_t *, struct interface *);
-ssize_t dhcp_env(char **, const char *, const struct bootp *, size_t,
-    const struct interface *);
+ssize_t dhcp_env(FILE *, const char *, const struct interface *,
+    const struct bootp *, size_t);
 
 void dhcp_handleifa(int, struct ipv4_addr *, pid_t pid);
 void dhcp_drop(struct interface *, const char *);
--- a/src/dhcp6.c	Thu Jul 04 12:18:28 2019 +0100
+++ b/src/dhcp6.c	Thu Jul 04 12:22:46 2019 +0100
@@ -3955,24 +3955,22 @@
 }
 
 ssize_t
-dhcp6_env(char **env, const char *prefix, const struct interface *ifp,
+dhcp6_env(FILE *fp, const char *prefix, const struct interface *ifp,
     const struct dhcp6_message *m, size_t len)
 {
 	const struct if_options *ifo;
 	struct dhcp_opt *opt, *vo;
 	const uint8_t *p;
 	struct dhcp6_option o;
-	size_t i, n;
+	size_t i;
 	char *pfx;
 	uint32_t en;
 	const struct dhcpcd_ctx *ctx;
 #ifndef SMALL
 	const struct dhcp6_state *state;
 	const struct ipv6_addr *ap;
-	char *v, *val;
 #endif
 
-	n = 0;
 	if (m == NULL)
 		goto delegated;
 
@@ -3987,28 +3985,20 @@
 	ctx = ifp->ctx;
 
 	/* Zero our indexes */
-	if (env) {
-		for (i = 0, opt = ctx->dhcp6_opts;
-		    i < ctx->dhcp6_opts_len;
-		    i++, opt++)
-			dhcp_zero_index(opt);
-		for (i = 0, opt = ifp->options->dhcp6_override;
-		    i < ifp->options->dhcp6_override_len;
-		    i++, opt++)
-			dhcp_zero_index(opt);
-		for (i = 0, opt = ctx->vivso;
-		    i < ctx->vivso_len;
-		    i++, opt++)
-			dhcp_zero_index(opt);
-		i = strlen(prefix) + strlen("_dhcp6") + 1;
-		pfx = malloc(i);
-		if (pfx == NULL) {
-			logerr(__func__);
-			return -1;
-		}
-		snprintf(pfx, i, "%s_dhcp6", prefix);
-	} else
-		pfx = NULL;
+	for (i = 0, opt = ctx->dhcp6_opts;
+	    i < ctx->dhcp6_opts_len;
+	    i++, opt++)
+		dhcp_zero_index(opt);
+	for (i = 0, opt = ifp->options->dhcp6_override;
+	    i < ifp->options->dhcp6_override_len;
+	    i++, opt++)
+		dhcp_zero_index(opt);
+	for (i = 0, opt = ctx->vivso;
+	    i < ctx->vivso_len;
+	    i++, opt++)
+		dhcp_zero_index(opt);
+	if (asprintf(&pfx, "%s_dhcp6", prefix) == -1)
+		return -1;
 
 	/* Unlike DHCP, DHCPv6 options *may* occur more than once.
 	 * There is also no provision for option concatenation unlike DHCP. */
@@ -4054,15 +4044,13 @@
 				opt = NULL;
 		}
 		if (opt) {
-			n += dhcp_envoption(ifp->ctx,
-			    env == NULL ? NULL : &env[n],
-			    pfx, ifp->name,
+			dhcp_envoption(ifp->ctx,
+			    fp, pfx, ifp->name,
 			    opt, dhcp6_getoption, p, o.len);
 		}
 		if (vo) {
-			n += dhcp_envoption(ifp->ctx,
-			    env == NULL ? NULL : &env[n],
-			    pfx, ifp->name,
+			dhcp_envoption(ifp->ctx,
+			    fp, pfx, ifp->name,
 			    vo, dhcp6_getoption,
 			    p + sizeof(en),
 			    o.len - sizeof(en));
@@ -4074,38 +4062,29 @@
 #ifndef SMALL
         /* Needed for Delegated Prefixes */
 	state = D6_CSTATE(ifp);
-	i = 0;
 	TAILQ_FOREACH(ap, &state->addrs, next) {
-		if (ap->delegating_prefix) {
-			i += strlen(ap->saddr) + 1;
-		}
+		if (ap->delegating_prefix)
+			break;
 	}
-	if (env && i) {
-		i += strlen(prefix) + strlen("_delegated_dhcp6_prefix=");
-                v = val = env[n] = malloc(i);
-		if (v == NULL) {
-			logerr(__func__);
-			return -1;
+	if (ap == NULL)
+		return 1;
+	if (fprintf(fp, "%s_delegated_dhcp6_prefix=", prefix) == -1)
+		return -1;
+	TAILQ_FOREACH(ap, &state->addrs, next) {
+		if (ap->delegating_prefix == NULL)
+			continue;
+		if (ap != TAILQ_FIRST(&state->addrs)) {
+			if (fputc(' ', fp) == EOF)
+				return -1;
 		}
-		v += snprintf(val, i, "%s_delegated_dhcp6_prefix=", prefix);
-		TAILQ_FOREACH(ap, &state->addrs, next) {
-			if (ap->delegating_prefix) {
-				/* Can't use stpcpy(3) due to "security" */
-				const char *sap = ap->saddr;
-
-				do
-					*v++ = *sap;
-				while (*++sap != '\0');
-				*v++ = ' ';
-			}
-		}
-		*--v = '\0';
+		if (fprintf(fp, "%s", ap->saddr) == -1)
+			return -1;
         }
-	if (i)
-		n++;
+	if (fputc('\0', fp) == EOF)
+		return -1;
 #endif
 
-	return (ssize_t)n;
+	return 1;
 }
 
 int
--- a/src/dhcp6.h	Thu Jul 04 12:18:28 2019 +0100
+++ b/src/dhcp6.h	Thu Jul 04 12:22:46 2019 +0100
@@ -229,7 +229,7 @@
 int dhcp6_start(struct interface *, enum DH6S);
 void dhcp6_reboot(struct interface *);
 void dhcp6_renew(struct interface *);
-ssize_t dhcp6_env(char **, const char *, const struct interface *,
+ssize_t dhcp6_env(FILE *, const char *, const struct interface *,
     const struct dhcp6_message *, size_t);
 void dhcp6_free(struct interface *);
 void dhcp6_handleifa(int, struct ipv6_addr *, pid_t);
--- a/src/dhcpcd.c	Thu Jul 04 12:18:28 2019 +0100
+++ b/src/dhcpcd.c	Thu Jul 04 12:22:46 2019 +0100
@@ -2134,5 +2134,11 @@
 	if (ctx.options & DHCPCD_FORKED)
 		_exit(i); /* so atexit won't remove our pidfile */
 #endif
+#ifdef HAVE_OPEN_MEMSTREAM
+	if (ctx.script_fp)
+		fclose(ctx.script_fp);
+#endif
+	free(ctx.script_buf);
+	free(ctx.script_env);
 	return i;
 }
--- a/src/dhcpcd.h	Thu Jul 04 12:18:28 2019 +0100
+++ b/src/dhcpcd.h	Thu Jul 04 12:22:46 2019 +0100
@@ -157,6 +157,14 @@
 #endif
 	struct eloop *eloop;
 
+#ifdef HAVE_OPEN_MEMSTREAM
+	FILE *script_fp;
+#endif
+	char *script_buf;
+	size_t script_buflen;
+	char **script_env;
+	size_t script_envlen;
+
 	int control_fd;
 	int control_unpriv_fd;
 	struct fd_list_head control_fds;
--- a/src/ipv4.c	Thu Jul 04 12:18:28 2019 +0100
+++ b/src/ipv4.c	Thu Jul 04 12:22:46 2019 +0100
@@ -39,6 +39,7 @@
 #include <ctype.h>
 #include <errno.h>
 #include <stdbool.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
--- a/src/ipv4ll.c	Thu Jul 04 12:18:28 2019 +0100
+++ b/src/ipv4ll.c	Thu Jul 04 12:22:46 2019 +0100
@@ -31,6 +31,7 @@
 #include <assert.h>
 #include <errno.h>
 #include <stdbool.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -138,7 +139,7 @@
 }
 
 ssize_t
-ipv4ll_env(char **env, const char *prefix, const struct interface *ifp)
+ipv4ll_env(FILE *fp, const char *prefix, const struct interface *ifp)
 {
 	const struct ipv4ll_state *state;
 	const char *pf = prefix == NULL ? "" : "_";
@@ -148,24 +149,21 @@
 	if ((state = IPV4LL_CSTATE(ifp)) == NULL || state->addr == NULL)
 		return 0;
 
-	if (env == NULL)
-		return 5;
-
 	/* Emulate a DHCP environment */
-	if (asprintf(&env[0], "%s%sip_address=%s",
+	if (efprintf(fp, "%s%sip_address=%s",
 	    prefix, pf, inet_ntoa(state->addr->addr)) == -1)
 		return -1;
-	if (asprintf(&env[1], "%s%ssubnet_mask=%s",
+	if (efprintf(fp, "%s%ssubnet_mask=%s",
 	    prefix, pf, inet_ntoa(state->addr->mask)) == -1)
 		return -1;
-	if (asprintf(&env[2], "%s%ssubnet_cidr=%d",
+	if (efprintf(fp, "%s%ssubnet_cidr=%d",
 	    prefix, pf, inet_ntocidr(state->addr->mask)) == -1)
 		return -1;
-	if (asprintf(&env[3], "%s%sbroadcast_address=%s",
+	if (efprintf(fp, "%s%sbroadcast_address=%s",
 	    prefix, pf, inet_ntoa(state->addr->brd)) == -1)
 		return -1;
 	netnum.s_addr = state->addr->addr.s_addr & state->addr->mask.s_addr;
-	if (asprintf(&env[4], "%s%snetwork_number=%s",
+	if (efprintf(fp, "%s%snetwork_number=%s",
 	    prefix, pf, inet_ntoa(netnum)) == -1)
 		return -1;
 	return 5;
--- a/src/ipv4ll.h	Thu Jul 04 12:18:28 2019 +0100
+++ b/src/ipv4ll.h	Thu Jul 04 12:22:46 2019 +0100
@@ -60,7 +60,7 @@
 
 int ipv4ll_subnetroute(rb_tree_t *, struct interface *);
 int ipv4ll_defaultroute(rb_tree_t *,struct interface *);
-ssize_t ipv4ll_env(char **, const char *, const struct interface *);
+ssize_t ipv4ll_env(FILE *, const char *, const struct interface *);
 void ipv4ll_start(void *);
 void ipv4ll_claimed(void *);
 void ipv4ll_handle_failure(void *);
--- a/src/ipv6.c	Thu Jul 04 12:18:28 2019 +0100
+++ b/src/ipv6.c	Thu Jul 04 12:22:46 2019 +0100
@@ -1541,23 +1541,17 @@
 }
 
 ssize_t
-ipv6_env(char **env, const char *prefix, const struct interface *ifp)
+ipv6_env(FILE *fp, const char *prefix, const struct interface *ifp)
 {
-	char **ep;
-	ssize_t n;
 	struct ipv6_addr *ia;
 
-	ep = env;
-	n = 0;
 	ia = ipv6_iffindaddr(UNCONST(ifp), &ifp->options->req_addr6,
 	    IN6_IFF_NOTUSEABLE);
-	if (ia) {
-		if (env)
-			addvar(&ep, prefix, "ip6_address", ia->saddr);
-		n++;
-	}
-
-	return n;
+	if (ia == NULL)
+		return 0;
+	if (efprintf(fp, "%s_ip6_address=%s", prefix, ia->saddr) == -1)
+		return -1;
+	return 1;
 }
 
 int
--- a/src/ipv6.h	Thu Jul 04 12:18:28 2019 +0100
+++ b/src/ipv6.h	Thu Jul 04 12:22:46 2019 +0100
@@ -288,7 +288,7 @@
 int ipv6_start(struct interface *);
 int ipv6_staticdadcompleted(const struct interface *);
 int ipv6_startstatic(struct interface *);
-ssize_t ipv6_env(char **, const char *, const struct interface *);
+ssize_t ipv6_env(FILE *, const char *, const struct interface *);
 void ipv6_ctxfree(struct dhcpcd_ctx *);
 bool inet6_getroutes(struct dhcpcd_ctx *, rb_tree_t *);
 #endif /* INET6 */
--- a/src/ipv6nd.c	Thu Jul 04 12:18:28 2019 +0100
+++ b/src/ipv6nd.c	Thu Jul 04 12:22:46 2019 +0100
@@ -1385,11 +1385,11 @@
 }
 
 ssize_t
-ipv6nd_env(char **env, const char *prefix, const struct interface *ifp)
+ipv6nd_env(FILE *fp, const struct interface *ifp)
 {
 	size_t i, j, n, len, olen;
 	struct ra *rap;
-	char ndprefix[32], abuf[24];
+	char ndprefix[32];
 	struct dhcp_opt *opt;
 	uint8_t *p;
 	struct nd_opt_hdr ndo;
@@ -1402,34 +1402,24 @@
 		if (rap->iface != ifp)
 			continue;
 		i++;
-		if (prefix != NULL)
-			snprintf(ndprefix, sizeof(ndprefix),
-			    "%s_nd%zu", prefix, i);
-		else
-			snprintf(ndprefix, sizeof(ndprefix),
-			    "nd%zu", i);
-		if (env)
-			setvar(&env[n], ndprefix, "from", rap->sfrom);
-		n++;
-		if (env)
-			setvard(&env[n], ndprefix, "acquired",
-			    (size_t)rap->acquired.tv_sec);
-		n++;
-		if (env)
-			setvard(&env[n], ndprefix, "now", (size_t)now.tv_sec);
-		n++;
+		snprintf(ndprefix, sizeof(ndprefix), "nd%zu", i);
+		if (efprintf(fp, "%s_from=%s", ndprefix, rap->sfrom) == -1)
+			return -1;
+		if (efprintf(fp, "%s_acquired=%ld", ndprefix,
+		    rap->acquired.tv_sec) == -1)
+			return -1;
+		if (efprintf(fp, "%s_now=%ld", ndprefix, now.tv_sec) == -1)
+			return -1;
 
 		/* Zero our indexes */
-		if (env) {
-			for (j = 0, opt = rap->iface->ctx->nd_opts;
-			    j < rap->iface->ctx->nd_opts_len;
-			    j++, opt++)
-				dhcp_zero_index(opt);
-			for (j = 0, opt = rap->iface->options->nd_override;
-			    j < rap->iface->options->nd_override_len;
-			    j++, opt++)
-				dhcp_zero_index(opt);
-		}
+		for (j = 0, opt = rap->iface->ctx->nd_opts;
+		    j < rap->iface->ctx->nd_opts_len;
+		    j++, opt++)
+			dhcp_zero_index(opt);
+		for (j = 0, opt = rap->iface->options->nd_override;
+		    j < rap->iface->options->nd_override_len;
+		    j++, opt++)
+			dhcp_zero_index(opt);
 
 		/* Unlike DHCP, ND6 options *may* occur more than once.
 		 * There is also no provision for option concatenation
@@ -1462,13 +1452,12 @@
 				if (j == rap->iface->ctx->nd_opts_len)
 					opt = NULL;
 			}
-			if (opt) {
-				n += dhcp_envoption(rap->iface->ctx,
-				    env == NULL ? NULL : &env[n],
-				    ndprefix, rap->iface->name,
-				    opt, ipv6nd_getoption,
-				    p + sizeof(ndo), olen - sizeof(ndo));
-			}
+			if (opt == NULL)
+				continue;
+			dhcp_envoption(rap->iface->ctx, fp,
+			    ndprefix, rap->iface->name,
+			    opt, ipv6nd_getoption,
+			    p + sizeof(ndo), olen - sizeof(ndo));
 		}
 
 		/* We need to output the addresses we actually made
@@ -1482,15 +1471,12 @@
 			    !(ia->flags & IPV6_AF_ADDED) ||
 			    ia->prefix_vltime == 0)
 				continue;
-			j++;
-			if (env) {
-				snprintf(abuf, sizeof(abuf), "addr%zu", j);
-				setvar(&env[n], ndprefix, abuf, ia->saddr);
-			}
-			n++;
+			if (efprintf(fp, "%s_addr%zu=%s",
+			    ndprefix, j++, ia->saddr) == -1)
+				return -1;
 		}
 	}
-	return (ssize_t)n;
+	return 1;
 }
 
 void
@@ -1622,13 +1608,13 @@
 #endif
 			case ND_OPT_DNSSL:
 				if (len < sizeof(dnssl))
-					break;
+					continue;
 				memcpy(&dnssl, p, sizeof(dnssl));
 				ltime = dnssl.nd_opt_dnssl_lifetime;
 				break;
 			case ND_OPT_RDNSS:
 				if (len < sizeof(rdnss))
-					break;
+					continue;
 				memcpy(&rdnss, p, sizeof(rdnss));
 				ltime = rdnss.nd_opt_rdnss_lifetime;
 				break;
--- a/src/ipv6nd.h	Thu Jul 04 12:18:28 2019 +0100
+++ b/src/ipv6nd.h	Thu Jul 04 12:22:46 2019 +0100
@@ -96,7 +96,7 @@
 void ipv6nd_printoptions(const struct dhcpcd_ctx *,
     const struct dhcp_opt *, size_t);
 void ipv6nd_startrs(struct interface *);
-ssize_t ipv6nd_env(char **, const char *, const struct interface *);
+ssize_t ipv6nd_env(FILE *, const struct interface *);
 const struct ipv6_addr *ipv6nd_iffindaddr(const struct interface *ifp,
     const struct in6_addr *addr, unsigned int flags);
 struct ipv6_addr *ipv6nd_findaddr(struct dhcpcd_ctx *,
--- a/src/script.c	Thu Jul 04 12:18:28 2019 +0100
+++ b/src/script.c	Thu Jul 04 12:22:46 2019 +0100
@@ -37,6 +37,7 @@
 #include <errno.h>
 #include <signal.h>
 #include <spawn.h>
+#include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -57,7 +58,7 @@
 #define RC_SVCNAME "RC_SVCNAME"
 #endif
 
-#define DEFAULT_PATH	"PATH=/usr/bin:/usr/sbin:/bin:/sbin"
+#define DEFAULT_PATH	"/usr/bin:/usr/sbin:/bin:/sbin"
 
 static const char * const if_params[] = {
 	"interface",
@@ -121,103 +122,23 @@
 }
 
 #ifdef INET
-static char *
-make_var(const char *prefix, const char *var)
+static int
+append_config(FILE *fp, const char *prefix, const char *const *config)
 {
-	size_t len;
-	char *v;
-
-	len = strlen(prefix) + strlen(var) + 2;
-	if ((v = malloc(len)) == NULL) {
-		logerr(__func__);
-		return NULL;
-	}
-	snprintf(v, len, "%s_%s", prefix, var);
-	return v;
-}
-
-
-static int
-append_config(char ***env, size_t *len,
-    const char *prefix, const char *const *config)
-{
-	size_t i, j, e1;
-	char **ne, *eq, **nep, *p;
-	int ret;
+	size_t i;
 
 	if (config == NULL)
 		return 0;
 
-	ne = *env;
-	ret = 0;
+	/* Do we need to replace existing config rather than append? */
 	for (i = 0; config[i] != NULL; i++) {
-		eq = strchr(config[i], '=');
-		e1 = (size_t)(eq - config[i] + 1);
-		for (j = 0; j < *len; j++) {
-			if (strncmp(ne[j], prefix, strlen(prefix)) == 0 &&
-			    ne[j][strlen(prefix)] == '_' &&
-			    strncmp(ne[j] + strlen(prefix) + 1,
-			    config[i], e1) == 0)
-			{
-				p = make_var(prefix, config[i]);
-				if (p == NULL) {
-					ret = -1;
-					break;
-				}
-				free(ne[j]);
-				ne[j] = p;
-				break;
-			}
-		}
-		if (j == *len) {
-			j++;
-			p = make_var(prefix, config[i]);
-			if (p == NULL) {
-				ret = -1;
-				break;
-			}
-			nep = realloc(ne, sizeof(char *) * (j + 1));
-			if (nep == NULL) {
-				logerr(__func__);
-				free(p);
-				ret = -1;
-				break;
-			}
-			ne = nep;
-			ne[j - 1] = p;
-			*len = j;
-		}
+		if (efprintf(fp, "%s_%s", prefix, config[i]) == -1)
+			return -1;
 	}
-	*env = ne;
-	return ret;
+	return 1;
 }
-#endif
-
-static ssize_t
-arraytostr(const char *const *argv, char **s)
-{
-	const char *const *ap;
-	char *p;
-	size_t len, l;
 
-	if (*argv == NULL)
-		return 0;
-	len = 0;
-	ap = argv;
-	while (*ap)
-		len += strlen(*ap++) + 1;
-	*s = p = malloc(len);
-	if (p == NULL)
-		return -1;
-	ap = argv;
-	while (*ap) {
-		l = strlen(*ap) + 1;
-		memcpy(p, *ap, l);
-		p += l;
-		ap++;
-	}
-	return (ssize_t)len;
-}
+#endif
 
 #define	PROTO_LINK	0
 #define	PROTO_DHCP	1
@@ -234,15 +155,32 @@
 	"static6"
 };
 
-static ssize_t
-make_env(const struct interface *ifp, const char *reason, char ***argv)
+int
+efprintf(FILE *fp, const char *fmt, ...)
 {
-	int protocol, r;
-	char **env, **nenv, *p;
-	size_t e, elen, l;
-#if defined(INET) || defined(INET6)
-	ssize_t n;
-#endif
+	va_list args;
+	int r;
+
+	va_start(args, fmt);
+	r = vfprintf(fp, fmt, args);
+	va_end(args);
+	if (r == -1)
+		return -1;
+	/* Write a trailing NULL so we can easily create env strings. */
+	if (fputc('\0', fp) == EOF)
+		return -1;
+	return r;
+}
+
+static ssize_t
+make_env(const struct interface *ifp, const char *reason)
+{
+	struct dhcpcd_ctx *ctx = ifp->ctx;
+	FILE *fp;
+	char **env, **envp, *buf, *bufp, *endp, *path;
+	size_t nenv;
+	long buf_pos, i;
+	int protocol;
 	const struct if_options *ifo = ifp->options;
 	const struct interface *ifp2;
 	int af;
@@ -256,6 +194,31 @@
 	const struct dhcp6_state *d6_state;
 #endif
 
+#ifdef HAVE_OPEN_MEMSTREAM
+	if (ctx->script_fp == NULL) {
+		fp = open_memstream(&ctx->script_buf, &ctx->script_buflen);
+		if (fp == NULL)
+			goto eexit;
+		ctx->script_fp = fp;
+	} else {
+		fp = ctx->script_fp;
+		rewind(fp);
+	}
+#else
+	char tmpfile[] = "/tmp/dhcpcd-script-env-XXXXXX";
+	int tmpfd;
+
+	fp = NULL;
+	tmpfd = mkstemp(tmpfile);
+	if (tmpfd == -1)
+		goto eexit;
+	unlink(tmpfile);
+	fp = fopen(tmpfile, "w+");
+	close(tmpfd);
+	if (fp == NULL)
+		goto eexit;
+#endif
+
 #ifdef INET
 	state = D_STATE(ifp);
 #ifdef IPV4LL
@@ -310,71 +273,60 @@
 		protocol = PROTO_DHCP;
 #endif
 
-	/* When dumping the lease, we only want to report interface and
-	   reason - the other interface variables are meaningless */
-	if (ifp->ctx->options & DHCPCD_DUMPLEASE)
-		elen = 2;
-	else
-		elen = 11;
+	/* Needed for scripts */
+	path = getenv("PATH");
+	if (efprintf(fp, "PATH=%s", path == NULL ? DEFAULT_PATH:path) == -1)
+		goto eexit;
 
-#define EMALLOC(i, l) if ((env[(i)] = malloc((l))) == NULL) goto eexit;
-	/* Make our env + space for profile, wireless and debug */
-	env = calloc(1, sizeof(char *) * (elen + 5 + 1));
-	if (env == NULL)
+	if (efprintf(fp, "interface=%s", ifp->name) == -1)
 		goto eexit;
-	e = strlen("interface") + strlen(ifp->name) + 2;
-	EMALLOC(0, e);
-	snprintf(env[0], e, "interface=%s", ifp->name);
-	e = strlen("reason") + strlen(reason) + 2;
-	EMALLOC(1, e);
-	snprintf(env[1], e, "reason=%s", reason);
+	if (efprintf(fp, "reason=%s", reason) == -1)
+		goto eexit;
 	if (ifp->ctx->options & DHCPCD_DUMPLEASE)
 		goto dumplease;
-	e = 20;
-	EMALLOC(2, e);
-	snprintf(env[2], e, "pid=%d", getpid());
-	EMALLOC(3, e);
-	snprintf(env[3], e, "ifcarrier=%s",
+	if (efprintf(fp, "pid=%d", getpid()) == -1)
+		goto eexit;
+	if (efprintf(fp, "ifcarrier=%s",
 	    ifp->carrier == LINK_UNKNOWN ? "unknown" :
-	    ifp->carrier == LINK_UP ? "up" : "down");
-	EMALLOC(4, e);
-	snprintf(env[4], e, "ifmetric=%d", ifp->metric);
-	EMALLOC(5, e);
-	snprintf(env[5], e, "ifwireless=%d", ifp->wireless);
-	EMALLOC(6, e);
-	snprintf(env[6], e, "ifflags=%u", ifp->flags);
-	EMALLOC(7, e);
-	snprintf(env[7], e, "ifmtu=%d", if_getmtu(ifp));
-	l = e = strlen("interface_order=");
+	    ifp->carrier == LINK_UP ? "up" : "down") == -1)
+		goto eexit;
+	if (efprintf(fp, "ifmetric=%d", ifp->metric) == -1)
+		goto eexit;
+	if (efprintf(fp, "ifwireless=%d", ifp->wireless) == -1)
+		goto eexit;
+	if (efprintf(fp, "ifflags=%u", ifp->flags) == -1)
+		goto eexit;
+	if (efprintf(fp, "ifmtu=%d", if_getmtu(ifp)) == -1)
+		goto eexit;
+
+	if (fprintf(fp, "interface_order=") == -1)
+		goto eexit;
 	TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
-		e += strlen(ifp2->name) + 1;
+		if (ifp2 != TAILQ_FIRST(ifp->ctx->ifaces)) {
+			if (fputc(' ', fp) == EOF)
+				return -1;
+		}
+		if (fprintf(fp, "%s", ifp2->name) == -1)
+			return -1;
 	}
-	EMALLOC(8, e);
-	p = env[8];
-	strlcpy(p, "interface_order=", e);
-	e -= l;
-	p += l;
-	TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
-		l = strlcpy(p, ifp2->name, e);
-		p += l;
-		e -= l;
-		*p++ = ' ';
-		e--;
-	}
-	*--p = '\0';
+	if (fputc('\0', fp) == EOF)
+		return -1;
+
 	if (strcmp(reason, "STOPPED") == 0) {
-		env[9] = strdup("if_up=false");
-		if (ifo->options & DHCPCD_RELEASE)
-			env[10] = strdup("if_down=true");
-		else
-			env[10] = strdup("if_down=false");
+		if (efprintf(fp, "if_up=false") == -1)
+			goto eexit;
+		if (efprintf(fp, "if_down=%s",
+		    ifo->options & DHCPCD_RELEASE ? "true" : "false") == -1)
+			goto eexit;
 	} else if (strcmp(reason, "TEST") == 0 ||
 	    strcmp(reason, "PREINIT") == 0 ||
 	    strcmp(reason, "CARRIER") == 0 ||
 	    strcmp(reason, "UNKNOWN") == 0)
 	{
-		env[9] = strdup("if_up=false");
-		env[10] = strdup("if_down=false");
+		if (efprintf(fp, "if_up=false") == -1)
+			goto eexit;
+		if (efprintf(fp, "if_down=false") == -1)
+			goto eexit;
 	} else if (1 == 2 /* appease ifdefs */
 #ifdef INET
 	    || (protocol == PROTO_DHCP && state && state->new)
@@ -391,24 +343,23 @@
 #endif
 	    )
 	{
-		env[9] = strdup("if_up=true");
-		env[10] = strdup("if_down=false");
+		if (efprintf(fp, "if_up=true") == -1)
+			goto eexit;
+		if (efprintf(fp, "if_down=false") == -1)
+			goto eexit;
 	} else {
-		env[9] = strdup("if_up=false");
-		env[10] = strdup("if_down=true");
+		if (efprintf(fp, "if_up=false") == -1)
+			goto eexit;
+		if (efprintf(fp, "if_down=true") == -1)
+			goto eexit;
 	}
-	if (env[9] == NULL || env[10] == NULL)
-		goto eexit;
 	if (protocols[protocol] != NULL) {
-		r = asprintf(&env[elen], "protocol=%s", protocols[protocol]);
-		if (r == -1)
+		if (efprintf(fp, "protocol=%s", protocols[protocol]) == -1)
 			goto eexit;
-		elen++;
 	}
 	if ((af = dhcpcd_ifafwaiting(ifp)) != AF_MAX) {
-		e = 20;
-		EMALLOC(elen, e);
-		snprintf(env[elen++], e, "if_afwaiting=%d", af);
+		if (efprintf(fp, "if_afwaiting=%d", af) == -1)
+			goto eexit;
 	}
 	if ((af = dhcpcd_afwaiting(ifp->ctx)) != AF_MAX) {
 		TAILQ_FOREACH(ifp2, ifp->ctx->ifaces, next) {
@@ -417,75 +368,42 @@
 		}
 	}
 	if (af != AF_MAX) {
-		e = 20;
-		EMALLOC(elen, e);
-		snprintf(env[elen++], e, "af_waiting=%d", af);
+		if (efprintf(fp, "af_waiting=%d", af) == -1)
+			goto eexit;
 	}
 	if (ifo->options & DHCPCD_DEBUG) {
-		e = strlen("syslog_debug=true") + 1;
-		EMALLOC(elen, e);
-		snprintf(env[elen++], e, "syslog_debug=true");
+		if (efprintf(fp, "syslog_debug=true") == -1)
+			goto eexit;
 	}
 	if (*ifp->profile) {
-		e = strlen("profile=") + strlen(ifp->profile) + 1;
-		EMALLOC(elen, e);
-		snprintf(env[elen++], e, "profile=%s", ifp->profile);
+		if (efprintf(fp, "profile=%s", ifp->profile) == -1)
+			goto eexit;
 	}
 	if (ifp->wireless) {
-		static const char *pfx = "ifssid=";
-		size_t pfx_len;
-		ssize_t psl;
+		char pssid[IF_SSIDLEN * 4];
 
-		pfx_len = strlen(pfx);
-		psl = print_string(NULL, 0, OT_ESCSTRING,
-		    (const uint8_t *)ifp->ssid, ifp->ssid_len);
-		if (psl != -1) {
-			EMALLOC(elen, pfx_len + (size_t)psl + 1);
-			memcpy(env[elen], pfx, pfx_len);
-			print_string(env[elen] + pfx_len, (size_t)psl + 1,
-			    OT_ESCSTRING,
-			    (const uint8_t *)ifp->ssid, ifp->ssid_len);
-			elen++;
+		if (print_string(pssid, sizeof(pssid), OT_ESCSTRING,
+		    ifp->ssid, ifp->ssid_len) != -1)
+		{
+			if (efprintf(fp, "ifssid=%s", pssid) == -1)
+				goto eexit;
 		}
 	}
 #ifdef INET
 	if (protocol == PROTO_DHCP && state && state->old) {
-		n = dhcp_env(NULL, NULL, state->old, state->old_len, ifp);
-		if (n == -1)
+		if (dhcp_env(fp, "old", ifp,
+		    state->old, state->old_len) == -1)
 			goto eexit;
-		if (n > 0) {
-			nenv = realloc(env, sizeof(char *) *
-			    (elen + (size_t)n + 1));
-			if (nenv == NULL)
-				goto eexit;
-			env = nenv;
-			n = dhcp_env(env + elen, "old",
-			    state->old, state->old_len, ifp);
-			if (n == -1)
-				goto eexit;
-			elen += (size_t)n;
-		}
-		if (append_config(&env, &elen, "old",
+		if (append_config(fp, "old",
 		    (const char *const *)ifo->config) == -1)
 			goto eexit;
 	}
 #endif
 #ifdef DHCP6
 	if (protocol == PROTO_DHCP6 && d6_state && d6_state->old) {
-		n = dhcp6_env(NULL, NULL, ifp,
-		    d6_state->old, d6_state->old_len);
-		if (n > 0) {
-			nenv = realloc(env, sizeof(char *) *
-			    (elen + (size_t)n + 1));
-			if (nenv == NULL)
-				goto eexit;
-			env = nenv;
-			n = dhcp6_env(env + elen, "old", ifp,
-			    d6_state->old, d6_state->old_len);
-			if (n == -1)
-				goto eexit;
-			elen += (size_t)n;
-		}
+		if (dhcp6_env(fp, "old", ifp,
+		    d6_state->old, d6_state->old_len) == -1)
+			goto eexit;
 	}
 #endif
 
@@ -493,144 +411,114 @@
 #ifdef INET
 #ifdef IPV4LL
 	if (protocol == PROTO_IPV4LL) {
-		n = ipv4ll_env(NULL, NULL, ifp);
-		if (n > 0) {
-			nenv = realloc(env, sizeof(char *) *
-			    (elen + (size_t)n + 1));
-			if (nenv == NULL)
-				goto eexit;
-			env = nenv;
-			if ((n = ipv4ll_env(env + elen,
-			    istate->down ? "old" : "new", ifp)) == -1)
-				goto eexit;
-			elen += (size_t)n;
-		}
+		if (ipv4ll_env(fp, istate->down ? "old" : "new", ifp) == -1)
+			goto eexit;
 	}
 #endif
 	if (protocol == PROTO_DHCP && state && state->new) {
-		n = dhcp_env(NULL, NULL, state->new, state->new_len, ifp);
-		if (n > 0) {
-			nenv = realloc(env, sizeof(char *) *
-			    (elen + (size_t)n + 1));
-			if (nenv == NULL)
-				goto eexit;
-			env = nenv;
-			n = dhcp_env(env + elen, "new",
-			    state->new, state->new_len, ifp);
-			if (n == -1)
-				goto eexit;
-			elen += (size_t)n;
-		}
-		if (append_config(&env, &elen, "new",
+		if (dhcp_env(fp, "new", ifp,
+		    state->new, state->new_len) == -1)
+			goto eexit;
+		if (append_config(fp, "new",
 		    (const char *const *)ifo->config) == -1)
 			goto eexit;
 	}
 #endif
 #ifdef INET6
 	if (protocol == PROTO_STATIC6) {
-		n = ipv6_env(NULL, NULL, ifp);
-		if (n > 0) {
-			nenv = realloc(env, sizeof(char *) *
-			    (elen + (size_t)n + 1));
-			if (nenv == NULL)
-				goto eexit;
-			env = nenv;
-			n = ipv6_env(env + elen, "new", ifp);
-			if (n == -1)
-				goto eexit;
-			elen += (size_t)n;
-		}
+		if (ipv6_env(fp, "new", ifp) == -1)
+			goto eexit;
 	}
 #ifdef DHCP6
 	if (protocol == PROTO_DHCP6 && D6_STATE_RUNNING(ifp)) {
-		n = dhcp6_env(NULL, NULL, ifp,
-		    d6_state->new, d6_state->new_len);
-		if (n > 0) {
-			nenv = realloc(env, sizeof(char *) *
-			    (elen + (size_t)n + 1));
-			if (nenv == NULL)
-				goto eexit;
-			env = nenv;
-			n = dhcp6_env(env + elen, "new", ifp,
-			    d6_state->new, d6_state->new_len);
-			if (n == -1)
-				goto eexit;
-			elen += (size_t)n;
-		}
+		if (dhcp6_env(fp, "new", ifp,
+		    d6_state->new, d6_state->new_len) == -1)
+			goto eexit;
 	}
 #endif
 	if (protocol == PROTO_RA) {
-		n = ipv6nd_env(NULL, NULL, ifp);
-		if (n > 0) {
-			nenv = realloc(env, sizeof(char *) *
-			    (elen + (size_t)n + 1));
-			if (nenv == NULL)
-				goto eexit;
-			env = nenv;
-			n = ipv6nd_env(env + elen, NULL, ifp);
-			if (n == -1)
-				goto eexit;
-			elen += (size_t)n;
-		}
+		if (ipv6nd_env(fp, ifp) == -1)
+			goto eexit;
 	}
 #endif
 
 	/* Add our base environment */
 	if (ifo->environ) {
-		e = 0;
-		while (ifo->environ[e++])
-			;
-		nenv = realloc(env, sizeof(char *) * (elen + e + 1));
-		if (nenv == NULL)
+		for (i = 0; ifo->environ[i] != NULL; i++)
+			if (efprintf(fp, "%s", ifo->environ[i]) == -1)
+				goto eexit;
+	}
+
+	/* Convert buffer to argv */
+	fflush(fp);
+
+	buf_pos = ftell(fp);
+	if (buf_pos == -1) {
+		logerr(__func__);
+		goto eexit;
+	}
+#ifdef HAVE_OPEN_MEMSTREAM
+	buf = ctx->script_buf;
+#else
+	size_t buf_len = (size_t)buf_pos;
+	if (ctx->script_buflen < buf_len) {
+		buf = realloc(ctx->script_buf, buf_len);
+		if (buf == NULL)
 			goto eexit;
-		env = nenv;
-		e = 0;
-		while (ifo->environ[e]) {
-			env[elen + e] = strdup(ifo->environ[e]);
-			if (env[elen + e] == NULL)
-				goto eexit;
-			e++;
-		}
-		elen += e;
+		ctx->script_buf = buf;
+		ctx->script_buflen = buf_len;
+	}
+	buf = ctx->script_buf;
+	rewind(fp);
+	if (fread(buf, sizeof(char), buf_len, fp) != buf_len)
+		goto eexit;
+	fclose(fp);
+	fp = NULL;
+#endif
+
+	nenv = 0;
+	endp = buf + buf_pos;
+	for (bufp = buf; bufp < endp; bufp++) {
+		if (*bufp == '\0')
+			nenv++;
 	}
-	env[elen] = NULL;
+	if (ctx->script_envlen < nenv) {
+		env = reallocarray(ctx->script_env, nenv + 1, sizeof(*env));
+		if (env == NULL)
+			goto eexit;
+		ctx->script_env = env;
+		ctx->script_envlen = nenv;
+	}
+	bufp = buf;
+	envp = ctx->script_env;
+	*envp++ = bufp++;
+	endp--; /* Avoid setting the last \0 to an invalid pointer */
+	for (; bufp < endp; bufp++) {
+		if (*bufp == '\0')
+			*envp++ = bufp + 1;
+	}
+	*envp = NULL;
 
-	*argv = env;
-	return (ssize_t)elen;
+	return (ssize_t)nenv;
 
 eexit:
 	logerr(__func__);
-	if (env) {
-		nenv = env;
-		while (*nenv)
-			free(*nenv++);
-		free(env);
-	}
+#ifndef HAVE_OPEN_MEMSTREAM
+	if (fp != NULL)
+		fclose(fp);
+#endif
 	return -1;
 }
 
 static int
-send_interface1(struct fd_list *fd, const struct interface *iface,
+send_interface1(struct fd_list *fd, const struct interface *ifp,
     const char *reason)
 {
-	char **env, **ep, *s;
-	size_t elen;
-	int retval;
+	struct dhcpcd_ctx *ctx = ifp->ctx;
 
-	if (make_env(iface, reason, &env) == -1)
+	if (make_env(ifp, reason) == -1)
 		return -1;
-	s = NULL;
-	elen = (size_t)arraytostr((const char *const *)env, &s);
-	if ((ssize_t)elen == -1) {
-		free(s);
-		retval = -1;
-	} else
-		retval = control_queue(fd, s, elen, 1);
-	ep = env;
-	while (*ep)
-		free(*ep++);
-	free(env);
-	return retval;
+	return control_queue(fd, ctx->script_buf, ctx->script_buflen, 1);
 }
 
 int
@@ -697,10 +585,8 @@
 int
 script_runreason(const struct interface *ifp, const char *reason)
 {
+	struct dhcpcd_ctx *ctx = ifp->ctx;
 	char *argv[2];
-	char **env = NULL, **ep;
-	char *svcname, *path, *bigenv;
-	size_t e, elen = 0;
 	pid_t pid;
 	int status = 0;
 	struct fd_list *fd;
@@ -710,8 +596,7 @@
 		return 0;
 
 	/* Make our env */
-	elen = (size_t)make_env(ifp, reason, &env);
-	if (elen == (size_t)-1) {
+	if (make_env(ifp, reason) == -1) {
 		logerr(__func__);
 		return -1;
 	}
@@ -723,43 +608,7 @@
 	argv[1] = NULL;
 	logdebugx("%s: executing `%s' %s", ifp->name, argv[0], reason);
 
-	/* Resize for PATH and RC_SVCNAME */
-	svcname = getenv(RC_SVCNAME);
-	ep = reallocarray(env, elen + 2 + (svcname ? 1 : 0), sizeof(char *));
-	if (ep == NULL) {
-		elen = 0;
-		goto out;
-	}
-	env = ep;
-	/* Add path to it */
-	path = getenv("PATH");
-	if (path) {
-		e = strlen("PATH") + strlen(path) + 2;
-		env[elen] = malloc(e);
-		if (env[elen] == NULL) {
-			elen = 0;
-			goto out;
-		}
-		snprintf(env[elen], e, "PATH=%s", path);
-	} else {
-		env[elen] = strdup(DEFAULT_PATH);
-		if (env[elen] == NULL) {
-			elen = 0;
-			goto out;
-		}
-	}
-	if (svcname) {
-		e = strlen(RC_SVCNAME) + strlen(svcname) + 2;
-		env[++elen] = malloc(e);
-		if (env[elen] == NULL) {
-			elen = 0;
-			goto out;
-		}
-		snprintf(env[elen], e, "%s=%s", RC_SVCNAME, svcname);
-	}
-	env[++elen] = NULL;
-
-	pid = exec_script(ifp->ctx, argv, env);
+	pid = exec_script(ctx, argv, ctx->script_env);
 	if (pid == -1)
 		logerr("%s: %s", __func__, argv[0]);
 	else if (pid != 0) {
@@ -782,36 +631,16 @@
 
 send_listeners:
 	/* Send to our listeners */
-	bigenv = NULL;
 	status = 0;
-	TAILQ_FOREACH(fd, &ifp->ctx->control_fds, next) {
+	TAILQ_FOREACH(fd, &ctx->control_fds, next) {
 		if (!(fd->flags & FD_LISTEN))
 			continue;
-		if (bigenv == NULL) {
-			elen = (size_t)arraytostr((const char *const *)env,
-			    &bigenv);
-			if ((ssize_t)elen == -1) {
-				logerr("%s: arraytostr", ifp->name);
-				    break;
-			}
-		}
-		if (control_queue(fd, bigenv, elen, 1) == -1)
+		if (control_queue(fd, ctx->script_buf, ctx->script_buflen, 1)
+		    == -1)
 			logerr("%s: control_queue", __func__);
 		else
 			status = 1;
 	}
-	if (!status)
-		free(bigenv);
 
-out:
-	/* Cleanup */
-	ep = env;
-	while (*ep)
-		free(*ep++);
-	free(env);
-	if (elen == 0) {
-		logerr(__func__);
-		return -1;
-	}
 	return WEXITSTATUS(status);
 }
--- a/src/script.h	Thu Jul 04 12:18:28 2019 +0100
+++ b/src/script.h	Thu Jul 04 12:22:46 2019 +0100
@@ -31,6 +31,7 @@
 
 #include "control.h"
 
+__printflike(2, 3) int efprintf(FILE *, const char *, ...);
 void if_printoptions(void);
 int send_interface(struct fd_list *, const struct interface *);
 int script_runreason(const struct interface *, const char *);