Mercurial > hgrepos > FreeBSD > ports > sysutils > local-bsdtools
comparison sbin/fjail @ 128:3dcae0e91769
Move all admin scripts into the "sbin" folder
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Thu, 17 Oct 2019 09:09:13 +0200 |
| parents | bin/fjail@52523a19797e |
| children | 6be3742d21f7 |
comparison
equal
deleted
inserted
replaced
| 127:37737f225887 | 128:3dcae0e91769 |
|---|---|
| 1 #!/bin/sh | |
| 2 # -*- indent-tabs-mode: nil; -*- | |
| 3 : 'A very minimal BSD Jail management tool. | |
| 4 | |
| 5 :Author: Franz Glasner | |
| 6 :Copyright: (c) 2019 Franz Glasner. | |
| 7 All rights reserved. | |
| 8 :License: BSD 3-Clause "New" or "Revised" License. | |
| 9 See LICENSE for details. | |
| 10 If you cannot find LICENSE see | |
| 11 <https://opensource.org/licenses/BSD-3-Clause> | |
| 12 :ID: @(#)@@PKGORIGIN@@ $HGid$ | |
| 13 | |
| 14 ' | |
| 15 | |
| 16 set -eu | |
| 17 | |
| 18 VERSION="@@VERSION@@" | |
| 19 | |
| 20 USAGE=' | |
| 21 USAGE: fjail [ OPTIONS ] COMMAND [ COMMAND OPTIONS ] [ ARG ... ] | |
| 22 | |
| 23 OPTIONS: | |
| 24 | |
| 25 -V Print the program name and version number to stdout and exit | |
| 26 | |
| 27 -h Print this help message to stdout and exit | |
| 28 | |
| 29 COMMANDS: | |
| 30 | |
| 31 datasets [-u] PARENT CHILD | |
| 32 | |
| 33 Create ZFS datasets to be used within a jail | |
| 34 | |
| 35 PARENT must exist already and CHILD must not exist. | |
| 36 | |
| 37 -u Do not automatically mount newly created datasets | |
| 38 | |
| 39 privs MOUNTPOINT | |
| 40 | |
| 41 Adjust some Unix privileges to mounted jail datasets | |
| 42 | |
| 43 populate MOUNTPOINT BASETXZ | |
| 44 | |
| 45 Populate the jail directory in MOUNTPOINT with the base system in BASETXZ | |
| 46 | |
| 47 copy SOURCE-DATASET DEST-DATASET | |
| 48 | |
| 49 Copy a tree of ZFS datasets with "zfs send -R" and "zfs receive". | |
| 50 Note that the destination dataset must not exist already. | |
| 51 | |
| 52 -u Do not automatically mount received datasets | |
| 53 | |
| 54 ENVIRONMENT: | |
| 55 | |
| 56 All environment variables that affect "zfs" are effective also. | |
| 57 | |
| 58 DESCRIPTION: | |
| 59 | |
| 60 All commands with the exception of "populate" require ZFS as | |
| 61 filesystem. | |
| 62 ' | |
| 63 | |
| 64 | |
| 65 # Reset to standard umask | |
| 66 umask 0022 | |
| 67 | |
| 68 | |
| 69 # | |
| 70 # "datasets" -- create the ZFS dataset tree | |
| 71 # | |
| 72 # command_datasets [ -u ] parent-dataset child-dataset | |
| 73 # | |
| 74 # -u do not automatically mount newly created datasets | |
| 75 # | |
| 76 command_datasets() { | |
| 77 # parent ZFS dataset -- child ZFS dataset name | |
| 78 local _pds _cds | |
| 79 # and its mount point | |
| 80 local _pmp _get _dummy | |
| 81 # full name of the dataset | |
| 82 local _ds | |
| 83 # dynamic ZFS options | |
| 84 local _zfsopts | |
| 85 | |
| 86 _zfsopts="" | |
| 87 while getopts "u" _opt ; do | |
| 88 case ${_opt} in | |
| 89 u) | |
| 90 # do not mount newly created datasets | |
| 91 _zfsopts="${_zfsopts} -u" | |
| 92 ;; | |
| 93 \?|:) | |
| 94 return 2; | |
| 95 ;; | |
| 96 esac | |
| 97 done | |
| 98 shift $((OPTIND-1)) | |
| 99 OPTIND=1 | |
| 100 | |
| 101 _pds="$1" | |
| 102 if [ -z "${_pds}" ]; then | |
| 103 echo "ERROR: no parent dataset given" >&2 | |
| 104 return 2 | |
| 105 fi | |
| 106 _get=$(zfs get -H mountpoint "${_pds}" 2>/dev/null) || { echo "ERROR: dataset \`${_pds}' does not exist" >&2; return 1; } | |
| 107 IFS=$'\t' read _dummy _dummy _pmp _dummy <<EOF | |
| 108 ${_get} | |
| 109 EOF | |
| 110 case "${_pmp}" in | |
| 111 none) | |
| 112 echo "ERROR: dataset \`${_pds}' has no mountpoint" >&2 | |
| 113 return 1 | |
| 114 ;; | |
| 115 legacy) | |
| 116 echo "ERROR: dataset \`${_pds}' has a \`${_mp}' mountpoint" >&2 | |
| 117 return 1 | |
| 118 ;; | |
| 119 *) | |
| 120 # VOID | |
| 121 ;; | |
| 122 esac | |
| 123 _cds="$2" | |
| 124 if [ -z "${_cds}" ]; then | |
| 125 echo "ERROR: no child dataset given" >&2 | |
| 126 return 2 | |
| 127 fi | |
| 128 _ds="${_pds}/${_cds}" | |
| 129 echo "Resulting new root dataset is \`${_ds}' at mountpoint \`${_pmp}/${_cds}'" | |
| 130 if zfs get -H mountpoint "${_ds}" >/dev/null 2>/dev/null; then | |
| 131 echo "ERROR: dataset \`${_ds}' does already exist" >&2 | |
| 132 return 1 | |
| 133 fi | |
| 134 zfs create ${_zfsopts} -o atime=off "${_ds}" | |
| 135 zfs create ${_zfsopts} -o sync=disabled -o setuid=off "${_ds}/tmp" | |
| 136 zfs create ${_zfsopts} "${_ds}/usr" | |
| 137 zfs create ${_zfsopts} "${_ds}/var" | |
| 138 zfs create ${_zfsopts} -o exec=off -o setuid=off "${_ds}/var/audit" | |
| 139 zfs create ${_zfsopts} -o exec=off -o setuid=off "${_ds}/var/cache" | |
| 140 zfs create ${_zfsopts} -o exec=off -o setuid=off -o compression=off "${_ds}/var/cache/pkg" | |
| 141 zfs create ${_zfsopts} -o exec=off -o setuid=off -o compression=off "${_ds}/var/crash" | |
| 142 zfs create ${_zfsopts} -o exec=off -o setuid=off "${_ds}/var/db" | |
| 143 zfs create ${_zfsopts} -o exec=on -o setuid=off "${_ds}/var/db/pkg" | |
| 144 zfs create ${_zfsopts} -o readonly=on -o exec=off -o setuid=off "${_ds}/var/empty" | |
| 145 zfs create ${_zfsopts} -o exec=off -o setuid=off -o primarycache=metadata "${_ds}/var/log" | |
| 146 zfs create ${_zfsopts} -o exec=off -o setuid=off -o atime=on "${_ds}/var/mail" | |
| 147 zfs create ${_zfsopts} -o sync=disabled -o exec=off -o setuid=off -o compression=off -o primarycache=all "${_ds}/var/run" | |
| 148 zfs create ${_zfsopts} -o sync=disabled -o setuid=off "${_ds}/var/tmp" | |
| 149 } | |
| 150 | |
| 151 | |
| 152 # | |
| 153 # "populate" -- populate the datasets with content from a FreeBSD base.txz | |
| 154 # | |
| 155 # command_populate mountpoint basetxz | |
| 156 # | |
| 157 command_populate() { | |
| 158 # MOUNTPOINT -- base.txz | |
| 159 local _mp _basetxz | |
| 160 | |
| 161 _mp="$1" | |
| 162 _basetxz="$2" | |
| 163 | |
| 164 if [ -z "${_mp}" ]; then | |
| 165 echo "ERROR: no mountpoint given" >&2 | |
| 166 return 2 | |
| 167 fi | |
| 168 if [ -z "${_basetxz}" ]; then | |
| 169 echo "ERROR: no base.txz given" >&2 | |
| 170 return 2 | |
| 171 fi | |
| 172 if [ ! -d "${_mp}" ]; then | |
| 173 echo "ERROR: mountpoint \`${_mp}' does not exist" >&2 | |
| 174 return 1 | |
| 175 fi | |
| 176 if [ ! -r "${_basetxz}" ]; then | |
| 177 echo "ERROR: file \`${_basetxz}' is not readable" >&2 | |
| 178 return 1 | |
| 179 fi | |
| 180 | |
| 181 tar -C "${_mp}" --exclude=./var/empty -xJp -f "${_basetxz}" || { echo "ERROR: tar encountered errors" >&2; return 1; } | |
| 182 } | |
| 183 | |
| 184 | |
| 185 # | |
| 186 # "copy" -- ZFS copy of datasets | |
| 187 # | |
| 188 # command_copy source-dataset destination-dataset | |
| 189 # | |
| 190 command_copy() { | |
| 191 # source dataset -- destination dataset | |
| 192 local _source _dest | |
| 193 # dynamic ZFS options | |
| 194 local _zfsopts | |
| 195 | |
| 196 _zfsopts="" | |
| 197 while getopts "u" _opt ; do | |
| 198 case ${_opt} in | |
| 199 u) | |
| 200 # do not mount newly created datasets | |
| 201 _zfsopts="${_zfsopts} -u" | |
| 202 ;; | |
| 203 \?|:) | |
| 204 return 2; | |
| 205 ;; | |
| 206 esac | |
| 207 done | |
| 208 shift $((OPTIND-1)) | |
| 209 OPTIND=1 | |
| 210 | |
| 211 _source="$1" | |
| 212 if [ -z "${_source}" ]; then | |
| 213 echo "ERROR: no source dataset given" >&2 | |
| 214 return 2 | |
| 215 fi | |
| 216 _dest="$2" | |
| 217 if [ -z "${_dest}" ]; then | |
| 218 echo "ERROR: no source dataset given" >&2 | |
| 219 return 2 | |
| 220 fi | |
| 221 zfs send -R -n -v ${_source} || { echo "ERROR: ZFS operation failed in no-op mode" >&2; return 1; } | |
| 222 zfs send -R "${_source}" | zfs receive ${_zfsopts} "${_dest}" || { echo "ERROR: ZFS operation failed" >&2; return 1; } | |
| 223 } | |
| 224 | |
| 225 | |
| 226 # | |
| 227 # "privs" -- adjust privileges | |
| 228 # | |
| 229 # To be used when all ZFS datasets are mounted. | |
| 230 # | |
| 231 command_privs() { | |
| 232 # mountpoint | |
| 233 local _mp _d | |
| 234 | |
| 235 _mp="$1" | |
| 236 if [ -z "${_mp}" ]; then | |
| 237 echo "ERROR: no mountpoint given" >&2 | |
| 238 return 2 | |
| 239 fi | |
| 240 if [ ! -d "${_mp}" ]; then | |
| 241 echo "ERROR: directory \`${_mp}' does not exist" >&2 | |
| 242 return 1 | |
| 243 fi | |
| 244 for _d in tmp var/tmp ; do | |
| 245 chmod 01777 "${_mp}/${_d}" | |
| 246 done | |
| 247 chown root:mail "${_mp}/var/mail" | |
| 248 chmod 0775 "${_mp}/var/mail" | |
| 249 } | |
| 250 | |
| 251 | |
| 252 # | |
| 253 # Global option handling | |
| 254 # | |
| 255 while getopts "Vh" _opt ; do | |
| 256 case ${_opt} in | |
| 257 V) | |
| 258 echo "fjail v${VERSION} (rv:@@HGREVISION@@)" | |
| 259 exit 0 | |
| 260 ;; | |
| 261 h) | |
| 262 echo "${USAGE}" | |
| 263 exit 0 | |
| 264 ;; | |
| 265 \?) | |
| 266 exit 2; | |
| 267 ;; | |
| 268 *) | |
| 269 echo "ERROR: option handling failed" >&2 | |
| 270 exit 2 | |
| 271 ;; | |
| 272 esac | |
| 273 done | |
| 274 | |
| 275 # | |
| 276 # Reset the Shell's option handling system to prepare for handling | |
| 277 # command-local options. | |
| 278 # | |
| 279 shift $((OPTIND-1)) | |
| 280 OPTIND=1 | |
| 281 | |
| 282 test $# -gt 0 || { echo "ERROR: no command given" >&2; exit 2; } | |
| 283 | |
| 284 command="$1" | |
| 285 shift | |
| 286 | |
| 287 case "${command}" in | |
| 288 datasets) | |
| 289 command_datasets "$@" | |
| 290 ;; | |
| 291 privs) | |
| 292 command_privs "$@" | |
| 293 ;; | |
| 294 populate) | |
| 295 command_populate "$@" | |
| 296 ;; | |
| 297 copy) | |
| 298 command_copy "$@" | |
| 299 ;; | |
| 300 *) | |
| 301 echo "ERROR: unknown command \`${command}'" >&2 | |
| 302 exit 2 | |
| 303 ;; | |
| 304 esac |
