|
#!/bin/sh |
|
set -e |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SCRIPT_COMMIT_SHA="e5543d473431b782227f8908005543bb4389b8de" |
|
|
|
|
|
VERSION="${VERSION#v}" |
|
|
|
|
|
|
|
|
|
|
|
|
|
DEFAULT_CHANNEL_VALUE="stable" |
|
if [ -z "$CHANNEL" ]; then |
|
CHANNEL=$DEFAULT_CHANNEL_VALUE |
|
fi |
|
|
|
DEFAULT_DOWNLOAD_URL="https://download.docker.com" |
|
if [ -z "$DOWNLOAD_URL" ]; then |
|
DOWNLOAD_URL=$DEFAULT_DOWNLOAD_URL |
|
fi |
|
|
|
DEFAULT_REPO_FILE="docker-ce.repo" |
|
if [ -z "$REPO_FILE" ]; then |
|
REPO_FILE="$DEFAULT_REPO_FILE" |
|
fi |
|
|
|
mirror='' |
|
DRY_RUN=${DRY_RUN:-} |
|
while [ $# -gt 0 ]; do |
|
case "$1" in |
|
--channel) |
|
CHANNEL="$2" |
|
shift |
|
;; |
|
--dry-run) |
|
DRY_RUN=1 |
|
;; |
|
--mirror) |
|
mirror="$2" |
|
shift |
|
;; |
|
--version) |
|
VERSION="${2#v}" |
|
shift |
|
;; |
|
--*) |
|
echo "Illegal option $1" |
|
;; |
|
esac |
|
shift $(( $# > 0 ? 1 : 0 )) |
|
done |
|
|
|
case "$mirror" in |
|
Aliyun) |
|
DOWNLOAD_URL="https://mirrors.aliyun.com/docker-ce" |
|
;; |
|
AzureChinaCloud) |
|
DOWNLOAD_URL="https://mirror.azure.cn/docker-ce" |
|
;; |
|
"") |
|
;; |
|
*) |
|
>&2 echo "unknown mirror '$mirror': use either 'Aliyun', or 'AzureChinaCloud'." |
|
exit 1 |
|
;; |
|
esac |
|
|
|
case "$CHANNEL" in |
|
stable|test) |
|
;; |
|
edge|nightly) |
|
>&2 echo "DEPRECATED: the $CHANNEL channel has been deprecated and is no longer supported by this script." |
|
exit 1 |
|
;; |
|
*) |
|
>&2 echo "unknown CHANNEL '$CHANNEL': use either stable or test." |
|
exit 1 |
|
;; |
|
esac |
|
|
|
command_exists() { |
|
command -v "$@" > /dev/null 2>&1 |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
version_gte() { |
|
if [ -z "$VERSION" ]; then |
|
return 0 |
|
fi |
|
eval version_compare "$VERSION" "$1" |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
version_compare() ( |
|
set +x |
|
|
|
yy_a="$(echo "$1" | cut -d'.' -f1)" |
|
yy_b="$(echo "$2" | cut -d'.' -f1)" |
|
if [ "$yy_a" -lt "$yy_b" ]; then |
|
return 1 |
|
fi |
|
if [ "$yy_a" -gt "$yy_b" ]; then |
|
return 0 |
|
fi |
|
mm_a="$(echo "$1" | cut -d'.' -f2)" |
|
mm_b="$(echo "$2" | cut -d'.' -f2)" |
|
|
|
|
|
mm_a="${mm_a#0}" |
|
mm_b="${mm_b#0}" |
|
|
|
if [ "${mm_a:-0}" -lt "${mm_b:-0}" ]; then |
|
return 1 |
|
fi |
|
|
|
return 0 |
|
) |
|
|
|
is_dry_run() { |
|
if [ -z "$DRY_RUN" ]; then |
|
return 1 |
|
else |
|
return 0 |
|
fi |
|
} |
|
|
|
is_wsl() { |
|
case "$(uname -r)" in |
|
*microsoft* ) true ;; |
|
*Microsoft* ) true ;; |
|
* ) false;; |
|
esac |
|
} |
|
|
|
is_darwin() { |
|
case "$(uname -s)" in |
|
*darwin* ) true ;; |
|
*Darwin* ) true ;; |
|
* ) false;; |
|
esac |
|
} |
|
|
|
deprecation_notice() { |
|
distro=$1 |
|
distro_version=$2 |
|
echo |
|
printf "\033[91;1mDEPRECATION WARNING\033[0m\n" |
|
printf " This Linux distribution (\033[1m%s %s\033[0m) reached end-of-life and is no longer supported by this script.\n" "$distro" "$distro_version" |
|
echo " No updates or security fixes will be released for this distribution, and users are recommended" |
|
echo " to upgrade to a currently maintained version of $distro." |
|
echo |
|
printf "Press \033[1mCtrl+C\033[0m now to abort this script, or wait for the installation to continue." |
|
echo |
|
sleep 10 |
|
} |
|
|
|
get_distribution() { |
|
lsb_dist="" |
|
|
|
if [ -r /etc/os-release ]; then |
|
lsb_dist="$(. /etc/os-release && echo "$ID")" |
|
fi |
|
|
|
|
|
echo "$lsb_dist" |
|
} |
|
|
|
echo_docker_as_nonroot() { |
|
if is_dry_run; then |
|
return |
|
fi |
|
if command_exists docker && [ -e /var/run/docker.sock ]; then |
|
( |
|
set -x |
|
$sh_c 'docker version' |
|
) || true |
|
fi |
|
|
|
|
|
echo |
|
echo "================================================================================" |
|
echo |
|
if version_gte "20.10"; then |
|
echo "To run Docker as a non-privileged user, consider setting up the" |
|
echo "Docker daemon in rootless mode for your user:" |
|
echo |
|
echo " dockerd-rootless-setuptool.sh install" |
|
echo |
|
echo "Visit https://docs.docker.com/go/rootless/ to learn about rootless mode." |
|
echo |
|
fi |
|
echo |
|
echo "To run the Docker daemon as a fully privileged service, but granting non-root" |
|
echo "users access, refer to https://docs.docker.com/go/daemon-access/" |
|
echo |
|
echo "WARNING: Access to the remote API on a privileged Docker daemon is equivalent" |
|
echo " to root access on the host. Refer to the 'Docker daemon attack surface'" |
|
echo " documentation for details: https://docs.docker.com/go/attack-surface/" |
|
echo |
|
echo "================================================================================" |
|
echo |
|
} |
|
|
|
|
|
check_forked() { |
|
|
|
|
|
if command_exists lsb_release; then |
|
|
|
set +e |
|
lsb_release -a -u > /dev/null 2>&1 |
|
lsb_release_exit_code=$? |
|
set -e |
|
|
|
|
|
if [ "$lsb_release_exit_code" = "0" ]; then |
|
|
|
cat <<-EOF |
|
You're using '$lsb_dist' version '$dist_version'. |
|
EOF |
|
|
|
|
|
lsb_dist=$(lsb_release -a -u 2>&1 | tr '[:upper:]' '[:lower:]' | grep -E 'id' | cut -d ':' -f 2 | tr -d '[:space:]') |
|
dist_version=$(lsb_release -a -u 2>&1 | tr '[:upper:]' '[:lower:]' | grep -E 'codename' | cut -d ':' -f 2 | tr -d '[:space:]') |
|
|
|
|
|
cat <<-EOF |
|
Upstream release is '$lsb_dist' version '$dist_version'. |
|
EOF |
|
else |
|
if [ -r /etc/debian_version ] && [ "$lsb_dist" != "ubuntu" ] && [ "$lsb_dist" != "raspbian" ]; then |
|
if [ "$lsb_dist" = "osmc" ]; then |
|
|
|
lsb_dist=raspbian |
|
else |
|
|
|
lsb_dist=debian |
|
fi |
|
dist_version="$(sed 's/\/.*//' /etc/debian_version | sed 's/\..*//')" |
|
case "$dist_version" in |
|
12) |
|
dist_version="bookworm" |
|
;; |
|
11) |
|
dist_version="bullseye" |
|
;; |
|
10) |
|
dist_version="buster" |
|
;; |
|
9) |
|
dist_version="stretch" |
|
;; |
|
8) |
|
dist_version="jessie" |
|
;; |
|
esac |
|
fi |
|
fi |
|
fi |
|
} |
|
|
|
do_install() { |
|
echo "# Executing docker install script, commit: $SCRIPT_COMMIT_SHA" |
|
|
|
if command_exists docker; then |
|
cat >&2 <<-'EOF' |
|
Warning: the "docker" command appears to already exist on this system. |
|
|
|
If you already have Docker installed, this script can cause trouble, which is |
|
why we're displaying this warning and provide the opportunity to cancel the |
|
installation. |
|
|
|
If you installed the current Docker package using this script and are using it |
|
again to update Docker, you can safely ignore this message. |
|
|
|
You may press Ctrl+C now to abort this script. |
|
EOF |
|
( set -x; sleep 20 ) |
|
fi |
|
|
|
user="$(id -un 2>/dev/null || true)" |
|
|
|
sh_c='sh -c' |
|
if [ "$user" != 'root' ]; then |
|
if command_exists sudo; then |
|
sh_c='sudo -E sh -c' |
|
elif command_exists su; then |
|
sh_c='su -c' |
|
else |
|
cat >&2 <<-'EOF' |
|
Error: this installer needs the ability to run commands as root. |
|
We are unable to find either "sudo" or "su" available to make this happen. |
|
EOF |
|
exit 1 |
|
fi |
|
fi |
|
|
|
if is_dry_run; then |
|
sh_c="echo" |
|
fi |
|
|
|
# perform some very rudimentary platform detection |
|
lsb_dist=$( get_distribution ) |
|
lsb_dist="$(echo "$lsb_dist" | tr '[:upper:]' '[:lower:]')" |
|
|
|
if is_wsl; then |
|
echo |
|
echo "WSL DETECTED: We recommend using Docker Desktop for Windows." |
|
echo "Please get Docker Desktop from https://www.docker.com/products/docker-desktop/" |
|
echo |
|
cat >&2 <<-'EOF' |
|
|
|
You may press Ctrl+C now to abort this script. |
|
EOF |
|
( set -x; sleep 20 ) |
|
fi |
|
|
|
case "$lsb_dist" in |
|
|
|
ubuntu) |
|
if command_exists lsb_release; then |
|
dist_version="$(lsb_release --codename | cut -f2)" |
|
fi |
|
if [ -z "$dist_version" ] && [ -r /etc/lsb-release ]; then |
|
dist_version="$(. /etc/lsb-release && echo "$DISTRIB_CODENAME")" |
|
fi |
|
;; |
|
|
|
debian|raspbian) |
|
dist_version="$(sed 's/\/.*//' /etc/debian_version | sed 's/\..*//')" |
|
case "$dist_version" in |
|
12) |
|
dist_version="bookworm" |
|
;; |
|
11) |
|
dist_version="bullseye" |
|
;; |
|
10) |
|
dist_version="buster" |
|
;; |
|
9) |
|
dist_version="stretch" |
|
;; |
|
8) |
|
dist_version="jessie" |
|
;; |
|
esac |
|
;; |
|
|
|
centos|rhel|sles) |
|
if [ -z "$dist_version" ] && [ -r /etc/os-release ]; then |
|
dist_version="$(. /etc/os-release && echo "$VERSION_ID")" |
|
fi |
|
;; |
|
|
|
*) |
|
if command_exists lsb_release; then |
|
dist_version="$(lsb_release --release | cut -f2)" |
|
fi |
|
if [ -z "$dist_version" ] && [ -r /etc/os-release ]; then |
|
dist_version="$(. /etc/os-release && echo "$VERSION_ID")" |
|
fi |
|
;; |
|
|
|
esac |
|
|
|
# Check if this is a forked Linux distro |
|
check_forked |
|
|
|
# Print deprecation warnings for distro versions that recently reached EOL, |
|
# but may still be commonly used (especially LTS versions). |
|
case "$lsb_dist.$dist_version" in |
|
debian.stretch|debian.jessie) |
|
deprecation_notice "$lsb_dist" "$dist_version" |
|
;; |
|
raspbian.stretch|raspbian.jessie) |
|
deprecation_notice "$lsb_dist" "$dist_version" |
|
;; |
|
ubuntu.xenial|ubuntu.trusty) |
|
deprecation_notice "$lsb_dist" "$dist_version" |
|
;; |
|
ubuntu.impish|ubuntu.hirsute|ubuntu.groovy|ubuntu.eoan|ubuntu.disco|ubuntu.cosmic) |
|
deprecation_notice "$lsb_dist" "$dist_version" |
|
;; |
|
fedora.*) |
|
if [ "$dist_version" -lt 36 ]; then |
|
deprecation_notice "$lsb_dist" "$dist_version" |
|
fi |
|
;; |
|
esac |
|
|
|
# Run setup for each distro accordingly |
|
case "$lsb_dist" in |
|
ubuntu|debian|raspbian) |
|
pre_reqs="apt-transport-https ca-certificates curl" |
|
if ! command -v gpg > /dev/null; then |
|
pre_reqs="$pre_reqs gnupg" |
|
fi |
|
apt_repo="deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] $DOWNLOAD_URL/linux/$lsb_dist $dist_version $CHANNEL" |
|
( |
|
if ! is_dry_run; then |
|
set -x |
|
fi |
|
$sh_c 'apt-get update -qq >/dev/null' |
|
$sh_c "DEBIAN_FRONTEND=noninteractive apt-get install -y -qq $pre_reqs >/dev/null" |
|
$sh_c 'install -m 0755 -d /etc/apt/keyrings' |
|
$sh_c "curl -fsSL \"$DOWNLOAD_URL/linux/$lsb_dist/gpg\" | gpg --dearmor --yes -o /etc/apt/keyrings/docker.gpg" |
|
$sh_c "chmod a+r /etc/apt/keyrings/docker.gpg" |
|
$sh_c "echo \"$apt_repo\" > /etc/apt/sources.list.d/docker.list" |
|
$sh_c 'apt-get update -qq >/dev/null' |
|
) |
|
pkg_version="" |
|
if [ -n "$VERSION" ]; then |
|
if is_dry_run; then |
|
echo "# WARNING: VERSION pinning is not supported in DRY_RUN" |
|
else |
|
# Will work for incomplete versions IE (17.12), but may not actually grab the "latest" if in the test channel |
|
pkg_pattern="$(echo "$VERSION" | sed 's/-ce-/~ce~.*/g' | sed 's/-/.*/g')" |
|
search_command="apt-cache madison docker-ce | grep '$pkg_pattern' | head -1 | awk '{\$1=\$1};1' | cut -d' ' -f 3" |
|
pkg_version="$($sh_c "$search_command")" |
|
echo "INFO: Searching repository for VERSION '$VERSION'" |
|
echo "INFO: $search_command" |
|
if [ -z "$pkg_version" ]; then |
|
echo |
|
echo "ERROR: '$VERSION' not found amongst apt-cache madison results" |
|
echo |
|
exit 1 |
|
fi |
|
if version_gte "18.09"; then |
|
search_command="apt-cache madison docker-ce-cli | grep '$pkg_pattern' | head -1 | awk '{\$1=\$1};1' | cut -d' ' -f 3" |
|
echo "INFO: $search_command" |
|
cli_pkg_version="=$($sh_c "$search_command")" |
|
fi |
|
pkg_version="=$pkg_version" |
|
fi |
|
fi |
|
( |
|
pkgs="docker-ce${pkg_version%=}" |
|
if version_gte "18.09"; then |
|
# older versions didn't ship the cli and containerd as separate packages |
|
pkgs="$pkgs docker-ce-cli${cli_pkg_version%=} containerd.io" |
|
fi |
|
if version_gte "20.10"; then |
|
pkgs="$pkgs docker-compose-plugin docker-ce-rootless-extras$pkg_version" |
|
fi |
|
if version_gte "23.0"; then |
|
pkgs="$pkgs docker-buildx-plugin" |
|
fi |
|
if ! is_dry_run; then |
|
set -x |
|
fi |
|
$sh_c "DEBIAN_FRONTEND=noninteractive apt-get install -y -qq $pkgs >/dev/null" |
|
) |
|
echo_docker_as_nonroot |
|
exit 0 |
|
;; |
|
centos|fedora|rhel) |
|
if [ "$(uname -m)" != "s390x" ] && [ "$lsb_dist" = "rhel" ]; then |
|
echo "Packages for RHEL are currently only available for s390x." |
|
exit 1 |
|
fi |
|
if [ "$lsb_dist" = "fedora" ]; then |
|
pkg_manager="dnf" |
|
config_manager="dnf config-manager" |
|
enable_channel_flag="--set-enabled" |
|
disable_channel_flag="--set-disabled" |
|
pre_reqs="dnf-plugins-core" |
|
pkg_suffix="fc$dist_version" |
|
else |
|
pkg_manager="yum" |
|
config_manager="yum-config-manager" |
|
enable_channel_flag="--enable" |
|
disable_channel_flag="--disable" |
|
pre_reqs="yum-utils" |
|
pkg_suffix="el" |
|
fi |
|
repo_file_url="$DOWNLOAD_URL/linux/$lsb_dist/$REPO_FILE" |
|
( |
|
if ! is_dry_run; then |
|
set -x |
|
fi |
|
$sh_c "$pkg_manager install -y -q $pre_reqs" |
|
$sh_c "$config_manager --add-repo $repo_file_url" |
|
|
|
if [ "$CHANNEL" != "stable" ]; then |
|
$sh_c "$config_manager $disable_channel_flag 'docker-ce-*'" |
|
$sh_c "$config_manager $enable_channel_flag 'docker-ce-$CHANNEL'" |
|
fi |
|
$sh_c "$pkg_manager makecache" |
|
) |
|
pkg_version="" |
|
if [ -n "$VERSION" ]; then |
|
if is_dry_run; then |
|
echo "# WARNING: VERSION pinning is not supported in DRY_RUN" |
|
else |
|
pkg_pattern="$(echo "$VERSION" | sed 's/-ce-/\\\\.ce.*/g' | sed 's/-/.*/g').*$pkg_suffix" |
|
search_command="$pkg_manager list --showduplicates docker-ce | grep '$pkg_pattern' | tail -1 | awk '{print \$2}'" |
|
pkg_version="$($sh_c "$search_command")" |
|
echo "INFO: Searching repository for VERSION '$VERSION'" |
|
echo "INFO: $search_command" |
|
if [ -z "$pkg_version" ]; then |
|
echo |
|
echo "ERROR: '$VERSION' not found amongst $pkg_manager list results" |
|
echo |
|
exit 1 |
|
fi |
|
if version_gte "18.09"; then |
|
|
|
search_command="$pkg_manager list --showduplicates docker-ce-cli | grep '$pkg_pattern' | tail -1 | awk '{print \$2}'" |
|
cli_pkg_version="$($sh_c "$search_command" | cut -d':' -f 2)" |
|
fi |
|
|
|
pkg_version="-$(echo "$pkg_version" | cut -d':' -f 2)" |
|
fi |
|
fi |
|
( |
|
pkgs="docker-ce$pkg_version" |
|
if version_gte "18.09"; then |
|
|
|
if [ -n "$cli_pkg_version" ]; then |
|
pkgs="$pkgs docker-ce-cli-$cli_pkg_version containerd.io" |
|
else |
|
pkgs="$pkgs docker-ce-cli containerd.io" |
|
fi |
|
fi |
|
if version_gte "20.10"; then |
|
pkgs="$pkgs docker-compose-plugin docker-ce-rootless-extras$pkg_version" |
|
fi |
|
if version_gte "23.0"; then |
|
pkgs="$pkgs docker-buildx-plugin" |
|
fi |
|
if ! is_dry_run; then |
|
set -x |
|
fi |
|
$sh_c "$pkg_manager install -y -q $pkgs" |
|
) |
|
echo_docker_as_nonroot |
|
exit 0 |
|
;; |
|
sles) |
|
if [ "$(uname -m)" != "s390x" ]; then |
|
echo "Packages for SLES are currently only available for s390x" |
|
exit 1 |
|
fi |
|
if [ "$dist_version" = "15.3" ]; then |
|
sles_version="SLE_15_SP3" |
|
else |
|
sles_minor_version="${dist_version##*.}" |
|
sles_version="15.$sles_minor_version" |
|
fi |
|
repo_file_url="$DOWNLOAD_URL/linux/$lsb_dist/$REPO_FILE" |
|
pre_reqs="ca-certificates curl libseccomp2 awk" |
|
( |
|
if ! is_dry_run; then |
|
set -x |
|
fi |
|
$sh_c "zypper install -y $pre_reqs" |
|
$sh_c "zypper addrepo $repo_file_url" |
|
if ! is_dry_run; then |
|
cat >&2 <<-'EOF' |
|
WARNING!! |
|
openSUSE repository (https://download.opensuse.org/repositories/security:SELinux) will be enabled now. |
|
Do you wish to continue? |
|
You may press Ctrl+C now to abort this script. |
|
EOF |
|
( set -x; sleep 30 ) |
|
fi |
|
opensuse_repo="https://download.opensuse.org/repositories/security:SELinux/$sles_version/security:SELinux.repo" |
|
$sh_c "zypper addrepo $opensuse_repo" |
|
$sh_c "zypper --gpg-auto-import-keys refresh" |
|
$sh_c "zypper lr -d" |
|
) |
|
pkg_version="" |
|
if [ -n "$VERSION" ]; then |
|
if is_dry_run; then |
|
echo "# WARNING: VERSION pinning is not supported in DRY_RUN" |
|
else |
|
pkg_pattern="$(echo "$VERSION" | sed 's/-ce-/\\\\.ce.*/g' | sed 's/-/.*/g')" |
|
search_command="zypper search -s --match-exact 'docker-ce' | grep '$pkg_pattern' | tail -1 | awk '{print \$6}'" |
|
pkg_version="$($sh_c "$search_command")" |
|
echo "INFO: Searching repository for VERSION '$VERSION'" |
|
echo "INFO: $search_command" |
|
if [ -z "$pkg_version" ]; then |
|
echo |
|
echo "ERROR: '$VERSION' not found amongst zypper list results" |
|
echo |
|
exit 1 |
|
fi |
|
search_command="zypper search -s --match-exact 'docker-ce-cli' | grep '$pkg_pattern' | tail -1 | awk '{print \$6}'" |
|
|
|
cli_pkg_version="$($sh_c "$search_command")" |
|
pkg_version="-$pkg_version" |
|
fi |
|
fi |
|
( |
|
pkgs="docker-ce$pkg_version" |
|
if version_gte "18.09"; then |
|
if [ -n "$cli_pkg_version" ]; then |
|
|
|
pkgs="$pkgs docker-ce-cli-$cli_pkg_version containerd.io" |
|
else |
|
pkgs="$pkgs docker-ce-cli containerd.io" |
|
fi |
|
fi |
|
if version_gte "20.10"; then |
|
pkgs="$pkgs docker-compose-plugin docker-ce-rootless-extras$pkg_version" |
|
fi |
|
if version_gte "23.0"; then |
|
pkgs="$pkgs docker-buildx-plugin" |
|
fi |
|
if ! is_dry_run; then |
|
set -x |
|
fi |
|
$sh_c "zypper -q install -y $pkgs" |
|
) |
|
echo_docker_as_nonroot |
|
exit 0 |
|
;; |
|
*) |
|
if [ -z "$lsb_dist" ]; then |
|
if is_darwin; then |
|
echo |
|
echo "ERROR: Unsupported operating system 'macOS'" |
|
echo "Please get Docker Desktop from https://www.docker.com/products/docker-desktop" |
|
echo |
|
exit 1 |
|
fi |
|
fi |
|
echo |
|
echo "ERROR: Unsupported distribution '$lsb_dist'" |
|
echo |
|
exit 1 |
|
;; |
|
esac |
|
exit 1 |
|
} |
|
|
|
|
|
|
|
do_install |
|
|