summaryrefslogtreecommitdiffstats
path: root/core/modules/dhcpc-busybox/data/opt/openslx/scripts/udhcpc-openslx
blob: 05a08b8fca4edbe11bfd495f3de0e9fb7bf6239f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
#!/bin/bash
# Needs bash for printf -v VARNAME
# -----------------------------------------------------------------------------
#
# Copyright (c) 2011..2018 bwLehrpool-Projektteam
#
# This program/file is free software distributed under the GPL version 2.
# See https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
#
# If you have any feedback please consult https://bwlehrpool.de and
# send your feedback to support@bwlehrpool.de.
#
# General information about bwLehrpool can be found at https://bwlehrpool.de
#
# -----------------------------------------------------------------------------
#
#                               Mini-Linux Toolkit
#
# -----------------------------------------------------------------------------

# DO NOT RENAME/MOVE THIS FILE!
# The slx-network dracut module checks for its existence

. /opt/openslx/config

declare -rg primary="${SLX_BRIDGE:-br0}"

declare -rg RESOLV_CONF="/opt/openslx/resolv.conf"
declare -rg THIS_RESOLV="/run/network/${interface}.resolv"
declare -rg flag="/run/network/primary-dhcp.flag"

if [ "$1" = "--lock-hostname-updates" ]; then
	# If we change the hostname while Xorg is running, this might
	# cause issues, depending on how it's set up. So we run this
	# script with the lock argument to touch the flag file right
	# before we try to launch lightdm/Xorg. In case the DHCP request
	# is too slow, this means we'll run with the wrong hostname
	# (potentially), but that's better than delaying the bootup
	# unnecessarily.
	# So also note that if you ever change this script's name/location
	# (JUST DON'T), you'd need to update all call sites, i.e. the
	# udhcpc startup script, and the lightdm service drop-in.
	mkdir -p /run/network
	touch "$flag"
	exit 0
fi

shopt -s extglob

