summaryrefslogtreecommitdiff
path: root/packages/slugos-init/files/functions
blob: 5b6b40b0910e7976de68d39eef2bb82de813cde6 (plain)
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
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
#!/bin/sh
# . this file to load the following utility functions
#
# hardware
#  the 'Hardware' string from cpuinfo
hardware(){
	sed -n 's!^Hardware	*: !!p' /proc/cpuinfo
}
#
# machine
#  outputs an identifier of the current machine - i.e. the board
#  slugos is running on.
machine(){
	case "$(hardware)" in
	*Coyote*)	echo coyote;;
	*IXDPG425*)	echo ixdpg425;;
	*WRV54G*)	echo wrv54g;;
	*IXDP425*)	echo ixdp425;;
	*IXDP465*)	echo ixdp465;;
	*IXCDP1100*)	echo ixcdp1100*;;
	*Avila*)	echo avila;;
	*Loft*)		echo loft;;
	*NAS?100d*)	echo nas100d;;
	*DSM?G600*)	echo dsmg600;;
	*NSLU2*)	echo nslu2;;
	*FSG?3*)	echo fsg3;;
	*)		echo unknown;;
	esac
}
#
# single_user_ok
#  if the machine is capable of single user interaction return
#  true, else return false.  The result of this function is
#  preempted by setting SULOGIN to 'yes' or 'ok' in /etc/default/rcS
single_user_ok() {
	# list known good machines in the 'case'
	test "$SULOGIN" = yes -o "$SULOGIN" = ok ||
		case "$(machine)" in
		ixdp*|avila|loft)
			test "$SULOGIN" != never;;
		*)	return 1;;
		esac
}
#
# load_functions "source"
#  load the functions in '/sbin/source' - relies on /sbin/source being
#  a shell script and having support for this function.
load_functions(){
	test -n "$1" -a -x "/sbin/$1" && . "/sbin/$1" || {
		echo "$0: /sbin/$1: script not found" >&2
		return 1
	}
}
#
# mtdev "name"
#  return (output) the character device name for flash parition "name"
#  /proc/mtd has the general form:
#    dev:    size   erasesize  name
#    mtd5: 00020000 00020000 "FIS directory"
#  use this rather than hard-wiring the device because the partition
#  table can change - looking in /proc/mtd is more reliable.
mtdev(){
	sed -n 's!^\(mtd[0-9][0-9]*\):[^"]*"'"$1"'"$!/dev/\1!p' /proc/mtd
}
#
# mtblockdev "name"
#  as mtdev but output the name of the block (not character) device
mtblockdev(){
	sed -n 's!^mtd\([0-9][0-9]*\):[^"]*"'"$1"'"$!/dev/mtdblock\1!p' /proc/mtd
}
#
# mtsize "name"
#  the size of the partition as a hexadecimal value (with 0x at the front)
mtsize(){
	sed -n 's!^mtd[0-9][0-9]*: \([^ ]*\)[^"]*"'"$1"'"$!0x\1!p' /proc/mtd
}
#
# sysvalmatch "section" "name" 'pattern' "configuration file"
# sysvalof "section" "name" "configuration file"
# sysval "section" "name"
#  outputs the value of the SysConf variable 'name' from section 'section',
#  if there are multiple definitions only the last is output
# NOTE: these functions should only be used internally, add entries to 'config'
#  below if necessary.  This is because 'config' does the defaulting.
sysvalmatch(){
	sed -n '/^\['"$1"'\]$/,/^\[.*\]$/s/^'"$2"'=\('"$3"'\)$/\1/p' "$4" | sed -n '$p'
}
sysvalof(){
	sysvalmatch "$1" "$2" '.*' "$3"
}
sysval(){
	test -r "$config_root/etc/default/sysconf" &&
		sysvalof "$1" "$2" "$config_root/etc/default/sysconf"
}
#
# syssection "section"
#  outputs all the values from the given section changed to the format "name value"
#  (i.e. the '=' is dropped).
syssection(){
	test -r "$config_root/etc/default/sysconf" &&
		sed -n '/^\['"$1"'\]$/,/^\[.*\]$/s/^\([^=]*\)=\(.*\)$/\1 \2/p' "$config_root/etc/default/sysconf"
}
#
# config "value"
#  convenience callers for specific values to avoid mis-typing in scripts
#  NOTE: this function does the defaulting, 'sysval' does not!
# config_root: if set this will override the root where config/sysval
#              looks for /etc/default/sysconf
config(){
	local mac
	mac="$(test -r /proc/net/maclist &&
		sed -n '/^[0-9A-Za-z][0-9A-Za-z]:[0-9A-Za-z][0-9A-Za-z]:[0-9A-Za-z][0-9A-Za-z]:[0-9A-Za-z][0-9A-Za-z]:[0-9A-Za-z][0-9A-Za-z]:[0-9A-Za-z][0-9A-Za-z]$/p' /proc/net/maclist |
		sed -n 1p)"
	#
	case "$1" in
	mac)	test -n "$mac" && echo "$mac";;
	host)	if test -n "$(sysval network disk_server_name)"
		then
			sysval network disk_server_name
		elif test -n "$(sysval network default_server_name)"
		then
			sysval network default_server_name
		elif test -n "$mac"
		then
			echo "$mac" | sed -n 's/^\(..\):\(..\):\(..\):\(..\):\(..\):\(..\)$/slug\1\2\3\4\5\6/p'
		else
			# because we want the name to remain constant:
			echo "brokenslug"
		fi;;
	domain)	sysval network w_d_name;;
	iface)  if test -n "$(sysval network lan_interface)"
		then
			sysval network lan_interface
		else
			echo eth0
		fi;;
	ip)	if test -n "$(sysval network ip_addr)"
		then
			sysval network ip_addr
		else
			echo 192.168.1.77
		fi;;
	netmask)sysval network netmask;;
	gateway)sysval network gateway;;
	dns)	sysval network dns_server1;;
	dns2)	sysval network dns_server2;;
	dns3)	sysval network dns_server3;;
	boot)	if test -n "$(sysval network bootproto)"
		then
			sysval network bootproto
		else
			echo dhcp
		fi;;
	valid)	test -r "$config_root/etc/default/sysconf" -a -n "$mac";;
	*)	return 1;;
	esac
}
#
# checkif "iface"
#  Validate an interface name by making sure that it exists
#  in /proc/net/dev (and is not lo).  The listing outputs the
#  interface followed by a :, the check function looks for
#  something of the form '$1[a-zA-Z0-9]*:' and outputs the
#  part preceding the ':'
checkif(){
	sed -n '/^[ 	]*lo:/d;s/^[ 	]*\('"$1"'[a-zA-Z0-9]*\):.*$/\1/p;tE;d;:E;q' /proc/net/dev
}
#
# checkmount "mountpoint"
#  tests an already mounted mountpoint to see whether to attempt to
#  boot with this as root.  Returns success if it appears ok.
checkmount(){
	# basic test for init (the kernel will try to load this)
	# but require a shell in bin/sh too
	test	\( -d "$1/mnt" \) -a \
		\( -d "$1/dev" \) -a \
		\( -x "$1/bin/sh" -o -h "$1/bin/sh" \) -a \
		\( -x "$1/usr/sbin/chroot" -o -h "$1/usr/sbin/chroot" -o \
		   -x "$1/sbin/chroot" -o -h "$1/sbin/chroot" \) -a \
		\( -x "$1/sbin/init" -o -h "$1/sbin/init" -o \
		   -x "$1/etc/init" -o -h "$1/etc/init" -o \
		   -x "$1/bin/init" -o -h "$1/bin/init" \)
}
#
# minimaldevnodes "mountpoint"
#  tests an already mounted mountpoint to see if a very minimal
#  set of devices exists or can be created in dev, and returns
#  failure if not.  This is required for booting to an nfsroot
#  with an empty dev directory, as commonly occurs when the rootfs
#  is created from a tar.gz image.  This is also required for mdev.
minimaldevnodes(){
	[ -c "$1/dev/console" ] || mknod -m 600 "$1/dev/console" c 5 1 || return 1
	[ -c "$1/dev/null"    ] || mknod -m 666 "$1/dev/null"    c 1 3 || return 1
	return 0
}
#
# swivel "new root" "old root"
#  NOTE: the arguments must be paths relative to /, bad things
#  will happen if the arguments themselves start with /
#  Pivot to a new root.  This does all the fancy pivot_root stuff
#  including closing streams and does a umount /proc - it doesn't
#  matter if this fails (failure codes are ignored), but if /proc
#  was mounted it must be restored by the caller on return.
#  Normally this function never returns!
#  On return 0,1,2 are connected to /dev/console - this may not
#  have been true before!
swivel(){
	cd "$1"
	exec <&- >&- 2>&-
	# This is just-in-case the called mounted /proc and was
	# unable to close it because of the streams
	umount /proc 2>/dev/null
	if pivot_root . "$2"
	then
		# everything must move out of the old root, this process
		# is $2/bin/sh so it must die, IO is redirected
		# just in case - typically it will be to a device so it
		# won't hold the old root open.
		# the exec here is the first point at which the old root
		# is unused - before the exec regardless of the close of
		# 0,1,2 above ash still has *this* shell script open!
		# (it's on fd 10).
		# init closes all file descriptors, there's no point
		# supplying it with fds.
		# NOTE: this used to use $2/usr/sbin/chroot, however on
		# linux / is already . when the command is executed
		# therefore it is essential to use the local (new root)
		# chroot to ensure it gets the correct shared libraries.
		if test -x usr/sbin/chroot -o -h usr/sbin/chroot
		then
			chroot=usr/sbin/chroot
		elif test -x sbin/chroot -o -h sbin/chroot
		then
			chroot=sbin/chroot
		else
			chroot=chroot
		fi
		#
		exec "$chroot" . bin/sh -c "\
			test -x sbin/init && exec sbin/init
			test -x etc/init && exec etc/init
			test -x bin/init && exec bin/init
			mount -t sysfs sysfs /mnt
			echo -n timer >/mnt/class/leds/ready/trigger
			echo -n timer >/mnt/class/leds/status/trigger
			echo -n 80 >/mnt/class/leds/ready/frequency
			echo -n 80 >/mnt/class/leds/status/frequency
			umount /mnt
			sync;sync;sync
			exit 1"
	fi
	#
	# recovery - must restore the old root
	cd "$2"
	sbin/pivot_root . "$1"
	# cd is back to $1 - either pivot_root doesn't change it and the
	# chroot above was not executed, or pivot_root does change it and
	# has just changed it back!
	exec <>/dev/console >&0 2>&0
}
#
# ifup "interface"
#  bring that interface up with the configured ip and other
#  information
ifup(){
	local ip hostname router subnet iface HOSTNAME NETMASK BROADCAST

	iface="$1"
	ip="$(config ip)"
	hostname="$(config host)"
	router="$(config gateway)"
	broadcast=

	if test -n "$ip"
	then
		# only if an ip was specified
		subnet="$(config netmask)"
	else
		ip=192.168.1.77
	fi

	# First try udhcpc - note that the /boot/udhcpc.script
	# simply records the values returned and the udhcpc
	# is not left running so this will only work for
	# the lease length time!
	ifconfig "$iface" up
	if test "$(config boot)" != static
	then
		test -n "$hostname" && HOSTNAME="-H $hostname"
		# Pause a moment in case link negotiation takes a while
		sleep 3
		# The script writes the required shell variable assignments
		# to file descriptor 9
		eval $(udhcpc -i "$iface" -n -q -r "$ip" $HOSTNAME -s /boot/udhcpc.script 9>&1 >/dev/null)
	fi

	test -n "$broadcast" && BROADCAST="broadcast $broadcast"
	test -n "$subnet" && NETMASK="netmask $subnet"

	if ifconfig "$iface" "$ip" $NETMASK $BROADCAST
	then
		for route in $router
		do
			route add default gw "$route" dev "$iface"
		done
		return 0
	else
		ifconfig "$iface" down
		return 1
	fi
}
#
# ifdown "interface"
#  take the interface down
ifdown(){
	ifconfig "$1" down
}
#
# mountflash "flash device" "flash root directory" {mount options}
#  Finds and mounts the flash file system on the given directory
mountflash(){
	local ffsdev ffsdir

	ffsdev="$1"
	test -n "$ffsdev" -a -b "$ffsdev" || {
		echo "$0: unable to find flash file system to copy ($ffsdev)" >&2
		return 1
	}
	shift

	ffsdir="$1"
	test -n "$ffsdir" -a -d "$ffsdir" || {
		echo "$0: mountflash $ffsdir: not a directory (internal error)" >&2
		return 1
	}
	shift

	mount -t jffs2 "$@" "$ffsdev" "$ffsdir" || {
		echo "$0: $ffsdev: unable to mount flash file system on $ffsdir" >&2
		return 1
	}
	return 0
}
#
# umountflash [-r] "flash device"
#  unmount any instance of the given flash device, if -r is specified a mount on
#  root is an error, otherwise a mount on root is ignored (and remains).
umountflash(){
	local rootok ffsno ffsdev
	rootok=1
	case "$1" in
	-r)	rootok=
		shift;;
	esac
	#
	# The argument is ffsdev
	ffsdev="$1"
	ffsno="$(devio "<<$ffsdev" prd)"
	test -n "$ffsno" -a "$ffsno" -ge 0 || {
		echo "$0: $ffsdev: device number $ffsno is not valid, cannot continue." >&2
		return 1
	}
	#
	# Make sure that Flashdisk isn't mounted on /
	if test -z "$rootok" -a "$(devio "<</etc/init.d/sysconfsetup" prd)" -eq "$ffsno"
	then
		echo "$0: $ffsdev is mounted on /, use turnup ram" >&2
		return 1
	fi
	#
	# The function is currently always used interactively, so output 
	echo "$0: umounting any existing mount of $ffsdev" >&2
	#
	# check each mount point, do this last first because otherwise nested
	# mounts of ffsdev cannot be umounted.
	ffs_umount() {
		local device mp type options stuff

		read device mp type options stuff
		test -z "$device" && return 0

		# handle following entries first
		ffs_umount || return 1

		# handle this entry, since this is currently only used for unmounting
		# the flash root partition we know a file which must exist...
		case "$mp/$type" in
		//jffs2);; # skip /
		*/jffs2)test "$(devio "<<$mp/etc/init.d/sysconfsetup" prd 2>/dev/null)" -ne "$ffsno" ||
			umount "$mp" || {
				echo "$0: $mp: unable to umount $ffsdev" >&2
				return 1
			};;
		esac

		return 0
	}
	#
	ffs_umount </proc/mounts || {
		echo "$0: umount $ffsdev from all mount points then re-run $0" >&2
		return 1
	}

	return 0
}

#
# uuid_by_partition
#  output a list of partitions and their UUIDs
uuid_by_partition() {
	blkid -c /dev/null -s UUID | sed -n 's/^\([^:]*\): .*UUID="\([^"]*\)".*$/\1 \2/p'
}

#
# partition_of uuid
#  return the partition corresponding to the UUID
partition_of() {
	sed -n 's/^\([^ ]*\) '"$1"'$/\1/p'
}