]> CyberLeo.Net >> Repos - CDN/cdn-ports-overlay.git/blob - apply
dns/py-dns: resurrect dead port
[CDN/cdn-ports-overlay.git] / apply
1 #!/bin/sh -e
2
3 # Run as root
4 [ "$(id -u)" -eq 0 ] || exec sudo "${0}" "${@}"
5 cd "$(dirname "${0}")"
6
7 meh() { printf " \033[1;32m*\033[0m %s\n" "${*}"; }
8 omg() { printf " \033[1;33m*\033[0m %s\n" "${*}"; }
9 wtf() { printf " \033[1;31m*\033[0m %s\n" "${*}"; kill $$; exit 1; }
10
11 # Return the zpool name for a given dataset
12 zfs_ds_pool() {
13   [ "${1}" ] || return 1
14   echo "${1%%/*}"
15 }
16
17 # Make sure the datasets have the same pool
18 zfs_is_same_pool() {
19   [ "${1}" -a "${2}" ] || return 1
20   [ "$(zfs_ds_pool "${1}")" = "$(zfs_ds_pool "${2}")" ]
21 }
22
23 # Translate a mountpoint into its containing dataset name
24 zfs_mount_to_ds() {
25   [ "${1}" ] || return 1
26   zfs list -H -o name "${1}"
27 }
28
29 # Returns the mountpoint for a given dataset
30 zfs_ds_mount() {
31   [ "${1}" ] || return 1
32   zfs get -H -o value mountpoint "${1}"
33 }
34
35 # Make sure the passed directory corresponds exactly
36 # with its containing dataset's mountpoint
37 zfs_is_mountpoint() {
38   [ "${1}" ] || return 1
39   ds="$(zfs_mount_to_ds "${1}")"
40   [ "${ds}" ] || return 1
41   mp="$(zfs_ds_mount "${ds}")"
42   [ "${mp}" = "${1}" ]
43 }
44
45 # Fetch the origin snapshot for a given dataset
46 zfs_ds_origin() {
47   [ "${1}" ] || return 1
48   zfs get -H -o value origin "${1}"
49 }
50
51 # Fetch the origin dataset for a given dataset
52 zfs_ds_origin_ds() {
53   [ "${1}" ] || return 1
54   sn="$(zfs_ds_origin "${1}")"
55   echo "${sn%%@*}"
56 }
57
58 # List all snapshots for a given dataset, sorted by creation time
59 zfs_ds_snapshots() {
60   [ "${1}" ] || return 1
61   zfs list -rHt snapshot -d 1 -o name -s creation "${1}" | sed -e 's/^[^@]*@//'
62 }
63
64 # Get creation time in seconds since epoch for given dataset
65 zfs_ds_creation() {
66   [ "${1}" ] || return 1
67   zfs get -H -p -o value creation "${1}"
68 }
69
70 zfs_tests() {
71   set +e -x
72   zfs_is_same_pool "mtumishi/srv/ports/upstream/ports" "mtumishi/devel/ports"; echo $?
73
74   zfs_mount_to_ds "/usr/ports"
75   zfs_mount_to_ds "/usr/ports/misc"
76
77   zfs_ds_mount "mtumishi/devel/ports"
78
79   zfs_is_mountpoint "/usr/ports"; echo $?
80   zfs_is_mountpoint "/usr/ports/misc"; echo $?
81
82   zfs_ds_origin "mtumishi/devel/ports"
83   zfs_ds_origin_ds "mtumishi/devel/ports"
84
85   zfs_ds_snapshots "mtumishi/srv/ports/upstream/ports"
86
87   zfs_ds_creation "mtumishi/devel/ports"
88   set +x -e
89 }
90
91 ports_tree_valid() {
92   [ "${1}" ] || return 1
93   [ -f "${1}/COPYRIGHT" -a -f "${1}/Makefile" -a -d "${1}/Mk" ]
94 }
95
96 overlays() {
97   ( cd "${overlay_fs}"
98     ls -1 | while read ovl
99     do
100       [ -d "${ovl}" -a -d "${ovl}/ports" -a -d "${ovl}/patch" ] || continue
101       echo "${ovl}"
102     done
103   )
104 }
105
106 #zfs_tests
107 #exit
108
109 # Location of overlays (Defaults to directory containing this script)
110 overlay_fs="$(realpath "$(dirname "${0}")")"
111
112 # Target ports tree (Defaults to ../assembled/ports)
113 ports_fs="$(realpath "${overlay_fs}/../assembled/ports")"
114
115 # Upstream ports tree (Defaults to ../upstream/ports)
116 upstream_fs="$(realpath "${overlay_fs}/../upstream/ports")"
117
118 # Dataset to hold temporary datasets (defaults to dataset holding overlays)
119 temp_ds="$(zfs_mount_to_ds "${ports_fs}/..")"
120 prep_ds="${temp_ds}/prepare"
121
122 # Compute upstream dataset name from upstream filesystem
123 zfs_is_mountpoint "${upstream_fs}" || { echo "Upstream filesystem ${upstream_fs} is not a ZFS dataset" >&2; exit 1; }
124 upstream_ds="$(zfs_mount_to_ds "${upstream_fs}")"
125 [ "${upstream_ds}" ] || { echo "Unable to get dataset for ${upstream_fs}" >&2; exit 1; }
126
127 # Compute target ports dataset name; synthesize a new one if nonexistent
128 if zfs_is_mountpoint "${ports_fs}"
129 then
130   ports_ds="$(zfs_mount_to_ds "${ports_fs}")"
131   [ "$(zfs_ds_origin_ds "${ports_ds}")" = "${upstream_ds}" ] || wtf "Ports dataset is not a descendent of upstream dataset"
132 else
133   # Compute a temporary dataset name for ports tree
134   ports_ds="${temp_ds}/liveports"
135   create_ports=true
136   omg "Unable to get dataset for ${ports_fs}; ports tree will be placed here instead:"
137   omg " ${ports_ds}"
138   omg "Make sure you move it to where you want it afterwards!"
139 fi
140
141 # Get most recent upstream snapshot; whine if it's more than 24 hours old
142 upstream_snap="$(zfs_ds_snapshots "${upstream_ds}" | tail -n 1)"
143 [ "${upstream_snap}" ] || wtf "No snapshots available for ${upstream_ds}"
144 upstream_snap_age="$(( $(date +%s) - $(zfs_ds_creation "${upstream_ds}@${upstream_snap}") ))"
145 [ "${upstream_snap_age}" -le 86400 ] || omg "Snapshot is stale! (${upstream_snap_age} seconds)"
146
147 # Make sure the snapshot actually looks like a ports tree
148 # Disabled because sauce is giving 'File name too long' now that it is a jail.
149 #ports_tree_valid "${upstream_fs}/.zfs/snapshot/${upstream_snap}" || wtf "This sure is an odd-looking ports tree... ${upstream_fs}"
150
151 # Make sure nothing is /
152 [ "${ports_fs}" -a "${ports_fs}" != '/' ] || wtf "ports_fs is / ?!"
153 [ "${upstream_fs}" -a "${upstream_fs}" != '/' ] || wtf "usptream_fs is / ?!"
154
155 # All tests pass! Now perform the overlay
156
157 meh Clone from ${upstream_ds}@${upstream_snap}
158 zfs clone "${upstream_ds}@${upstream_snap}" "${prep_ds}" || wtf "Clone failed"
159 zfs set atime=off "${prep_ds}" || wtf "atimes failed"
160 prep_fs="$(zfs_ds_mount "${prep_ds}")"
161
162 rm "${prep_fs}/.portsnap.INDEX"
163
164 overlays | while read ovl
165 do
166   meh "=> Applying ${ovl} <="
167
168   meh Overlay
169   ( cd "${overlay_fs}/${ovl}/ports"; find * | cpio -pR root:wheel "${prep_fs}" ) || wtf "overlay failed"
170
171   meh Patch
172   for patch in "${overlay_fs}/${ovl}/patch"/*.patch
173   do
174     meh "... ${patch##*/}"
175     ( cd "${prep_fs}"; patch -p0 ) < "${patch}" || wtf "patch failed"
176     # Maintain rsyncability in the face of patches
177     sed -e '/^+++ /!d; s/^+++ //; s/    .*$//' "${patch}" | while read file
178     do
179       rm -f "${prep_fs}/${file}.orig"
180       touch -r "${patch}" "${prep_fs}/${file}"
181     done
182   done
183 done
184
185 meh Install
186 zfs set readonly=on "${prep_ds}" || wtf "readonly failed"
187
188 # Remember and unmount any null-mounts of the filesystem that is about to be replaced
189 umount_and_generate_mount_lines() {
190   mount | grep '(nullfs, ' | grep "^${ports_fs} " | while read null_src null_on null_dst null_opt
191   do
192     # Figure out if mount is read-only
193     mount_flags=""
194     case "${null_opt}" in
195     *read-only*) mount_flags="-r"
196     esac
197     # Craft a mount line
198     echo "echo '${null_dst}'; /sbin/mount -t nullfs ${mount_flags} '${null_src}' '${null_dst}';"
199     meh "Unmounting ${null_dst}" >&2
200     /sbin/umount "${null_dst}"
201   done
202 }
203 remount_cmd="$(umount_and_generate_mount_lines)"
204
205 # Half of this is conditional on the old ports tree's existence
206 [ "${create_ports}" ] || zfs set mountpoint=legacy "${ports_ds}" || wtf "failed hiding old ports"
207 zfs set mountpoint="${ports_fs}" "${prep_ds}" || wtf "failed mounting new ports"
208 [ "${create_ports}" ] || zfs destroy "${ports_ds}" || wtf "failed destroying old ports"
209 zfs rename "${prep_ds}" "${ports_ds}" || wtf "failed renaming ports"
210
211 # Remount null-mounts of this filesystem
212 [ "${remount_cmd}" ] && meh "Remounting filesystems"
213 eval "${remount_cmd}"
214
215 echo "All done!"