1#!/bin/sh
2#
3# Copyright (c) 2018-2021, Christer Edwards <christer.edwards@gmail.com>
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
31PATH=${PATH}:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
32
33. /usr/local/share/bastille/common.sh
34
35## root check first.
36bastille_root_check() {
37    if [ "$(id -u)" -ne 0 ]; then
38        ## permission denied
39        error_notify "Bastille: Permission Denied"
40        error_exit "root / sudo / doas required"
41    fi
42}
43
44bastille_root_check
45
46## check for config existance
47bastille_conf_check() {
48    if [ ! -r "/usr/local/etc/bastille/bastille.conf" ]; then
49        error_exit "Missing Configuration"
50    fi
51}
52
53bastille_conf_check
54
55## we only load the config if conf_check passes
56. /usr/local/etc/bastille/bastille.conf
57
58## bastille_prefix should be 0750
59## this restricts file system access to privileged users
60bastille_perms_check() {
61    if [ -d "${bastille_prefix}" ]; then
62        BASTILLE_PREFIX_PERMS=$(stat -f "%Op" "${bastille_prefix}")
63        if [ "${BASTILLE_PREFIX_PERMS}" != 40750 ]; then
64            error_notify "Insecure permissions on ${bastille_prefix}"
65            error_exit "Try: chmod 0750 ${bastille_prefix}"
66        fi
67    fi
68}
69
70bastille_perms_check
71
72## version
73BASTILLE_VERSION="0.9.20211225"
74
75usage() {
76    cat << EOF
77Bastille is an open-source system for automating deployment and management of
78containerized applications on FreeBSD.
79
80Usage:
81  bastille command TARGET [args]
82
83Available Commands:
84  bootstrap   Bootstrap a FreeBSD release for container base.
85  cmd         Execute arbitrary command on targeted container(s).
86  clone       Clone an existing container.
87  config      Get or set a config value for the targeted container(s).
88  console     Console into a running container.
89  convert     Convert a Thin container into a Thick container.
90  cp          cp(1) files from host to targeted container(s).
91  create      Create a new thin container or a thick container if -T|--thick option specified.
92  destroy     Destroy a stopped container or a FreeBSD release.
93  edit        Edit container configuration files (advanced).
94  export      Exports a specified container.
95  help        Help about any command.
96  htop        Interactive process viewer (requires htop).
97  import      Import a specified container.
98  limits      Apply resources limits to targeted container(s). See rctl(8).
99  list        List containers (running and stopped).
100  mount       Mount a volume inside the targeted container(s).
101  pkg         Manipulate binary packages within targeted container(s). See pkg(8).
102  rdr         Redirect host port to container port.
103  rename      Rename a container.
104  restart     Restart a running container.
105  service     Manage services within targeted container(s).
106  start       Start a stopped container.
107  stop        Stop a running container.
108  sysrc       Safely edit rc files within targeted container(s).
109  template    Apply file templates to targeted container(s).
110  top         Display and update information about the top(1) cpu processes.
111  umount      Unmount a volume from within the targeted container(s).
112  update      Update container base -pX release.
113  upgrade     Upgrade container release to X.Y-RELEASE.
114  verify      Compare release against a "known good" index.
115  zfs         Manage (get|set) ZFS attributes on targeted container(s).
116
117Use "bastille -v|--version" for version information.
118Use "bastille command -h|--help" for more information about a command.
119
120EOF
121    exit 1
122}
123
124[ $# -lt 1 ] && usage
125
126CMD=$1
127shift
128
129# Handle special-case commands first.
130case "${CMD}" in
131version|-v|--version)
132    info "${BASTILLE_VERSION}"
133    exit 0
134    ;;
135help|-h|--help)
136    usage
137    ;;
138bootstrap|create|destroy|export|import|list|rdr|restart|start|update|upgrade|verify)
139    # Nothing "extra" to do for these commands. -- cwells
140    ;;
141clone|config|cmd|console|convert|cp|edit|htop|limits|mount|pkg|rename|service|stop|sysrc|template|top|umount|zfs)
142    # Parse the target and ensure it exists. -- cwells
143    if [ $# -eq 0 ]; then # No target was given, so show the command's help. -- cwells
144        PARAMS='help'
145    elif [ "${1}" != 'help' ] && [ "${1}" != '-h' ] && [ "${1}" != '--help' ]; then
146        TARGET="${1}"
147        shift
148
149        if [ "${TARGET}" = 'ALL' ]; then
150            _JAILS=$(/usr/sbin/jls name)
151            JAILS=""
152            for _jail in ${_JAILS}; do
153                _JAILPATH=$(/usr/sbin/jls -j "${_jail}" path)
154                if [ -z ${_JAILPATH##${bastille_jailsdir}*} ]; then
155                    JAILS="${JAILS} ${_jail}"
156                fi
157            done
158        elif [ "${CMD}" = "pkg" ] && [ "${TARGET}" = '-H' ] || [ "${TARGET}" = '--host' ]; then
159            TARGET="${1}"
160            USE_HOST_PKG=1
161            JAILS="${TARGET}"
162            shift
163
164            # Require the target to be running
165            if [ ! "$(/usr/sbin/jls name | awk "/^${TARGET}$/")" ]; then
166                error_exit "[${TARGET}]: Not started. See 'bastille start ${TARGET}'."
167            fi
168        elif [ "${CMD}" = 'template' ] && [ "${TARGET}" = '--convert' ]; then
169            # This command does not act on a jail, so we are temporarily bypassing the presence/started
170            # checks. The command will simply convert a template from hooks to a Bastillefile. -- cwells
171        else
172            JAILS="${TARGET}"
173
174            # Ensure the target exists. -- cwells
175            if [ ! -d "${bastille_jailsdir}/${TARGET}" ]; then
176                error_exit "[${TARGET}]: Not found."
177            fi
178
179            case "${CMD}" in
180            cmd|console|htop|pkg|service|stop|sysrc|template|top)
181                # Require the target to be running. -- cwells
182                if [ ! "$(/usr/sbin/jls name | awk "/^${TARGET}$/")" ]; then
183                    error_exit "[${TARGET}]: Not started. See 'bastille start ${TARGET}'."
184                fi
185                ;;
186            convert|rename)
187                # Require the target to be stopped. -- cwells
188                if [ "$(/usr/sbin/jls name | awk "/^${TARGET}$/")" ]; then
189                    error_exit "${TARGET} is running. See 'bastille stop ${TARGET}'."
190                fi
191                ;;
192            esac
193        fi
194        export USE_HOST_PKG
195        export TARGET
196        export JAILS
197    fi
198    ;;
199*) # Filter out all non-commands
200    usage
201    ;;
202esac
203
204SCRIPTPATH="${bastille_sharedir}/${CMD}.sh"
205if [ -f "${SCRIPTPATH}" ]; then
206    : "${UMASK:=022}"
207    umask "${UMASK}"
208
209    : "${SH:=sh}"
210
211    if [ -n "${PARAMS}" ]; then
212        exec "${SH}" "${SCRIPTPATH}" "${PARAMS}"
213    else
214        exec "${SH}" "${SCRIPTPATH}" "$@"
215    fi
216else
217    error_exit "${SCRIPTPATH} not found."
218fi
219