Files
lamp/lamp.sh
Teddysun d5a13b118f Update comments
Feat: add unified package installation abstraction
Refactor: modularize PHP configuration with associative arrays
Refactor: add unified Apache service management
Refactor: decompose main() into phase-based functions

Signed-off-by: Teddysun <i@teddysun.com>
2026-02-07 17:17:40 +09:00

1167 lines
39 KiB
Bash

#!/bin/bash
#
# LAMP (Linux + Apache + MariaDB + PHP) Installation Script
#
# Supported OS:
# - Enterprise Linux 8/9/10 (CentOS Stream, RHEL, Rocky Linux, AlmaLinux, Oracle Linux)
# - Debian 11/12/13
# - Ubuntu 20.04/22.04/24.04
#
# Copyright (C) 2013 - 2026 Teddysun <i@teddysun.com>
set -uo pipefail
shopt -s inherit_errexit 2>/dev/null || true
trap _exit_handler INT QUIT TERM
#==============================================================================
# Configuration & Constants
#==============================================================================
SCRIPT_DIR="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
readonly SCRIPT_DIR
readonly DEFAULT_DB_PASS="Teddysun.com"
readonly LOG_FILE="/var/log/lamp-install.log"
# Package manager type
PM_TYPE=""
#==============================================================================
# Color Output Functions
#==============================================================================
_red() { printf '\033[1;31m%b\033[0m' "$1"; }
_green() { printf '\033[1;32m%b\033[0m' "$1"; }
_yellow() { printf '\033[1;33m%b\033[0m' "$1"; }
#==============================================================================
# Logging Functions
#==============================================================================
_log() {
local level="$1"
shift 1
local message="$*"
local timestamp
timestamp=$(date)
echo "[${timestamp}] [${level}] ${message}" | tee -a "${LOG_FILE}" || echo "[${timestamp}] [${level}] ${message}"
}
_info() { _log "INFO" "$@"; }
_warn() { _log "WARN" "$(_yellow "$@")"; }
_error() { _log "ERROR" "$(_red "$@")" >&2; exit 2; }
#==============================================================================
# Signal Handlers
#==============================================================================
_exit_handler() {
printf "\n"
_red "Script $0 has been terminated."
printf "\n"
exit 1
}
#==============================================================================
# Utility Functions
#==============================================================================
# Check if a command exists
_exists() {
command -v "$1" &>/dev/null
}
# Execute command with error detection
_error_detect() {
local cmd="$1"
_info "Executing: ${cmd}"
if ! eval "${cmd}" >>"${LOG_FILE}" 2>&1; then
_error "Command failed: ${cmd}"
fi
}
# Compare versions (returns 0 if $1 >= $2)
_version_ge() {
local ver1="$1"
local ver2="$2"
printf '%s\n%s\n' "${ver1}" "${ver2}" | sort -rV | head -n1 | grep -qx "${ver1}"
}
# Detect and set package manager type
_detect_package_manager() {
if _check_sys rhel; then
PM_TYPE="dnf"
elif _check_sys debian || _check_sys ubuntu; then
PM_TYPE="apt"
else
_error "Unable to detect package manager for this OS"
fi
}
# Update package cache
_update_package_cache() {
case "${PM_TYPE}" in
dnf)
_error_detect "dnf makecache"
;;
apt)
_error_detect "apt-get update"
;;
esac
}
#==============================================================================
# OS Detection Functions
#==============================================================================
# Get OS ID from /etc/os-release
_get_os_id() {
if [[ -f /etc/os-release ]]; then
awk -F= '/^ID=/{gsub(/"/, ""); print $2}' /etc/os-release
fi
}
# Get OS version ID
_get_os_version() {
if [[ -f /etc/os-release ]]; then
awk -F= '/^VERSION_ID=/{gsub(/"/, ""); print $2}' /etc/os-release
fi
}
# Get pretty OS name
_get_opsy() {
if [[ -f /etc/os-release ]]; then
awk -F= '/^PRETTY_NAME=/{gsub(/^"|"$/, "", $2); print $2}' /etc/os-release
elif [[ -f /etc/lsb-release ]]; then
awk -F= '/^DISTRIB_DESCRIPTION=/{gsub(/^"|"$/, "", $2); print $2}' /etc/lsb-release
elif [[ -f /etc/redhat-release ]]; then
cat /etc/redhat-release
fi
}
# Check OS type
_check_sys() {
local os_type="$1"
local os_id
os_id=$(_get_os_id)
case "${os_type}" in
rhel)
[[ "${os_id}" =~ ^(centos|rhel|rocky|almalinux|ol|fedora)$ ]] || \
[[ -f /etc/redhat-release ]]
;;
debian)
[[ "${os_id}" == "debian" ]]
;;
ubuntu)
[[ "${os_id}" == "ubuntu" ]]
;;
*)
return 1
;;
esac
}
# Get RHEL major version
get_rhelversion() {
local target_ver="$1"
local actual_ver
actual_ver=$(_get_os_version)
actual_ver="${actual_ver%%.*}"
[[ "${actual_ver}" == "${target_ver}" ]]
}
# Get Debian major version
get_debianversion() {
local target_ver="$1"
local actual_ver
actual_ver=$(_get_os_version)
actual_ver="${actual_ver%%.*}"
[[ "${actual_ver}" == "${target_ver}" ]]
}
# Get Ubuntu version
get_ubuntuversion() {
local target_ver="$1"
local actual_ver
actual_ver=$(_get_os_version)
[[ "${actual_ver}" == "${target_ver}" ]]
}
# Get extra repository name for RHEL
get_rhel_extra_repo() {
local ver="$1"
case "${ver}" in
8) echo "powertools" ;;
9|10) echo "crb" ;;
*) _error "Unsupported RHEL version: ${ver}" ;;
esac
}
#==============================================================================
# System Configuration Functions
#==============================================================================
# Check kernel version for BBR support
check_kernel_version() {
local kernel_version
kernel_version=$(uname -r | cut -d- -f1)
_version_ge "${kernel_version}" "4.9.0"
}
# Check if BBR is enabled
check_bbr_status() {
local param
param=$(sysctl -n net.ipv4.tcp_congestion_control 2>/dev/null || echo "")
[[ "${param}" == "bbr" ]]
}
# Get single character input
get_char() {
SAVEDSTTY=$(stty -g)
stty -echo
stty cbreak
dd if=/dev/tty bs=1 count=1 2>/dev/null
stty -raw
stty echo
stty "${SAVEDSTTY}"
}
#==============================================================================
# RHEL Initialization
#==============================================================================
set_rhel_inputrc() {
local ver="$1"
if [[ "${ver}" =~ ^(9|10)$ ]]; then
if ! grep -q "set enable-bracketed-paste off" /etc/inputrc 2>/dev/null; then
_error_detect "echo 'set enable-bracketed-paste off' >> /etc/inputrc"
fi
fi
}
initialize_rhel() {
local rhel_ver=""
for ver in 8 9 10; do
if get_rhelversion "${ver}"; then
rhel_ver="${ver}"
break
fi
done
[[ -z "${rhel_ver}" ]] && _error "Unsupported RHEL version"
_info "Detected RHEL version: ${rhel_ver}"
# Install EPEL
_error_detect "dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-${rhel_ver}.noarch.rpm"
# Enable CodeReady Builder repository
if _exists "subscription-manager"; then
_error_detect "subscription-manager repos --enable codeready-builder-for-rhel-${rhel_ver}-$(uname -m)-rpms"
elif [[ -s "/etc/yum.repos.d/oracle-linux-ol${rhel_ver}.repo" ]]; then
_error_detect "dnf config-manager --set-enabled ol${rhel_ver}_codeready_builder"
else
_error_detect "dnf config-manager --set-enabled $(get_rhel_extra_repo "${rhel_ver}")"
fi
set_rhel_inputrc "${rhel_ver}"
# Install custom repository
_error_detect "dnf install -y https://dl.lamp.sh/linux/rhel/el${rhel_ver}/x86_64/teddysun-release-1.0-1.el${rhel_ver}.noarch.rpm"
# Update cache and install base packages
_update_package_cache
install_packages "vim" "nano" "tar" "zip" "unzip" "net-tools" "screen" "git" "virt-what" "wget" "mtr" "traceroute" "iftop" "htop" "jq" "tree"
install_packages "libnghttp2" "libnghttp2-devel" "c-ares" "c-ares-devel" "curl" "libcurl" "libcurl-devel"
# Disable SELinux
if [[ -s "/etc/selinux/config" ]] && grep -q 'SELINUX=enforcing' /etc/selinux/config; then
sed -i 's/^SELINUX=.*/SELINUX=disabled/g' /etc/selinux/config
setenforce 0 2>/dev/null || true
_info "SELinux disabled"
fi
# Remove cockpit motd
if [[ -s "/etc/motd.d/cockpit" ]]; then
rm -f /etc/motd.d/cockpit
_info "Removed /etc/motd.d/cockpit"
fi
# Configure firewall
if systemctl is-active --quiet firewalld 2>/dev/null; then
local default_zone
default_zone=$(firewall-cmd --get-default-zone)
firewall-cmd --permanent --add-service=https --zone="${default_zone}" &>/dev/null || true
firewall-cmd --permanent --add-service=http --zone="${default_zone}" &>/dev/null || true
firewall-cmd --permanent --zone="${default_zone}" --add-port=443/udp &>/dev/null || true
firewall-cmd --reload &>/dev/null || true
sed -i 's/AllowZoneDrifting=yes/AllowZoneDrifting=no/' /etc/firewalld/firewalld.conf 2>/dev/null || true
_error_detect "systemctl restart firewalld"
_info "Firewall configuration completed"
else
_warn "firewalld is not running, skipping firewall configuration"
fi
}
#==============================================================================
# Debian/Ubuntu Initialization
#==============================================================================
initialize_deb() {
_update_package_cache
install_packages "lsb-release" "ca-certificates" "curl" "gnupg"
install_packages "vim" "nano" "tar" "zip" "unzip" "net-tools" "screen" "git" "virt-what" "wget" "mtr" "traceroute" "iftop" "htop" "jq" "tree"
# Configure UFW
if ufw status &>/dev/null; then
_error_detect "ufw allow http"
_error_detect "ufw allow https"
_error_detect "ufw allow 443/udp"
else
_warn "ufw is not running, skipping firewall configuration"
fi
}
#==============================================================================
# System Initialization
#==============================================================================
initialize_system() {
if _check_sys rhel; then
initialize_rhel
elif _check_sys debian || _check_sys ubuntu; then
initialize_deb
else
_error "Unsupported operating system"
fi
}
#==============================================================================
# BBR Configuration
#==============================================================================
configure_bbr() {
if ! check_kernel_version; then
_warn "Kernel version < 4.9, skipping BBR configuration"
return 0
fi
if check_bbr_status; then
_info "BBR is already enabled"
return 0
fi
# Backup and configure sysctl
local sysctl_conf="/etc/sysctl.conf"
# Remove old BBR settings
if [[ -f "${sysctl_conf}" ]]; then
sed -i '/net.core.default_qdisc/d' "${sysctl_conf}"
sed -i '/net.ipv4.tcp_congestion_control/d' "${sysctl_conf}"
sed -i '/net.core.rmem_max/d' "${sysctl_conf}"
fi
# Add new BBR settings
cat >> "${sysctl_conf}" << EOF
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
net.core.rmem_max = 2500000
EOF
sysctl -p &>/dev/null || _warn "Failed to apply sysctl settings"
_info "BBR configuration completed"
}
#==============================================================================
# Journald Configuration
#==============================================================================
configure_journald() {
local journald_config=""
if ! systemctl is-active --quiet systemd-journald 2>/dev/null; then
return 0
fi
if [[ -s "/etc/systemd/journald.conf" ]]; then
journald_config="/etc/systemd/journald.conf"
elif [[ -s "/usr/lib/systemd/journald.conf" ]]; then
journald_config="/usr/lib/systemd/journald.conf"
fi
[[ -z "${journald_config}" ]] && return 0
sed -i 's/^#\?Storage=.*/Storage=volatile/' "${journald_config}"
sed -i 's/^#\?SystemMaxUse=.*/SystemMaxUse=16M/' "${journald_config}"
sed -i 's/^#\?RuntimeMaxUse=.*/RuntimeMaxUse=16M/' "${journald_config}"
_error_detect "systemctl restart systemd-journald"
}
#==============================================================================
# Version Selection Functions
#==============================================================================
select_mariadb_version() {
local choice
_info "Please choose a MariaDB version:"
_info "$(_green "1"). MariaDB 10.11"
_info "$(_green "2"). MariaDB 11.4"
_info "$(_green "3"). MariaDB 11.8"
while true; do
read -r -p "[$(date)] [INFO] Please input a number (Default: 2): " choice
choice="${choice:-2}"
case "${choice}" in
1) mariadb_ver="10.11"; return 0 ;;
2) mariadb_ver="11.4"; return 0 ;;
3) mariadb_ver="11.8"; return 0 ;;
*) _warn "Invalid input. Please enter 1, 2, or 3" ;;
esac
done
}
select_php_version() {
local choice
_info "Please choose a PHP version:"
_info "$(_green "1"). PHP 7.4"
_info "$(_green "2"). PHP 8.0"
_info "$(_green "3"). PHP 8.1"
_info "$(_green "4"). PHP 8.2"
_info "$(_green "5"). PHP 8.3"
_info "$(_green "6"). PHP 8.4"
_info "$(_green "7"). PHP 8.5"
while true; do
read -r -p "[$(date)] [INFO] Please input a number (Default: 6): " choice
choice="${choice:-6}"
case "${choice}" in
1) php_ver="7.4"; return 0 ;;
2) php_ver="8.0"; return 0 ;;
3) php_ver="8.1"; return 0 ;;
4) php_ver="8.2"; return 0 ;;
5) php_ver="8.3"; return 0 ;;
6) php_ver="8.4"; return 0 ;;
7) php_ver="8.5"; return 0 ;;
*) _warn "Invalid input. Please enter 1-7" ;;
esac
done
}
read_db_password() {
local password
_info "Please enter the MariaDB root password:"
read -s -r -p "[$(date)] [INFO] (Default: ${DEFAULT_DB_PASS}, password will not shown): " password
password="${password:-${DEFAULT_DB_PASS}}"
echo
db_pass="${password}"
_info "---------------------------"
_info "Password: $(_red "********")"
_info "---------------------------"
}
#==============================================================================
# MariaDB Configuration
#==============================================================================
setup_debian_cnf() {
local db_pass="$1"
[[ ! -x "/etc/mysql/debian-start" ]] && return 0
cat > "/etc/mysql/debian.cnf" << EOF
# THIS FILE IS OBSOLETE. STOP USING IT IF POSSIBLE.
[client]
host = localhost
user = root
password = '${db_pass}'
[mysql_upgrade]
host = localhost
user = root
password = '${db_pass}'
EOF
chmod 600 /etc/mysql/debian.cnf
_info "Debian maintenance credentials configuration completed"
}
configure_mariadb() {
local db_pass="$1"
local mariadb_cnf="$2"
# Configure MariaDB settings
local lnum
lnum=$(sed -n '/\[mysqld\]/=' "${mariadb_cnf}" | head -1)
if [[ -n "${lnum}" ]]; then
sed -i "${lnum}a innodb_buffer_pool_size = 100M\nmax_allowed_packet = 1024M\nnet_read_timeout = 3600\nnet_write_timeout = 3600" "${mariadb_cnf}"
fi
lnum=$(sed -n '/\[mariadb\]/=' "${mariadb_cnf}" | tail -1)
if [[ -n "${lnum}" ]]; then
sed -i "${lnum}a character-set-server = utf8mb4\n\n[client-mariadb]\ndefault-character-set = utf8mb4" "${mariadb_cnf}"
fi
# Start MariaDB
_error_detect "systemctl start mariadb"
sleep 3
# Create root user with new authentication method
_info "Create root user with new authentication method"
/usr/bin/mariadb -e "GRANT ALL PRIVILEGES ON *.* TO root@'127.0.0.1' IDENTIFIED BY '${db_pass}' WITH GRANT OPTION;"
/usr/bin/mariadb -e "GRANT ALL PRIVILEGES ON *.* TO root@'localhost' IDENTIFIED BY '${db_pass}' WITH GRANT OPTION;"
# Secure installation
/usr/bin/mariadb -uroot -p"${db_pass}" 2>/dev/null << EOF
drop database if exists test;
delete from mysql.db where user='';
delete from mysql.db where user='PUBLIC';
delete from mysql.user where user='';
delete from mysql.user where user='mysql';
delete from mysql.user where user='PUBLIC';
flush privileges;
exit
EOF
_info "MariaDB configuration completed"
}
#==============================================================================
# PHP Configuration
#==============================================================================
# PHP pool configuration for RHEL using associative array
configure_php_rhel_pool() {
local php_conf="$1"
# Define RHEL pool settings using associative array
declare -A rhel_pool_settings=(
["user"]="apache"
["group"]="apache"
["listen.acl_users"]="apache,nginx"
)
# Apply settings
sed -i "s@^user.*@user = ${rhel_pool_settings[user]}@" "${php_conf}"
sed -i "s@^group.*@group = ${rhel_pool_settings[group]}@" "${php_conf}"
sed -i "s@^listen.acl_users.*@listen.acl_users = ${rhel_pool_settings[listen.acl_users]}@" "${php_conf}"
sed -i "s@^;php_value\[opcache.file_cache\].*@php_value[opcache.file_cache] = /var/lib/php/opcache@" "${php_conf}"
}
# PHP pool configuration for Debian/Ubuntu using associative array
configure_php_deb_pool() {
local php_conf="$1"
# Define Debian pool settings using associative array
declare -A deb_pool_settings=(
["user"]="www-data"
["group"]="www-data"
["listen.acl_users"]="www-data"
["listen.allowed_clients"]="127.0.0.1"
["pm.max_children"]="50"
["pm.start_servers"]="5"
["pm.min_spare_servers"]="5"
["pm.max_spare_servers"]="35"
["slowlog"]="/var/log/www-slow.log"
["php_admin_value[error_log]"]="/var/log/www-error.log"
)
# Apply settings
sed -i "s@^user.*@user = ${deb_pool_settings[user]}@" "${php_conf}"
sed -i "s@^group.*@group = ${deb_pool_settings[group]}@" "${php_conf}"
sed -i "s@^listen.owner.*@;&@" "${php_conf}"
sed -i "s@^listen.group.*@;&@" "${php_conf}"
sed -i "s@^;listen.acl_users.*@listen.acl_users = ${deb_pool_settings[listen.acl_users]}@" "${php_conf}"
sed -i "s@^;listen.allowed_clients.*@listen.allowed_clients = ${deb_pool_settings[listen.allowed_clients]}@" "${php_conf}"
sed -i "s@^pm.max_children.*@pm.max_children = ${deb_pool_settings[pm.max_children]}@" "${php_conf}"
sed -i "s@^pm.start_servers.*@pm.start_servers = ${deb_pool_settings[pm.start_servers]}@" "${php_conf}"
sed -i "s@^pm.min_spare_servers.*@pm.min_spare_servers = ${deb_pool_settings[pm.min_spare_servers]}@" "${php_conf}"
sed -i "s@^pm.max_spare_servers.*@pm.max_spare_servers = ${deb_pool_settings[pm.max_spare_servers]}@" "${php_conf}"
sed -i "s@^;slowlog.*@slowlog = ${deb_pool_settings[slowlog]}@" "${php_conf}"
sed -i "s@^;php_admin_value\[error_log\].*@php_admin_value[error_log] = ${deb_pool_settings[php_admin_value[error_log]]}@" "${php_conf}"
sed -i "s@^;php_admin_flag\[log_errors\].*@php_admin_flag[log_errors] = on@" "${php_conf}"
cat >> "${php_conf}" << EOF
php_value[session.save_handler] = files
php_value[session.save_path] = /var/lib/php/session
php_value[soap.wsdl_cache_dir] = /var/lib/php/wsdlcache
php_value[opcache.file_cache] = /var/lib/php/opcache
EOF
}
# PHP ini configuration using associative array
configure_php_ini() {
local php_ini="$1"
local sock_location="$2"
# Define php.ini settings using associative array
declare -A php_ini_settings=(
["disable_functions"]="passthru,exec,shell_exec,system,chroot,chgrp,chown,proc_open,proc_get_status,ini_alter,ini_restore"
["max_execution_time"]="300"
["max_input_time"]="300"
["post_max_size"]="128M"
["upload_max_filesize"]="128M"
["expose_php"]="Off"
["short_open_tag"]="On"
)
# Apply settings
sed -i "s@^disable_functions.*@disable_functions = ${php_ini_settings[disable_functions]}@" "${php_ini}"
sed -i "s@^max_execution_time.*@max_execution_time = ${php_ini_settings[max_execution_time]}@" "${php_ini}"
sed -i "s@^max_input_time.*@max_input_time = ${php_ini_settings[max_input_time]}@" "${php_ini}"
sed -i "s@^post_max_size.*@post_max_size = ${php_ini_settings[post_max_size]}@" "${php_ini}"
sed -i "s@^upload_max_filesize.*@upload_max_filesize = ${php_ini_settings[upload_max_filesize]}@" "${php_ini}"
sed -i "s@^expose_php.*@expose_php = ${php_ini_settings[expose_php]}@" "${php_ini}"
sed -i "s@^short_open_tag.*@short_open_tag = ${php_ini_settings[short_open_tag]}@" "${php_ini}"
sed -i "s#mysqli.default_socket.*#mysqli.default_socket = ${sock_location}#" "${php_ini}"
sed -i "s#pdo_mysql.default_socket.*#pdo_mysql.default_socket = ${sock_location}#" "${php_ini}"
}
# Main PHP configuration dispatcher
configure_php_settings() {
local php_conf="$1"
local php_ini="$2"
local sock_location="$3"
local is_rhel="$4"
if [[ "${is_rhel}" == "true" ]]; then
configure_php_rhel_pool "${php_conf}"
else
configure_php_deb_pool "${php_conf}"
fi
# Configure php.ini (common for both)
configure_php_ini "${php_ini}" "${sock_location}"
_info "PHP configuration completed"
}
#==============================================================================
# PHP Installation
#==============================================================================
configure_php_rhel() {
local php_ver="$1"
# Install Remi repository
if get_rhelversion 8; then
_error_detect "dnf install -y https://rpms.remirepo.net/enterprise/remi-release-8.rpm"
elif get_rhelversion 9; then
_error_detect "dnf install -y https://rpms.remirepo.net/enterprise/remi-release-9.rpm"
elif get_rhelversion 10; then
_error_detect "dnf install -y https://rpms.remirepo.net/enterprise/remi-release-10.rpm"
fi
_error_detect "dnf module reset -y php"
_error_detect "dnf module install -y php:remi-${php_ver}"
# Install PHP packages
install_packages \
"php-common" "php-fpm" "php-cli" "php-bcmath" "php-embedded" \
"php-gd" "php-imap" "php-mysqlnd" "php-dba" "php-pdo" "php-pdo-dblib" \
"php-pgsql" "php-odbc" "php-enchant" "php-gmp" "php-intl" "php-ldap" \
"php-snmp" "php-soap" "php-tidy" "php-opcache" "php-process" \
"php-pspell" "php-shmop" "php-sodium" "php-ffi" "php-brotli" \
"php-lz4" "php-xz" "php-zstd" "php-pecl-imagick-im7" "php-pecl-zip" \
"php-pecl-rar" "php-pecl-grpc" "php-pecl-yaml" "php-pecl-uuid"
}
configure_php_deb() {
local php_ver="$1"
# Add PHP repository
if _check_sys debian; then
_error_detect "curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg"
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list
elif _check_sys ubuntu; then
_error_detect "add-apt-repository -y ppa:ondrej/php"
fi
_update_package_cache
# Install PHP packages
install_packages \
"php-common" "php${php_ver}-common" "php${php_ver}-cli" "php${php_ver}-fpm" \
"php${php_ver}-opcache" "php${php_ver}-readline" "libphp${php_ver}-embed" \
"php${php_ver}-bcmath" "php${php_ver}-gd" "php${php_ver}-imap" \
"php${php_ver}-mysql" "php${php_ver}-dba" "php${php_ver}-mongodb" \
"php${php_ver}-sybase" "php${php_ver}-pgsql" "php${php_ver}-odbc" \
"php${php_ver}-enchant" "php${php_ver}-gmp" "php${php_ver}-intl" \
"php${php_ver}-ldap" "php${php_ver}-snmp" "php${php_ver}-soap" \
"php${php_ver}-mbstring" "php${php_ver}-curl" "php${php_ver}-pspell" \
"php${php_ver}-xml" "php${php_ver}-zip" "php${php_ver}-bz2" \
"php${php_ver}-lz4" "php${php_ver}-zstd" "php${php_ver}-tidy" \
"php${php_ver}-sqlite3" "php${php_ver}-imagick" "php${php_ver}-grpc" \
"php${php_ver}-yaml" "php${php_ver}-uuid"
# Create PHP directories
_error_detect "mkdir -m770 /var/lib/php/{session,wsdlcache,opcache}"
# Install Apache PHP connector
if [[ -f "${SCRIPT_DIR}/conf/php.conf" ]]; then
_error_detect "cp -f ${SCRIPT_DIR}/conf/php.conf /etc/apache2/mods-available/"
pushd /etc/apache2/mods-enabled/ >/dev/null 2>&1
ln -sf ../mods-available/php.conf php.conf
popd >/dev/null 2>&1
fi
}
#==============================================================================
# Apache Installation
#==============================================================================
install_apache_rhel() {
# Install Apache
install_packages "httpd" "mod_ssl" "mod_http2" "mod_md" "mod_session" "mod_lua" "pcre2"
_info "Apache installation completed"
# Create directories
_error_detect "mkdir -p /data/www/default"
_error_detect "mkdir -p /data/wwwlog"
_error_detect "mkdir -p /etc/httpd/conf.d/vhost"
_error_detect "mkdir -p /etc/httpd/ssl"
# Copy configuration files
if [[ -f "${SCRIPT_DIR}/conf/httpd.conf" ]]; then
_error_detect "cp -f ${SCRIPT_DIR}/conf/httpd.conf /etc/httpd/conf/httpd.conf"
fi
if [[ -f "${SCRIPT_DIR}/conf/httpd-info.conf" ]]; then
_error_detect "cp -f ${SCRIPT_DIR}/conf/httpd-info.conf /etc/httpd/conf.d/"
fi
if [[ -f "${SCRIPT_DIR}/conf/ssl_rpm.conf" ]]; then
_error_detect "cp -f ${SCRIPT_DIR}/conf/ssl_rpm.conf /etc/httpd/conf.d/ssl.conf"
fi
if [[ -f "${SCRIPT_DIR}/conf/vhosts.conf" ]]; then
_error_detect "cp -f ${SCRIPT_DIR}/conf/vhosts.conf /etc/httpd/conf.d/"
fi
if [[ -f "${SCRIPT_DIR}/conf/welcome.conf" ]]; then
_error_detect "cp -f ${SCRIPT_DIR}/conf/welcome.conf /etc/httpd/conf.d/"
fi
# Create default virtual host
cat > "/etc/httpd/conf.d/vhost/_default.conf" << EOF
<VirtualHost _default_:80>
ServerName localhost
DocumentRoot /data/www/default
<Directory /data/www/default>
SetOutputFilter DEFLATE
Options FollowSymLinks
AllowOverride All
Order Deny,Allow
Allow from All
DirectoryIndex index.html index.htm index.php
</Directory>
</VirtualHost>
EOF
}
install_apache_deb() {
# Install Apache
install_packages "apache2" "libapache2-mod-perl2" "libapache2-mod-md"
manage_apache_service "stop" "false"
_error_detect "a2enmod ssl http2 brotli data unique_id vhost_alias echo expires info"
_error_detect "a2enmod headers perl rewrite lua request md mime_magic remoteip userdir"
_error_detect "a2enmod auth*"
_error_detect "a2enmod cache*"
_error_detect "a2enmod dav*"
_error_detect "a2enmod proxy*"
_error_detect "a2enmod session*"
sed -i "s@^ServerTokens.*@ServerTokens Minimal@" "/etc/apache2/conf-available/security.conf"
sed -i "s@^ServerSignature.*@ServerSignature Off@" "/etc/apache2/conf-available/security.conf"
_info "Apache installation completed"
# Create directories
_error_detect "mkdir -p /data/www/default"
_error_detect "mkdir -p /data/wwwlog"
_error_detect "mkdir -p /etc/apache2/ssl"
# Copy configuration files
if [[ -f "${SCRIPT_DIR}/conf/apache2.conf" ]]; then
_error_detect "cp -f ${SCRIPT_DIR}/conf/apache2.conf /etc/apache2/apache2.conf"
fi
if [[ -f "${SCRIPT_DIR}/conf/ssl_deb.conf" ]]; then
_error_detect "cp -f ${SCRIPT_DIR}/conf/ssl_deb.conf /etc/apache2/mods-available/ssl.conf"
fi
# Create default virtual host
cat > "/etc/apache2/sites-available/000-default.conf" << EOF
<VirtualHost _default_:80>
ServerName localhost
DocumentRoot /data/www/default
<Directory /data/www/default>
SetOutputFilter DEFLATE
Options FollowSymLinks
AllowOverride All
Order Deny,Allow
Allow from All
DirectoryIndex index.html index.htm index.php
</Directory>
</VirtualHost>
EOF
}
# Unified package installation function
# Usage: install_packages "pkg1" "pkg2" ...
install_packages() {
local packages=("$@")
if [[ ${#packages[@]} -eq 0 ]]; then
return 0
fi
case "${PM_TYPE}" in
dnf)
_error_detect "dnf install -y ${packages[*]}"
;;
apt)
_error_detect "DEBIAN_FRONTEND=noninteractive apt-get install -y ${packages[*]}"
;;
*)
_error "Unknown package manager: ${PM_TYPE}"
;;
esac
}
# Unified Apache service management
# Usage: manage_apache_service <action> [is_rhel]
# action: start, stop, restart, enable, status
manage_apache_service() {
local action="$1"
local is_rhel="${2:-false}"
local service_name
if [[ "${is_rhel}" == "true" ]]; then
service_name="httpd"
else
service_name="apache2"
fi
case "${action}" in
start|stop|restart|enable)
_error_detect "systemctl ${action} ${service_name}"
;;
status)
_info "systemctl --no-pager -l status ${service_name}"
systemctl --no-pager -l status "${service_name}" || true
;;
*)
_error "Unknown Apache service action: ${action}"
;;
esac
}
install_apache() {
local is_rhel="$1"
if [[ "${is_rhel}" == "true" ]]; then
install_apache_rhel
else
install_apache_deb
fi
# Copy default files
if [[ -f "${SCRIPT_DIR}/conf/favicon.ico" ]]; then
_error_detect "cp -f ${SCRIPT_DIR}/conf/favicon.ico /data/www/default/"
fi
if [[ -f "${SCRIPT_DIR}/conf/index.html" ]]; then
_error_detect "cp -f ${SCRIPT_DIR}/conf/index.html /data/www/default/"
fi
if [[ -f "${SCRIPT_DIR}/conf/lamp.png" ]]; then
_error_detect "cp -f ${SCRIPT_DIR}/conf/lamp.png /data/www/default/"
fi
if [[ -f "${SCRIPT_DIR}/conf/phpinfo.php" ]]; then
_error_detect "cp -f ${SCRIPT_DIR}/conf/phpinfo.php /data/www/default/"
fi
_info "Apache configuration completed"
}
#==============================================================================
# phpMyAdmin Installation
#==============================================================================
install_phpmyadmin() {
local db_pass="$1"
_error_detect "wget -qO pma.tar.gz https://dl.lamp.sh/files/pma.tar.gz"
_error_detect "tar zxf pma.tar.gz -C /data/www/default/"
_error_detect "rm -f pma.tar.gz"
# Import phpMyAdmin tables
if [[ -f "/data/www/default/pma/sql/create_tables.sql" ]]; then
/usr/bin/mariadb -uroot -p"${db_pass}" 2>/dev/null < /data/www/default/pma/sql/create_tables.sql || \
_warn "Failed to import phpMyAdmin tables"
fi
_info "phpMyAdmin installation completed"
}
#==============================================================================
# MariaDB Installation
#==============================================================================
install_mariadb() {
local mariadb_ver="$1"
# shellcheck disable=SC2034
local -n cnf_path_ref="$2"
_info "Setting up MariaDB repository"
_error_detect "curl -sLo mariadb_repo_setup.sh https://dl.lamp.sh/files/mariadb_repo_setup.sh"
_error_detect "chmod +x mariadb_repo_setup.sh"
# Fix crypto policy for RHEL 10
if get_rhelversion 10 && _exists "update-crypto-policies"; then
_error_detect "update-crypto-policies --set LEGACY"
fi
_info "Executing: ./mariadb_repo_setup.sh --mariadb-server-version=mariadb-${mariadb_ver}"
./mariadb_repo_setup.sh --mariadb-server-version="mariadb-${mariadb_ver}" &>/dev/null || \
_warn "MariaDB repo setup may have issues"
_error_detect "rm -f mariadb_repo_setup.sh"
if _check_sys rhel; then
_error_detect "dnf config-manager --disable mariadb-maxscale"
install_packages "MariaDB-common" "MariaDB-server" "MariaDB-client" "MariaDB-shared" "MariaDB-backup"
cnf_path_ref="/etc/my.cnf.d/server.cnf"
elif _check_sys debian || _check_sys ubuntu; then
if [[ -f "/etc/apt/sources.list.d/mariadb.list" ]]; then
sed -i 's|^deb \[arch=amd64,arm64\] https://dlm.mariadb.com/repo/maxscale/latest/apt|#&|' /etc/apt/sources.list.d/mariadb.list
fi
_update_package_cache
install_packages "mariadb-common" "mariadb-server" "mariadb-client" "mariadb-backup"
# shellcheck disable=SC2034
cnf_path_ref="/etc/mysql/mariadb.conf.d/50-server.cnf"
fi
_info "MariaDB installation completed"
}
#==============================================================================
# PHP Installation
#==============================================================================
install_php() {
local php_ver="$1"
local -n php_conf_ref="$2"
local -n php_ini_ref="$3"
local -n php_fpm_ref="$4"
local -n sock_location_ref="$5"
local is_rhel="false"
if _check_sys rhel; then
is_rhel="true"
php_conf_ref="/etc/php-fpm.d/www.conf"
php_ini_ref="/etc/php.ini"
php_fpm_ref="php-fpm"
sock_location_ref="/var/lib/mysql/mysql.sock"
configure_php_rhel "${php_ver}"
elif _check_sys debian || _check_sys ubuntu; then
php_conf_ref="/etc/php/${php_ver}/fpm/pool.d/www.conf"
php_ini_ref="/etc/php/${php_ver}/fpm/php.ini"
# shellcheck disable=SC2034
php_fpm_ref="php${php_ver}-fpm"
sock_location_ref="/run/mysqld/mysqld.sock"
configure_php_deb "${php_ver}"
fi
_info "PHP installation completed"
configure_php_settings "${php_conf_ref}" "${php_ini_ref}" "${sock_location_ref}" "${is_rhel}"
}
#==============================================================================
# Permissions Setup
#==============================================================================
set_permissions() {
local is_rhel="$1"
if [[ "${is_rhel}" == "true" ]]; then
_error_detect "chown -R apache:apache /data/www /data/wwwlog"
_error_detect "chown root:apache /var/lib/php/{session,wsdlcache,opcache}"
else
_error_detect "chown -R www-data:www-data /data/www /data/wwwlog"
_error_detect "chown root:www-data /var/lib/php/{session,wsdlcache,opcache}"
fi
}
#==============================================================================
# Main Function - Refactored into Phase Functions
#==============================================================================
# Phase 1: Check prerequisites
_check_prerequisites() {
# Check root
if [[ ${EUID} -ne 0 ]]; then
_red "This script must be run as root!\n"
exit 1
fi
# Initialize log
mkdir -p "$(dirname "${LOG_FILE}")"
if [[ ! -f "${LOG_FILE}" ]]; then
touch "${LOG_FILE}"
fi
chmod 600 "${LOG_FILE}"
# Detect package manager
_detect_package_manager
}
# Phase 2: Show banner
_show_banner() {
_info "+-------------------------------------------+"
_info "| LAMP Installation, Written by Teddysun |"
_info "+-------------------------------------------+"
_info "OS: $(_get_opsy)"
_info "Arch: $(uname -m)"
_info "Kernel: $(uname -r)"
_info "Starting LAMP installation script"
}
# Phase 3: Check OS support
_check_os_support() {
local supported="false"
is_rhel="false"
if _check_sys rhel; then
for ver in 8 9 10; do
get_rhelversion "${ver}" && supported="true" && break
done
is_rhel="true"
fi
if _check_sys debian; then
for ver in 11 12 13; do
get_debianversion "${ver}" && supported="true" && break
done
fi
if _check_sys ubuntu; then
for ver in 20.04 22.04 24.04; do
get_ubuntuversion "${ver}" && supported="true" && break
done
fi
[[ "${supported}" == "false" ]] && _error "Unsupported OS. Please use Enterprise Linux 8+, Debian 11+, or Ubuntu 20.04+"
}
# Phase 4: Get user input
_get_user_input() {
select_mariadb_version
read_db_password
select_php_version
_info "---------------------------"
_info "MariaDB version: $(_green "${mariadb_ver}")"
_info "PHP version: $(_green "${php_ver}")"
_info "---------------------------"
}
# Phase 5: Confirm installation
_confirm_installation() {
_info "Press any key to start installation, or Ctrl+C to cancel"
get_char > /dev/null
}
# Phase 6: Run system initialization
_run_system_init() {
_info "Starting system initialization"
configure_bbr
configure_journald
initialize_system
echo
netstat -nxtulpe 2>/dev/null || ss -tunlp
echo
_info "System initialization completed"
sleep 3
clear
}
# Phase 7: Install LAMP stack
_install_lamp_stack() {
local is_rhel="$1"
_info "Starting LAMP (Linux + Apache + MariaDB + PHP) installation"
# Install Apache
install_apache "${is_rhel}"
# Install MariaDB
local mariadb_cnf
install_mariadb "${mariadb_ver}" mariadb_cnf
configure_mariadb "${db_pass}" "${mariadb_cnf}"
# Setup Debian maintenance credentials
if _check_sys debian || _check_sys ubuntu; then
setup_debian_cnf "${db_pass}"
fi
# Install phpMyAdmin
install_phpmyadmin "${db_pass}"
# Install PHP
local php_conf php_ini php_fpm sock_location
install_php "${php_ver}" php_conf php_ini php_fpm sock_location
# Set permissions
set_permissions "${is_rhel}"
}
# Phase 8: Install lamp helper
_install_lamp_helper() {
# Install lamp helper script
if [[ -f "${SCRIPT_DIR}/conf/lamp" ]]; then
_error_detect "cp -f ${SCRIPT_DIR}/conf/lamp /usr/bin/"
_error_detect "chmod +x /usr/bin/lamp"
fi
}
# Phase 9: Start and enable services
_start_and_enable_services() {
local is_rhel="$1"
local php_fpm="$2"
_error_detect "systemctl daemon-reload"
_error_detect "systemctl start ${php_fpm}"
manage_apache_service "start" "${is_rhel}"
sleep 3
_error_detect "systemctl restart ${php_fpm}"
_error_detect "systemctl restart mariadb"
manage_apache_service "restart" "${is_rhel}"
sleep 1
# Enable services
_error_detect "systemctl enable mariadb"
_error_detect "systemctl enable ${php_fpm}"
manage_apache_service "enable" "${is_rhel}"
# Clean up
pkill -9 gpg-agent 2>/dev/null || true
}
# Phase 10: Show final status
_show_final_status() {
local is_rhel="$1"
local php_fpm="$2"
echo
_info "systemctl --no-pager -l status mariadb"
systemctl --no-pager -l status mariadb || true
_info "systemctl --no-pager -l status ${php_fpm}"
systemctl --no-pager -l status "${php_fpm}" || true
manage_apache_service "status" "${is_rhel}"
echo
_info "netstat -nxtulpe"
netstat -nxtulpe 2>/dev/null || ss -tunlp
echo
_info "LAMP (Linux + Apache + MariaDB + PHP) installation completed"
_info "Intro: $(_green "https://github.com/teddysun/lamp")"
_info "Log file: ${LOG_FILE}"
}
# Main entry point
main() {
# Phase 1: Check prerequisites
_check_prerequisites
# Phase 2: Show banner
_show_banner
# Phase 3: Check OS support
_check_os_support
# Phase 4: Get user input
_get_user_input
# Phase 5: Confirm installation
_confirm_installation
# Phase 6: Run system initialization
_run_system_init
# Phase 7: Install LAMP stack
_install_lamp_stack "${is_rhel}"
# Phase 8: Install lamp helper
_install_lamp_helper
# Phase 9: Start and enable services
_start_and_enable_services "${is_rhel}" "${php_fpm}"
# Phase 10: Show final status
_show_final_status "${is_rhel}" "${php_fpm}"
}
# Run main function
main "$@"