]> git.proxmox.com Git - mirror_lxc.git/blame - src/tests/lxc-test-usernsexec
tests: use busybox in lxc-test-usernic.in
[mirror_lxc.git] / src / tests / lxc-test-usernsexec
CommitLineData
9026f5c2
SM
1#!/bin/bash
2#
3# This is a bash test case to test lxc-usernsexec.
4# It basically supports usring lxc-usernsexec to execute itself
5# and then create files and check that their ownership is as expected.
6#
7# It requires that the current user has at least 1 value in subuid and /etc/subgid
8TEMP_D=""
4c93c06e 9VERBOSITY=0
9026f5c2
SM
10set -f
11
12fail() { echo "$@" 1>&2; exit 1; }
13error() { echo "$@" 1>&2; }
14skip() {
15 error "SKIP:" "$@"
16 exit 0
17}
4c93c06e
SM
18debug() {
19 local level=${1}; shift;
20 [ "${level}" -gt "${VERBOSITY}" ] && return
21 error "${@}"
22}
9026f5c2
SM
23
24collect_owners() {
25 # collect_owners([--dir=dir], file1, file2 ...)
26 # set _RET to a space delimited array of
27 # <file1>:owner:group <file2>:owner:group ...
28 local out="" ret="" dir=""
29 if [ "${1#--dir=}" != "$1" ]; then
30 dir="${1#--dir=}"
31 shift
32 fi
33 for arg in "$@"; do
34 # drop the :* so that input can be same as touch_files.
35 out=$(stat --format "%n:%u:%g" "${dir}${arg}") || {
36 error "failed to stat ${arg}"
37 return 1;
38 }
39 ret="$ret ${out##*/}"
40 done
41 _RET="${ret# }"
42}
43
44cleanup() {
45 if [ -d "$TEMP_D" ]; then
46 rm -Rf "$TEMP_D"
47 fi
48}
49
50touch_files() {
51 # touch_files tok [tok ...]
52 # tok is filename:chown_id:chown_gid
53 # if chown_id or chown_gid is empty, then chown will do the right thing
54 # and only change the provided value.
55 local args="" tok="" fname="" uidgid=""
56 args=( "$@" )
57 for tok in "$@"; do
58 fname=${tok%%:*}
59 uidgid=${tok#$fname}
60 uidgid=${uidgid#:}
61 : > "$fname" || { error "failed to create $fname"; return 1; }
62 [ -z "$uidgid" ] && continue
63 chown $uidgid "$fname" || { error "failed to chmod '$uidgid' $fname ($?)"; return 1; }
64 done
65}
66
67inside_cleanup() {
68 local f=""
69 rm -f "${FILES[@]}"
70 echo "$STATUS" >&5
71 echo "$STATUS" >&6
72}
73
74set_files() {
75 local x=""
76 FILES=( )
77 for x in "$@"; do
78 FILES[${#FILES[@]}]="${x%%:*}"
79 done
80}
81
82inside() {
83 # this what gets run inside the usernsexec environment.
84 # basically expects arguments of <filename>:uid:gid
85 # it will create the file, and then chmod it to the provided uid:gid
86 # it writes to file descriptor 5 a single line with space delimited
87 # exit_value uid gid [<filename>:<owner>:<group> ... ]
88 STATUS=127
89 trap inside_cleanup EXIT
90 local uid="" gid="" x=""
91
92 uid=$(id -u) || fail "failed execution of id -u"
93 gid=$(id -g) || fail "failed execution of id -g"
94
95 set_files "$@"
96
97 touch_files "$@" || fail "failed to create files"
98
99 collect_owners "${FILES[@]}" || fail "failed to collect owners"
100 result="$_RET"
101
102 # tell caller we are done.
103 echo "0" "$uid" "$gid" "$result" >&5
104 STATUS=0
105
106 # let the caller do things while the files are around.
107 read -t 30 x <&6
108
109 exit
110}
111
112runtest() {
113 # runtest(mydir, nsexec_args, [inside [...]])
114 # - use 'mydir' as a working dir.
115 # - execute lxc-usernsexec $nsexec_args -- <self> inside <inside args>
116 #
117 # write to stdout
118 # exit_value inside_exit_value inside_uid:inside_gid <results>
119 #
120 # where results are a list of space separated
121 # filename:uid:gid
122 # for each file passed in inside_args
123 [ $# -ge 3 ] || { error "runtest expects 2 args"; return 1; }
124 local mydir="$1" nsexec_args="$2"
125 shift 2
126 local ret inside_owners t=""
127 KIDPID=""
128
129 mkfifo "${mydir}/5" && exec 5<>"${mydir}/5" || return
130 mkfifo "${mydir}/6" && exec 6<>"${mydir}/6" || return
131 mkdir --mode=777 "${mydir}/work" || return
132 cd "${mydir}/work"
133
134 set_files "$@"
135
136 local results="" oresults="" iresults="" iuid="" igid="" n=0
137
138 error "$" $USERNSEXEC ${nsexec_args} -- "$MYPATH" inside "$*"
139 ${USERNSEXEC} ${nsexec_args} -- "$MYPATH" inside "$@" &
140 KIDPID=$!
141
142 [ -d "/proc/$KIDPID" ] || {
143 wait $KIDPID
144 fail "kid $KIDPID died quickly $?"
145 }
146
147 # if lxc-usernsexec fails to execute MYPATH inside, then
148 # the read below would timeout. To avoid a long timeout,
149 # we do a short timeout and check the pid is alive.
150 while ! read -t 1 ret iuid igid inside_owners <&5; do
151 n=$((n+1))
152 if [ ! -d "/proc/$KIDPID" ]; then
153 wait $KIDPID
154 fail "kid $KIDPID is gone $?"
155 fi
156 [ $n -ge 30 ] && fail "child never wrote to pipe"
157 done
158 iresults=( $inside_owners )
159
160 collect_owners "--dir=${mydir}/work/" "${FILES[@]}" || return
161 oresults=( $_RET )
162
163 echo 0 >&6
164 wait
165
166 ret=$?
167
168 results=( )
169 for((i=0;i<${#iresults[@]};i++)); do
170 results[$i]="${oresults[$i]}:${iresults[$i]#*:}"
171 done
172
173 echo 0 $ret "$iuid:$igid" "${results[@]}"
174}
175
176runcheck() {
177 local name="$1" expected="$2" nsexec_args="$3" found=""
178 shift 3
179 mkdir "${TEMP_D}/$name" || fail "failed mkdir <TEMP_D>/$name.d"
180 local err="${TEMP_D}/$name.err"
181 out=$("$MYPATH" runtest "${TEMP_D}/$name" "$nsexec_args" "$@" 2>"$err") || {
182 error "$name: FAIL - runtest failed $?"
183 [ -n "$out" ] && error " $out"
184 sed 's,^, ,' "$err" 1>&2
185 ERRORS="${ERRORS} $name"
186 return 1
187 }
188 set -- $out
189 local parentrc=$1 kidrc=$2 iuidgid="$3" found=""
190 shift 3
191 found="$*"
192 [ "$parentrc" = "0" -a "$kidrc" = "0" ] || {
193 error "$name: FAIL - parentrc=$parentrc kidrc=$kidrc found=$found"
194 ERRORS="${ERRORS} $name"
195 return 1
196 }
197 [ "$expected" = "$found" ] && {
198 error "$name: PASS"
199 PASS="${PASSES} $name"
200 return 0
201 }
202 echo "$name: FAIL expected '$expected' != found '$found'"
203 FAILS="${FAILS} $name"
204 return 1
205}
206
4c93c06e
SM
207setup_Usage() {
208 cat <<EOF
209${0} setup_and_run [-- run-args]
210
211setup the system by creating a user (default is '${asuser:-test-userns}')
212and then run test as that user. Must be root.
213
214If user exists, then do not create the user.
215
216 -v | --verbose - be more verbose
217 --create-subuid=UID:RANGE
218 --create-subgid=UID:RANGE if adding subuid/subgid use this START:RANGE
219 example (default) 3000000000:5
220EOF
221}
222
223setup_and_run() {
224 local short_opts="hv"
225 local long_opts="help,user:,create-subuid:,create-subgid:,verbose"
226 local getopt_out=""
227 getopt_out=$(getopt --name "${0##*/}" \
228 --options "${short_opts}" --long "${long_opts}" -- "$@") &&
229 eval set -- "${getopt_out}" ||
230 { bad_Usage; return; }
231
232 local cur="" next="" asuser="test-userns"
233 local create_subuid="3000000000:5" create_subgid="3000000000:5"
234 while [ $# -ne 0 ]; do
235 cur="$1"; next="$2";
236 case "$cur" in
237 -h|--help) setup_Usage ; exit 0;;
238 --user) asuser="$next"; shift;;
239 --create-subuid) create_subuid=$next; shift;;
240 --create-subgid) create_subgid=$next; shift;;
241 -v|--verbose) VERBOSITY=$((${VERBOSITY}+1));;
242 --) shift; break;;
243 esac
244 shift;
245 done
246
247 local pt_args=""
248 pt_args=( "$@" )
249
250 if [ "$(id -u)" != "0" ]; then
251 error "Sorry, setup_and_run has to be done as root, not uid=$(id -u)"
252 return 1
253 fi
254
255 local home="/home/$asuser"
256 if [ ! -d "$home" ]; then
257 debug 1 "creating user $asuser"
258 useradd "$asuser" --create-home "--home-dir=$home" || {
259 error "failed to create $asuser"
260 return 1
261 }
262 else
263 debug 1 "$asuser existed"
264 fi
265
266 local subuid="" subgid=""
267 subuid=$(awk -F: '$1 == n { print $2; exit(0); }' "n=$asuser" /etc/subuid) || {
268 error "failed to read /etc/subuid for $asuser"
269 return 1
270 }
271
272 if [ -n "$subuid" ]; then
273 debug 1 "$asuser already had subuid=$subuid"
274 else
275 debug 1 "adding $asuser:$create_subuid to /etc/subuid"
276 echo "$asuser:$create_subuid" >> /etc/subuid || {
277 error "failed to add $asuser to /etc/subuid"
278 }
279 fi
280
281 subgid=$(awk -F: '$1 == n { print $2; exit(0); }' "n=$asuser" /etc/subgid) || {
282 error "failed to read /etc/subgid for $asuser"
283 return 1
284 }
285 if [ -n "$subgid" ]; then
286 debug 1 "$asuser already had subgid=$subgid"
287 else
288 debug 1 "adding $asuser:$create_subgid to /etc/subgid"
289 echo "$asuser:$create_subgid" >> /etc/subgid || {
290 error "failed to add $asuser to /etc/subgid"
291 }
292 fi
293
294 debug 0 "as $asuser executing ${MYPATH} ${pt_args[*]}"
5f850cf9 295 sudo -Hu "$asuser" ASAN_OPTIONS=${ASAN_OPTIONS:-} UBSAN_OPTIONS=${UBSAN_OPTIONS:-} "${MYPATH}" "${pt_args[@]}"
4c93c06e
SM
296}
297
9026f5c2 298USERNSEXEC=${USERNSEXEC:-lxc-usernsexec}
4c93c06e
SM
299MYPATH=$(readlink -f "$0") || { echo "failed to get full path to self: $0"; exit 1; }
300export MYPATH
301
9026f5c2
SM
302if [ "$1" = "inside" ]; then
303 shift
304 inside "$@"
305 exit
306elif [ "$1" = "runtest" ]; then
307 shift
308 runtest "$@"
309 exit
4c93c06e
SM
310elif [ "$1" = "setup_and_run" ]; then
311 shift
312 setup_and_run "$@"
313 exit
9026f5c2
SM
314fi
315
316name=$(id --user --name) || fail "failed to get username"
4c93c06e
SM
317if [ "$name" = "root" ]; then
318 setup_and_run "$@"
319 exit
320fi
321
9026f5c2 322subuid=$(awk -F: '$1 == n { print $2; exit(0); }' "n=$name" /etc/subuid) &&
4c93c06e 323 [ -n "$subuid" ] || fail "did not find $name in /etc/subuid"
9026f5c2
SM
324
325subgid=$(awk -F: '$1 == n { print $2; exit(0); }' "n=$name" /etc/subgid) &&
4c93c06e 326 [ -n "$subgid" ] || fail "did not find $name in /etc/subgid"
9026f5c2
SM
327
328
329uid=$(id --user) || fail "failed to get uid"
330gid=$(id --group) || fail "failed to get gid"
331
332mapuid="u:0:$uid:1"
333mapgid="g:0:$gid:1"
334
335ver=$(dpkg-query --show lxc-utils | awk '{print $2}')
336error "uid=$uid gid=$gid name=$name subuid=$subuid subgid=$subgid ver=$ver"
337error "lxc-utils=$ver kver=$(uname -r)"
338error "USERNSEXEC=$USERNSEXEC"
339
340TEMP_D=$(mktemp -d)
341trap cleanup EXIT
9026f5c2
SM
342
343PASSES=""; FAILS=""; ERRORS=""
344runcheck nouidgid "f0:$subuid:$subgid:0:0" "" f0
345
346runcheck myuidgid "f0:$uid:$gid:0:0" \
347 "-m$mapuid -m$mapgid" f0
348
349runcheck subuidgid \
350 "f0:$subuid:$subgid:0:0" \
351 "-mu:0:$subuid:1 -mg:0:$subgid:1" f0:0:0
352
353runcheck bothsets "f0:$uid:$gid:0:0 f1:$subuid:$subgid:1:1 f2:$uid:$subgid:0:1" \
354 "-m$mapuid -m$mapgid -mu:1:$subuid:1 -mg:1:$subgid:1" \
355 f0 f1:1:1 f2::1
356
357runcheck mismatch "f0:$uid:$subgid:0:0 f1:$subuid:$gid:15:31" \
358 "-mu:0:$uid:1 -mg:0:$subgid:1 -mu:15:$subuid:1 -mg:31:$gid:1" \
359 f0 f1:15:31
360
361FAILS=${FAILS# }
362ERRORS=${ERRORS# }
363PASSES=${PASSES# }
364
365[ -z "${FAILS}" ] || error "FAILS: ${FAILS}"
366[ -z "${ERRORS}" ] || error "ERRORS: ${ERRORS}"
367[ -z "${FAILS}" -a -z "${ERRORS}" ] || exit 1
368exit 0