Skip to content

Commit d34e51e

Browse files
committed
chore(scripts): support remote pre-compiled binary
1 parent ae0f62a commit d34e51e

1 file changed

Lines changed: 243 additions & 34 deletions

File tree

scripts/install.sh

Lines changed: 243 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,206 @@ myname=${0##*/}
33

44
set -e
55

6-
install_rattan() {
7-
local binary_path=${1:-"rattan"}
8-
install_binary "$binary_path"
9-
config_networkd
6+
GITHUB_REPO="stack-rs/rattan"
7+
APP_NAME="rattan"
8+
9+
# --- Utility functions ---
10+
11+
err() {
12+
local red reset
13+
red=$(tput setaf 1 2>/dev/null || echo '')
14+
reset=$(tput sgr0 2>/dev/null || echo '')
15+
echo "${red}ERROR${reset}: $1" >&2
16+
exit 1
17+
}
18+
19+
warn() {
20+
local yellow reset
21+
yellow=$(tput setaf 3 2>/dev/null || echo '')
22+
reset=$(tput sgr0 2>/dev/null || echo '')
23+
echo "${yellow}WARN${reset}: $1" >&2
24+
}
25+
26+
need_cmd() {
27+
if ! command -v "$1" >/dev/null 2>&1; then
28+
err "need '$1' (command not found)"
29+
fi
30+
}
31+
32+
# This wraps curl or wget. Try curl first, if not installed, use wget instead.
33+
downloader() {
34+
local _dld
35+
local _snap_curl=0
36+
37+
if command -v curl >/dev/null 2>&1; then
38+
if echo "$(command -v curl)" | grep -q '/snap/'; then
39+
_snap_curl=1
40+
fi
41+
fi
42+
43+
if command -v curl >/dev/null 2>&1 && [ "$_snap_curl" = "0" ]; then
44+
_dld=curl
45+
elif command -v wget >/dev/null 2>&1; then
46+
_dld=wget
47+
elif [ "$_snap_curl" = "1" ]; then
48+
err "curl installed via snap cannot download files due to missing permissions. Please reinstall curl with apt or another package manager."
49+
else
50+
err "need 'curl' or 'wget' (command not found)"
51+
fi
52+
53+
if [ "$_dld" = "curl" ]; then
54+
curl -sSfL "$1" -o "$2"
55+
elif [ "$_dld" = "wget" ]; then
56+
wget -q "$1" -O "$2"
57+
fi
58+
}
59+
60+
# --- Architecture detection ---
61+
check_glibc() {
62+
local _min_major="$1"
63+
local _min_minor="$2"
64+
local _local_glibc
65+
66+
_local_glibc="$(ldd --version 2>/dev/null | awk -F' ' 'FNR==1 { print $NF }')"
67+
if [ -z "$_local_glibc" ]; then
68+
return 1
69+
fi
70+
71+
local _major _minor
72+
_major="$(echo "$_local_glibc" | awk -F. '{ print $1 }')"
73+
_minor="$(echo "$_local_glibc" | awk -F. '{ print $2 }')"
74+
75+
if [ "$_major" = "$_min_major" ] && [ "$_minor" -ge "$_min_minor" ] 2>/dev/null; then
76+
return 0
77+
else
78+
warn "System glibc version ('${_local_glibc}') may be too old; trying musl variant"
79+
return 1
80+
fi
81+
}
82+
83+
get_target_triple() {
84+
local _ostype _cputype _clibtype
85+
86+
need_cmd uname
87+
88+
_ostype="$(uname -s)"
89+
_cputype="$(uname -m)"
90+
91+
if [ "$_ostype" != "Linux" ]; then
92+
err "$APP_NAME only supports Linux (requires network namespaces and XDP). Detected OS: $_ostype"
93+
fi
94+
95+
# Detect musl vs glibc
96+
if ldd --version 2>&1 | grep -q 'musl'; then
97+
_clibtype="musl"
98+
else
99+
_clibtype="gnu"
100+
fi
101+
102+
case "$_cputype" in
103+
x86_64 | x86-64 | x64 | amd64)
104+
_cputype=x86_64
105+
;;
106+
aarch64 | arm64)
107+
_cputype=aarch64
108+
;;
109+
*)
110+
err "Unsupported CPU architecture: $_cputype. Only x86_64 and aarch64 are supported."
111+
;;
112+
esac
113+
114+
# For glibc targets, check the minimum required version.
115+
# Fall back to musl if glibc is too old.
116+
if [ "$_clibtype" = "gnu" ]; then
117+
if ! check_glibc "2" "39"; then
118+
_clibtype="musl"
119+
fi
120+
fi
121+
122+
echo "${_cputype}-unknown-linux-${_clibtype}"
10123
}
11124

