]> CyberLeo.Net >> Repos - CDN/Mosi.git/blob - script/gentree
script/makepkg: make sure kvs directory exists before loading kvs module
[CDN/Mosi.git] / script / gentree
1 #!/bin/sh
2
3 # Boilerplate
4 _root="$(dirname "${0}")"; . "${_root}/lib/sh/env.sh"
5
6 # Load needed modules
7 want root ansi log ask
8
9 targets="prepwork admin overlay packages patch whiteout scripts prepboot preptmp prepetc imgboot imgconf imgetc imgall custom"
10
11 pebkac() {
12   [ "${*}" ] && printf "${*}\n\n"
13   echo "********************************************************************************"
14   echo "Usage: $(basename "${0}") <-a arch> <-c conf> <-b basedir> <-t stagedir>"
15   echo '        <-i pkgsdir> <-p patchdir> <-r rootdir> <-l logfile> [-h] <targets>'
16   echo "  -a arch       Arch for image target (default: current arch: $(uname -m))"
17   echo '  -c conf       Conf name for image target (default: GENERIC)'
18   echo '  -b basedir    Basedir for automagic defaults'
19   echo '  -t stagedir   Staging directory name (${target}/tree)'
20   echo '  -r rootdir    Directory holding the virgin source tree (${target}/world/root)'
21   echo '  -o overlaydir Directory holding an overlay tree (${target}/config/overlay)'
22   echo '  -i pkgsdir    Directory holding packages to install (${target}/pkg)'
23   echo '  -p patchdir   Directory holding patches to apply (${target}/config/patch)'
24   echo '  -w whiteout   Listfile of paths to remove (${target}/config/whiteout.lst)'
25   echo '  -s scriptdir  Directory holding scripts to apply (${target}/config/script)'
26   echo '  -d confdir    Conf md_size files and old cpios (${target}/config/conf)'
27   echo '  -l logfile    File to hold stderr spam (${stage}/gentree.log'
28   echo '  -h            Hello! >^-^<'
29   echo ''
30   echo 'Available targets:'
31   for target in ${targets}
32   do
33     echo "  ${target}"
34   done
35   exit 1
36 }
37
38 while getopts "a:c:b:t:r:o:i:p:w:s:l:h" opt
39 do
40   case "${opt}" in
41     a) arch="${OPTARG}" ;;
42     c) conf="${OPTARG}" ;;
43     b) base="${OPTARG}" ;;
44     t) tree="${OPTARG}" ;;
45     r) root="${OPTARG}" ;;
46     o) ovly="${OPTARG}" ;;
47     i) pkgs="${OPTARG}" ;;
48     p) ptch="${OPTARG}" ;;
49     w) rmrf="${OPTARG}" ;;
50     s) scpt="${OPTARG}" ;;
51     d) cnfd="${OPTARG}" ;;
52     l) logf="${OPTARG}" ;;
53     h) pebkac ;;
54     [?]) pebkac "Unrecognized option ${opt}" ;;
55   esac
56 done
57 shift $(( $OPTIND - 1 ))
58
59 sequence="${*:-${targets}}"
60
61 base="${base:-$(dirname "${0}")/..}" #"
62 base="$(realpath "${base}")"
63
64 arch="${arch:-$(uname -m)}"
65 conf="${conf:-GENERIC}"
66
67 target="${base}/targets/${arch}/${conf}"
68
69 tree="${tree:-${target}/tree}"
70 root="${root:-${target}/world/root}"
71 ovly="${ovly:-${target}/config/overlay}"
72 pkgs="${pkgs:-${target}/pkg}"
73 ptch="${ptch:-${target}/config/patch}"
74 rmrf="${rmrf:-${target}/config/whiteout.lst}"
75 scpt="${scpt:-${target}/config/script}"
76 cnfd="${cnfd:-${target}/config/conf}"
77 logf="${logf:-${tree}/gentree.log}"
78
79 stage="${stage:-${base}/tree}"
80 pkgs="${pkgs:-${base}/pkg}"
81 patch="${patch:-${base}/patch}"
82 overlay="${overlay:-${base}/overlay}"
83 root="${root:-${base}/root}"
84 logfile="${logfile:-${stage}/gentree.log}"
85
86 mkdir -p "${tree}"
87 tree="$(realpath "${tree}")"
88 sroot="${tree}/root"
89 sboot="${tree}/boot"
90 sconf="${tree}/conf"
91
92 exec 2>>"${logf}"
93 _log_to_stderr=yes
94 meh "Logging to ${logf}"
95
96 # Helper functions
97 onelink() {
98   # Make sure the provided file(s) have only one link!
99   while [ -n "${1}" ]
100   do
101     if [ -f "${1}" -a "$(stat -f '%l' "${1}")" -gt 1 ]
102     then
103       cp -p "${1}" "${1}.tmp" && mv "${1}.tmp" "${1}" || err "breaklink failed"
104     fi
105     shift
106   done
107 }
108
109 in_chroot() {
110   chroot "${sroot}" "${@}"
111 }
112
113 in_fakeroot() {
114   for fakeroot in $(which fakeroot) /usr/local/bin/fakeroot
115   do
116     [ -x "${fakeroot}" ] && break
117     unset fakeroot
118   done
119
120   [ -x "${fakeroot}" ] || warn "security/fakeroot not found! Expect weird ownership from overlay"
121   "${fakeroot}" "${@}"
122 }
123
124 # Build steps
125 do_prepwork() {
126   log Prepare workspace
127   if [ -d "${tree}" -a \( -d "${sroot}" -o -d "${sboot}" -o -d "${sconf}" \) ]
128   then
129     yn n " ${a_yellow}*${a_normal} Workspace already exists. Delete? [y/N]" || err Aborting
130     chk rm -Rf "${sroot}" "${sboot}" "${sconf}"
131   fi
132   chk mkdir -p "${sroot}"
133   # Eliminate schg, because it interferes with hardlinks
134   chk chflags -R noschg "${root}/lib"
135   chk chflags -R noschg "${root}/libexec"
136   chk chflags -R noschg "${root}/sbin"
137   chk chflags -R noschg "${root}/usr"
138   ( cd "${root}" && find . | cpio -pl "${sroot}" ) || chk
139 }
140
141 do_admin() {
142   log Create an emergency user admin/admin
143   # delink passwd to ensure it doesn't get patched in-place
144   chk onelink "${sroot}/etc/passwd" "${sroot}/etc/master.passwd" "${sroot}/etc/group"
145   echo '$1$2rXOWsK/$eiBHA6K7xL96DZbcY24YR0' | chk in_chroot /usr/sbin/pw useradd admin -u 999 -g wheel -G operator -c Administrator -d /usr/home/admin -m -s /bin/csh -H 0
146 }
147
148 do_overlay() {
149   [ -d "${ovly}" ] || return
150   log Apply overlay from "${ovly##${base}/}"
151   ( cd "${ovly}" && find . | cpio -pR root:wheel "${sroot}" ) || chk
152 }
153
154 do_packages() {
155   [ -d "${pkgs}" ] || return
156   count="$(ls -1 "${pkgs}" | wc -l)"
157   [ "${count}" -gt 0 ] || return
158   log Install ${count} packages from "${pkgs##${base}/}"
159   chk mkdir -p "${sroot}/pkg"
160   ( cd "${pkgs}" && find . | cpio -pl "${sroot}/pkg" ) || chk
161   chk chroot "${sroot}" /bin/sh -c 'cd /pkg; exec pkg_add -F *'
162   chk rm -Rf "${sroot}/pkg"
163 }
164
165 do_patch() {
166   [ -d "${ptch}" ] || return
167   log Apply patches from "${ptch##${base}/}"
168   for file in "${ptch}"/*
169   do
170     note "... $(basename "${file}")"
171     ( cd "${sroot}" && patch < "${file}" ) || chk
172     # Remove .orig files
173     sed -e '/^+++ /!d; s/^+++ //; s/[   ]*[0-9: .+-]*$//' "${file}" | while read orig
174     do
175       rm -f "${sroot}/${orig}.orig"
176     done || chk
177   done
178 }
179
180 do_whiteout() {
181   [ -f "${rmrf}" ] || return
182   log Whiteout files from "${rmrf##${base}/}"
183   while read entry < "${rmrf}"
184   do
185     # Strip off terminating slash
186     entry="${entry%%/}"
187     # Obtain directory name, for path resolution
188     entry_path="$(dirname "${entry}")"
189     entry_path="$(realpath "${sroot}/${entry_path}" 2>/dev/null)"
190     entry_file="$(basename "${entry}")"
191
192     # Ignore paths that do not exist
193     [ "${entry_path}" ] || continue
194
195     # Warn and ignore paths that fall outside ${sroot} after resolution
196     if [ "$(echo ${entry_path} | sed -e "s/^\(.\{${#sroot}\}\).*$/\1/")" != "${sroot}" ]
197     then
198       warn "Whiteout '${entry}' malformed: leads outside '${sroot}'"
199       continue
200     fi
201
202     # Warn and ignore paths that cannot be chdir()'d
203     if [ ! -d "${entry_path}" ]
204     then
205       warn "Whiteout '${entry}' malformed: non-directory in path"
206       continue
207     fi
208
209     ( cd "${entry_path}"
210       if [ -d "${entry}" ]
211       then
212         echo rm -Rf "${entry}"
213       else
214         echo rm -f "${entry}"
215       fi
216     )
217   done
218 }
219
220 # Run arbitrary user scripts to perform additional functions prior to carving and packaging
221 # Warning! These run as root and no chroot is performed! BE CAREFUL HERE!
222 do_scripts() {
223   [ -d "${scpt}" ] && ls -1 "${scpt}" | grep -q '.' || return
224   log Run user scripts from "${scpt##${base}/}"
225   ls -1 "${scpt}" | LANG=C sort | while read file
226   do
227     [ -f "${scpt}/${file}" ] || continue
228     note "... ${file}"
229     ( cd "${sroot}"
230       . "${scpt}/${file}"
231     ) || chk
232   done
233 }
234
235 do_prepboot() {
236   log Prepare /boot
237   chk mv "${sroot}/boot/boot" "${sroot}/boot/boot.blk"
238   chk ln -sf . "${sroot}/boot/boot"
239
240   # Move /boot/zfs to /etc/zfs and symlink
241   ls -1 "${sroot}/boot/zfs"/* 2>&- && chk mv "${sroot}/boot/zfs"/* "${sroot}/etc/zfs/"
242   chk rmdir "${sroot}/boot/zfs"
243   chk ln -svf ../etc/zfs "${sroot}/boot/zfs"
244 }
245
246 do_preptmp() {
247   log Prepare /tmp
248   ( cd "${sroot}/tmp" && find . | cpio -p --link ../var/tmp ) || chk
249   chk rm -Rf "${sroot}/tmp"
250   chk ln -sf var/tmp "${sroot}/tmp"
251 }
252
253 do_prepetc() {
254   log Prepare /etc
255   chk mkdir -p "${sroot}/etc/local"
256   chk mkdir -p "${sroot}/usr/local/etc" # Silence warnings
257   ( cd "${sroot}/usr/local/etc" && find . | cpio -pl ../../../etc/local ) || chk
258   chk rm -Rf "${sroot}/usr/local/etc"
259   chk ln -sf ../../etc/local "${sroot}/usr/local/etc"
260 }
261
262 do_imgboot() {
263   log Create boot imgsrc
264   chk mv "${sroot}/boot" "${tree}"
265   chk mkdir -p "${sroot}/boot"
266   chk mkdir -p "${sroot}/modules"
267   chk find "${sboot}/kernel" -type f -name '*.symbols' -delete
268
269   # Link all modules into /modules, as an alternative search path
270   ( cd "${sboot}/kernel" && find . -name '*.ko' -o -name "${conf}" | cpio -pl "${sroot}/modules" ) || chk
271   ( cd "${sboot}/modules" && find . -name '*.ko' -o -name "${conf}" | cpio -pl "${sroot}/modules" ) || chk
272
273   # Remove all modules from the root fs that are preloaded by the preloader
274   [ ! -f "${sboot}/loader.conf" ] || cat "${sboot}/loader.conf" | grep '_load=' | \
275     sed -e 's#^\(.*\)_load=.*$#'"${sroot}/modules/"'\1.ko#' | xargs rm -f
276
277   # Remove all modules from the boot fs that are present in the root fs
278   ( cd "${sroot}/modules" && ls -1 ) | sed -e 's#^#'"${sboot}/kernel/"'#' | xargs rm -f
279   ( cd "${sroot}/modules" && ls -1 ) | sed -e 's#^#'"${sboot}/modules/"'#' | xargs rm -f
280
281   # Rebuild linker hints in both places, to make sure the kernel can find everything once running
282   kldxref "${sboot}/kernel" "${sboot}/modules" "${sroot}/modules"
283
284   # Gzip kernel to save boot space
285   chk onelink "${sboot}/kernel/kernel"
286   chk gzip -9 "${sboot}/kernel/kernel"
287
288   # Compress all the modules as well, since /loader can handle them; kldload cannot,
289   # but I think I can safely assume you will not be kldunloading boot-loaded modules
290   ls -1 "${sboot}/kernel/"*.ko "${sboot}/modules/"*.ko 2>/dev/null | while read kmod
291   do
292     chk onelink "${kmod}"
293     chk gzip -9 "${kmod}"
294   done
295
296   # Make sure /boot/zfs -> /etc/zfs symlink exists even without /boot mounted
297   [ -L "${sroot}/boot/zfs" ] || chk ln -sf ../etc/zfs "${sroot}/boot/zfs"
298 }
299
300 do_imgconf() {
301   log Create conftree
302   chk mkdir -p "${sroot}/conf"
303   echo "ufs:/dev/ufs/conf" > "${sroot}/conf/diskless_remount" || chk
304   chk mkdir -p "${sconf}/backup"
305   chk mkdir -p "${sconf}/base"
306   chk mkdir -p "${sconf}/default"
307
308   # This will screw up permissions when applied to /conf/{base,default}/*
309   # so do it before their creation
310   chk chown -R :operator "${sconf}"
311   ( find "${sconf}" -exec chmod o-rwx {} + ) || chk
312
313   # Create packdirs for each
314   for pack in "${cnfd}"/*.md_size
315   do
316     pack="$(basename "${pack%%.md_size}")"
317     chk mkdir -p "${sconf}/base/${pack}" "${sconf}/default/${pack}"
318     chk chown root:wheel "${sconf}/base/${pack}" "${sconf}/default/${pack}"
319     chk chmod 755 "${sconf}/base/${pack}" "${sconf}/default/${pack}"
320     ( cat "${cnfd}/${pack}.md_size" > "${sconf}/base/${pack}/md_size" ) || chk
321   done
322 }
323
324 do_imgetc() {
325   # etc requires special handling to ensure everything is properly arranged.
326   log Create etc confpack
327   chk touch "${sroot}/etc/diskless"
328   # Touch /COPYRIGHT to provide an immutable time anchor for saveconfig
329   chk touch "${sroot}/COPYRIGHT"
330   chk sleep 1 # Make sure anchor is at least one second older than everything in custom
331   chk mkdir -p "${tree}/pack"
332   chk mv "${sroot}/etc" "${tree}/pack"
333   chk mkdir -p "${sroot}/etc"
334   chk cp -p "${tree}/pack/etc/rc" "${tree}/pack/etc/rc.subr" "${tree}/pack/etc/rc.initdiskless" "${tree}/pack/etc/login.conf.db" "${tree}/pack/etc/diskless" "${sroot}/etc"
335
336   # Make sure etc confpack exists; default if necessary
337   chk mkdir -p "${sconf}/base/etc"
338   chk mkdir -p "${sconf}/default/etc"
339   [ -e "${sconf}/base/etc/md_size" ] || echo "10240" > "${sconf}/base/etc/md_size"
340
341   ( cd "${tree}/pack" && find etc | cpio -o ) | gzip -9 > "${sconf}/base/etc.cpio.gz" || chk
342   chk rm -Rf "${tree}/pack"
343 }
344
345 do_imgall() {
346   log Create remaining confpacks
347   chk mkdir -p "${tree}/pack"
348   for pack in "${cnfd}"/*.md_size
349   do
350     pack="$(basename "${pack%%.md_size}")"
351     # Ignore etc, as it was processed in do_imgetc
352     [ "${pack}" = "etc" ] && continue
353     note "... ${pack}"
354     chk mv "${sroot}/${pack}" "${tree}/pack"
355     chk mkdir -p "${sroot}/${pack}"
356     ( cd "${tree}/pack" && find "${pack}" | cpio -o ) | gzip -9 > "${sconf}/base/${pack}.cpio.gz" || chk
357     rm -Rf "${tree}/pack/${pack}" || chflags -R noschg "${tree}/pack/${pack}" && rm -Rf "${tree}/pack/${pack}" || chk
358   done
359   chk rm -Rf "${tree}/pack"
360 }
361
362 do_custom() {
363   log Patch in custom config
364   ( cd "${cnfd}" && find . -name '*.md_size' -o -print | cpio -pl "${sconf}/default" ) || chk
365   # Make sure files in default are newer than the tagfile, so they will be caught by saveconfig
366   find "${sconf}/default" -type f -print0 | xargs -0 touch
367   # If there are scavenged cpio.gz confpacks, touch their contents as well.
368   chk mkdir -p "${tree}/pack"
369   if ls -1 "${sconf}/default" | grep -q '.cpio.gz'
370   then
371     chk rm -Rf "${tree}/pack"/*
372     for file in "${sconf}/default"/*.cpio.gz
373     do
374       pack="$(basename "${file%%.cpio.gz}")"
375       zcat "${file}" | ( cd "${tree}/pack"; cpio -id ) || chk
376       find "${tree}/pack" -type f -exec touch {} +
377       ( cd "${tree}/pack"; find . | cpio -o ) | gzip -9 > "${file}"
378       chk rm -Rf "${tree}/pack"/*
379     done
380   fi
381   chk rm -Rf "${tree}/pack"
382 }
383
384 for step in ${sequence}
385 do
386   echo "${targets}" | grep -q "${step}" || err Unrecognized target "${step}"
387   do_${step}
388 done