script/lib/ansi: add additional ANSI style codes
[CDN/Mosi.git] / script / gentree
1 #!/bin/sh
2
3 # Boilerplate
4 _root="$(dirname "${0}")"
5 . "${_root}/lib/env.sh"
6
7 # Load needed modules
8 want root ansi log ask
9
10 targets="prepwork admin overlay packages patch prepboot preptmp prepetc imgboot imgconf imgetc imgall custom"
11
12 pebkac() {
13   [ "${*}" ] && printf "${*}\n\n"
14   echo "Usage: $(basename "${0}") <-b basedir> <-t stagedir> <-i pkgsdir> <-p patchdir>"
15   echo "        <-r rootdir> <-l logfile> [-h] <targets>"
16   echo '  -b basedir    Basedir for automagic defaults'
17   echo '  -t stagedir   Staging directory name (Default: ${base}/stage)'
18   echo '  -i pkgsdir    Directory holding packages to install (Default: ${base}/pkg)'
19   echo '  -p patchdir   Directory holding patches to apply (Default: ${base}/patch)'
20   echo '  -o overlaydir Directory holding an overlay tree (Default: ${base}/overlay)'
21   echo '  -r rootdir    Directory holding the virgin source tree (Default: ${base}/root)'
22   echo '  -l logfile    File to hold stderr spam (Default: ${stage}/gentree.log'
23   echo '  -h            Hello! >^-^<'
24   echo ''
25   echo 'Available targets:'
26   for target in ${targets}
27   do
28     echo "  ${target}"
29   done
30   exit 1
31 }
32
33 while getopts "b:t:i:p:o:r:l:h" opt
34 do
35   case "${opt}" in
36     b) base="${OPTARG}" ;;
37     t) stage="${OPTARG}" ;;
38     i) pkgs="${OPTARG}" ;;
39     p) patch="${OPTARG}" ;;
40     o) overlay="${OPTARG}" ;;
41     r) root="${OPTARG}" ;;
42     l) logfile="${OPTARG}" ;;
43     h) pebkac ;;
44     [?]) pebkac "Unrecognized option ${opt}" ;;
45   esac
46 done
47 shift $(( $OPTIND - 1 ))
48
49 sequence="${*:-${targets}}"
50
51 base="${base:-/usr/home/cyberleo/world}"
52 base="$(realpath "${base}")"
53 stage="${stage:-${base}/tree}"
54 pkgs="${pkgs:-${base}/pkg}"
55 patch="${patch:-${base}/patch}"
56 overlay="${overlay:-${base}/overlay}"
57 root="${root:-${base}/root}"
58 logfile="${logfile:-${stage}/gentree.log}"
59
60 mkdir -p "${stage}"
61 stage="$(realpath "${stage}")"
62 sroot="${stage}/root"
63 sboot="${stage}/boot"
64 sconf="${stage}/conf"
65
66 exec 2>>"${logfile}"
67 _log_to_stderr=yes
68
69 # Helper functions
70 onelink() {
71   # Make sure the provided file(s) have only one link!
72   while [ -n "${1}" ]
73   do
74     if [ -f "${1}" -a "$(stat -f '%l' "${1}")" -gt 1 ]
75     then
76       cp -p "${1}" "${1}.tmp" && mv "${1}.tmp" "${1}" || err "breaklink failed"
77     fi
78     shift
79   done
80 }
81
82 # Build steps
83 do_prepwork() {
84   log Prepare workspace
85   if [ -d "${stage}" -a \( -d "${sroot}" -o -d "${sboot}" -o -d "${sconf}" \) ]
86   then
87     yn n " ${a_yellow}*${a_normal} Workspace already exists. Delete? [y/N]" || err Aborting
88     chk rm -Rf "${sroot}" "${sboot}" "${sconf}"
89   fi
90   chk mkdir -p "${sroot}"
91   # Eliminate schg, because it interferes with hardlinks
92   chk chflags -R noschg "${root}/lib"
93   chk chflags -R noschg "${root}/libexec"
94   chk chflags -R noschg "${root}/sbin"
95   chk chflags -R noschg "${root}/usr"
96   ( cd "${root}" && find . | cpio -p --link "${sroot}" ) || chk
97 }
98
99 do_admin() {
100   log Create an emergency user admin/admin
101   # delink passwd to ensure it doesn't get patched in-place
102   chk onelink "${sroot}/etc/passwd" "${sroot}/etc/master.passwd"
103   echo '$1$2rXOWsK/$eiBHA6K7xL96DZbcY24YR0' | chk chroot "${sroot}" /usr/sbin/pw useradd admin -u 999 -g wheel -G operator -c Administrator -d /usr/home/admin -m -s /bin/csh -H 0
104 }
105
106 do_overlay() {
107   log Apply overlay from "${overlay##${base}/}"
108   [ -d "${overlay}" ] || return
109   ( cd "${overlay}" && find . | cpio -p "${sroot}" ) || chk
110 }
111
112 do_packages() {
113   if [ -d "${pkgs}" -a "$(ls -1 "${pkgs}" | wc -l)" -gt 0 ]
114   then
115     count="$(ls -1 "${pkgs}" | wc -l)"
116     log Install ${count} packages from "${pkgs##${base}/}"
117     chk mkdir -p "${sroot}/pkg"
118     ( cd "${pkgs}" && find . | cpio -p --link "${sroot}/pkg" ) || chk
119     chk chroot "${sroot}" /bin/sh -c 'cd /pkg; exec pkg_add -F *'
120     chk rm -Rf "${sroot}/pkg"
121   fi
122 }
123
124 do_patch() {
125   log Apply patches from "${patch##${base}/}"
126   for file in "${patch}"/*
127   do
128     note "... $(basename "${file}")"
129     ( cd "${sroot}" && patch < "${file}" ) || chk
130     # Remove .orig files
131     sed -e '/^+++ /!d; s/^+++ //; s/[   ]*[0-9: .+-]*$//' "${file}" | while read target
132     do
133       rm -f "${sroot}/${target}.orig"
134     done || chk
135   done
136 }
137
138 do_prepboot() {
139   log Prepare /boot
140   chk mv "${sroot}/boot/boot" "${sroot}/boot/boot.blk"
141   chk ln -sf . "${sroot}/boot/boot"
142 }
143
144 do_preptmp() {
145   log Prepare /tmp
146   ( cd "${sroot}/tmp" && find . | cpio -p --link ../var/tmp ) || chk
147   chk rm -Rf "${sroot}/tmp"
148   chk ln -sf var/tmp "${sroot}/tmp"
149 }
150
151 do_prepetc() {
152   log Prepare /etc
153   chk mkdir -p "${sroot}/etc/local"
154   chk mkdir -p "${sroot}/usr/local/etc" # Silence warnings
155   ( cd "${sroot}/usr/local/etc" && find . | cpio -p --link ../../../etc/local ) || chk
156   chk rm -Rf "${sroot}/usr/local/etc"
157   chk ln -sf ../../etc/local "${sroot}/usr/local/etc"
158 }
159
160 do_imgboot() {
161   log Create boot imgsrc
162   chk mv "${sroot}/boot" "${stage}"
163   chk mkdir -p "${sroot}/boot"
164   chk rm -f "${sboot}/kernel"/*.symbols
165   # Gzipped kernel is okay
166   chk onelink "${sboot}/kernel/kernel"
167   chk gzip -9 "${sboot}/kernel/kernel"
168   # Compress all files in /boot/kernel
169   # Loader cannot handle gzipped modules. Decompress the required modules
170   # kldload cannot handle gzipped modules either
171   #find "${sboot}/kernel" -type f | xargs gzip -9f || chk
172   #cat "${sboot}/loader.conf" | grep '_load=' | sed -e 's/^\(.*\)_load=.*$/\1/' | while read mod
173   #do
174   #  [ -f "${sboot}/kernel/${mod}.ko.gz" ] && gunzip "${sboot}/kernel/${mod}.ko.gz" || chk
175   #done
176   #
177   # Instead: put all modules in the root image, except those needed to boot the kernel
178   chk mkdir -p "${sroot}/boot/boot"
179   chk mkdir -p "${sroot}/boot/kernel"
180   chk ln -sf "../etc/zfs" "${sroot}/boot/zfs"
181
182   # Link all modules into the root fs
183   ( cd "${sboot}/kernel" && find . -name '*.ko' -o -name 'linker.hints' | cpio -p --link "${sroot}/boot/kernel" ) || chk
184
185   # Remove all modules from the root fs that are preloaded by the loader
186   cat "${sboot}/loader.conf" | grep '_load=' | sed -e 's#^\(.*\)_load=.*$#'"${sroot}/boot/kernel/"'\1.ko#' | xargs rm -f
187
188   # Remove all modules from the boot fs that are present in the root fs
189   ( cd "${sroot}/boot/kernel" && ls -1 ) | sed -e 's#^#'"${sboot}/kernel/"'#' | xargs rm -f
190
191   # Link the preloaded modules from the boot fs to the root fs, to provide a homogenous view
192   ( cd "${sboot}/kernel" && ls -1 ) | while read mod
193   do
194     ln -sf "../boot/kernel/${mod}" "${sroot}/boot/kernel/${mod}"
195   done
196 }
197
198 do_imgconf() {
199   log Create conftree
200   chk mkdir -p "${sroot}/conf"
201   echo "ufs:/dev/ufs/conf" > "${sroot}/conf/diskless_remount" || chk
202   chk mkdir -p "${sconf}/backup"
203   chk mkdir -p "${sconf}/base"
204   chk mkdir -p "${sconf}/default"
205
206   # Create packdirs for each
207   for pack in "${base}/conf"/*.md_size
208   do
209     pack="$(basename "${pack%%.md_size}")"
210     chk mkdir -p "${sconf}/base/${pack}"
211     chk mkdir -p "${sconf}/default/${pack}"
212     ( cat "${base}/conf/${pack}.md_size" > "${sconf}/base/${pack}/md_size" ) || chk
213   done
214   chk chown -R :operator "${sconf}"
215   ( find "${sconf}" -print0 | xargs -0 chmod o-rwx ) || chk
216 }
217
218 do_imgetc() {
219   # etc requires special handling to ensure everything is properly arranged.
220   log Create etc confpack
221   chk touch "${sroot}/etc/diskless"
222   # Touch /COPYRIGHT to provide an immutable time anchor for saveconfig
223   chk touch "${sroot}/COPYRIGHT"
224   chk sleep 1 # Make sure diskless is at least one second older than everything in custom
225   chk mkdir -p "${stage}/pack"
226   chk mv "${sroot}/etc" "${stage}/pack"
227   chk mkdir -p "${sroot}/etc"
228   chk cp -p "${stage}/pack/etc/rc" "${stage}/pack/etc/rc.subr" "${stage}/pack/etc/rc.initdiskless" "${stage}/pack/etc/login.conf.db" "${stage}/pack/etc/diskless" "${sroot}/etc"
229
230   # Make sure etc confpack exists; default if necessary
231   chk mkdir -p "${sconf}/base/etc"
232   chk mkdir -p "${sconf}/default/etc"
233   [ -e "${sconf}/base/etc/md_size" ] || echo "10240" > "${sconf}/base/etc/md_size"
234
235   ( cd "${stage}/pack" && find etc | cpio -o ) | gzip -9 > "${sconf}/base/etc.cpio.gz" || chk
236   chk rm -Rf "${stage}/pack/etc"
237 }
238
239 do_imgall() {
240   log Create remaining confpacks
241   chk mkdir -p "${stage}/pack"
242   for pack in "${base}/conf"/*.md_size
243   do
244     pack="$(basename "${pack%%.md_size}")"
245     # Ignore etc, as it was processed in do_imgetc
246     [ "${pack}" = "etc" ] && continue
247     note "... ${pack}"
248     chk mv "${sroot}/${pack}" "${stage}/pack"
249     chk mkdir -p "${sroot}/${pack}"
250     ( cd "${stage}/pack" && find "${pack}" | cpio -o ) | gzip -9 > "${sconf}/base/${pack}.cpio.gz" || chk
251     chk rm -Rf "${stage}/pack/${pack}"
252   done
253 }
254
255 do_custom() {
256   log Patch in custom config
257   ( cd "${base}/conf" && find . | egrep -v '^\./.*\.md_size$' | cpio -p --link "${sconf}/default" ) || chk
258   # Make sure files in default are newer than the tagfile, so they will be caught by saveconfig
259   find "${sconf}/default" -type f -print0 | xargs -0 touch
260   # If there are scavenged cpio.gz confpacks, touch their contents as well.
261   chk mkdir -p "${stage}/pack"
262   chk rm -Rf "${stage}/pack"/*
263   for file in "${sconf}/default"/*.cpio.gz
264   do
265     pack="$(basename "${file%%.cpio.gz}")"
266     zcat "${file}" | ( cd "${stage}/pack"; cpio -id ) || chk
267     find "${stage}/pack" -type f -print0 | xargs -0 touch
268     ( cd "${stage}/pack"; find . | cpio -o ) | gzip -9 > "${file}"
269     chk rm -Rf "${stage}/pack"/*
270   done
271 }
272
273 for step in ${sequence}
274 do
275   echo "${targets}" | grep -q "${step}" || err Unrecognized target "${step}"
276   do_${step}
277 done