├── LICENSE ├── README.md ├── backup ├── apache ├── configs └── configs-custom │ ├── .gitignore │ └── README.md ├── config ├── jail-create ├── jail-ezjail ├── jail-syncthing ├── jail-turnserver └── jail-unifi ├── lib ├── shell.common ├── shell.io └── shell.pkg ├── portsfetch └── update-host /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018, Chris Wells 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # freebsd-scripts 2 | 3 | A collection of shell scripts to perform common actions (mostly related to system administration) on FreeBSD. 4 | 5 | Feedback and contributions are welcome! 6 | 7 | ## Scripts 8 | 9 | [backup/apache](backup/apache) backs up Let's Encrypt certs (if present), Apache includes, and the www directory. If Apache is installed in a jail, you can provide the path to the jail to back up from there. 10 | 11 | --- 12 | 13 | [backup/configs](backup/configs) backs up common config files that may have been modified on the system. Includes /etc and /usr/local/etc from the host as well as from inside all jails. 14 | 15 | --- 16 | 17 | [portsfetch](portsfetch) can be used to switch to the current quarterly branch for FreeBSD ports and to update your local ports tree after switching. See [Using Quarterly Ports on FreeBSD](https://chriswells.io/blog/using-quarterly-ports-on-freebsd) for background information and usage instructions. 18 | 19 | --- 20 | 21 | [update-host](update-host) performs a basic update sequence including `freebsd-update` on the host plus a vulnerability audit and upgrade of packages on the host and inside each jail. 22 | 23 | ## Libraries 24 | 25 | Currently, the bulk of this project consists of the scripts in the [lib](lib) directory that provide functions for other scripts to use. I've made changes to make those libraries more flexible before sharing them, and I'll release additional scripts over time as I update them to incorporate those changes. 26 | 27 | --- 28 | 29 | ### shell.common 30 | 31 | [lib/shell.common](lib/shell.common) defines functions frequently used in shell scripts. 32 | 33 | #### General 34 | 35 | * getScriptPath: Returns full path to the current script. 36 | * importScript: Imports another script. 37 | * pressEnterTo: Asks the user to press enter to perform an action. 38 | * requireRootOrExit: Exits the script with an error message if the user is not root or using sudo. 39 | 40 | #### Hashes (Associative Arrays / Dictionaries) 41 | 42 | It includes functions for managing hashes (associative arrays / dictionaries), which are used by other functions in the file: 43 | 44 | * hashContainsKey: Returns result code indicating whether the specified key exists in the hash. 45 | * hashContainsValue: Returns result code indicating whether the specified value exists in the hash. 46 | * hashGet: Returns value found for the specified key. 47 | * hashPut: Adds an item to the hash. 48 | 49 | #### Script Options 50 | 51 | There are limited functions for reading script options such as `-p` and `-param value`, but not `-abc` where all 3 are different options: 52 | 53 | * getOption: Returns the value provided for the specified option. 54 | * optionIsEnabled: Returns result code indicating whether the specified option was passed to the script. 55 | * parseScriptOptions: Parses the command line options for the other option functions. 56 | * setOption: Sets an option. Used by parseScriptOptions, but available to scripts as well. 57 | 58 | #### Note Management 59 | 60 | Finally, a couple of functions that allow scripts to accumulate a list of notes to be shown to the user after the script completes: 61 | 62 | * addNote: Adds a note such as a status message to a file. 63 | * showNotes: Displays the contents of the notes file and its location to the user. 64 | 65 | --- 66 | 67 | ### shell.io 68 | 69 | [lib/shell.io](lib/shell.io) defines functions commonly used for file input/output. 70 | 71 | #### Utility Functions 72 | 73 | * generateHexString: Returns a random hex string of the requested length (default 8). 74 | * getTempFileName: Provides a string that can be used as the name of a temp file. 75 | * runAsScriptInJail: Executes commands as a shell script inside the specified jail. 76 | 77 | #### Text Manipulation 78 | 79 | * escapeAwkSearchText: Returns input string with special characters escaped for awk. 80 | * escapeForSed: Returns input string with special characters escaped for sed. 81 | * escapeNewlines: Returns input string with newlines escaped. 82 | 83 | #### File Manipulation 84 | 85 | * appendToFile: Appends text to the end of the specified file. 86 | * createOrReplaceFile: Creates or replaces the specified file with the provided contents. 87 | * deleteLine: Deletes the provided line of text from the specified file. 88 | * insertAfterLine: Inserts the provided text after the specified text. 89 | * insertBeforeLine: Inserts the provided text before the specified text. 90 | * replaceLine: Replaces the specified line of text with the provided text. 91 | * replacePattern: Replaces the specified regular expression with the provided text. 92 | * replaceText: Replaces the specified text with the provided text. 93 | * commentLine: Comments a line by prefixing with # or a provided value. 94 | * uncommentLine: Uncomments a line by removing # or a provided value. 95 | 96 | --- 97 | 98 | ### shell.pkg 99 | 100 | [lib/shell.pkg](lib/shell.pkg) defines functions to assist with package management. 101 | 102 | #### Package Management Functions 103 | 104 | * hasPackage: Checks to see whether a given package is installed on the host. 105 | * installPackages: Installs one or more packages on the host. 106 | * jailHasPackage: Checks to see whether a given package is installed in a jail. 107 | * jailPackages: Installs one or more packages in a jail. 108 | -------------------------------------------------------------------------------- /backup/apache: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | :<.txt`. For example, the custom list for a system named **Beastie** would be named `host-Beastie.txt` (case sensitive, so execute `hostname` to check the proper case). 15 | 16 | To include a list of files/directories across a type or class of system, create a file named `type-.txt` and provide the type on the command line when backing up. You can create any types that you want. For example, to back up a consistent set of config files on all desktop systems, create the list in `type-desktop.txt` and use `sudo backup/configs -t desktop` to perform the backup. 17 | 18 | The lists that are included in a given backup are: 19 | 20 | * The default config files defined in the [backup/configs](../configs) script. 21 | * If `-t ` is specified, the files listed in `type-.txt`. 22 | * The files listed in `host-all.txt`. 23 | * The files listed in `host-.txt`. 24 | 25 | The `configs-custom/*.txt` files are excluded from git to prevent conflicts, but they are included in all backups to preserve your custom lists. 26 | -------------------------------------------------------------------------------- /config/jail-create: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | :<.mc 99 | cd /etc/mail/certs 100 | openssl dhparam -out dh.param 2048 101 | ' 102 | 103 | ################################################################################ 104 | 105 | # shellcheck disable=SC2039 106 | echo -n ' 107 | Creating hosts entry...' 108 | appendToFile /etc/hosts " 109 | # Jail: 110 | $JAIL_IP $JAIL_NAME" 111 | echo ' Done.' 112 | 113 | service ezjail restart 114 | ezjail-admin update -u 115 | 116 | ################################################################################ 117 | 118 | # Back up the fresh jail config files. -- cwells 119 | "$SCRIPT_PATH/../backup/configs" -s "jail-create-${JAIL_NAME}-base" 120 | addNote "Created backup: jail-create-${JAIL_NAME}-base" 121 | 122 | echo 'Done.' 123 | 124 | exit 0 125 | -------------------------------------------------------------------------------- /config/jail-ezjail: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | :< ($ext_if) 75 | ' 76 | echo ' Done.' 77 | 78 | # Enable the cloned loopback interface, start ezjail, and install the ports tree. -- cwells 79 | service netif cloneup 80 | service ezjail start 81 | if optionIsEnabled '-q'; then 82 | ezjail-admin install -p 83 | "$SCRIPT_PATH/../portsfetch" /usr/jails/basejail/usr/ports 84 | elif optionIsEnabled '-p'; then 85 | ezjail-admin install -p 86 | else 87 | ezjail-admin install 88 | fi 89 | 90 | # Back up the config files. -- cwells 91 | "$SCRIPT_PATH/../backup/configs" -s jail-ezjail-configured 92 | addNote 'Created backup: jail-ezjail-configured' 93 | 94 | addNote 'Installed and configured ezjail, but no jails were created.' 95 | showNotes 96 | 97 | # Saved until the end because it disconnects the SSH session. -- cwells 98 | pressEnterTo 'enable NAT for jails (disconnects SSH session)' 99 | pfctl -F all -f /etc/pf.conf 100 | # Can I use this instead without losing the connection? 101 | # pfctl -s nat 102 | 103 | exit 0 104 | -------------------------------------------------------------------------------- /config/jail-syncthing: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | :<' " 60 | 61 | 62 | 63 | 64 | syslog 65 | /usr/jails/$JAIL_NAME/var/log/syncthing.log 66 | 67 | 68 | " 69 | fi 70 | 71 | ################################################################################ 72 | 73 | # shellcheck disable=SC2039 74 | echo -n ' 75 | Updating pf.conf...' 76 | # shellcheck disable=SC2016 77 | # shellcheck disable=SC2140 78 | insertAfterLine /etc/pf.conf 'jail_net=$jail_if:network' "${JAIL_NAME}_jail="\""$JAIL_IP"\"" 79 | ${JAIL_NAME}_tcp_ports="\""{ 22000 }"\"" 80 | ${JAIL_NAME}_udp_ports="\""{ 21027 }"\" 81 | # shellcheck disable=SC2016 82 | insertAfterLine /etc/pf.conf 'nat on $ext_if from $jail_net to any -> ($ext_if)' "rdr pass on \$ext_if inet proto tcp to port \$${JAIL_NAME}_tcp_ports -> \$${JAIL_NAME}_jail 83 | rdr pass on \$ext_if inet proto udp to port \$${JAIL_NAME}_udp_ports -> \$${JAIL_NAME}_jail" 84 | echo ' Done.' 85 | 86 | ################################################################################ 87 | 88 | # Back up the final config files. -- cwells 89 | "$SCRIPT_PATH/../backup/configs" -s "jail-$JAIL_NAME-done" 90 | addNote "Created backup: jail-$JAIL_NAME-done" 91 | 92 | showNotes 93 | 94 | # Saved until the end because it disconnects the SSH session. -- cwells 95 | pressEnterTo 'reload pf config (disconnects SSH session)' 96 | pfctl -F all -f /etc/pf.conf 97 | 98 | exit 0 99 | -------------------------------------------------------------------------------- /config/jail-turnserver: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | :<' " 151 | 152 | 153 | 154 | 155 | syslog 156 | /usr/jails/$JAIL_NAME/var/log/turn_* 157 | 158 | 159 | " 160 | fi 161 | 162 | ################################################################################ 163 | 164 | # shellcheck disable=SC2039 165 | echo -n ' 166 | Updating pf.conf...' 167 | # shellcheck disable=SC2016 168 | # shellcheck disable=SC2140 169 | insertAfterLine /etc/pf.conf 'jail_net=$jail_if:network' "${JAIL_NAME}_jail="\""$JAIL_IP"\"" 170 | ${JAIL_NAME}_tcp_ports="\""{ 3478, 5349 }"\"" 171 | ${JAIL_NAME}_udp_ports="\""{ 3478, 5349, 49152:65535 }"\" 172 | # shellcheck disable=SC2016 173 | insertAfterLine /etc/pf.conf 'nat on $ext_if from $jail_net to any -> ($ext_if)' "rdr pass on \$ext_if inet proto tcp to port \$${JAIL_NAME}_tcp_ports -> \$${JAIL_NAME}_jail 174 | rdr pass on \$ext_if inet proto udp to port \$${JAIL_NAME}_udp_ports -> \$${JAIL_NAME}_jail" 175 | echo ' Done.' 176 | 177 | ################################################################################ 178 | 179 | # Back up the final config files. -- cwells 180 | "$SCRIPT_PATH/../backup/configs" -s "jail-$JAIL_NAME-done" 181 | addNote "Created backup: jail-$JAIL_NAME-done" 182 | 183 | showNotes 184 | 185 | # Saved until the end because it disconnects the SSH session. -- cwells 186 | pressEnterTo 'reload pf config (disconnects SSH session)' 187 | pfctl -F all -f /etc/pf.conf 188 | 189 | exit 0 190 | -------------------------------------------------------------------------------- /config/jail-unifi: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | :< ($ext_if)' "rdr pass on \$ext_if inet proto tcp to port \$${JAIL_NAME}_tcp_ports -> \$${JAIL_NAME}_jail 72 | rdr pass on \$ext_if inet proto udp to port \$${JAIL_NAME}_udp_ports -> \$${JAIL_NAME}_jail" 73 | echo ' Done.' 74 | 75 | ################################################################################ 76 | 77 | # Back up the final config files. -- cwells 78 | "$SCRIPT_PATH/../backup/configs" -s "jail-$JAIL_NAME-done" 79 | addNote "Created backup: jail-$JAIL_NAME-done" 80 | 81 | showNotes 82 | 83 | # Saved until the end because it disconnects the SSH session. -- cwells 84 | pressEnterTo 'reload pf config (disconnects SSH session)' 85 | pfctl -F all -f /etc/pf.conf 86 | 87 | exit 0 88 | -------------------------------------------------------------------------------- /lib/shell.common: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | :<&2 63 | exit 1 64 | fi 65 | } 66 | 67 | 68 | ################################################################################ 69 | # Hashes (Associative Arrays / Dictionaries) 70 | ################################################################################ 71 | 72 | # Parameters: 73 | # $1 = name of the hash variable 74 | # $2 = key to search for 75 | # Returns: 76 | # result code indicating whether the specified key exists in the hash 77 | hashContainsKey() { 78 | alias "HASH_$1_$2" > /dev/null 2>&1 79 | } 80 | 81 | # Parameters: 82 | # $1 = name of the hash variable 83 | # $2 = value to search for 84 | # Returns: 85 | # result code indicating whether the specified value exists in the hash 86 | hashContainsValue() { 87 | alias | grep -Eq "^HASH_$1_.+='?$2'?\$" 88 | } 89 | 90 | # Parameters: 91 | # $1 = name of the hash variable 92 | # $2 = key for the value that should be retrieved 93 | # Returns: 94 | # value found for the specified key 95 | hashGet() { 96 | alias "HASH_$1_$2" 2> /dev/null | awk -F"=" '{ gsub(/^'\''|'\''$/, "", $2); print $2; }' 97 | } 98 | 99 | # Parameters: 100 | # $1 = name of the hash variable 101 | # $2 = key to use 102 | # $3 (optional) = the value to store 103 | # Default is an empty string when not provided. 104 | hashPut() { 105 | # shellcheck disable=SC2139 106 | alias "HASH_$1_$2=$3" 107 | } 108 | 109 | 110 | ################################################################################ 111 | # Script Options 112 | ################################################################################ 113 | 114 | # Parameters: 115 | # $1 = name of the option whose value should be returned 116 | # Returns: 117 | # value provided for the specified option 118 | getOption() { 119 | hashGet 'SCRIPT_OPTS' "$1" 120 | } 121 | 122 | # Parameters: 123 | # $1 = name of the option to check 124 | # Returns: 125 | # result code indicating whether the specified option was passed to the script 126 | optionIsEnabled() { 127 | hashContainsKey 'SCRIPT_OPTS' "$1" 128 | } 129 | 130 | # Parameters: 131 | # $@ = full list of options passed to the script 132 | parseScriptOptions() { 133 | for SCRIPT_ARG in "$@"; do 134 | case $SCRIPT_ARG in 135 | -* ) 136 | setOption "$SCRIPT_ARG" 137 | LAST_SWITCH=$SCRIPT_ARG ;; 138 | * ) 139 | if [ -n "$LAST_SWITCH" ]; then 140 | setOption "$LAST_SWITCH" "$SCRIPT_ARG" 141 | else 142 | setOption "$SCRIPT_ARG" 143 | fi 144 | LAST_SWITCH='' ;; 145 | esac 146 | done 147 | } 148 | 149 | # Parameters: 150 | # $1 = name of the option to set 151 | # $2 = value to set for the specified option 152 | setOption() { 153 | hashPut 'SCRIPT_OPTS' "$1" "$2" 154 | } 155 | 156 | 157 | ################################################################################ 158 | # Note Management 159 | ################################################################################ 160 | 161 | # Parameters: 162 | # $1 = message to append to the notes for the currently running script 163 | addNote() { 164 | # Not using appendToFile here to ensure this file has no dependencies. -- cwells 165 | #appendToFile "$0.notes" "$1" 166 | echo "$1" >> "$(getScriptPath)/$(basename $0).notes" 167 | } 168 | 169 | # Parameters: 170 | # $1 (optional) = message to display above the notes for this script 171 | showNotes() { 172 | if [ -n "$1" ]; then 173 | MESSAGE="$1" 174 | else 175 | MESSAGE='The following notes were saved in' 176 | fi 177 | echo "$MESSAGE $0.notes: 178 | 179 | $(cat "$(getScriptPath)/$(basename $0).notes")" 180 | } 181 | -------------------------------------------------------------------------------- /lib/shell.io: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | :<> "$1" 92 | } 93 | 94 | # Parameters: 95 | # $1 = file path 96 | # $2 = text to save as file 97 | createOrReplaceFile() { 98 | echo "$2" > "$1" 99 | } 100 | 101 | # Parameters: 102 | # $1 = file path 103 | # $2 = line to delete 104 | deleteLine() { 105 | TEMP_FILE=$(getTempFileName "$1") 106 | if awk -v line="$2" '$0 != line' "$1" > "$TEMP_FILE"; then 107 | cat "$TEMP_FILE" > "$1" 108 | fi 109 | if [ -f "$TEMP_FILE" ]; then 110 | rm "$TEMP_FILE" 111 | fi 112 | } 113 | 114 | # Parameters: 115 | # $1 = file path 116 | # $2 = line to insert after 117 | # $3 = text to insert into file 118 | insertAfterLine() { 119 | TEMP_FILE=$(getTempFileName "$1") 120 | if awk -v line="$2" -v insert="$(escapeNewlines "$3")" ' 121 | BEGIN { 122 | gsub(/\n$/, "", insert); 123 | } 124 | $0 == line { 125 | $0 = sprintf("%s\n%s", $0, insert); 126 | } 127 | 1 128 | ' "$1" > "$TEMP_FILE"; then 129 | cat "$TEMP_FILE" > "$1" 130 | fi 131 | if [ -f "$TEMP_FILE" ]; then 132 | rm "$TEMP_FILE" 133 | fi 134 | } 135 | 136 | # Parameters: 137 | # $1 = file path 138 | # $2 = line to insert before 139 | # $3 = text to insert into file 140 | insertBeforeLine() { 141 | TEMP_FILE=$(getTempFileName "$1") 142 | if awk -v line="$2" -v insert="$(escapeNewlines "$3")" ' 143 | BEGIN { 144 | gsub(/\n$/, "", insert); 145 | } 146 | $0 == line { 147 | $0 = sprintf("%s\n%s", insert, $0); 148 | } 149 | 1 150 | ' "$1" > "$TEMP_FILE"; then 151 | cat "$TEMP_FILE" > "$1" 152 | fi 153 | if [ -f "$TEMP_FILE" ]; then 154 | rm "$TEMP_FILE" 155 | fi 156 | } 157 | 158 | # Parameters: 159 | # $1 = file path 160 | # $2 = old line 161 | # $3 = new line 162 | replaceLine() { 163 | TEMP_FILE=$(getTempFileName "$1") 164 | if awk -v line="$2" -v replace="$(escapeNewlines "$3")" ' 165 | BEGIN { 166 | gsub(/\n$/, "", replace); 167 | } 168 | $0 == line { 169 | $0 = replace; 170 | } 171 | 1 172 | ' "$1" > "$TEMP_FILE"; then 173 | cat "$TEMP_FILE" > "$1" 174 | fi 175 | if [ -f "$TEMP_FILE" ]; then 176 | rm "$TEMP_FILE" 177 | fi 178 | } 179 | 180 | # Parameters: 181 | # $1 = file path 182 | # $2 = pattern to match 183 | # $3 = replacement text 184 | replacePattern() { 185 | TEMP_FILE=$(getTempFileName "$1") 186 | if awk -v search="$2" -v replace="$(escapeNewlines "$3")" ' 187 | BEGIN { 188 | gsub(/\n$/, "", replace); 189 | } 190 | 1 { 191 | gsub(search, replace); 192 | print $0; 193 | } 194 | ' "$1" > "$TEMP_FILE"; then 195 | cat "$TEMP_FILE" > "$1" 196 | fi 197 | if [ -f "$TEMP_FILE" ]; then 198 | rm "$TEMP_FILE" 199 | fi 200 | } 201 | 202 | # Parameters: 203 | # $1 = file path 204 | # $2 = old text 205 | # $3 = new text 206 | replaceText() { 207 | TEMP_FILE=$(getTempFileName "$1") 208 | if awk -v search="$(escapeAwkSearchText "$2")" -v replace="$(escapeNewlines "$3")" ' 209 | BEGIN { 210 | gsub(/\n$/, "", replace); 211 | } 212 | 1 { 213 | gsub(search, replace); 214 | print $0; 215 | } 216 | ' "$1" > "$TEMP_FILE"; then 217 | cat "$TEMP_FILE" > "$1" 218 | fi 219 | if [ -f "$TEMP_FILE" ]; then 220 | rm "$TEMP_FILE" 221 | fi 222 | } 223 | 224 | # Parameters: 225 | # $1 = file path 226 | # $2 = line to comment 227 | # $3 (optional) = string used to comment lines 228 | commentLine() { 229 | if echo "$2" | grep -Eq '^\s*<.+>$'; then # This line is markup. -- cwells 230 | COMMENT=$(echo "$2" | sed -e 's/^\( *\)<\(.*\)>$/\1/') 231 | replaceText "$1" "$2" "$COMMENT" 232 | else 233 | if [ -n "$3" ]; then 234 | COMMENT_MARKER="$3" 235 | else 236 | COMMENT_MARKER="#" 237 | fi 238 | replaceLine "$1" "$2" "$COMMENT_MARKER$2" 239 | fi 240 | } 241 | 242 | # Parameters: 243 | # $1 = file path 244 | # $2 = line to uncomment 245 | # $3 (optional) = string used to comment lines 246 | uncommentLine() { 247 | if echo "$2" | grep -Eq '^\s*<.+>$'; then # This line is markup. -- cwells 248 | MARKUP=$(echo "$2" | sed -e 's/^\( *\)$/\1<\2>/') 249 | replaceText "$1" "$2" "$MARKUP" 250 | else 251 | if [ -n "$3" ]; then 252 | COMMENT_MARKER="$3" 253 | else 254 | COMMENT_MARKER="#" 255 | fi 256 | SEARCH="$(echo "$2" | sed -e 's/[().*^$?&[]/\\\\&/g')" 257 | REPLACE="$(echo "$2" | sed -e 's/&/\\\\&/g')" 258 | replacePattern "$1" "^$COMMENT_MARKER+ *$SEARCH$" "$REPLACE" 259 | fi 260 | } 261 | -------------------------------------------------------------------------------- /lib/shell.pkg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | :< /dev/null 2>&1 21 | } 22 | 23 | # Parameters: 24 | # $1 = name of the package(s) to install 25 | # Returns: 26 | # result code indicating whether the specified package was installed on the host 27 | installPackages() { 28 | # shellcheck disable=SC2086 29 | pkg install -y $1 30 | } 31 | 32 | 33 | ################################################################################ 34 | # Package Management for Jails 35 | ################################################################################ 36 | 37 | # Parameters: 38 | # $1 = name of the jail 39 | # $2 = name of the package to check for 40 | # Returns: 41 | # result code indicating whether the specified package is installed in the jail 42 | jailHasPackage() { 43 | pkg -j "$1" info "$2" > /dev/null 2>&1 44 | } 45 | 46 | # Parameters: 47 | # $1 = name of the jail 48 | # $2 = name of the package(s) to install 49 | # Returns: 50 | # result code indicating whether the specified package was installed in the jail 51 | jailPackages() { 52 | # shellcheck disable=SC2086 53 | pkg -j "$1" install -y $2 54 | } 55 | -------------------------------------------------------------------------------- /portsfetch: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | :<&2 16 | exit 1 17 | fi 18 | 19 | if ! which git > /dev/null; then 20 | pkg install git 21 | fi 22 | 23 | # If a parameter was passed, use it as the destination directory. For example, 24 | # to update the ezjail ports tree: portsfetch /usr/jails/basejail/usr/ports 25 | if [ -n "$1" ]; then 26 | DESTDIR="$1" 27 | else 28 | DESTDIR=/usr/ports 29 | fi 30 | 31 | if [ ! -d "${DESTDIR}" ]; then 32 | mkdir "${DESTDIR}" 33 | fi 34 | 35 | # Clear the contents of the destination directory if it's not being managed by Git. -- cwells 36 | if [ ! -z "$(ls -qAL -- "${DESTDIR}")" ] && [ ! -d "${DESTDIR}/.git" ]; then 37 | echo 'Destination directory contains files but is not a Git repo.' 38 | echo 'You must delete the existing files to switch to a quarterly branch.' 39 | # shellcheck disable=SC2039 40 | read -r -p "Do you want to delete the contents of \"${DESTDIR}\"? [y/N]: " DELETE_FILES 41 | echo '' 42 | case $DELETE_FILES in 43 | [Yy]* ) find "${DESTDIR}" -mindepth 1 -delete ;; # Delete all files in the dir. -- cwells 44 | * ) 45 | if [ -d "${DESTDIR}/.svn" ]; then 46 | echo 'The destination directory is an SVN repo, so no update was performed.' 47 | echo 'Your ports tree is still out of date. You should switch to Git.' 48 | elif [ -f "${DESTDIR}/.portsnap.INDEX" ]; then 49 | portsnap fetch update # Use portsnap to update from HEAD. -- cwells 50 | echo '' 51 | echo 'The destination directory is managed by portsnap, so an update was performed.' 52 | echo 'This means you are still using the latest branch instead of quarterly.' 53 | fi 54 | echo 'Run portsfetch again and choose to delete your files to switch.' 55 | exit 0 ;; 56 | esac 57 | echo '' 58 | fi 59 | 60 | if [ ! -d "${DESTDIR}/.git" ]; then # Initial clone of the repo. -- cwells 61 | git clone "${REPO_URL}" "${DESTDIR}" 62 | else # Update the existing repo. -- cwells 63 | git -C "${DESTDIR}" pull --ff-only 64 | fi 65 | 66 | BRANCH=$(git -C "${DESTDIR}" branch --list -r | grep -E '^[[:space:]]*origin/20[0-9]{2}Q[1-4]$' | sort | tail -1 | sed 's/ *origin\///') 67 | if [ "${#BRANCH}" != "6" ]; then 68 | echo 'Failed to determine the correct branch to use.' 69 | exit 1 70 | fi 71 | 72 | if [ "$(git -C "${DESTDIR}" branch --show-current)" != "${BRANCH}" ]; then 73 | git -C "${DESTDIR}" checkout "${BRANCH}" 74 | fi 75 | 76 | exit 0 77 | -------------------------------------------------------------------------------- /update-host: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | :< /dev/null; then 19 | bectl create $(date +%Y-%m-%d.%H%M%S) 20 | fi 21 | 22 | freebsd-update fetch install 23 | 24 | printf "\nChecking for vulnerable packages on the host...\n" 25 | pkg audit -F 26 | printf "\nUpgrading packages on the host...\n" 27 | pkg upgrade 28 | 29 | for JAIL in $(jls -N host.hostname); do 30 | printf "\nChecking for vulnerable packages in the %s jail...\n" "$JAIL" 31 | pkg -j "$JAIL" audit -F 32 | printf "\nUpgrading packages in the %s jail...\n" "$JAIL" 33 | pkg -j "$JAIL" upgrade 34 | done 35 | 36 | exit 0 37 | --------------------------------------------------------------------------------