@@ -6,35 +6,147 @@ set -euo pipefail
66# shellcheck disable=SC1091
77. freebsd-common.sh
88
9- export PACKAGESITE=/opt/freebsd-packagesite/packagesite.yaml
10- export PKG_SOURCE=" https://pkg.freebsd.org/FreeBSD:${BSD_MAJOR} :${BSD_ARCH} /quarterly"
11- export TARGET=" ${ARCH} -unknown-freebsd${BSD_MAJOR} "
9+ # list of SRV records to query if the default mirror fails
10+ FREEBSD_HTTP_TCP_SOURCES=(
11+ # these return all mirrors, including local ones
12+ " _http._tcp.pkg.all.freebsd.org"
13+ # this only returns geodns mirrors
14+ " _http._tcp.pkg.freebsd.org"
15+ )
16+ FREEBSD_PACKAGEDIR=" /opt/freebsd-packagesite"
17+ FREEBSD_PACKAGESITE=" ${FREEBSD_PACKAGEDIR} /packagesite.yaml"
18+ FREEBSD_TARGET=" ${ARCH} -unknown-freebsd${FREEBSD_MAJOR} "
19+ FREEBSD_DEFAULT_MIRROR=" pkg.freebsd.org"
20+ # NOTE: these mirrors were known to work as of 2022-11-28.
21+ # no availability guarantees are made for any of them.
22+ FREEBSD_BACKUP_MIRRORS=(
23+ " pkg0.syd.freebsd.org"
24+ " pkg0.bme.freebsd.org"
25+ " pkg0.bra.freebsd.org"
26+ " pkg0.fra.freebsd.org"
27+ " pkg0.jinx.freebsd.org"
28+ " pkg0.kul.freebsd.org"
29+ " pkg0.kwc.freebsd.org"
30+ " pkg0.nyi.freebsd.org"
31+ " pkg0.tuk.freebsd.org"
32+ " pkg0.twn.freebsd.org"
33+ )
1234
13- setup_packagesite () {
14- apt-get update && apt-get install --assume-yes --no-install-recommends \
15- curl \
16- jq \
17- xz-utils
35+ # NOTE: out of convention, we use `url` for mirrors with the scheme,
36+ # and `mirror` for those without the scheme for consistent naming.
37+ freebsd_package_source () {
38+ local url=" ${1} "
39+ echo " ${url} /FreeBSD:${FREEBSD_MAJOR} :${FREEBSD_ARCH} /quarterly"
40+ }
41+
42+ freebsd_mirror_works () {
43+ local mirror=" ${1} "
44+ local scheme=" ${2} "
45+ local pkg_source=
46+
47+ # meta.conf is a small file for quick confirmation the mirror works
48+ pkg_source=$( freebsd_package_source " ${scheme} ://${mirror} " )
49+ local path=" ${pkg_source} /meta.conf"
50+
51+ timeout 20s curl --retry 3 -sSfL " ${path} " > /dev/null 2>&1
52+ }
1853
19- mkdir /opt/freebsd-packagesite
20- curl --retry 3 -sSfL " ${PKG_SOURCE} /packagesite.txz" -O
21- tar -C /opt/freebsd-packagesite -xJf packagesite.txz
54+ _fetch_best_freebsd_mirror () {
55+ # in case if the default mirror is down, we can use various known
56+ # fallbacks, or at worst, SRV fallbacks to find the ideal package
57+ # site. no individual mirror other than the default mirror is
58+ # guaranteed to exist, so we use a tiered approach. only
59+ # the default mirror supports https.
60+ if freebsd_mirror_works " ${FREEBSD_DEFAULT_MIRROR} " " https" ; then
61+ echo " https://${FREEBSD_DEFAULT_MIRROR} "
62+ return 0
63+ fi
64+
65+ # if we've gotten here, it could be a DNS issue, so using a DNS
66+ # resolver to fetch SRV fallbacks may not work. let's first try
67+ # a few previously tested mirrors and see if any work.
68+ local mirror=
69+ for mirror in " ${FREEBSD_BACKUP_MIRRORS[@]} " ; do
70+ if freebsd_mirror_works " ${mirror} " " http" ; then
71+ echo " http://${mirror} "
72+ return 0
73+ fi
74+ done
75+
76+ local http_tcp_source=
77+ local response=
78+ local lines=
79+ # shellcheck disable=SC2016
80+ local regex=' /\d+\s+\d+\s+\d+\s+(.*)\./; print $1'
81+ for http_tcp_source in " ${FREEBSD_HTTP_TCP_SOURCES[@]} " ; do
82+ # the output will have the following format, but we only want the
83+ # target and ignore everything else:
84+ # $priority $port $weight $target.
85+ #
86+ # some output may not match, so we skip those lines, for example:
87+ # 96.47.72.71
88+ response=$( dig +short srv " ${http_tcp_source} " )
89+ readarray -t lines <<< " ${response}"
90+ for line in " ${lines[@]} " ; do
91+ mirror=$( echo " ${line} " | perl -nle " ${regex} " )
92+ if [[ -n " ${mirror} " ]]; then
93+ if freebsd_mirror_works " ${mirror} " " http" ; then
94+ echo " http://${mirror} "
95+ return 0
96+ fi
97+ fi
98+ done
99+ done
100+
101+ echo -e " \e[31merror:\e[0m could not find a working FreeBSD package mirror." 1>&2
102+ exit 1
103+ }
104+
105+ fetch_best_freebsd_mirror () {
106+ set +e
107+ _fetch_best_freebsd_mirror
108+ code=$?
109+ set -e
110+
111+ return " ${code} "
112+ }
113+
114+ setup_freebsd_packagesite () {
115+ local url=" ${FREEBSD_MIRROR:- } "
116+ local pkg_source=
117+
118+ if [[ -z " ${url} " ]]; then
119+ url=$( fetch_best_freebsd_mirror)
120+ fi
121+ pkg_source=$( freebsd_package_source " ${url} " )
122+
123+ mkdir -p " ${FREEBSD_PACKAGEDIR} "
124+ curl --retry 3 -sSfL " ${pkg_source} /packagesite.txz" -O
125+ tar -C " ${FREEBSD_PACKAGEDIR} " -xJf packagesite.txz
22126
23127 rm packagesite.txz
24128}
25129
130+ # don't provide the mirror as a positional argument, so it can be optional
26131install_freebsd_package () {
132+ local url=" ${FREEBSD_MIRROR:- } "
133+ local pkg_source=
27134 local name
28135 local path
29136 local pkg
30137 local td
31- local destdir=" /usr/local/${TARGET} "
138+ local destdir=" /usr/local/${FREEBSD_TARGET} "
139+
140+ if [[ -z " ${url} " ]]; then
141+ url=$( fetch_best_freebsd_mirror)
142+ fi
143+ pkg_source=$( freebsd_package_source " ${url} " )
32144
33145 td=" $( mktemp -d) "
34146 pushd " ${td} "
35147
36148 for name in " ${@ } " ; do
37- path=$( jq -c ' . | select ( .name == "' " ${name} " ' " ) | .repopath' " ${PACKAGESITE } " )
149+ path=$( jq -c ' . | select ( .name == "' " ${name} " ' " ) | .repopath' " ${FREEBSD_PACKAGESITE } " )
38150 if [[ -z " ${path} " ]]; then
39151 echo " Unable to find package ${name} " >&2
40152 exit 1
@@ -43,7 +155,7 @@ install_freebsd_package() {
43155 pkg=$( basename " ${path} " )
44156
45157 mkdir " ${td} " /package
46- curl --retry 3 -sSfL " ${PKG_SOURCE } /${path} " -O
158+ curl --retry 3 -sSfL " ${pkg_source } /${path} " -O
47159 tar -C " ${td} /package" -xJf " ${pkg} "
48160 cp -r " ${td} /package/usr/local" /* " ${destdir} " /
49161
0 commit comments