1#!/bin/sh
2# ASR live update script by David van Moolenbroek <david@minix3.org>
3
4# The path to the initial, standard system service binaries.
5SERVICE_PATH=/service
6
7# The path to the alternative, ASR-rerandomized system service binaries.
8# The path used here is typically a symlink into /usr for size reasons.
9SERVICE_ASR_PATH=$SERVICE_PATH/asr
10
11# A space-separated list of labels not to update in any case.  The list
12# includes the memory service, which is currently not compiled with bitcode
13# and therefore also not instrumented.  It also contains the VM service,
14# for which ASR is possible but too dangerous: much of its address space is
15# deliberately ignored by the instrumentation, and ASR would invalidate any
16# pointers from the ignored memory to the relocated memory.  Note that
17# skipped services may still have rerandomized binaries on disk.
18SKIP="memory vm"
19
20# Custom live update states to use for certain label prefixes.  This list is
21# made up of space-separated tokens, each token consisting of a label prefix,
22# followed by a colon, followed by the state number to use for those labels.
23# Currently it contains all services that make use of worker threads.  This
24# setting should not need to exist; see the corresponding TODO item below.
25STATES="vfs:2 ahci_:2 virtio_blk_:2"
26
27# If this variable is set, it is used as timeout for the live updates.  The
28# service(8) argument takes a number of click ticks, or a number of seconds
29# if the value ends with "HZ".
30TIMEOUT=300HZ
31
32# Configuration ends here.
33
34debug() {
35	if [ $verbose -eq 1 ]; then
36		echo "$@"
37	fi
38}
39
40verbose=0
41ret=0
42
43while getopts 'v' opt; do
44	case $opt in
45	v)	verbose=1
46		;;
47	?)	echo "Usage: $0 [-v] [label [label..]]" >&2
48		exit 1
49	esac
50done
51shift $(($OPTIND - 1))
52
53if [ $# -eq 0 ]; then
54	services=$(echo /proc/service/*)
55else
56	services=
57	for label in $@; do
58		services="$services /proc/service/$label"
59	done
60fi
61
62for service in $services; do
63	label=$(basename $service)
64	filename=$(grep filename: $service | cut -d' ' -f2)
65	count=$(grep ASRcount: $service | cut -d' ' -f2)
66
67	# Start by making sure we are not supposed to skip this service.
68	if echo " $SKIP " | grep -q " $label "; then
69		debug "skipping $label: found in skip list"
70		continue
71	fi
72
73	# The base binary of the program has number 0 and must be present.
74	if [ ! -f $SERVICE_PATH/$filename ]; then
75		debug "skipping $label: no base binary found"
76		continue
77	fi
78
79	# Count the ASR binaries for this program, starting from number 1.
80	# There must be at least one, so that we can switch between versions.
81	# By counting using a number rather than using a directory iterator,
82	# we avoid potential problems with gaps between the numbers by
83	# stopping at the first number for which no binary is present.
84	total=1
85	while [ -f $SERVICE_ASR_PATH/$filename-$total ]; do
86		total=$(($total + 1))
87	done
88
89	if [ $total -eq 1 ]; then
90		debug "skipping $label: no ASR binaries found"
91		continue
92	fi
93
94	# Determine the path name of the binary to use for this update.
95	# TODO: pick the next binary at random rather than round-robin.
96	count=$((($count + 1) % $total))
97	if [ $count -eq 0 ]; then
98		binary=$SERVICE_PATH/$filename
99	else
100		binary=$SERVICE_ASR_PATH/$filename-$count
101	fi
102
103	# Check whether the live update should use a state other than the
104	# default (namely state 1, which is "work free").  In particular, any
105	# programs that use threads typically need another state (namely state
106	# 2, which is "request free".  TODO: allow services to specify their
107	# own default state, thus avoiding the redundancy introduced here.
108	state=
109	for token in $STATES; do
110		prefix=$(echo $token | cut -d: -f1)
111		if echo "$label" | grep -q -e "^$prefix"; then
112			state="-state $(echo $token | cut -d: -f2)"
113		fi
114	done
115
116	# Apply a custom timeout if present.  This may be necessary in VMs.
117	maxtime=
118	if [ -n "$TIMEOUT" ]; then
119		maxtime="-maxtime $TIMEOUT"
120	fi
121
122	# Perform the live update.  The update may legitimately fail if the
123	# service is not in the right state.  TODO: report transient errors
124	# as debugging output only.
125	service -a update $binary -progname $filename -label $label \
126		-asr-count $count $state $maxtime
127	error=$?
128	if [ $error -eq 0 ]; then
129		debug "updated $label to number $count, total $total"
130	else
131		echo "failed updating $label: error $error" >&2
132		ret=1
133	fi
134done
135
136exit $ret
137