-
Notifications
You must be signed in to change notification settings - Fork 0
/
apppot-init.sh
executable file
·275 lines (235 loc) · 8.69 KB
/
apppot-init.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
#! /bin/sh
#
# Sets up the the AppPot environment, including mounting hostfs
# directories, setting up MPI networking, etc. and then runs the job
# specified by users on the kernel command-line. If no job is
# specified, starts SCREEN on the first console.
#
# Author: Riccardo Murri <[email protected]>
#
# Copyright (C) 2009-2012 GC3, University of Zurich. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
VERSION='(SVN $Revision$)'
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/usr/local/bin:/bin:/usr/bin:/sbin:/usr/sbin
NAME=apppot
echo "==== Starting AppPot $VERSION ..."
# Read configuration variable file if it is present
if [ -r /etc/default/$NAME ]; then
. /etc/default/$NAME
fi
## defaults
APPPOT_USER=user
APPPOT_GROUP=users
APPPOT_HOME=/home/user
## auxiliary functions
# value EXPR
#
# Given an EXPR of the form `name=value`, output the `value` part.
#
value () {
echo "$1" | cut -d= -f2-
}
# warn [MSG]
#
# Output MSG to STDERR, with a suitable "warning" prefix.
# If MSG is omitted, read it from STDIN.
#
warn () {
(echo -n "== WARNING: ";
if [ $# -gt 0 ]; then echo "$@"; else cat; fi) 1>&2
}
# setup_apppot_user UID GID [USER GROUP]
#
# Create a user with the given UID and GID. If given, arguments USER
# and GROUP specify the UNIX user name and primary group name; if
# omitted, they default to `user` (user name) and `users` (group
# name).
#
setup_apppot_user () {
# parse arguments
uid=${1:?Missing required argument UID to setup_apppot_user}
gid=${2:?Missing required argument GID to setup_apppot_user}
user=${3:-user}
group=${4:-users}
echo "== Setting up AppPot user $user($uid) in group $group($gid) ..."
# ensure GROUP exists with the given GID
if getent group "$group" >/dev/null; then
# group already exists, ensure given GID
groupmod --gid "$gid" "$group"
else
# create user with given GID
groupadd --gid "$gid" "$group"
fi
# ensure USER exists with the given UID/GID
if getent passwd "$user" >/dev/null; then
# user already exists, ensure given UID/GID
usermod --uid "$uid" --gid "$gid" --shell /bin/sh "$user"
else
# create user with given UID/GID
useradd --create-home --uid "$uid" --gid "$gid" --shell /bin/sh "$user"
fi
# ensure USER is authorized to sudo
fgrep -q "$user" /etc/sudoers \
|| cat >> /etc/sudoers <<__EOF__
# user '$user' enabled by '$0'
$user ALL=(ALL) NOPASSWD: ALL
__EOF__
# define $APPPOT_HOME to the user home directory
export APPPOT_HOME="`getent passwd "$user" | cut -d: -f6`"
find "$APPPOT_HOME" -xdev -print0 | xargs --null chown "$uid":"$gid"
# give $user access to the system console
chown "$user" /dev/console
# ensure that we can still use `screen` if UID/GID has changed from the default
rm -rf /var/run/screen/S-$APPPOT_USER /var/run/screen/S-$user
return 0
}
# mount_hostfs HOSTDIR MOUNTDIR
#
# Mount host directory HOSTDIR onto local mount point MOUNTDIR
#
mount_hostfs () {
hostdir=${1:?Missing required parameter HOSTDIR to 'mount_hostfs'}
mountdir=${2:?Missing required parameter MOUNTDIR to 'mount_hostfs'}
if fgrep -q hostfs /proc/filesystems; then
echo "== Mounting host directory '$hostdir' on local directory '$mountdir' ..."
mkdir -p "$mountdir"
mount -t hostfs -o "$hostdir" host "$mountdir"
else
warn "No 'hostfs' support in this kernel, cannot mount '$hostdir' on '$mountdir' ..."
return 1
fi
}
# merge_changes FILE
#
# Merge the changes from FILE (path in the host filesystem).
# The named FILE must have been created by 'apppot-snap changes FILE'.
#
merge_changes () {
changes_file="${1:?Missing required parameter FILE to 'merge_changes'}"
echo "== Merging changes from host file '$changes_file' ..."
if mount -t hostfs -o / hostroot /mnt; then
if [ ! -r "/mnt/$changes_file" ]; then
warn "Cannot read changes file '$changes_file': not merging changes."
rc=1
else
apppot-snap merge "/mnt/$changes_file"
rc=$?
fi
umount /mnt
return $rc
fi
}
# setup_term TERM [DEFAULT]
#
# Set the `TERM` environment variable to the given value,
# or fall back to the DEFAULT if the specified
# terminal is not supported on this system.
#
# If DEFAULT is omitted, use 'vt100'.
#
setup_term () {
term="${1:?Missing required parameter TERM to 'setup_term'}"
default_term="${2:-vt100}"
term_name=`tput -T"$term" longname`
if [ -z "$term_name" ]; then
# terminal not supported, fall back to default
warn "Terminal '$term' not supported, falling back to '$default_term'."
export TERM="$default_term"
else
echo "== Using terminal $name"
export TERM="$term"
fi
}
## main
# Set the command line arguments to this shell from
# the ones that were passed to the kernel at boot.
# Return 1 if the `/proc/cmdline` file is not accessible.
#
# In addition, the following parameters are parsed
# and set as environment variables:
# - `apppot.user`: sets variable APPPOT_USER
# - `apppot.uid`: sets variable APPPOT_UID
# - `apppot.group`: sets variable APPPOT_GROUP
# - `apppot.gid`: sets variable APPPOT_GID
# - `apppot.jobdir`: sets variable APPPOT_JOBDIR
# - `apppot.tmpdir`: sets variable APPPOT_TMPDIR
#
if [ -r /proc/cmdline ]; then
set -- `cat /proc/cmdline`
while [ $# -gt 0 ]; do
case "$1" in
apppot.changes=*) export APPPOT_CHANGESFILE="$(value "$1")" ;;
apppot.user=*) export APPPOT_USER="$(value "$1")" ;;
apppot.uid=*) export APPPOT_UID="$(value "$1")" ;;
apppot.group=*) export APPPOT_GROUP="$(value "$1")" ;;
apppot.gid=*) export APPPOT_GID="$(value "$1")" ;;
apppot.jobdir=*) export APPPOT_JOBDIR="$(value "$1")" ;;
apppot.tmpdir=*) export APPPOT_TMPDIR="$(value "$1")" ;;
TERM=*) setup_term "$(value "$1")" ;;
# `--` means end of kernel params and start of job arguments
--) shift; break ;;
esac
shift
done
fi
# mount job and scratch directories, if present
[ -n "$APPPOT_JOBDIR" ] && mount_hostfs $APPPOT_JOBDIR $APPPOT_HOME/job
[ -n "$APPPOT_TMPDIR" ] && mount_hostfs $APPPOT_TMPDIR /tmp
# extract a snapshot, if there is any
if [ -n "$APPPOT_CHANGESFILE" ]; then
merge_changes "$APPPOT_CHANGESFILE"
fi
# ensure that the UID and GID of the user account are the same of the
# mounted "job" directory; we need to do this *after* the changes
# merge because it could have overwritten /etc/passwd
setup_apppot_user \
${APPPOT_UID:-1000} ${APPPOT_GID:-1000} \
${APPPOT_USER:-user} ${APPPOT_GROUP:-users}
# now set arguments according to the kernel command-line (and remove
# quotes that apppot-start.sh put there)
eval set -- "$@"
# run a job or start an interactive shell
if [ $# -eq 0 ]; then
if [ -x "$APPPOT_HOME/job/apppot-run" ]; then
# run the job script as the specified user
echo "== Running job script '$APPPOT_HOME/job/apppot-run' ..."
su -l "$APPPOT_USER" -c "$APPPOT_HOME/job/apppot-run"
elif [ -x "$APPPOT_HOME/apppot-autorun" ]; then
# run the autostart script as the specified user
echo "== Running autostart script '$APPPOT_HOME/apppot-autorun' ..."
su -l "$APPPOT_USER" -c "$APPPOT_HOME/apppot-autorun"
else
echo "== Starting shell on /dev/console ..."
su -l "$APPPOT_USER" -c '/usr/bin/screen /bin/bash'
fi
else
# execute command-line
echo "== Running command-line \"$@\" ..."
if mountpoint -q "$APPPOT_HOME/job"; then
# if /home/user/job is mounted, execute job in it
eval su -l "$APPPOT_USER" -c "'cd $APPPOT_HOME/job; $@'"
else
# otherwise, execute in $HOME directory
eval su -l "$APPPOT_USER" -c "'cd $APPPOT_HOME; $@'"
fi
fi
echo "==== AppPot done, commencing shutdown ..."
# sleep a few seconds to give /sbin/init time to end the boot sequence
# before shutting down the system
(sleep 1; halt) &
return 0