125+
# --- GitHub release download ---
126+
get_latest_release_tag() {
127+
local _api_url="https://api.github.com/repos/${GITHUB_REPO}/releases/latest"
128+
local _tag
129+
130+
if command -v curl >/dev/null 2>&1; then
131+
_tag="$(curl -sSfL "$_api_url" | grep '"tag_name"' | head -1 | sed 's/.*"tag_name": *"\([^"]*\)".*/\1/')"
132+
elif command -v wget >/dev/null 2>&1; then
133+
_tag="$(wget -qO- "$_api_url" | grep '"tag_name"' | head -1 | sed 's/.*"tag_name": *"\([^"]*\)".*/\1/')"
134+
else
135+
err "need 'curl' or 'wget' to fetch the latest release"
136+
fi
137+
138+
if [ -z "$_tag" ]; then
139+
err "Failed to determine the latest release tag from GitHub. Check your network connection."
140+
fi
141+
142+
echo "$_tag"
143+
}
144+
145+
download_rattan() {
146+
need_cmd uname
147+
need_cmd mktemp
148+
need_cmd tar
149+
150+
local _target
151+
_target="$(get_target_triple)"
152+
echo "Detected platform: $_target" >&2
153+
154+
local _tag
155+
_tag="$(get_latest_release_tag)"
156+
echo "Latest release: $_tag" >&2
157+
158+
local _archive="${APP_NAME}-${_target}.tar.xz"
159+
local _url="https://github.com/${GITHUB_REPO}/releases/download/${_tag}/${_archive}"
160+
161+
local _dir
162+
_dir="$(mktemp -d)"
163+
local _file="${_dir}/${_archive}"
164+
165+
echo "Downloading ${_url} ..." >&2
166+
if ! downloader "$_url" "$_file"; then
167+
# Try .tar.gz if .tar.xz fails
168+
_archive="${APP_NAME}-${_target}.tar.gz"
169+
_url="https://github.com/${GITHUB_REPO}/releases/download/${_tag}/${_archive}"
170+
_file="${_dir}/${_archive}"
171+
echo "Retrying with ${_url} ..." >&2
172+
if ! downloader "$_url" "$_file"; then
173+
rm -rf "$_dir"
174+
err "Failed to download release archive. No compatible release found for platform '${_target}'."
175+
fi
176+
fi
177+
178+
echo "Extracting archive..." >&2
179+
tar xf "$_file" --strip-components 1 -C "$_dir"
180+
181+
local _binary="${_dir}/${APP_NAME}"
182+
if [ ! -f "$_binary" ]; then
183+
rm -rf "$_dir"
184+
err "Binary '${APP_NAME}' not found in extracted archive."
185+
fi
186+
187+
echo "$_binary"
188+
# Caller is responsible for cleanup; pass dir via global so we can rm it after install
189+
_DOWNLOAD_TMPDIR="$_dir"
190+
}
191+
192+
# --- Install steps ---
12193
install_binary() {
13-
sudo install -m 755 "$1" /usr/local/bin/rattan
14-
sudo setcap 'cap_dac_override,cap_dac_read_search,cap_sys_ptrace,cap_net_admin,cap_sys_admin,cap_net_raw+ep' /usr/local/bin/rattan
194+
sudo install -m 755 "$1" /usr/local/bin/rattan
195+
sudo setcap 'cap_dac_override,cap_dac_read_search,cap_sys_ptrace,cap_net_admin,cap_sys_admin,cap_net_raw+ep' /usr/local/bin/rattan
15196
}
16197

17198
# Config systemd-networkd to not change MAC address of veth interfaces
18199
# Ref: https://github.com/stack-rs/rattan/issues/42
19200
config_networkd() {
20-
if [ -d "/lib/systemd/network" ]; then
21-
sudo sh -c "cat <<EOF >/lib/systemd/network/80-rattan.link
201+
if [ -d "/lib/systemd/network" ]; then
202+
if [ -f "/lib/systemd/network/80-rattan.link" ]; then
203+
return
204+
fi
205+
sudo sh -c "cat <<EOF >/lib/systemd/network/80-rattan.link
22206
[Match]
23207
OriginalName=ns*-v*-*
24208
Driver=veth
@@ -27,45 +211,70 @@ Driver=veth
27211
MACAddressPolicy=none
28212
EOF
29213
"
30-
sudo systemctl daemon-reload
31-
sudo systemctl restart systemd-networkd.service
32-
fi
214+
sudo systemctl daemon-reload
215+
sudo systemctl restart systemd-networkd.service
216+
fi
33217
}
34218

219+
install_rattan() {
220+
local _binary_path="$1"
221+
install_binary "$_binary_path"
222+
config_networkd
223+
echo "$APP_NAME installed successfully to /usr/local/bin/$APP_NAME"
224+
}
225+
226+
# --- Entry point ---
227+
35228
usage() {
36-
cat >&2 <<EOL
229+
cat >&2 <<EOL
37230
Install rattan and grant necessary capabilities
231+
38232
Usage:
39-
$myname options RATTAN_BINARY_PATH ...
233+
$myname [options] [RATTAN_BINARY_PATH]
40234
41-
options:
42-
--help|-h Print this help message
235+
If RATTAN_BINARY_PATH is not provided, the latest release is downloaded
236+
automatically from https://github.com/${GITHUB_REPO}/releases
43237
44-
Example:
45-
$myname target/release/rattan
238+
Options:
239+
--help|-h Print this help message
240+
241+
Examples:
242+
$myname # Download and install latest release
243+
$myname target/release/rattan # Install a locally-built binary
46244
47245
EOL
48-
exit 1
246+
exit 1
49247
}
50248

51249
POSITIONAL_ARGS=()
52-
53250
while [[ $# -gt 0 ]]; do
54-
case $1 in
55-
--help | -h)
56-
usage
57-
;;
58-
--* | -*)
59-
echo "Unknown option $1"
60-
usage
61-
;;
62-
*)
63-
POSITIONAL_ARGS+=("$1") # save positional arg
64-
shift # past argument
65-
;;
66-
esac
251+
case $1 in
252+
--help | -h)
253+
usage
254+
;;
255+
--* | -*)
256+
echo "Unknown option $1"
257+
usage
258+
;;
259+
*)
260+
POSITIONAL_ARGS+=("$1")
261+
shift
262+
;;
263+
esac
67264
done
68265

69-
set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters
266+
set -- "${POSITIONAL_ARGS[@]+"${POSITIONAL_ARGS[@]}"}"
267+
268+
_DOWNLOAD_TMPDIR=""
70269

71-
install_rattan "$@"
270+
if [[ $# -gt 0 ]]; then
271+
# Local binary provided
272+
install_rattan "$1"
273+
else
274+
# Download from GitHub
275+
_binary="$(download_rattan)"
276+
install_rattan "$_binary"
277+
if [ -n "$_DOWNLOAD_TMPDIR" ]; then
278+
rm -rf "$_DOWNLOAD_TMPDIR"
279+
fi
280+
fi

0 commit comments

Comments
 (0)