From 96e42feb3f41e2161141d4958e2637d9dee6f90a Mon Sep 17 00:00:00 2001
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
Date: Tue, 20 Apr 2021 21:31:45 -0600
Subject: [PATCH] wg-quick: kill route monitor when loop terminates

If the route monitor doesn't attempt to write more to stdout, then this
leaves a process hanging around. Kill it explicitly. We also switch to
using exec in the process substitution, to reduce a bash process.

Closes: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=255286
Reported-by: Christos Chatzaras <chris@cretaforce.gr>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
---
 src/wg-quick/darwin.bash  | 10 ++++++----
 src/wg-quick/freebsd.bash |  8 +++++---
 src/wg-quick/openbsd.bash |  8 +++++---
 3 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/src/wg-quick/darwin.bash b/src/wg-quick/darwin.bash
index cde1b54..c77441f 100755
--- a/src/wg-quick/darwin.bash
+++ b/src/wg-quick/darwin.bash
@@ -324,22 +324,24 @@ monitor_daemon() {
 	echo "[+] Backgrounding route monitor" >&2
 	(trap 'del_routes; del_dns; exit 0' INT TERM EXIT
 	exec >/dev/null 2>&1
-	local event pid=$BASHPID
+	exec 19< <(exec route -n monitor)
+	local event bpid=$BASHPID mpid=$!
 	[[ ${#DNS[@]} -gt 0 ]] && trap set_dns ALRM
 	# TODO: this should also check to see if the endpoint actually changes
 	# in response to incoming packets, and then call set_endpoint_direct_route
 	# then too. That function should be able to gracefully cleanup if the
 	# endpoints change.
-	while read -r event; do
+	while read -u 19 -r event; do
 		[[ $event == RTM_* ]] || continue
 		ifconfig "$REAL_INTERFACE" >/dev/null 2>&1 || break
 		[[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
 		[[ -z $MTU ]] && set_mtu
 		if [[ ${#DNS[@]} -gt 0 ]]; then
 			set_dns
-			sleep 2 && kill -ALRM $pid 2>/dev/null &
+			sleep 2 && kill -ALRM $bpid 2>/dev/null &
 		fi
-	done < <(route -n monitor)) &
+	done
+	kill $mpid) &
 	[[ -n $LAUNCHED_BY_LAUNCHD ]] || disown
 }
 
diff --git a/src/wg-quick/freebsd.bash b/src/wg-quick/freebsd.bash
index e731384..b529ab2 100755
--- a/src/wg-quick/freebsd.bash
+++ b/src/wg-quick/freebsd.bash
@@ -284,17 +284,19 @@ monitor_daemon() {
 	(make_temp
 	trap 'del_routes; clean_temp; exit 0' INT TERM EXIT
 	exec >/dev/null 2>&1
-	local event
+	exec 19< <(exec route -n monitor)
+	local event pid=$!
 	# TODO: this should also check to see if the endpoint actually changes
 	# in response to incoming packets, and then call set_endpoint_direct_route
 	# then too. That function should be able to gracefully cleanup if the
 	# endpoints change.
-	while read -r event; do
+	while read -u 19 -r event; do
 		[[ $event == RTM_* ]] || continue
 		ifconfig "$INTERFACE" >/dev/null 2>&1 || break
 		[[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
 		# TODO: set the mtu as well, but only if up
-	done < <(route -n monitor)) & disown
+	done
+	kill $pid) & disown
 }
 
 HAVE_SET_DNS=0
diff --git a/src/wg-quick/openbsd.bash b/src/wg-quick/openbsd.bash
index 15550c8..9826aa3 100755
--- a/src/wg-quick/openbsd.bash
+++ b/src/wg-quick/openbsd.bash
@@ -266,17 +266,19 @@ monitor_daemon() {
 	echo "[+] Backgrounding route monitor" >&2
 	(trap 'del_routes; exit 0' INT TERM EXIT
 	exec >/dev/null 2>&1
-	local event
+	exec 19< <(exec route -n monitor)
+	local event pid=$!
 	# TODO: this should also check to see if the endpoint actually changes
 	# in response to incoming packets, and then call set_endpoint_direct_route
 	# then too. That function should be able to gracefully cleanup if the
 	# endpoints change.
-	while read -r event; do
+	while read -u 19 -r event; do
 		[[ $event == RTM_* ]] || continue
 		ifconfig "$REAL_INTERFACE" >/dev/null 2>&1 || break
 		[[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
 		# TODO: set the mtu as well, but only if up
-	done < <(route -n monitor)) & disown
+	done
+	kill $pid) & disown
 }
 
 set_dns() {