common 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. #!/usr/bin/env bash
  2. # Copyright (C) 2016 Paul Kocialkowski <contact@paulk.fr>
  3. #
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. BUILD_SYSTEM="libreboot"
  17. PROJECTS="projects"
  18. SOURCES="sources"
  19. BUILD="build"
  20. INSTALL="install"
  21. RELEASE="release"
  22. SYSTEMS="systems"
  23. IMAGES="images"
  24. TOOLS="tools"
  25. CONFIGS="configs"
  26. PATCHES="patches"
  27. TARGETS="targets"
  28. REVISION="revision"
  29. BLOBS="blobs"
  30. BLOBS_IGNORE="blobs-ignore"
  31. BLOBS_DISCOVER="blobs-discover"
  32. DOTEPOCH=".epoch"
  33. DOTRNDSEED=".rndseed"
  34. DOTVERSION=".version"
  35. DOTREVISION=".revision"
  36. DOTTARFILES=".tarfiles"
  37. ARCHIVE="tar.xz"
  38. CHECKSUM="sha256sum"
  39. DSIG="asc"
  40. CONFIG_SHELL="${CONFIG_SHELL:-$(which bash)}"
  41. EDITOR="${EDITOR:-$(which vi || true)}"
  42. TASKS="${TASKS:-1}"
  43. function_check() {
  44. local function=$1
  45. declare -f -F "$function" > /dev/null
  46. }
  47. variable_check() {
  48. local variable=$1
  49. test ! -z "${!variable}"
  50. }
  51. arguments_list() {
  52. local argument
  53. for argument in "$@"
  54. do
  55. printf '%s\n' "$argument"
  56. done
  57. }
  58. download_wrapper() {
  59. local download_dir="$1"
  60. shift
  61. local uris=($@)
  62. local wget_options=(
  63. '--config=/dev/null'
  64. '--secure-protocol=PFS'
  65. "--directory-prefix=$download_dir"
  66. '--continue'
  67. '--'
  68. )
  69. local curl_options=(
  70. '-q'
  71. '--continue-at -'
  72. '--remote-name'
  73. '--retry 20'
  74. '--ssl'
  75. '--tlsv1.2'
  76. '--'
  77. )
  78. if hash wget > /dev/null 2>&1; then
  79. wget "${wget_options[@]}" "${uris[@]}"
  80. elif hash curl > /dev/null 2>&1; then
  81. (
  82. cd "$download_dir"
  83. curl "${curl_options[@]}" "${uris[@]}"
  84. )
  85. else
  86. printf '\n%s\n\n' 'Error: Neither wget nor curl were found' 1>&2
  87. return 1
  88. fi
  89. }
  90. diff_patch_file() {
  91. local repository_path="$1"
  92. local patch_file_path="$2"
  93. # TODO: Improve handling of filenames to avoid gotchas w/ \n, \t, etc.
  94. local filename_in_diff="$(sed -rne 's/^-{3}\s+([^ \r\n]*).*/\1/p' "$patch_file_path")"
  95. local source_file_path
  96. if ! ( grep -E '^-{3}.*/' "$patch_file_path" >/dev/null 2>&1 ); then
  97. source_file_path="$repository_path/$filename_in_diff"
  98. else
  99. source_file_path="$repository_path/${filename_in_diff##*/}"
  100. fi
  101. patch "$source_file_path" "$patch_file_path"
  102. }
  103. path_wildcard_expand() {
  104. local path=$@
  105. # Evaluation fails with unescaped whitespaces.
  106. path=$( printf '%s\n' "$path" | sed "s/ /\\\ /g" )
  107. eval "arguments_list "$path""
  108. }
  109. file_checksum_create() {
  110. local path=$1
  111. local checksum_path="$path.$CHECKSUM"
  112. local name=$( basename "$path" )
  113. local directory_path=$( dirname "$path" )
  114. (
  115. cd "$directory_path"
  116. sha256sum "$name" > "$checksum_path"
  117. )
  118. }
  119. file_checksum_check() {
  120. local path=$1
  121. local checksum_path="$path.$CHECKSUM"
  122. local name=$( basename "$path" )
  123. local directory_path=$( dirname "$path" )
  124. if ! [ -f "$checksum_path" ]
  125. then
  126. printf '%s\n' 'Could not verify file checksum!' >&2
  127. return 1
  128. fi
  129. (
  130. cd "$directory_path"
  131. sha256sum -c "$checksum_path"
  132. )
  133. }
  134. file_signature_create() {
  135. local path=$1
  136. local signature_path="$path.$DSIG"
  137. if [ -z "$RELEASE_KEY" ]
  138. then
  139. return 0
  140. fi
  141. gpg --default-key "$RELEASE_KEY" --armor --output "$signature_path" --detach-sign --yes "$path"
  142. }
  143. file_signature_check() {
  144. local path=$1
  145. local signature_path="$path.$DSIG"
  146. if ! [ -f "$signature_path" ]
  147. then
  148. printf '%s\n' 'Could not verify file signature!' >&2
  149. return 1
  150. fi
  151. gpg --armor --verify "$signature_path" "$path"
  152. }
  153. file_verification_create() {
  154. local path=$1
  155. file_checksum_create "$path"
  156. file_signature_create "$path"
  157. }
  158. file_verification_check() {
  159. local path=$1
  160. file_checksum_check "$path"
  161. file_signature_check "$path"
  162. }
  163. file_exists_check() {
  164. local path=$1
  165. test -f "$path"
  166. }
  167. directory_filled_check() {
  168. local path=$1
  169. if [ -z "$( ls -A "$path" 2> /dev/null )" ]
  170. then
  171. return 1
  172. else
  173. return 0
  174. fi
  175. }
  176. archive_files_create() {
  177. local source_path="$1"
  178. local directory="$(basename "$source_path")"
  179. local tarfiles_path="$source_path/$DOTTARFILES"
  180. local revision_path="$source_path/$DOTREVISION"
  181. local version_path="$source_path/$DOTVERSION"
  182. local epoch_path="$source_path/$DOTEPOCH"
  183. local rnd_seed_path="$source_path/$DOTRNDSEED"
  184. # Files in "$tarfiles_path" are NUL terminated.
  185. # `tr '\0' '\n'` for human-readable output.
  186. if git_check "$source_path"; then
  187. git_files "$source_path" > "$tarfiles_path"
  188. printf '%s\0' "$DOTTARFILES" >> "$tarfiles_path"
  189. else
  190. find "$source_path" -print0 | env LC_ALL='C.UTF-8' sort -z | sed -z "1d;s,^$source_path/\\?,,;/^$DOTTARFILES\$/d" > "$tarfiles_path"
  191. fi
  192. for dotfile in "$revision_path" \
  193. "$version_path" \
  194. "$epoch_path" \
  195. "$rnd_seed_path"
  196. do
  197. if [[ -f "$dotfile" ]]; then
  198. printf '%s\0' ".${dotfile##*.}" >> "$tarfiles_path"
  199. fi
  200. done
  201. }
  202. archive_files_date() {
  203. local source_path="$1"
  204. local epoch_path="$source_path/$DOTEPOCH"
  205. if [[ -n "$SOURCE_DATE_EPOCH" ]]; then
  206. find "$source_path" -execdir touch --no-dereference --date="@$SOURCE_DATE_EPOCH" {} +
  207. fi
  208. }
  209. archive_create() {
  210. local archive_path="$1"
  211. local source_path="$2"
  212. local directory="$3"
  213. local tarfiles_path="$source_path/$DOTTARFILES"
  214. local directory_path="$(dirname "$archive_path")"
  215. mkdir -p "$directory_path"
  216. if [[ -z "$directory" ]]; then
  217. directory="$(basename "$source_path")"
  218. fi
  219. archive_files_create "$source_path"
  220. archive_files_date "$source_path"
  221. local tar_options=(
  222. --create
  223. --xz
  224. --file="$archive_path"
  225. --files-from="$tarfiles_path"
  226. --transform="s,^,$directory/,S"
  227. --no-recursion
  228. --warning=no-filename-with-nuls
  229. --null
  230. --owner=0
  231. --group=0
  232. --numeric-owner
  233. )
  234. (
  235. cd "$source_path"
  236. tar "${tar_options[@]}"
  237. )
  238. }
  239. archive_extract() {
  240. local archive_path="$1"
  241. local destination_path="$2"
  242. if [[ -z "$destination_path" ]]; then
  243. destination_path="$(dirname "$archive_path")"
  244. fi
  245. tar -xf "$archive_path" -ps -C "$destination_path"
  246. }
  247. rootfs_files_create() {
  248. local source_path="$1"
  249. local directory="$(basename "$source_path")"
  250. local tarfiles_path="$source_path/$DOTTARFILES"
  251. # Files in "$tarfiles_path" are NUL terminated.
  252. # `tr '\0' '\n'` for human-readable output.
  253. execute_root find "$source_path" -print0 | env LC_ALL='C.UTF-8' sort -z | sed -z "1d;s,^$source_path/\\?,,;/^$DOTTARFILES\$/d" > "$tarfiles_path"
  254. }
  255. rootfs_files_date() {
  256. local source_path="$1"
  257. local epoch_path="$source_path/$DOTEPOCH"
  258. if [[ -n "$SOURCE_DATE_EPOCH" ]]; then
  259. execute_root find "$source_path" -execdir touch --no-dereference --date="@$SOURCE_DATE_EPOCH" {} +
  260. fi
  261. }
  262. rootfs_create() {
  263. local rootfs_path="$1"
  264. local source_path="$2"
  265. local directory="$3"
  266. local tarfiles_path="$source_path/$DOTTARFILES"
  267. local directory_path="$(dirname "$rootfs_path")"
  268. mkdir -p "$directory_path"
  269. if [[ -z "$directory" ]]; then
  270. directory="$(basename "$source_path")"
  271. fi
  272. rootfs_files_create "$source_path"
  273. rootfs_files_date "$source_path"
  274. local tar_options=(
  275. --create
  276. --xz
  277. --file="$rootfs_path"
  278. --files-from="$tarfiles_path"
  279. --no-recursion
  280. --warning=no-filename-with-nuls
  281. --null
  282. --owner=0
  283. --group=0
  284. --numeric-owner
  285. )
  286. (
  287. cd "$source_path"
  288. execute_root tar "${tar_options[@]}"
  289. )
  290. execute_root chmod 644 "$rootfs_path"
  291. execute_root chown "$USER:$USER" "$rootfs_path"
  292. }
  293. requirements() {
  294. local requirement
  295. local requirement_path
  296. for requirement in "$@"
  297. do
  298. requirement_path=$( which "$requirement" || true )
  299. if [ -z "$requirement_path" ]
  300. then
  301. printf '%s\n' "Missing requirement: $requirement" >&2
  302. exit 1
  303. fi
  304. done
  305. }
  306. requirements_root() {
  307. local requirement
  308. local requirement_path
  309. for requirement in "$@"
  310. do
  311. # We need to keep stdout output to show the command.
  312. requirement_path=$( execute_root which "$requirement" || true )
  313. if [ -z "$requirement_path" ]
  314. then
  315. printf '%s\n' "Missing requirement: $requirement" >&2
  316. exit 1
  317. fi
  318. done
  319. }
  320. arguments_concat() {
  321. local delimiter=$1
  322. shift
  323. local concat
  324. for argument in "$@"
  325. do
  326. if ! [ -z "$concat" ]
  327. then
  328. concat="$concat""$delimiter""$argument"
  329. else
  330. concat="$argument"
  331. fi
  332. done
  333. printf '%s\n' "$concat"
  334. }
  335. execute_root() {
  336. local sudo=$( which sudo 2> /dev/null || true )
  337. local arguments
  338. printf '%s' 'Running command as root: ' >&2
  339. printf '%b\n' "$*" >&2
  340. if ! [ -z "$sudo" ]
  341. then
  342. sudo "$@"
  343. else
  344. # Quote arguments for eval through su.
  345. arguments=$( printf '%q ' "$@" )
  346. su -c "$arguments"
  347. fi
  348. }