script/gentree: make cpio subshells fail if cd(1) fails.
[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 packages patch prepboot preptmp prepetc prepvar imgboot imgconf imgetc imgvar 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 '  -r rootdir  Directory holding the virgin source tree (Default: ${root})'
21   echo '  -l logfile  File to hold stderr spam (Default: ${stage}/gentree.log'
22   echo '  -h          Hello! >^-^<'
23   echo ''
24   echo 'Available targets:'
25   for target in ${targets}
26   do
27     echo "  ${target}"
28   done
29   exit 1
30 }
31
32 while getopts "b:t:i:p:r:l:h" opt
33 do
34   case "${opt}" in
35     b) base="${OPTARG}" ;;
36     t) stage="${OPTARG}" ;;
37     i) pkgs="${OPTARG}" ;;
38     p) patch="${OPTARG}" ;;
39     r) root="${OPTARG}" ;;
40     l) logfile="${OPTARG}" ;;
41     h) pebkac ;;
42     [?]) pebkac "Unrecognized option ${opt}" ;;
43   esac
44 done
45 shift $(( $OPTIND - 1 ))
46
47 sequence="${*:-${targets}}"
48
49 base="${base:-/usr/home/cyberleo/world}"
50 stage="${stage:-${base}/tree}"
51 pkgs="${pkgs:-${base}/pkg}"
52 patch="${patch:-${base}/patch}"
53 root="${root:-${root}}"
54 logfile="${logfile:-${stage}/gentree.log}"
55
56 mkdir -p "${stage}"
57 stage="$(realpath "${stage}")"
58 sroot="${stage}/root"
59 sboot="${stage}/boot"
60 sconf="${stage}/conf"
61
62 exec 2>>"${logfile}"
63 _log_to_stderr=yes
64
65 # Helper functions
66 onelink() {
67   # Make sure the provided file(s) have only one link!
68   while [ -n "${1}" ]
69   do
70     if [ -f "${1}" -a "$(stat -f '%l' "${1}")" -gt 1 ]
71     then
72       cp -p "${1}" "${1}.tmp" && mv "${1}.tmp" "${1}" || err "breaklink failed"
73     fi
74     shift
75   done
76 }
77
78 # Build steps
79 do_prepwork() {
80   log Prepare workspace
81   if [ -d "${stage}" -a \( -d "${sroot}" -o -d "${sboot}" -o -d "${sconf}" \) ]
82   then
83     yn n " ${a_yellow}*${a_normal} Workspace already exists. Delete? [y/N]" || err Aborting
84     chk rm -Rf "${sroot}" "${sboot}" "${sconf}"
85   fi
86   chk mkdir -p "${sroot}"
87   # Eliminate schg, because it interferes with hardlinks
88   chk chflags -R noschg "${root}/lib"
89   chk chflags -R noschg "${root}/libexec"
90   chk chflags -R noschg "${root}/sbin"
91   chk chflags -R noschg "${root}/usr"
92   ( cd "${root}" && find . | cpio -p --link "${sroot}" ) || chk
93 }
94
95 do_admin() {
96   log Create an emergency user admin/admin
97   # delink passwd to ensure it doesn't get patched in-place
98   chk onelink "${sroot}/etc/passwd" "${sroot}/etc/master.passwd"
99   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
100 }
101
102 do_packages() {
103   if [ -d "${pkgs}" -a "$(ls -1 "${pkgs}" | wc -l)" -gt 0 ]
104   then
105     count="$(ls -1 "${pkgs}" | wc -l)"
106     log Install ${count} packages from "${pkgs}"
107     chk mkdir -p "${sroot}/pkg"
108     ( cd "${pkgs}" && find . | cpio -p --link "${sroot}/pkg" ) || chk
109     chk chroot "${sroot}" /bin/sh -c 'cd /pkg; exec pkg_add -F *'
110     chk rm -Rf "${sroot}/pkg"
111   fi
112 }
113
114 do_patch() {
115   log Apply patches from "${patch}"
116   for file in "${patch}"/*
117   do
118     note "... $(basename "${file}")"
119     echo "... $(basename "${file}")" >&2
120     ( cd "${sroot}" && patch < "${file}" ) || chk
121   done
122 }
123
124 do_prepboot() {
125   log Prepare /boot
126   chk mv "${sroot}/boot/boot" "${sroot}/boot/boot.blk"
127   chk ln -sf . "${sroot}/boot/boot"
128 }
129
130 do_preptmp() {
131   log Prepare /tmp
132   ( cd "${sroot}/tmp" && find . | cpio -p --link ../var/tmp ) || chk
133   chk rm -Rf "${sroot}/tmp"
134   chk ln -sf var/tmp "${sroot}/tmp"
135 }
136
137 do_prepetc() {
138   log Prepare /etc
139   chk mkdir -p "${sroot}/etc/local"
140   chk mkdir -p "${sroot}/usr/local/etc" # Silence warnings
141   ( cd "${sroot}/usr/local/etc" && find . | cpio -p --link ../../../etc/local ) || chk
142   chk rm -Rf "${sroot}/usr/local/etc"
143   chk ln -sf ../../etc/local "${sroot}/usr/local/etc"
144 }
145
146 do_prepvar() {
147   log Prepare /var
148   # Nothing necessary here
149 }
150
151 do_imgboot() {
152   log Create boot imgsrc
153   chk mv "${sroot}/boot" "${stage}"
154   chk mkdir -p "${sroot}/boot"
155   chk rm -f "${sboot}/kernel"/*.symbols
156   # Gzipped kernel is okay
157   gzip -9 "${sboot}/kernel/kernel"
158   # Compress all files in /boot/kernel
159   # Loader cannot handle gzipped modules. Decompress the required modules
160   # kldload cannot handle gzipped modules either
161   #find "${sboot}/kernel" -type f | xargs gzip -9f || chk
162   #cat "${sboot}/loader.conf" | grep '_load=' | sed -e 's/^\(.*\)_load=.*$/\1/' | while read mod
163   #do
164   #  [ -f "${sboot}/kernel/${mod}.ko.gz" ] && gunzip "${sboot}/kernel/${mod}.ko.gz" || chk
165   #done
166   #
167   # Instead: put all modules in the root image, except those needed to boot the kernel
168   chk mkdir -p "${sroot}/boot/boot"
169   chk mkdir -p "${sroot}/boot/kernel"
170   
171   # Link all modules into the root fs
172   ( cd "${sboot}/kernel" && find . -name '*.ko' -o -name 'linker.hints' | cpio -p --link "${sroot}/boot/kernel" ) || chk
173   
174   # Remove all modules from the root fs that are preloaded by the loader
175   cat "${sboot}/loader.conf" | grep '_load=' | sed -e 's#^\(.*\)_load=.*$#'"${sroot}/boot/kernel/"'\1.ko#' | xargs rm -f
176   
177   # Remove all modules from the boot fs that are present in the root fs
178   ( cd "${sroot}/boot/kernel" && ls -1 ) | sed -e 's#^#'"${sboot}/kernel/"'#' | xargs rm -f
179   
180   # Link the preloaded modules from the boot fs to the root fs, to provide a homogenous view
181   ( cd "${sboot}/kernel" && ls -1 ) | while read mod
182   do
183     ln -sf "../boot/kernel/${mod}" "${sroot}/boot/kernel/${mod}"
184   done
185 }
186
187 do_imgconf() {
188   log Create conf imgsrc
189   chk mkdir -p "${sroot}/conf"
190   echo "ufs:/dev/ufs/conf" > "${sroot}/conf/diskless_remount" || chk
191   chk mkdir -p "${sconf}/base/etc"
192   chk mkdir -p "${sconf}/base/var"
193   chk mkdir -p "${sconf}/default/etc"
194   chk mkdir -p "${sconf}/default/var"
195 }
196
197 do_imgetc() {
198   log Create etc imgsrc
199   chk touch "${sroot}/etc/diskless"
200   chk mv "${sroot}/etc" "${stage}"
201   chk mkdir -p "${sroot}/etc"
202   chk cp -p "${stage}/etc/rc" "${stage}/etc/rc.subr" "${stage}/etc/rc.initdiskless" "${stage}/etc/login.conf.db" "${stage}/etc/diskless" "${sroot}/etc"
203   echo "10240" > "${sconf}/base/etc/md_size" || chk
204   ( cd "${stage}" && find etc | cpio -o ) | gzip -9 > "${sconf}/base/etc.cpio.gz" || chk
205   chk rm -Rf "${stage}/etc"
206 }
207
208 do_imgvar() {
209   log Create var imgsrc
210   chk mv "${sroot}/var" "${stage}"
211   chk mkdir -p "${sroot}/var"
212   echo "131072" > "${sconf}/base/var/md_size" || chk
213   ( cd "${stage}" && find var | cpio -o ) | gzip -9 > "${sconf}/base/var.cpio.gz" || chk
214   chk rm -Rf "${stage}/var"
215 }
216
217 do_custom() {
218   log Patch in custom config
219   ( cd "${base}/conf" && find . | cpio -p --link "${sconf}/default" ) || chk
220   # Make sure files in default are newer than the tagfile, so they will be caught by saveconfig
221   find "${sconf}/default" -type f -print0 | xargs -0 touch
222 }
223
224 for step in ${sequence}
225 do
226   echo "${targets}" | grep -q "${step}" || err Unrecognized target "${step}"
227   do_${step}
228 done