rebuild_resolv_conf () {
	# Don't do anything if the active resolv.conf is not ours
	# Also this will not run resolvconf update.d... No idea if we should still do so...
	[ -L "/etc/resolv.conf" ] || return 0
	[ "$(readlink -f "/etc/resolv.conf")" = "${RESOLV_CONF}" ] || return 0
	# Fake file to merge in search domains from /opt/openslx/config
	echo "search $SLX_NET_SEARCH" > "/run/network/zzzzz-openslx.resolv"
	# Use extglob trickery to make sure br0/$primary stuff comes first
	# Then print them in a first-come-first-served manner.
	# Print nameserver entries one per line, print only first domain value,
	# group everything else together into one line.
	# Skip domain entirely if any search lines are found.
	cat "/run/network/$primary.resolv" /run/network/!("$primary").resolv \
		| gawk '{
			if ( $1 ~ /^[a-z]+$/ ) {
				for (i = 2; i <= NF; ++i) {
					if (done[$1][$i])
						continue
					done[$1][$i] = 1
					output[$1][++idx] = $i
				}
			} else {
				print $0
			}
		}
		END {
			for (s in output) {
				if (s == "nameserver") {
					for (t in output[s]) {
						print s " " output[s][t]
					}
				} else if (s == "domain" && isarray(output["search"])) {
				} else {
					printf "%s", s
					for (t in output[s]) {
						printf " %s", output[s][t]
						if (s == "domain")
							break
					}
					print ""
				}
			}
		}' \
		> "$RESOLV_CONF" 2> /dev/null

	# add support for resolv.conf update scripts // see man(8) resolvconf
	for s in /etc/resolvconf/update.d/*.sh; do
		[ -f "$s" ] && [ -x "$s" ] && "$s"
	done
}

escape_search() {
	sed -e 's/[]\/()$*.^|[]/\\&/g' <<<"$@"
}

escape_replace() {
	sed -e 's/[\/&]/\\&/g' <<<"$@"
}

check_env() {
	if [ -z "$ip" ] || [ -z "$subnet" ] || [ -z "$interface" ]; then
		echo "$1 event with missing data" >&2
		echo "ip = '$ip'" >&2
		echo "subnet = '$subnet'" >&2
		echo "interface = '$interface'" >&2
		exit 1
	fi
}

mkdir -p "/run/network"

case "$1" in
	bound|renew)
		check_env "$1"
		mkdir -p "/run/dhcpc"
		# Set address on interface
		ip addr add "$ip/$( ipcalc -s -p "$ip" "$subnet" | sed 's/.*=//' )" dev "$interface"
		# Set default route, if given
		if [ -n "$router" ]; then
			# Only replace route if it's the same interface as the current default route, or we don't have any
			current="$( ip route show | awk '{ if ($1 == "default") {print $5; exit 0}}' )"
			if [ -z "$current" ] || [ "$interface" = "$current" ]; then
				ip route replace default via "$router"
			fi
		fi

		# get domain, hostname and thus fqdn from DNS
		dns_fqdn=$(busybox timeout 3 rdns "$ip")
		dns_short="${dns_fqdn%%.*}"
		# check if it is fqdn
		if [ "$dns_fqdn" == "$dns_short" ]; then
			unset dns_fqdn dns_short
		fi

		# Update resolver configuration file
		conf_file="$(mktemp)"
		# Own domain suffix
		if [ -n "$domain" ]; then
			:
		elif [ -n "$dns_fqdn" ]; then
			domain="${dns_fqdn#*.}"

		elif [ -n "$SLX_NET_DOMAIN" ]; then
			domain="$SLX_NET_DOMAIN"
		fi
		if [ -n "$domain" ]; then
			echo "domain ${domain%% *}" >> "${conf_file}"
		fi
		# Add domain to list of search domains if not in there yet
		if [ -n "$domain" ] && [ -n "$search" ]; then
			FOUND=no
			for sd in $search; do
				[ "x$sd" = "x$domain" ] && FOUND=yes
			done
			[ "$FOUND" = "no" ] && search="$domain $search"
		elif [ -n "$domain" ]; then
			search="$domain"
		fi
		# Search domains
		if [ -n "$search" ]; then
			echo "search $search" >> "${conf_file}"
		elif [ -n "$SLX_NET_SEARCH" ]; then
			echo "search $SLX_NET_SEARCH" >> "${conf_file}"
		elif [ -n "$SLX_NET_DOMAIN" ]; then
			echo "search $SLX_NET_DOMAIN" >> "${conf_file}"
		fi
		for i in $dns; do
			echo "$0: Adding DNS $i"
			echo "nameserver $i" >> "${conf_file}"
		done
		
		# Manual handling required :-(
		mv -f "$conf_file" "$THIS_RESOLV"
		rebuild_resolv_conf

		
		# Things that should only happen for the main interface that was used for booting
		if [ "$interface" = "$primary" ]; then
			# Update IP
			sed -i "s/^\(SLX_PXE_CLIENT_IP=\).*$/\1'$ip'/" /opt/openslx/config
			# Write DOMAIN and SEARCH to /opt/openslx/config if empty
			if [ -z "$SLX_NET_DOMAIN" ] && [ -n "$domain" ]; then
				echo "SLX_NET_DOMAIN='$domain'" >> /opt/openslx/config
			fi
			if [ -z "$SLX_NET_SEARCH" ] && [ -n "$search" ]; then
				echo "SLX_NET_SEARCH='$search'" >> /opt/openslx/config
			fi
			# Same for WINS servers
			if [ -z "$SLX_NET_WINS" ] && [ -n "$wins" ]; then
				echo "SLX_NET_WINS='$wins'" >> /opt/openslx/config
			fi

			# Only update hostname if network is not ready yet
			# later on this might cause issues
			if ! [ -e "$flag" ]; then
				# Fallback for hostname
				if [ -z "$dns_fqdn" ] && [ -n "$domain" ] && [ -n "$hostname" ]; then
					# fallback to what the dhcp told us
					dns_fqdn="${hostname}.${domain}"
				fi
				if [ -z "$dns_fqdn" ]; then
					# Try currently set fqdn
					dns_fqdn="$( hostname -f )"
				fi
				if [ -z "$dns_fqdn" ]; then
					if [ -s "/etc/hostname" ]; then
						dns_fqdn="$( head -n 1 /etc/hostname )"
					else
						# Final fallback, nothing valid found
						dns_fqdn="noname-${ip//./-}.invalid"
					fi
				fi
				# finally, if dns_fqdn was set to anything, apply it
				if [ -n "$dns_fqdn" ]; then
					dns_short="${dns_fqdn%%.*}"
					echo "$dns_short" > "/proc/sys/kernel/hostname"
					echo "$dns_short" > "/etc/hostname"
					if [ -z "$SLX_HOSTNAME" ]; then
						echo "# Config written by openslx-dhcp-script (1)" >> /opt/openslx/config
						echo "SLX_HOSTNAME='$dns_short'" >> /opt/openslx/config
					elif [ "$SLX_HOSTNAME" != "$dns_short" ]; then
						sed -i "s/^\(SLX_HOSTNAME=\).*$/\1'$dns_short'/" /opt/openslx/config
					fi
				fi

				# Update /etc/issue for proper spacing
				/opt/openslx/scripts/openslx-create_issue
				touch "$flag"
				# end "network not ready yet"
			else
				# Network already going, make sure we don't change the primary hostname in /etc/hosts
				hostname=
				domain=
				dns_fqdn=
			fi
			# Remove any stray addresses; we expect the primary interface to only have one
			# address supplied via DHCP. We do this after adding the new one, obviously.
			rem_list=$( ip -o addr show "$interface" | awk '{ for (i=1;i<NF;++i) if ($i == "inet") print $(i+1) }' | grep -v "^${ip}/" )
			if [ -n "$rem_list" ]; then
				echo "PRIMARY: Removing $rem_list since we just got assigned $ip"
				echo 1 > "/proc/sys/net/ipv4/conf/$interface/promote_secondaries"
				for addr in $rem_list; do
					ip addr del "$addr" dev "$interface"
					sed -i "/^$(escape_search "${addr%/*}")(\s|$)/d" /etc/hosts
				done
			fi
		fi # end "primary only"
		
		# Hostname in /etc/hosts
		touch "/etc/hosts"
		hostlist=""
		[ -n "$dns_fqdn" ] && hostlist="$dns_fqdn"
		[ -n "$hostname" ] && [ -n "$domain" ] && [ "x${hostname}.${domain}" != "x$dns_fqdn" ] && hostlist="$hostlist ${hostname}.${domain}"
		if [ -n "$hostlist" ]; then
			for host in $hostlist; do
				host_short="${host%%.*}"
				[ "x$host_short" = "x$host" ] && host_short=""
				sed -i -r "s/\s$(escape_search "$host")(\s|$)/ /g" /etc/hosts
				[ -n "$host_short" ] && sed -i -r "s/\s$(escape_search "$host_short")(\s|$)/ /g" /etc/hosts
				if grep -q -E "^$ip\s" /etc/hosts; then
					sed -i "s/^$(escape_search "$ip")\s.*/$(escape_replace "$ip $host $host_short")/g" /etc/hosts
				else
					echo "$ip $host $host_short" >> /etc/hosts
				fi
			done
		fi
		# Get rid of orphaned lines in /etc/hosts
		sed -i -r '/^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\s*$/d' /etc/hosts
		
		if [ -n "$dns" ] && [ -z "$SLX_DNS" ]; then
			# Write to openslx-config
			echo "# Config written by openslx-dhcp-script (2)" >> /opt/openslx/config
			echo "SLX_DNS='$dns'" >> /opt/openslx/config
		fi

		# Rewrite nat1 dhcpd config for VMs, and restart dnsmasq if it changed
		/opt/openslx/scripts/runvirt-start_dhcpd
	;;
	
	deconfig)
		check_env "$1"
		if [ "$interface" = "$primary" ]; then
			echo "Ignoring deconfig for primary interface"
		else
			echo 1 > "/proc/sys/net/ipv4/conf/$interface/promote_secondaries"
			clientip=${ip%%:*}
			ip addr del "$clientip/$(ipcalc -s -p "$clientip" "$subnet" | sed s/.*=//)" dev "$interface"
			sed -i "/^$(escape_search "$ip")(\s|$)/d" /etc/hosts
		fi
		
		# Manual handling required :-(
		rm -f -- "$THIS_RESOLV"
		rebuild_resolv_conf
	;;
	
	leasefail)
		echo "$0: Lease failed: $message" >&2
		
	;;
	
	nak)
		echo "$0: Received a NAK: $message" >&2
		
	;;
	
	*)
		echo "$0: Unknown udhcpc command: $1" >&2
		exit 1
		
	;;
esac

exit 0