]> CyberLeo.Net >> Repos - CDN/j.git/blob - j
j: support init for debian chroots using debootstrap
[CDN/j.git] / j
1 #!/bin/sh
2 # Copyright 2011 CyberLeo, All Rights Reserved
3 # http://wiki.cyberleo.net/wiki/CyberLeo/COPYRIGHT
4
5 # Need root beyond here
6 [ "$(id -u)" -eq 0 ] || exec sudo env "J_ARCH=${J_ARCH}" "J_BASE=${J_BASE}" "J_NAME=${J_NAME}" "J_USER=${J_USER:-${USER}}" "${0}" "${@}"
7
8 meh() { printf " \033[1;32m*\033[0m %s%s\n" "${jname:+${jname}: }" "${*}"; }
9 omg() { printf " \033[1;33m*\033[0m %s%s\n" "${jname:+${jname}: }" "${*}"; }
10 wtf() { printf " \033[1;31m*\033[0m %s%s\n" "${jname:+${jname}: }" "${*}"; exit 1; }
11 pebkac() {
12   [ "${*}" ] && printf "%s\n\n" "${*}"
13   cat <<EOF
14 Usage:
15   <command> <name> [arguments]
16   list
17   ls      list available chroots
18
19   status  show jail status
20
21   start   prepare an existing chroot for use
22
23   stop    undo what 'start' did
24
25   enter
26   shell   spawn a shell or command within the chroot
27
28   eval    evaluate a shell command line within the chroot
29
30 EOF
31   exit 1
32 }
33
34 cmd="$(basename "${0}")"
35 jarch="${J_ARCH:-$(uname -m)}"
36 jbase="${J_BASE:-$(realpath "$(dirname "${0}")/../")}"
37 jname="${J_NAME:-$(basename "${1}")}" #"
38 juser="${J_USER}"
39
40 # Remove chroot name from argument stack, if passed in
41 [ "${J_NAME}" ] || shift
42
43 # Propagate certain environment variables; sterilize the rest of the environment
44 jenv="
45   LANG=${LANG}
46   TERM=${TERM}
47   USER=${USER}
48 "
49
50 # Debian-specific init: prepare Debian chroot with debootstrap
51 j_init_debian() {
52   jdir="${1}"
53   suite="$(echo "${2:-squeeze}" | tr 'A-Z' 'a-z')"
54
55   # Validation
56   [ "$(which debootstrap 2>&-)" ] || pebkac "j_init_debian: debootstrap not found"
57   [ "${jdir}" ] || pebkac "j_init_debian: jdir must be specified"
58   [ ! -d "${jdir}" ] || pebkac "j_init_debian: jdir must not exist ('${jdir}')"
59   [ -d "$(dirname "${jdir}")" ] || pebkac "j_init_debian: parent of jdir must exist ('${jdir}')"
60
61   # Figure out arch
62   case "${jarch}" in
63   x86) arch=x86 ;;
64   amd64|x86_64|x64) arch=amd64 ;;
65   *) pebkac "Unsupported arch '${jarch}'" ;;
66   esac
67
68   cmd="debootstrap --arch=${arch} --include=curl,file,less,locales,sudo,build-essential,libreadline-dev,zlib1g-dev '${suite}' '${jdir}'"
69   echo "Executing ${cmd}"
70   eval "${cmd}"
71   
72   # Make sure locales are generated on first start
73   mkdir -p "${jdir}/etc/rcJ.d"
74   cat > "${jdir}/etc/rcJ.d/S00localegen" <<"EOF"
75 #!/bin/sh
76 /bin/sed -i '/en_US/s/^# //' /etc/locale.gen
77 /usr/sbin/locale-gen
78 /bin/rm -f "${0}"
79 EOF
80   chmod 755 "${jdir}/etc/rcJ.d/S00localegen"
81 }
82
83 # Gentoo-specific init: prepare Gentoo chroot with stage3+portage tarballs
84 j_init_gentoo() {
85   arch="$(uname -m)"
86   base="http://distfiles.gentoo.org/releases/${arch}/autobuilds"
87   pointer="${base}/latest-stage3.txt"
88   # Fetch stage3
89   # Fetch portage
90   # Validate signatures
91   # Unpack stage3
92   # Unpack portage
93 }
94
95 # Create a new chroot, somehow
96 j_init() {
97   # Make sure this does NOT exist
98   jname="${1:-jname}"
99   dist="$(echo "${2:-debian}" | tr 'A-Z' 'a-z')"
100   jdir="${jbase}/${jname}"
101   [ -d "${jdir}" ] && pebkac "j_init: ${jname} already exists"
102   shift 2
103   case "${dist}" in
104   debian) j_init_debian "${jdir}" "${@}" ;;
105   gentoo) j_init_gentoo "${jdir}" "${@}" ;;
106   *) pebkac "Unsupported distro '${dist}'" ;;
107   esac
108 }
109
110 # Figure out and set chroot parameters; needed for all functions that follow
111 j_params() {
112   ( jname="${1:-jname}"
113
114     # Make sure jname is not empty
115     if [ -z "${jname}" ]
116     then
117       printf "jerror='%s'\n" "jname empty"
118       return 1
119     fi
120
121     # Given a chroot name, find and set up the chroot dir
122     jdir="${jbase}/${jname}"
123     if [ ! -d "${jdir}" ]
124     then
125       printf "jerror='%s'\n" "not a directory"
126       return 1
127     fi
128
129     # Where is the shell?
130     jshell=""
131     for shell in /bin/bash /usr/bin/bash /usr/local/bin/bash /bin/sh
132     do
133       if [ -f "${jdir}/${shell}" ]
134       then
135         jshell=${shell}
136         break
137       fi
138     done
139     if [ -z "${jshell}" ]
140     then
141       printf "jerror='%s'\n" "unable to locate usable shell; is this a jail?"
142       return 1
143     fi
144
145     printf "jerror='' jname='%s' jdir='%s' jshell='%s'\n" "${jname}" "${jdir}" "${jshell}"
146   )
147 }
148
149 # Is this a chroot?
150 j_is() {
151   eval $(j_params "${1}")
152   [ "${jerror}" ] && return 1 || return 0
153 }
154
155 # List available chroots
156 j_ls() {
157   ( cd "${jbase}"; ls -1 ) | while read jname
158   do
159     j_is "${jname}" && echo "${jname}"
160   done
161 }
162
163 # Chroot is 'up' if /dev/pts and /proc are mounted
164 j_up() {
165   jname="${1:-${jname}}"
166   eval "$(j_params "${jname}")"
167   [ "${jerror}" ] && wtf "${jerror}"
168   grep -q "^devpts ${jdir}/dev/pts devpts" /proc/mounts || return 1
169   grep -q "^proc ${jdir}/proc proc" /proc/mounts || return 1
170   return 0
171 }
172
173 # Poll chroot status (j_up)
174 j_status() {
175   [ -z "${1}" ] && set - $(l_ls)
176   while [ "${1}" ]
177   do
178     j_up "${1}" && meh "$(printf '\033[1;32mup\033[0m')" || meh "$(printf '\033[1;31mdown\033[0m')"
179     shift
180   done
181 }
182
183 # Mount /dev/pts and /proc in the chroot
184 j_start() {
185   jname="${1:-${jname}}"
186   j_up "${jname}" && return 0
187   eval "$(j_params "${jname}")"
188   meh "starting ${jname} ..."
189   mount -t devpts devpts "${jdir}/dev/pts"
190   mount -t proc proc "${jdir}/proc"
191
192   # Start all services in /etc/rcJ.d
193   j_root_eval "${jname}" '[ -d /etc/rcJ.d ] && ( ls -1 /etc/rcJ.d/* 2>&- | grep /S | sort | sed -e "s/$/ start/" | sh )'
194 }
195
196 # Execute command in chroot as root
197 j_root_eval() {
198   jname="${1:-${jname}}"
199   j_up "${jname}" || wtf "chroot not running"
200   eval "$(j_params "${jname}")"
201   shift
202   env -i ${jenv} /usr/bin/chroot "${jdir}" /bin/sh -c "${*}"
203 }
204
205 # Execute command in chroot
206 j_eval() {
207   jname="${1:-${jname}}"
208   j_up "${jname}" || wtf "chroot not running"
209   eval "$(j_params "${jname}")"
210   shift
211   env -i ${jenv} /usr/bin/chroot "${jdir}" /bin/su "${juser:-${USER}}" -c "${*}"
212 }
213
214 j_shell() {
215   jname="${1:-${jname}}"
216   eval "$(j_params "${jname}")"
217   j_eval "${jname}" "cd; exec ${jshell} -l"
218 }
219
220 # Unmount /dev/pts and /proc in the chroot
221 j_stop() {
222   jname="${1:-${jname}}"
223   eval "$(j_params "${jname}")"
224   j_up "${jname}" || return 0
225   meh "stopping ${jname} ..."
226
227   # Stop all services in /etc/rcJ.d
228   j_root_eval "${jname}" '[ -d /etc/rcJ.d ] && ( ls -1 /etc/rcJ.d/* 2>&- | grep /S | sort -r | sed -e "s/$/ stop/" | sh )'
229
230   umount "${jdir}/proc"
231   umount "${jdir}/dev/pts"
232 }
233
234 case "${cmd}" in
235 init|create) j_init "${jname}" "${@}" ;;
236 ls|list) j_ls ;;
237 status) j_status "${jname}" "${@}" ;;
238 start) j_start "${jname}" ;;
239 shell|enter) j_shell "${jname}" ;;
240 eval) j_eval "${jname}" "${*}" ;;
241 stop) j_stop "${jname}" ;;
242 *) pebkac ;;
243 esac