forked from 89luca89/distrobox
-
Notifications
You must be signed in to change notification settings - Fork 0
/
distrobox-enter
executable file
·733 lines (677 loc) · 24 KB
/
distrobox-enter
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
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
#!/bin/sh
# SPDX-License-Identifier: GPL-3.0-only
#
# This file is part of the distrobox project:
# https://github.com/89luca89/distrobox
#
# Copyright (C) 2021 distrobox contributors
#
# distrobox is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3
# as published by the Free Software Foundation.
#
# distrobox 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 distrobox; if not, see <http://www.gnu.org/licenses/>.
# POSIX
# Expected env variables:
# HOME
# USER
# Optional env variables:
# DBX_CONTAINER_ALWAYS_PULL
# DBX_CONTAINER_CUSTOM_HOME
# DBX_CONTAINER_GENERATE_ENTRY
# DBX_CONTAINER_HOME_PREFIX
# DBX_CONTAINER_HOSTNAME
# DBX_CONTAINER_IMAGE
# DBX_CONTAINER_MANAGER
# DBX_CONTAINER_NAME
# DBX_CONTAINER_CLEAN_PATH
# DBX_NON_INTERACTIVE
# DBX_VERBOSE
# DBX_SKIP_WORKDIR
# DBX_SUDO_PROGRAM
# Ensure we have our env variables correctly set
[ -z "${USER}" ] && USER="$(id -run)"
[ -z "${HOME}" ] && HOME="$(getent passwd "${USER}" | cut -d':' -f6)"
[ -z "${SHELL}" ] && SHELL="$(getent passwd "${USER}" | cut -d':' -f7)"
app_cache_dir=${XDG_CACHE_HOME:-"${HOME}/.cache"}/distrobox
trap cleanup TERM INT HUP EXIT
# cleanup will remove fifo and temp files, and print to stdout
# container's logs in case of error and verbose.
# Arguments:
# None
# Expected global variables:
# container_manager: string container manager to use
# container_name: string container name
# app_cache_dir: string cache dire to write file into
# logs_pid: string pid of the podman/docker logs process
# verbose: bool verbose
# Expected env variables:
# None
# Outputs:
# None
cleanup()
{
rm -f "${app_cache_dir}/.${container_name}.fifo"
if [ -n "${logs_pid:-}" ]; then
kill "${logs_pid:-}" 2> /dev/null || :
fi
if [ "${verbose}" -eq 1 ]; then
${container_manager} logs "${container_name}"
fi
}
# Despite of running this script via SUDO/DOAS being not supported (the
# script itself will call the appropriate tool when necessary), we still want
# to allow people to run it as root, logged in in a shell, and create rootful
# containers.
#
# SUDO_USER is a variable set by SUDO and can be used to check whether the script was called by it. Same thing for DOAS_USER, set by DOAS.
if {
[ -n "${SUDO_USER}" ] || [ -n "${DOAS_USER}" ]
} && [ "$(id -ru)" -eq 0 ]; then
printf >&2 "Running %s via SUDO/DOAS is not supported. Instead, please try running:\n" "$(basename "${0}")"
printf >&2 " %s --root %s\n" "$(basename "${0}")" "$*"
exit 1
fi
# Defaults
# by default we use getent to get the login shell of the user and use that
container_custom_command=0
container_command_user="$(echo "${USER}" | sed 's|\\|\\\\|g')"
container_image_default="registry.fedoraproject.org/fedora-toolbox:latest"
container_manager="autodetect"
container_manager_additional_flags=""
container_name=""
container_name_default="my-distrobox"
non_interactive=0
# Use cd + dirname + pwd so that we do not have relative paths in mount points
# We're not using "realpath" here so that symlinks are not resolved this way
# "realpath" would break situations like Nix or similar symlink based package
# management.
distrobox_enter_path="$(cd "$(dirname "$0")" && pwd)/distrobox-enter"
dryrun=0
headless=0
# If the user runs this script as root in a login shell, set rootful=1.
# There's no need for them to pass the --root flag option in such cases.
[ "$(id -ru)" -eq 0 ] && rootful=1 || rootful=0
skip_workdir=0
verbose=0
clean_path=0
version="1.8.0.1"
# Source configuration files, this is done in an hierarchy so local files have
# priority over system defaults
# leave priority to environment variables.
#
# On NixOS, for the distrobox derivation to pick up a static config file shipped
# by the package maintainer the path must be relative to the script itself.
self_dir="$(dirname "$(realpath "$0")")"
nix_config_file="${self_dir}/../share/distrobox/distrobox.conf"
config_files="
${nix_config_file}
/usr/share/distrobox/distrobox.conf
/usr/share/defaults/distrobox/distrobox.conf
/usr/etc/distrobox/distrobox.conf
/usr/local/share/distrobox/distrobox.conf
/etc/distrobox/distrobox.conf
${XDG_CONFIG_HOME:-"${HOME}/.config"}/distrobox/distrobox.conf
${HOME}/.distroboxrc
"
for config_file in ${config_files}; do
# Shellcheck will give error for sourcing a variable file as it cannot follow
# it. We don't care so let's disable this linting for now.
# shellcheck disable=SC1090
[ -e "${config_file}" ] && . "$(realpath "${config_file}")"
done
# If we're running this script as root -- as in, logged in in the shell as root
# user, and not via SUDO/DOAS --, we don't need to set distrobox_sudo_program
# as it's meaningless for this use case.
if [ "$(id -ru)" -ne 0 ]; then
# If the DBX_SUDO_PROGRAM/distrobox_sudo_program variable was set by the
# user, use its value instead of "sudo". But only if not running the script
# as root (UID 0).
distrobox_sudo_program=${DBX_SUDO_PROGRAM:-${distrobox_sudo_program:-"sudo"}}
fi
[ -n "${DBX_CONTAINER_MANAGER}" ] && container_manager="${DBX_CONTAINER_MANAGER}"
[ -n "${DBX_CONTAINER_NAME}" ] && container_name="${DBX_CONTAINER_NAME}"
[ -n "${DBX_CONTAINER_CLEAN_PATH}" ] && clean_path=1
[ -n "${DBX_SKIP_WORKDIR}" ] && skip_workdir="${DBX_SKIP_WORKDIR}"
[ -n "${DBX_NON_INTERACTIVE}" ] && non_interactive="${DBX_NON_INTERACTIVE}"
[ -n "${DBX_VERBOSE}" ] && verbose="${DBX_VERBOSE}"
# Fixup variable=[true|false], in case we find it in the config file(s)
[ "${non_interactive}" = "true" ] && non_interactive=1
[ "${non_interactive}" = "false" ] && non_interactive=0
[ "${verbose}" = "true" ] && verbose=1
[ "${verbose}" = "false" ] && verbose=0
# show_help will print usage to stdout.
# Arguments:
# None
# Expected global variables:
# version: distrobox version
# Expected env variables:
# None
# Outputs:
# print usage with examples.
show_help()
{
cat << EOF
distrobox version: ${version}
Usage:
distrobox-enter --name fedora-39 -- bash -l
distrobox-enter my-alpine-container -- sh -l
distrobox-enter --additional-flags "--preserve-fds" --name test -- bash -l
distrobox-enter --additional-flags "--env MY_VAR=value" --name test -- bash -l
MY_VAR=value distrobox-enter --additional-flags "--preserve-fds" --name test -- bash -l
Options:
--name/-n: name for the distrobox default: my-distrobox
--/-e: end arguments execute the rest as command to execute at login default: default ${USER}'s shell
--clean-path: reset PATH inside container to FHS standard
--no-tty/-T: do not instantiate a tty
--no-workdir/-nw: always start the container from container's home directory
--additional-flags/-a: additional flags to pass to the container manager command
--help/-h: show this message
--root/-r: launch podman/docker/lilipod with root privileges. Note that if you need root this is the preferred
way over "sudo distrobox" (note: if using a program other than 'sudo' for root privileges is necessary,
specify it through the DBX_SUDO_PROGRAM env variable, or 'distrobox_sudo_program' config variable)
--dry-run/-d: only print the container manager command generated
--verbose/-v: show more verbosity
--version/-V: show version
EOF
}
# Parse arguments
while :; do
case $1 in
-h | --help)
# Call a "show_help" function to display a synopsis, then exit.
show_help
exit 0
;;
-v | --verbose)
shift
verbose=1
;;
-T | -H | --no-tty)
shift
headless=1
;;
-r | --root)
shift
rootful=1
;;
-V | --version)
printf "distrobox: %s\n" "${version}"
exit 0
;;
-d | --dry-run)
shift
dryrun=1
;;
-nw | --no-workdir)
shift
skip_workdir=1
;;
-n | --name)
if [ -n "$2" ]; then
container_name="$2"
shift
shift
fi
;;
-a | --additional-flags)
if [ -n "$2" ]; then
if [ -z "${container_manager_additional_flags=}" ]; then
container_manager_additional_flags="${2}"
else
container_manager_additional_flags="${container_manager_additional_flags}
${2}"
fi
shift
shift
fi
;;
-Y | --yes)
non_interactive=1
shift
;;
-e | --exec | --)
container_custom_command=1
shift
# We pass the rest of arguments as $@ at the end
break
;;
--clean-path)
shift
clean_path=1
;;
-*) # Invalid options.
printf >&2 "ERROR: Invalid flag '%s'\n\n" "$1"
show_help
exit 1
;;
*) # Default case: If no more options then break out of the loop.
# If we have a flagless option and container_name is not specified
# then let's accept argument as container_name
if [ -n "$1" ]; then
container_name="$1"
shift
else
break
fi
;;
esac
done
set -o errexit
set -o nounset
# set verbosity
if [ "${verbose}" -ne 0 ]; then
set -o xtrace
fi
if [ -z "${container_name}" ]; then
container_name="${container_name_default}"
fi
if [ ! -t 0 ] || [ ! -t 1 ]; then
headless=1
fi
# We depend on a container manager let's be sure we have it
# First we use podman, else docker, else lilipod
case "${container_manager}" in
autodetect)
if command -v podman > /dev/null; then
container_manager="podman"
elif command -v podman-launcher > /dev/null; then
container_manager="podman-launcher"
elif command -v docker > /dev/null; then
container_manager="docker"
elif command -v lilipod > /dev/null; then
container_manager="lilipod"
fi
;;
podman)
container_manager="podman"
;;
podman-launcher)
container_manager="podman-launcher"
;;
lilipod)
container_manager="lilipod"
;;
docker)
container_manager="docker"
;;
*)
printf >&2 "Invalid input %s.\n" "${container_manager}"
printf >&2 "The available choices are: 'autodetect', 'podman', 'docker', 'lilipod'\n"
;;
esac
# Be sure we have a container manager to work with.
if ! command -v "${container_manager}" > /dev/null && [ "${dryrun}" -eq 0 ]; then
# Error: we need at least one between docker, podman or lilipod.
printf >&2 "Missing dependency: we need a container manager.\n"
printf >&2 "Please install one of podman, docker or lilipod.\n"
printf >&2 "You can follow the documentation on:\n"
printf >&2 "\tman distrobox-compatibility\n"
printf >&2 "or:\n"
printf >&2 "\thttps://github.com/89luca89/distrobox/blob/main/docs/compatibility.md\n"
exit 127
fi
# add verbose if -v is specified
if [ "${verbose}" -ne 0 ]; then
container_manager="${container_manager} --log-level debug"
fi
# prepend sudo (or the specified sudo program) if we want our container manager to be rootful
if [ "${rootful}" -ne 0 ]; then
container_manager="${distrobox_sudo_program-} ${container_manager}"
fi
# generate_enter_command will produce a Podman, Docker or Lilipod command to execute to enter the container.
# Arguments:
# None
# Expected global variables:
# container_manager: string container manager to use
# container_name: string container name
# container_manager_additional_flags: string container manager additional flags to use
# container_home: string container's home path
# container_path: string container's default PATH variable
# headless: bool headless mode
# skip_workdir: bool skip workdir
# verbose: bool verbose
# unshare_groups
# distrobox_enter_path
# Expected env variables:
# PATH
# USER
# PWD
# XDG_DATA_DIRS
# XDG_CONFIG_DIRS
# Outputs:
# prints the podman, docker or lilipod command to enter the distrobox container
generate_enter_command()
{
result_command="exec"
result_command="${result_command}
--interactive"
result_command="${result_command}
--detach-keys="
# In case of initful systems or unshared groups, we don't enter directly
# as our user, but we instead enter as root, and then su $USER, in order
# to trigger a proper login
if [ "${unshare_groups:-0}" -eq 1 ]; then
result_command="${result_command}
--user=root"
else
result_command="${result_command}
--user ${USER}"
fi
# For some usage, like use in service, or launched by non-terminal
# eg. from desktop files, TTY can fail to instantiate, and fail to enter
# the container.
# To work around this, --headless let's you skip the --tty flag and make it
# work in tty-less situations.
# Disable tty also if we're NOT in a tty (test -t 0, test -t 1).
if [ "${headless}" -eq 0 ]; then
result_command="${result_command}
--tty"
fi
# Entering container using our user and workdir.
# Start container from working directory. Else default to home. Else do /.
# Since we are entering from host, drop at workdir through '/run/host'
# which represents host's root inside container. Any directory on host
# even if not explicitly mounted is bound to exist under /run/host.
# Since user $HOME is very likely present in container, enter there directly
# to avoid confusing the user about shifted paths.
# pass distrobox-enter path, it will be used in the distrobox-export tool.
if [ "${skip_workdir}" -eq 0 ]; then
workdir="${PWD:-${container_home:-"/"}}"
if [ -n "${workdir##*"${container_home}"*}" ]; then
workdir="/run/host${workdir}"
fi
else
# Skipping workdir we just enter $HOME of the container.
workdir="${container_home}"
fi
result_command="${result_command}
--workdir ${workdir}"
result_command="${result_command}
--env CONTAINER_ID=${container_name}"
result_command="${result_command}
--env DISTROBOX_ENTER_PATH=${distrobox_enter_path}"
# Loop through all the environment vars
# and export them to the container.
set +o xtrace
# disable logging for this snippet, or it will be too talkative.
for i in $(printenv | grep '=' | grep -Ev ' |"|`|\$' |
grep -Ev '^(CONTAINER_ID|HOST|HOSTNAME|HOME|PATH|PROFILEREAD|SHELL|XDG_SEAT|XDG_VTNR|XDG_.*_DIRS|^_)'); do
# We filter the environment so that we do not have strange variables,
# multiline or containing spaces.
# We also NEED to ignore the HOME variable, as this is set at create time
# and needs to stay that way to use custom home dirs.
result_command="${result_command}
--env ${i}"
done
# Start with the $PATH set in the container's config
container_paths="${container_path:-""}"
# Ensure the standard FHS program paths are in PATH environment
standard_paths="/usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin"
if [ "${clean_path}" -eq 1 ]; then
# only add the standard paths
for standard_path in ${standard_paths}; do
if [ -z "${container_paths}" ]; then
container_paths="${standard_path}"
else
container_paths="${container_paths}:${standard_path}"
fi
done
else
# collect standard paths not existing from host PATH
for standard_path in ${standard_paths}; do
pattern="(:|^)${standard_path}(:|$)"
if ! echo "${PATH}" | grep -Eq "${pattern}"; then
if [ -z "${container_paths}" ]; then
container_paths="${standard_path}"
else
container_paths="${container_paths}:${standard_path}"
fi
fi
done
# append additional standard paths to host PATH to get final container_paths
if [ -n "${container_paths}" ]; then
container_paths="${PATH}:${container_paths}"
else
container_paths="${PATH}"
fi
fi
result_command="${result_command}
--env PATH=${container_paths}"
# Ensure the standard FHS program paths are in XDG_DATA_DIRS environment
standard_paths="/usr/local/share /usr/share"
container_paths="${XDG_DATA_DIRS:-}"
# add to the XDG_DATA_DIRS only after the host's paths, and only if not already present.
for standard_path in ${standard_paths}; do
pattern="(:|^)${standard_path}(:|$)"
if [ -z "${container_paths}" ]; then
container_paths="${standard_path}"
elif ! echo "${container_paths}" | grep -Eq "${pattern}"; then
container_paths="${container_paths}:${standard_path}"
fi
done
result_command="${result_command}
--env XDG_DATA_DIRS=${container_paths}"
# This correctly sets the XDG_* dirs to the container_home
# it will be $HOME if using regular home dirs
# if will be $container_home if using a custom home during create
result_command="${result_command}
--env XDG_CACHE_HOME=${container_home}/.cache
--env XDG_CONFIG_HOME=${container_home}/.config
--env XDG_DATA_HOME=${container_home}/.local/share
--env XDG_STATE_HOME=${container_home}/.local/state"
# Ensure the standard FHS program paths are in XDG_CONFIG_DIRS environment
standard_paths="/etc/xdg"
container_paths="${XDG_CONFIG_DIRS:-}"
# add to the XDG_CONFIG_DIRS only after the host's paths, and only if not already present.
for standard_path in ${standard_paths}; do
pattern="(:|^)${standard_path}(:|$)"
if [ -z "${container_paths}" ]; then
container_paths="${standard_path}"
elif ! echo "${container_paths}" | grep -Eq "${pattern}"; then
container_paths="${container_paths}:${standard_path}"
fi
done
result_command="${result_command}
--env XDG_CONFIG_DIRS=${container_paths}"
# re-enable logging if it was enabled previously.
if [ "${verbose}" -ne 0 ]; then
set -o xtrace
fi
# Add additional flags
if [ -n "${container_manager_additional_flags}" ]; then
result_command="${result_command}
${container_manager_additional_flags}"
fi
# Run selected container with specified command.
result_command="${result_command}
${container_name}"
# Return generated command.
# here we remove tabs as an artifact of using indentation in code to improve
# readability
printf "%s\n" "${result_command}" | tr -d '\t'
}
container_home="${HOME}"
container_path="${PATH}"
unshare_groups=0
# Now inspect the container we're working with.
container_status="unknown"
eval "$(${container_manager} inspect --type container --format \
'container_status={{.State.Status}};
unshare_groups={{ index .Config.Labels "distrobox.unshare_groups" }};
{{range .Config.Env}}{{if and (ge (len .) 5) (eq (slice . 0 5) "HOME=")}}container_home={{slice . 5 | printf "%q"}}{{end}}{{end}};
{{range .Config.Env}}{{if and (ge (len .) 5) (eq (slice . 0 5) "PATH=")}}container_path={{slice . 5 | printf "%q"}}{{end}}{{end}}' \
"${container_name}")"
# dry run mode, just generate the command and print it. No execution.
if [ "${dryrun}" -ne 0 ]; then
cmd="$(generate_enter_command | sed 's/\t//g')"
printf "%s %s\n" "${cmd}" "$*"
exit 0
fi
# Check if the container is even there
if [ "${container_status}" = "unknown" ]; then
# If not, prompt to create it first
# If we're not-interactive, just don't ask questions
if [ "${non_interactive}" -eq 1 ]; then
response="yes"
else
printf >&2 "Create it now, out of image %s? [Y/n]: " "${container_image_default}"
read -r response
response="${response:-"Y"}"
fi
# Accept only y,Y,Yes,yes,n,N,No,no.
case "${response}" in
y | Y | Yes | yes | YES)
# Ok, let's create the container with just 'distrobox create $container_name
create_command="$(dirname "${0}")/distrobox-create"
if [ "${rootful}" -ne 0 ]; then
create_command="${create_command} --root"
fi
create_command="${create_command} --yes -i ${container_image_default} -n ${container_name}"
printf >&2 "Creating the container %s\n" "${container_name}"
if [ "${dryrun}" -ne 1 ]; then
${create_command}
fi
;;
n | N | No | no | NO)
printf >&2 "Ok. For creating it, run this command:\n"
printf >&2 "\tdistrobox create <name-of-container> --image <remote>/<docker>:<tag>\n"
exit 0
;;
*) # Default case: If no more options then break out of the loop.
printf >&2 "Invalid input.\n"
printf >&2 "The available choices are: y,Y,Yes,yes,YES or n,N,No,no,NO.\nExiting.\n"
exit 1
;;
esac
fi
# If the container is not already running, we need to start if first
if [ "${container_status}" != "running" ]; then
# If container is not running, start it first
#
# Here, we save the timestamp before launching the start command, so we can
# be sure we're working with this very same session of logs later.
log_timestamp="$(date -u +%FT%T).000000000+00:00"
${container_manager} start "${container_name}" > /dev/null
#
# Check if the container is going in error status earlier than the
# entrypoint
if [ "$(${container_manager} inspect \
--type container \
--format "{{.State.Status}}" "${container_name}")" != "running" ]; then
printf >&2 "\033[31m Error: could not start entrypoint.\n\033[0m"
container_manager_log="$(${container_manager} logs "${container_name}")"
printf >&2 "%s\n" "${container_manager_log}"
exit 1
fi
printf >&2 "%-40s\t" "Starting container..."
mkdir -p "${app_cache_dir}"
rm -f "${app_cache_dir}/.${container_name}.fifo"
mkfifo "${app_cache_dir}/.${container_name}.fifo"
while true; do
# Exit early in case of crashed/stopped container during setup
if [ "$(${container_manager} inspect --type container --format '{{.State.Status}}' "${container_name}")" != "running" ]; then
printf >&2 "\nContainer Setup Failure!\n"
exit 1
fi
# save starting loop timestamp in temp variable, we'll use it
# after to let logs command minimize possible holes
${container_manager} logs --since "${log_timestamp}" -f "${container_name}" \
> "${app_cache_dir}/.${container_name}.fifo" 2>&1 &
logs_pid="$!"
# read logs from log_timestamp to now, line by line
while IFS= read -r line; do
case "${line}" in
"+"*)
# Ignoring logging commands
;;
"Error:"*)
printf >&2 "\033[31m %s\n\033[0m" "${line}"
exit 1
;;
"Warning:"*)
printf >&2 "\n\033[33m %s\033[0m" "${line}"
;;
"distrobox:"*)
current_line="$(echo "${line}" | cut -d' ' -f2-)"
# Save current line in the status, to avoid printing the same line multiple times
printf >&2 "\033[32m [ OK ]\n\033[0m%-40s\t" "${current_line}"
;;
"container_setup_done"*)
printf >&2 "\033[32m [ OK ]\n\033[0m"
kill "${logs_pid}" > /dev/null 2>&1
break 2
;;
*) ;;
esac
done < "${app_cache_dir}/.${container_name}.fifo"
done
# cleanup fifo
rm -f "${app_cache_dir}/.${container_name}.fifo"
printf >&2 "\nContainer Setup Complete!\n"
fi
################################################################################
# Execution section, in this section we will manipulate the positional parameters
# in order to generate our long docker/podman/lilipod command to execute.
#
# We use positional parameters in order to have the shell manage escaping and spaces
# so we remove the problem of we having to handle them.
#
# 1 - handle absence of custom command, we will need to add a getent command to
# execute the right container's user's shell
# 2 - in case of unshared groups (or initful) we need to trigger a proper login
# using `su`, so we will need to manipulate these arguments accorodingly
# 3 - prepend our generated command
# to do this, we use `tac` so we reverse loop it and prepend each argument.
# 4 - now that we're done, we can prepend our container_command
# we will need to use `rev` to reverse it as we reverse loop and prepend each
# argument
################################################################################
#
# Setup default commands if none are specified
# execute a getent command using the /bin/sh shell
# to find out the default shell of the user, and
# do a login shell with it (eg: /bin/bash -l)
if [ "${container_custom_command}" -eq 0 ]; then
set - "$@" "/bin/sh" "-c" "\$(getent passwd '${container_command_user}' | cut -f 7 -d :) -l"
fi
# If we have a command and we're unsharing groups, we need to execute those
# command using su $container_command_user
# if we're in a tty, also allocate one
if [ "${unshare_groups:-0}" -eq 1 ]; then
# shellcheck disable=SC2089,SC2016
set -- "-c" '"$0" "$@"' -- "$@"
set -- "-s" "/bin/sh" "$@"
if [ "${headless}" -eq 0 ]; then
set -- "--pty" "$@"
fi
set -- "${container_command_user}" "$@"
set -- "su" "$@"
fi
# Generate the exec command and run it
cmd="$(generate_enter_command | awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--]}')"
# Reverse it so we can reverse loop and prepend the command's arguments
# to our positional parameters
IFS='
'
for arg in ${cmd}; do
if echo "${arg}" | grep -q " "; then
set - "$(echo "${arg}" | cut -d' ' -f1)" "$(echo "${arg}" | cut -d' ' -f2-)" "$@"
else
set - "${arg}" "$@"
fi
done
# Prepend the container manager command
# reverse it first, so we can loop backward as we're prepending not appending
IFS=' '
for arg in $(echo "${container_manager}" | rev); do
arg="$(echo "${arg}" | rev)"
set - "${arg}" "$@"
done
exec "$@"