diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7ba29375..5383a97f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -28,6 +28,7 @@ jobs: sed -i "s/AUTO_REMOVE=false/AUTO_REMOVE=true/g" .env sed -i "s/FORCE_REMOVE=false/FORCE_REMOVE=true/g" .env sed -i "s/LEMPER_ADMIN_EMAIL=\"mail@example.com\"/LEMPER_ADMIN_EMAIL=\"me@masedi.net\"/g" .env + sed -i "s/NGINX_INSTALLER="source"/NGINX_INSTALLER="repo"/g" .env sed -i "s/INSTALL_PHP_LOADER=false/INSTALL_PHP_LOADER=true/g" .env sed -i "s/PHP_LOADER=\"none\"/PHP_LOADER=\"ioncube\"/g" .env sed -i "s/IMAGEMAGICK_INSTALLER=\"source\"/IMAGEMAGICK_INSTALLER=\"repo\"/g" .env diff --git a/README.md b/README.md index ed25ef69..ddbcc141 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # [L]inux [E]ngine-X [M]ariaDB [P]HP Install[ER]

- Serverd by LEMPer Stack © @joglomedia + Served by LEMPer Stack © @joglomedia

@@ -14,7 +14,7 @@

-LEMPer stands for Linux, Engine-X (Nginx), MariaDB and PHP installer written in Bash script. This is just a small toolset (a bunch collection of scripts) that usually I use to deploy and manage LEMP stack on Debian/Ubuntu server. LEMPer is crafted to support wide-range PHP framework & CMS. It is available as Free Alternative to control panel such as cPanel, Plesk, CloudWays, Ploi, RunCloud, ServerPilot, etc. +LEMPer stands for Linux, Engine-X (Nginx), MariaDB and PHP installer written in Bash script, also known as LEMP / LNMP installer. This is just a small toolset (a bunch collection of scripts) that I use to deploy and manage LEMP stack on Debian and Ubuntu server. LEMPer is crafted to support wide-range PHP framework & CMS. It is available as Free Alternative to the paid control panel such as cPanel, Plesk, CloudWays, Ploi, RunCloud, ServerPilot, etc.

## Features @@ -27,7 +27,7 @@ LEMPer stands for Linux, Engine-X (Nginx), MariaDB and PHP installer written in * Support HTTP/2 natively for your secure website. * Free SSL certificates from [Let's Encrypt](https://letsencrypt.org/). * Get an A+ grade on several SSL Security Test ([Qualys SSL Labs](https://www.ssllabs.com/ssltest/analyze.html?d=masedi.net), [ImmuniWeb](https://www.immuniweb.com/ssl/?id=bVrykFnK), and Wormly). -* Multiple PHP versions 5.6 [EOL], 7.0 [EOL], 7.1 [EOL], 7.2 [EOL], 7.3 [SFO], 7.4, 8.0 from [Ondrej's repository](https://launchpad.net/~ondrej/+archive/ubuntu/php). +* Multiple PHP versions 5.6 [EOL], 7.0 [EOL], 7.1 [EOL], 7.2 [EOL], 7.3 [SFO], 7.4, 8.0, 8.1 from [Ondrej's repository](https://launchpad.net/~ondrej/+archive/ubuntu/php). * Run PHP as user who own the file (Multi-user isolation via FPM pool). Feel the faster Nginx with secure multi-user environment like a top-notch shared hosting. * Supported PHP Framework and CMS: * Vanilla PHP: default, @@ -40,6 +40,7 @@ LEMPer stands for Linux, Engine-X (Nginx), MariaDB and PHP installer written in * NoSQL database with MongoDB. * In-memory database with Redis. * Memory cache with Memcached. +* FTP server with VSFTPD or Pure-FTPd. * [Adminer](https://www.adminer.org/) web-based SQL & MongoDB database manager (PhpMyAdmin replacement). * [phpRedisAdmin](https://github.com/erikdubbelboer/phpRedisAdmin) web-based Redis database manager. * [phpMemcachedAdmin](https://github.com/elijaa/phpmemcachedadmin) web-based Memcached manager. @@ -124,23 +125,23 @@ sudo lemper-cli manage --help sudo lemper-cli help ``` -Note: Lemper CLI will automagically add a new PHP-FPM user's pool configuration if it doesn't exists. You must add the user account first. +Note: LEMPer CLI automagically add a new PHP-FPM user's pool configuration if it doesn't exists. You must add the user account first. ### Web-based Administration -You can access pre-installed web-based administration tools here +You can access pre-installed web-based administration tools here. ```bash http://YOUR_IP_ADDRESS:8082/lcp/ ``` -Adminer (Web-based SQL database managemer) +Adminer (Web-based SQL database manager) ```bash http://YOUR_DOMAIN_NAME:8082/lcp/dbadmin ``` -TinyFileManager (Web-based file managemer) +TinyFileManager (Web-based file manager) ```bash http://YOUR_DOMAIN_NAME:8082/lcp/filemanager @@ -161,7 +162,7 @@ Add your feature [request here](https://github.com/joglomedia/LEMPer/issues/new) ## Security Vulnerabilities and Bugs -If you discover any security vulnerability or any bug within _LEMPer Stack_, please open an issue. +If you discover any security vulnerability or any bug within _LEMPer Stack_, please open an [issue](https://github.com/joglomedia/LEMPer/issues/new). ## Contributing @@ -204,16 +205,16 @@ Made with [contributors-img](https://contrib.rocks). ### Financial Contributors -You can support us using any of the methods below: +You can support development by using any of the methods below: **[Buy Me a Bottle of Milk or a Cup of Coffee](https://paypal.me/masedi) !!** ## Licence -LEMPer stack is open-source project licensed under the GNU GPLv3 license. +LEMPer Stack is open-source project licensed under the GNU GPLv3 license. ## Copyright -(c) 2014-2021 | [MasEDI.Net](https://masedi.net/) +(c) 2014-2022 | [MasEDI.Net](https://masedi.net/) ### Enjoy LEMPer Stack ;) diff --git a/bin/lemper-cli.sh b/bin/lemper-cli.sh index d56747f5..3f226017 100644 --- a/bin/lemper-cli.sh +++ b/bin/lemper-cli.sh @@ -21,13 +21,108 @@ set -e PROG_NAME=$(basename "$0") PROG_VER="2.x.x" -# May need to run this as sudo! -if [[ "$(id -u)" -ne 0 ]]; then - echo "This command can only be run by root." +# Test mode. +DRYRUN=false + +# Color decorator. +RED=91 +GREEN=92 +YELLOW=93 + +## +# Helper Functions. +## +function begin_color() { + color="${1}" + echo -e -n "\e[${color}m" +} + +function end_color() { + echo -e -n "\e[0m" +} + +function echo_color() { + color="${1}" + shift + begin_color "${color}" + echo "$@" + end_color +} + +function status() { + echo_color "${GREEN}" "$@" +} + +function warning() { + echo_color "${YELLOW}" "$@" +} + +function success() { + echo_color "${GREEN}" -n "Success: " >&2 + echo "$@" >&2 +} + +function info() { + echo_color "${YELLOW}" -n "Info: " >&2 + echo "$@" >&2 +} + +function error() { + echo_color "${RED}" -n "Error: " >&2 + echo "$@" >&2 +} + +# Prints an error message and exits with an error code. +function fail() { + error "$@" + #echo >&2 + echo "For usage information, run this script with --help" >&2 exit 1 -fi +} + +function echo_ok() { + echo_color "${GREEN}" "OK" +} + +function echo_warn() { + echo_color "${YELLOW}" "WARN" +} + +function echo_err() { + echo_color "${RED}" "ERROR" +} -# Export LEMPer stack configuration. +# Run command. +function run() { + if "${DRYRUN}"; then + echo_color "${YELLOW}" -n "would run " + echo "$@" + else + if ! "$@"; then + local CMDSTR="$*" + error "Failure running '${CMDSTR}', exiting." + exit 1 + fi + fi +} + +# Make sure only root can run this script. +function requires_root() { + if [[ "$(id -u)" -ne 0 ]]; then + if ! hash sudo 2>/dev/null; then + echo "${PROG_NAME} command must be run as 'root' or with sudo." + exit 1 + else + #echo "Switching to root user to run this script." + sudo -E "$0" "$@" + exit 0 + fi + fi +} + +requires_root "$@" + +# Export LEMPer Stack configuration. if [[ -f "/etc/lemper/lemper.conf" ]]; then # Clean environemnt first. # shellcheck source=/etc/lemper/lemper.conf @@ -39,8 +134,8 @@ if [[ -f "/etc/lemper/lemper.conf" ]]; then # shellcheck disable=SC1091 source <(grep -v '^#' /etc/lemper/lemper.conf | grep -v '^\[' | sed -E 's|^(.+)=(.*)$|: ${\1=\2}; export \1|g') else - echo "LEMPer stack configuration required, but the file doesn't exist." - echo "It should be created during installation process and placed under '/etc/lemper/lemper.conf'" + echo "LEMPer Stack configuration required, but the file doesn't exist." + echo "It should be created during installation process and placed under '/etc/lemper/lemper.conf'." exit 1 fi @@ -49,13 +144,13 @@ LEMPER_USERNAME=${LEMPER_USERNAME:-"lemper"} LEMPER_PASSWORD=${LEMPER_PASSWORD:-""} MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-""} -# CLI plugins directory. +# Set CLI plugins directory. CLI_PLUGINS_DIR="/etc/lemper/cli-plugins" ## # Show usage # output to STDERR. -# +## function cmd_help() { cat <<- EOL ${PROG_NAME} ${PROG_VER} @@ -76,19 +171,19 @@ EOL ## # Show version. -# +## function cmd_version() { echo "${PROG_NAME} version $PROG_VER" } ## -# Main LEMPer CLI Wrapper -# +# Main LEMPer CLI Wrapper. +## function init_lemper_cli() { # Check command line arguments. if [[ -n "${1}" ]]; then CMD="${1}" - shift # Pass the remaining arguments to the next function. + shift # Pass the remaining arguments as sub-command options (parameters). case ${CMD} in help | -h | --help) @@ -101,7 +196,9 @@ function init_lemper_cli() { ;; *) if [[ -x "${CLI_PLUGINS_DIR}/lemper-${CMD}" ]]; then - "${CLI_PLUGINS_DIR}/lemper-${CMD}" "$@" + # Source the plugin executable file. + # shellcheck disable=SC1090 + . "${CLI_PLUGINS_DIR}/lemper-${CMD}" "$@" exit 0 else echo "${PROG_NAME}: '${CMD}' is not ${PROG_NAME} command." diff --git a/etc/logrotate.d/php5.6-fpm b/etc/logrotate.d/php5.6-fpm new file mode 100644 index 00000000..c5d39d40 --- /dev/null +++ b/etc/logrotate.d/php5.6-fpm @@ -0,0 +1,13 @@ +/var/log/php5.6-fpm.log /home/*/logs/php/php5.6-fpm.log { + rotate 12 + weekly + missingok + notifempty + compress + delaycompress + postrotate + if [ -x /usr/lib/php/php5.6-fpm-reopenlogs ]; then + /usr/lib/php/php5.6-fpm-reopenlogs; + fi + endscript +} \ No newline at end of file diff --git a/etc/logrotate.d/php7.0-fpm b/etc/logrotate.d/php7.0-fpm new file mode 100644 index 00000000..2b264fbe --- /dev/null +++ b/etc/logrotate.d/php7.0-fpm @@ -0,0 +1,13 @@ +/var/log/php7.0-fpm.log /home/*/logs/php/php7.0-fpm.log { + rotate 12 + weekly + missingok + notifempty + compress + delaycompress + postrotate + if [ -x /usr/lib/php/php7.0-fpm-reopenlogs ]; then + /usr/lib/php/php7.0-fpm-reopenlogs; + fi + endscript +} \ No newline at end of file diff --git a/etc/logrotate.d/php7.1-fpm b/etc/logrotate.d/php7.1-fpm new file mode 100644 index 00000000..647f6850 --- /dev/null +++ b/etc/logrotate.d/php7.1-fpm @@ -0,0 +1,13 @@ +/var/log/php7.1-fpm.log /home/*/logs/php/php7.1-fpm.log { + rotate 12 + weekly + missingok + notifempty + compress + delaycompress + postrotate + if [ -x /usr/lib/php/php7.1-fpm-reopenlogs ]; then + /usr/lib/php/php7.1-fpm-reopenlogs; + fi + endscript +} \ No newline at end of file diff --git a/etc/logrotate.d/php7.2-fpm b/etc/logrotate.d/php7.2-fpm new file mode 100644 index 00000000..3097e5b6 --- /dev/null +++ b/etc/logrotate.d/php7.2-fpm @@ -0,0 +1,13 @@ +/var/log/php7.2-fpm.log /home/*/logs/php/php7.2-fpm.log { + rotate 12 + weekly + missingok + notifempty + compress + delaycompress + postrotate + if [ -x /usr/lib/php/php7.2-fpm-reopenlogs ]; then + /usr/lib/php/php7.2-fpm-reopenlogs; + fi + endscript +} \ No newline at end of file diff --git a/etc/logrotate.d/php7.3-fpm b/etc/logrotate.d/php7.3-fpm new file mode 100644 index 00000000..e147f134 --- /dev/null +++ b/etc/logrotate.d/php7.3-fpm @@ -0,0 +1,13 @@ +/var/log/php7.3-fpm.log /home/*/logs/php/php7.3-fpm.log { + rotate 12 + weekly + missingok + notifempty + compress + delaycompress + postrotate + if [ -x /usr/lib/php/php7.3-fpm-reopenlogs ]; then + /usr/lib/php/php7.3-fpm-reopenlogs; + fi + endscript +} \ No newline at end of file diff --git a/etc/logrotate.d/php7.4-fpm b/etc/logrotate.d/php7.4-fpm new file mode 100644 index 00000000..fe39a320 --- /dev/null +++ b/etc/logrotate.d/php7.4-fpm @@ -0,0 +1,13 @@ +/var/log/php7.4-fpm.log /home/*/logs/php/php7.4-fpm.log { + rotate 12 + weekly + missingok + notifempty + compress + delaycompress + postrotate + if [ -x /usr/lib/php/php7.4-fpm-reopenlogs ]; then + /usr/lib/php/php7.4-fpm-reopenlogs; + fi + endscript +} \ No newline at end of file diff --git a/etc/logrotate.d/php8.0-fpm b/etc/logrotate.d/php8.0-fpm new file mode 100644 index 00000000..f1a4a884 --- /dev/null +++ b/etc/logrotate.d/php8.0-fpm @@ -0,0 +1,13 @@ +/var/log/php8.0-fpm.log /home/*/logs/php/php8.0-fpm.log { + rotate 12 + weekly + missingok + notifempty + compress + delaycompress + postrotate + if [ -x /usr/lib/php/php8.0-fpm-reopenlogs ]; then + /usr/lib/php/php8.0-fpm-reopenlogs; + fi + endscript +} \ No newline at end of file diff --git a/etc/logrotate.d/php8.1-fpm b/etc/logrotate.d/php8.1-fpm new file mode 100644 index 00000000..d6003549 --- /dev/null +++ b/etc/logrotate.d/php8.1-fpm @@ -0,0 +1,13 @@ +/var/log/php8.1-fpm.log /home/*/logs/php/php8.1-fpm.log { + rotate 12 + weekly + missingok + notifempty + compress + delaycompress + postrotate + if [ -x /usr/lib/php/php8.1-fpm-reopenlogs ]; then + /usr/lib/php/php8.1-fpm-reopenlogs; + fi + endscript +} diff --git a/etc/nginx/sites-available/default b/etc/nginx/sites-available/default index 018586aa..6dec3964 100644 --- a/etc/nginx/sites-available/default +++ b/etc/nginx/sites-available/default @@ -35,19 +35,15 @@ server { auth_basic_user_file /srv/.htpasswd; } - location ~ \.(php|php74)$ { + location ~ \.php81$ { try_files $uri =404; - fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_index index.php; - include /etc/nginx/fastcgi_params; include /etc/nginx/includes/fastcgi.conf; - # Uncomment to Enable PHP FastCGI cache. #include /etc/nginx/includes/fastcgi_cache.conf; - - fastcgi_pass unix:/run/php/php7.4-fpm.sock; + fastcgi_pass unix:/run/php/php8.1-fpm.sock; } location ~ \.php80$ { @@ -60,6 +56,17 @@ server { fastcgi_pass unix:/run/php/php8.0-fpm.sock; } + location ~ \.(php|php74)$ { + try_files $uri =404; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_index index.php; + include /etc/nginx/fastcgi_params; + include /etc/nginx/includes/fastcgi.conf; + # Uncomment to Enable PHP FastCGI cache. + #include /etc/nginx/includes/fastcgi_cache.conf; + fastcgi_pass unix:/run/php/php7.4-fpm.sock; + } + location ~ \.php73$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; @@ -111,7 +118,6 @@ server { } #include /etc/nginx/includes/error_pages.conf; - #include /etc/nginx/includes/fcgiwrap.conf; } @@ -143,7 +149,6 @@ server { auth_basic "Denied"; auth_basic_user_file /srv/.htpasswd; - # Pass the PHP scripts to FastCGI with basic auth. location ~ \.php$ { try_files $uri =404; @@ -156,25 +161,18 @@ server { # Uncomment to Enable PHP FastCGI cache. #include /etc/nginx/includes/fastcgi_cache.conf; - fastcgi_pass unix:/run/php/php7.3-fpm.sock; + fastcgi_pass unix:/run/php/php7.4-fpm.sock; } } - # Pass the PHP scripts to FastCGI server listening on Unix socket. - # - location ~ \.(php|php74)$ { + location ~ \.php81$ { try_files $uri =404; - fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_index index.php; - include /etc/nginx/fastcgi_params; include /etc/nginx/includes/fastcgi.conf; - - # Uncomment to Enable PHP FastCGI cache. #include /etc/nginx/includes/fastcgi_cache.conf; - - fastcgi_pass unix:/run/php/php7.4-fpm.sock; + fastcgi_pass unix:/run/php/php8.1-fpm.sock; } location ~ \.php80$ { @@ -187,6 +185,16 @@ server { fastcgi_pass unix:/run/php/php8.0-fpm.sock; } + location ~ \.(php|php74)$ { + try_files $uri =404; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_index index.php; + include /etc/nginx/fastcgi_params; + include /etc/nginx/includes/fastcgi.conf; + #include /etc/nginx/includes/fastcgi_cache.conf; + fastcgi_pass unix:/run/php/php7.4-fpm.sock; + } + location ~ \.php73$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; @@ -238,7 +246,6 @@ server { } #include /etc/nginx/includes/error_pages.conf; - #include /etc/nginx/includes/fcgiwrap.conf; } diff --git a/etc/php/5.6/fpm/pool.d/lemper.conf b/etc/php/5.6/fpm/pool.d/lemper.conf index d2b53e4d..3d50be59 100644 --- a/etc/php/5.6/fpm/pool.d/lemper.conf +++ b/etc/php/5.6/fpm/pool.d/lemper.conf @@ -18,7 +18,7 @@ pm.max_spare_servers = 3 pm.process_idle_timeout = 30s; pm.max_requests = 500 -slowlog = /var/log/php/php5.6-fpm_slow.$pool.log +slowlog = /home/lemper/logs/php/php5.6-fpm_slow.log request_slowlog_timeout = 10s ;chroot = /home/lemper @@ -34,7 +34,8 @@ php_flag[display_errors] = On ;php_admin_value[error_reporting] = E_ALL & ~E_DEPRECATED & ~E_STRICT ;php_admin_value[disable_functions] = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,exec,passthru,popen,proc_open,shell_exec,system php_admin_flag[log_errors] = On -php_admin_value[error_log] = /var/log/php/php5.6-fpm.$pool.log +;php_admin_value[error_log] = /var/log/php/php5.6-fpm.$pool.log +php_admin_value[error_log] = /home/lemper/logs/php/php5.6-fpm.log php_admin_value[date.timezone] = UTC php_admin_value[memory_limit] = 128M php_admin_value[opcache.file_cache] = /home/lemper/.lemper/php/opcache diff --git a/etc/php/7.0/fpm/pool.d/lemper.conf b/etc/php/7.0/fpm/pool.d/lemper.conf index ad1df500..923f3e2e 100644 --- a/etc/php/7.0/fpm/pool.d/lemper.conf +++ b/etc/php/7.0/fpm/pool.d/lemper.conf @@ -21,7 +21,7 @@ pm.max_requests = 500 pm.status_path = /status ping.path = /ping -slowlog = /var/log/php/php7.0-fpm_slow.$pool.log +slowlog = /home/lemper/logs/php/php7.0-fpm_slow.log request_slowlog_timeout = 10s ;chroot = /home/lemper @@ -37,7 +37,8 @@ php_flag[display_errors] = On ;php_admin_value[error_reporting] = E_ALL & ~E_DEPRECATED & ~E_STRICT ;php_admin_value[disable_functions] = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,exec,passthru,popen,proc_open,shell_exec,system php_admin_flag[log_errors] = On -php_admin_value[error_log] = /var/log/php/php7.0-fpm.$pool.log +;php_admin_value[error_log] = /var/log/php/php8.1-fpm.$pool.log +php_admin_value[error_log] = /home/lemper/logs/php/php7.0-fpm.log php_admin_value[date.timezone] = UTC php_admin_value[memory_limit] = 128M php_admin_value[opcache.file_cache] = /home/lemper/.lemper/php/opcache diff --git a/etc/php/7.1/fpm/pool.d/lemper.conf b/etc/php/7.1/fpm/pool.d/lemper.conf index 447370ec..08a919be 100644 --- a/etc/php/7.1/fpm/pool.d/lemper.conf +++ b/etc/php/7.1/fpm/pool.d/lemper.conf @@ -21,7 +21,7 @@ pm.max_requests = 500 pm.status_path = /status ping.path = /ping -slowlog = /var/log/php/php7.1-fpm_slow.$pool.log +slowlog = /home/lemper/logs/php/php7.1-fpm_slow.log request_slowlog_timeout = 10s ;chroot = /home/lemper @@ -37,7 +37,8 @@ php_flag[display_errors] = On ;php_admin_value[error_reporting] = E_ALL & ~E_DEPRECATED & ~E_STRICT ;php_admin_value[disable_functions] = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,exec,passthru,popen,proc_open,shell_exec,system php_admin_flag[log_errors] = On -php_admin_value[error_log] = /var/log/php/php7.1-fpm.$pool.log +;php_admin_value[error_log] = /var/log/php/php7.1-fpm.$pool.log +php_admin_value[error_log] = /home/lemper/logs/php/php7.1-fpm.log php_admin_value[date.timezone] = UTC php_admin_value[memory_limit] = 128M php_admin_value[opcache.file_cache] = /home/lemper/.lemper/php/opcache diff --git a/etc/php/7.2/fpm/pool.d/lemper.conf b/etc/php/7.2/fpm/pool.d/lemper.conf index fef28b51..c876cb85 100644 --- a/etc/php/7.2/fpm/pool.d/lemper.conf +++ b/etc/php/7.2/fpm/pool.d/lemper.conf @@ -21,7 +21,7 @@ pm.max_requests = 500 pm.status_path = /status ping.path = /ping -slowlog = /var/log/php/php7.2-fpm_slow.$pool.log +slowlog = /home/lemper/logs/php/php7.2-fpm_slow.log request_slowlog_timeout = 10s ;chroot = /home/lemper @@ -37,7 +37,8 @@ php_flag[display_errors] = On ;php_admin_value[error_reporting] = E_ALL & ~E_DEPRECATED & ~E_STRICT ;php_admin_value[disable_functions] = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,exec,passthru,popen,proc_open,shell_exec,system php_admin_flag[log_errors] = On -php_admin_value[error_log] = /var/log/php/php7.2-fpm.$pool.log +;php_admin_value[error_log] = /var/log/php/php7.2-fpm.$pool.log +php_admin_value[error_log] = /home/lemper/logs/php/php7.2-fpm.log php_admin_value[date.timezone] = UTC php_admin_value[memory_limit] = 128M php_admin_value[opcache.file_cache] = /home/lemper/.lemper/php/opcache diff --git a/etc/php/7.3/fpm/pool.d/lemper.conf b/etc/php/7.3/fpm/pool.d/lemper.conf index 297bc3b5..538ed8e9 100644 --- a/etc/php/7.3/fpm/pool.d/lemper.conf +++ b/etc/php/7.3/fpm/pool.d/lemper.conf @@ -21,7 +21,7 @@ pm.max_requests = 500 pm.status_path = /status ping.path = /ping -slowlog = /var/log/php/php7.3-fpm_slow.$pool.log +slowlog = /home/lemper/logs/php/php7.3-fpm_slow.log request_slowlog_timeout = 10s ;chroot = /home/lemper @@ -37,7 +37,8 @@ php_flag[display_errors] = On ;php_admin_value[error_reporting] = E_ALL & ~E_DEPRECATED & ~E_STRICT ;php_admin_value[disable_functions] = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,exec,passthru,popen,proc_open,shell_exec,system php_admin_flag[log_errors] = On -php_admin_value[error_log] = /var/log/php/php7.3-fpm.$pool.log +;php_admin_value[error_log] = /var/log/php/php7.3-fpm.$pool.log +php_admin_value[error_log] = /home/lemper/logs/php/php7.3-fpm.log php_admin_value[date.timezone] = UTC php_admin_value[memory_limit] = 128M php_admin_value[opcache.file_cache] = /home/lemper/.lemper/php/opcache diff --git a/etc/php/7.4/fpm/pool.d/lemper.conf b/etc/php/7.4/fpm/pool.d/lemper.conf index e32a0c08..f74abd61 100644 --- a/etc/php/7.4/fpm/pool.d/lemper.conf +++ b/etc/php/7.4/fpm/pool.d/lemper.conf @@ -21,7 +21,7 @@ pm.max_requests = 500 pm.status_path = /status ping.path = /ping -slowlog = /var/log/php/php7.4-fpm_slow.$pool.log +slowlog = /home/lemper/logs/php/php7.4-fpm_slow.log request_slowlog_timeout = 10s ;chroot = /home/lemper @@ -37,7 +37,8 @@ php_flag[display_errors] = On ;php_admin_value[error_reporting] = E_ALL & ~E_DEPRECATED & ~E_STRICT ;php_admin_value[disable_functions] = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,exec,passthru,popen,proc_open,shell_exec,system php_admin_flag[log_errors] = On -php_admin_value[error_log] = /var/log/php/php7.4-fpm.$pool.log +;php_admin_value[error_log] = /var/log/php/php7.4-fpm.$pool.log +php_admin_value[error_log] = /home/lemper/logs/php/php7.4-fpm.log php_admin_value[date.timezone] = UTC php_admin_value[memory_limit] = 128M php_admin_value[opcache.file_cache] = /home/lemper/.lemper/php/opcache diff --git a/etc/php/8.0/fpm/pool.d/lemper.conf b/etc/php/8.0/fpm/pool.d/lemper.conf index eaa6c2d8..7242b11d 100644 --- a/etc/php/8.0/fpm/pool.d/lemper.conf +++ b/etc/php/8.0/fpm/pool.d/lemper.conf @@ -21,7 +21,7 @@ pm.max_requests = 500 pm.status_path = /status ping.path = /ping -slowlog = /var/log/php/php8.0-fpm_slow.$pool.log +slowlog = /home/lemper/logs/php/php8.0-fpm_slow.log request_slowlog_timeout = 10s ;chroot = /home/lemper @@ -37,7 +37,8 @@ php_flag[display_errors] = On ;php_admin_value[error_reporting] = E_ALL & ~E_DEPRECATED & ~E_STRICT ;php_admin_value[disable_functions] = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,exec,passthru,popen,proc_open,shell_exec,system php_admin_flag[log_errors] = On -php_admin_value[error_log] = /var/log/php/php8.0-fpm.$pool.log +;php_admin_value[error_log] = /var/log/php/php8.0-fpm.$pool.log +php_admin_value[error_log] = /home/lemper/logs/php/php8.0-fpm.log php_admin_value[date.timezone] = UTC php_admin_value[memory_limit] = 128M php_admin_value[opcache.file_cache] = /home/lemper/.lemper/php/opcache diff --git a/etc/php/8.1/fpm/pool.d/lemper.conf b/etc/php/8.1/fpm/pool.d/lemper.conf index f4c80aa9..131d01d1 100644 --- a/etc/php/8.1/fpm/pool.d/lemper.conf +++ b/etc/php/8.1/fpm/pool.d/lemper.conf @@ -21,7 +21,7 @@ pm.max_requests = 500 pm.status_path = /status ping.path = /ping -slowlog = /var/log/php/php8.1-fpm_slow.$pool.log +slowlog = /home/lemper/logs/php/php8.1-fpm_slow.log request_slowlog_timeout = 10s ;chroot = /home/lemper @@ -37,7 +37,8 @@ php_flag[display_errors] = On ;php_admin_value[error_reporting] = E_ALL & ~E_DEPRECATED & ~E_STRICT ;php_admin_value[disable_functions] = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,exec,passthru,popen,proc_open,shell_exec,system php_admin_flag[log_errors] = On -php_admin_value[error_log] = /var/log/php/php8.1-fpm.$pool.log +;php_admin_value[error_log] = /var/log/php/php8.1-fpm.$pool.log +php_admin_value[error_log] = /home/lemper/logs/php/php8.1-fpm.log php_admin_value[date.timezone] = UTC php_admin_value[memory_limit] = 128M php_admin_value[opcache.file_cache] = /home/lemper/.lemper/php/opcache diff --git a/install.sh b/install.sh index 1e38fb60..127fd2f9 100755 --- a/install.sh +++ b/install.sh @@ -36,7 +36,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" # Make sure only supported distribution can run this installer script. preflight_system_check diff --git a/lib/lemper-adduser.sh b/lib/lemper-adduser.sh index b0ba53c7..a923e844 100755 --- a/lib/lemper-adduser.sh +++ b/lib/lemper-adduser.sh @@ -1,4 +1,34 @@ -#!/bin/bash +#!/usr/bin/env bash + +# +-------------------------------------------------------------------------+ +# | Lemper Create - Simple LEMP Virtual Host Creator | +# +-------------------------------------------------------------------------+ +# | Copyright (c) 2014-2021 MasEDI.Net (https://masedi.net/lemper) | +# +-------------------------------------------------------------------------+ +# | This source file is subject to the GNU General Public License | +# | that is bundled with this package in the file LICENSE.md. | +# | | +# | If you did not receive a copy of the license and are unable to | +# | obtain it through the world-wide-web, please send an email | +# | to license@lemper.cloud so we can send you a copy immediately. | +# +-------------------------------------------------------------------------+ +# | Authors: Edi Septriyanto | +# +-------------------------------------------------------------------------+ + +set -e + +# Version control. +#PROG_NAME=$(basename "$0") +#PROG_VER="2.x.x" +#CMD_PARENT="lemper-cli" +#CMD_NAME="adduser" + +# Make sure only root can access and not direct access. +if ! declare -F "requires_root" &>/dev/null; then + echo "Direct access to this script is not permitted." + exit 1 +fi + function header_msg() { clear cat <<- EOL @@ -12,12 +42,6 @@ function header_msg() { EOL } -# Check if user is root -if [[ "$(id -u)" -ne 0 ]]; then - echo "Error: Please use root to add new user." - exit 1 -fi - header_msg echo -en "\nAdd new user? [y/n]: " diff --git a/lib/lemper-create.sh b/lib/lemper-create.sh index 031e97b0..ff3b3e15 100755 --- a/lib/lemper-create.sh +++ b/lib/lemper-create.sh @@ -23,79 +23,9 @@ PROG_VER="2.x.x" CMD_PARENT="lemper-cli" CMD_NAME="create" -# Color decorator. -RED=91 -GREEN=92 -YELLOW=93 - -## -# Helper Functions -# -function begin_color() { - color="${1}" - echo -e -n "\e[${color}m" -} - -function end_color() { - echo -e -n "\e[0m" -} - -function echo_color() { - color="${1}" - shift - begin_color "${color}" - echo "$@" - end_color -} - -function error() { - echo_color "${RED}" -n "Error: " >&2 - echo "$@" >&2 -} - -# Prints an error message and exits with an error code. -function fail() { - error "$@" - echo >&2 - echo "For usage information, run this script with --help" >&2 - exit 1 -} - -function status() { - echo_color "${GREEN}" "$@" -} - -function warning() { - echo_color "${YELLOW}" "$@" -} - -function success() { - echo_color "${GREEN}" -n "Success: " >&2 - echo "$@" >&2 -} - -function info() { - echo_color "${YELLOW}" -n "Info: " >&2 - echo "$@" >&2 -} - -# Run command -function run() { - if "$DRYRUN"; then - echo_color "${YELLOW}" -n "would run " - echo "$@" - else - if ! "$@"; then - local CMDSTR="$*" - error "Failure running '${CMDSTR}', exiting." - exit 1 - fi - fi -} - -# May need to run this as sudo! -if [[ "$(id -u)" -ne 0 ]]; then - error "This command can only be run by root." +# Make sure only root can access and not direct access. +if ! declare -F "requires_root" &>/dev/null; then + echo "Direct access to this script is not permitted." exit 1 fi @@ -109,7 +39,7 @@ done if [[ ${#NO_PACKAGES[@]} -gt 0 ]]; then printf -v NO_PACKAGES_STR '%s, ' "${NO_PACKAGES[@]}" - error "${PROG_NAME} requires: ${NO_PACKAGES_STR%, }, please install it first!" + error "${PROG_NAME} ${COMMAND_NAME} requires: ${NO_PACKAGES_STR%, }, please install it first!" echo "help: run 'sudo apt-get install ${NO_PACKAGES[*]}'" exit 1 fi @@ -767,7 +697,7 @@ pm.max_requests = 500 pm.status_path = /status ping.path = /ping -slowlog = /var/log/php/php${PHP_VERSION}-fpm_slow.\$pool.log +slowlog = /home/${USERNAME}/logs/php/php${PHP_VERSION}-fpm_slow.log request_slowlog_timeout = 5s chdir = /home/${USERNAME} @@ -782,7 +712,7 @@ php_flag[display_errors] = On ;php_admin_value[error_reporting] = E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_WARNING & ~E_NOTICE ;php_admin_value[disable_functions] = pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,exec,passthru,popen,proc_open,shell_exec,system php_admin_flag[log_errors] = On -php_admin_value[error_log] = /var/log/php/php${PHP_VERSION}-fpm.\$pool.log +php_admin_value[error_log] = /home/${USERNAME}/logs/php/php${PHP_VERSION}-fpm.log php_admin_value[date.timezone] = ${TIMEZONE} php_admin_value[memory_limit] = 128M php_admin_value[opcache.file_cache] = /home/${USERNAME}/.lemper/php/opcache @@ -955,7 +885,7 @@ function init_lemper_create() { ENABLE_FAIL2BAN=false TMPDIR="/tmp/lemper" - # Test mode + # Dry run (test mode). DRYRUN=false # Args counter @@ -1397,7 +1327,7 @@ function init_lemper_create() { --admin_user="${APP_ADMIN_USER}" --admin_password="${APP_ADMIN_PASS}" \ --admin_email="${APP_ADMIN_EMAIL}" --path="${WEBROOT}" && \ run sudo -u "${USERNAME}" -i -- wp-cli plugin install \ - akismet classic-editor nginx-helper redis-cache statically --activate --path="${WEBROOT}" + akismet autoptimize cache-enabler classic-editor nginx-helper redis-cache --activate --path="${WEBROOT}" fi # Install WooCommerce. @@ -1442,7 +1372,7 @@ function init_lemper_create() { --title="WordPress Multi-site Managed by LEMPer" --admin_user="${APP_ADMIN_USER}" \ --admin_password="${APP_ADMIN_PASS}" --admin_email="${APP_ADMIN_EMAIL}" --path="${WEBROOT}" && \ run sudo -u "${USERNAME}" -i -- wp-cli plugin install \ - akismet classic-editor nginx-helper redis-cache statically --activate-network --path="${WEBROOT}" + akismet autoptimize cache-enabler classic-editor nginx-helper redis-cache --activate-network --path="${WEBROOT}" fi # Mercator domain mapping. diff --git a/lib/lemper-db.sh b/lib/lemper-db.sh index f2534a50..838a213f 100755 --- a/lib/lemper-db.sh +++ b/lib/lemper-db.sh @@ -23,94 +23,20 @@ PROG_VER="2.x.x" CMD_PARENT="lemper-cli" CMD_NAME="db" -# Test mode. -DRYRUN=false - -# Color decorator. -RED=91 -GREEN=92 -YELLOW=93 - -## -# Helper Functions -# -function begin_color() { - color="${1}" - echo -e -n "\e[${color}m" -} - -function end_color() { - echo -e -n "\e[0m" -} - -function echo_color() { - color="${1}" - shift - begin_color "${color}" - echo "$@" - end_color -} - -function error() { - echo_color "${RED}" -n "Error: " >&2 - echo "$@" >&2 -} - -# Prints an error message and exits with an error code. -function fail() { - error "$@" - #echo >&2 - echo "For usage information, run this script with --help" >&2 - exit 1 -} - -function status() { - echo_color "${GREEN}" "$@" -} - -function warning() { - echo_color "${YELLOW}" "$@" -} - -function success() { - echo_color "${GREEN}" -n "Success: " >&2 - echo "$@" >&2 -} - -function info() { - echo_color "${YELLOW}" -n "Info: " >&2 - echo "$@" >&2 -} - -# Run command -function run() { - if "${DRYRUN}"; then - echo_color "${YELLOW}" -n "would run " - echo "$@" - else - if ! "$@"; then - local CMDSTR="$*" - error "Failure running '${CMDSTR}', exiting." - exit 1 - fi - fi -} - -# May need to run this as sudo! -if [[ "$(id -u)" -ne 0 ]]; then - error "This command can only be run by root." +# Make sure only root can access and not direct access. +if [[ "$(type -t requires_root)" != "function" ]]; then + echo "Direct access to this script is not permitted." exit 1 fi - ## # Main Functions -# +## ## # Trim whitespace. # Ref: https://stackoverflow.com/a/3352015/12077262 -# +## function str_trim() { local str="$*" @@ -126,7 +52,7 @@ function str_trim() { ## # Convert string to uppercase. # Ref: https://unix.stackexchange.com/a/51987 -# +## function str_to_upper() { local str="$*" @@ -135,7 +61,7 @@ function str_to_upper() { ## # Prints help. -# +## function cmd_help() { cat <<- EOL ${CMD_PARENT} ${CMD_NAME} ${PROG_VER} @@ -302,7 +228,7 @@ function cmd_users() { ## # Initialize account subcommand. -# +## function sub_cmd_account() { # account subcommands function cmd_account_help() { @@ -541,8 +467,8 @@ EOL } ## -# Main database operations. -# +# Main Database Operations. +## function db_ops() { OPTS=$(getopt -o a:H:P:u:p:n:b:C:g:f:q:x:DrhVv \ -l action:,dbhost:,dbport:,dbuser:,dbpass:,dbname:,dbprefix:,dbcollation:,dbprivileges:,dbfile:,dbquery:,extra-args: \ @@ -559,79 +485,97 @@ function db_ops() { while true do case "${1}" in - -a | --action) shift + -a | --action) + shift local ACTION="${1}" MAIN_ARGS=$((MAIN_ARGS + 1)) shift ;; - -b | --dbprefix) shift + -b | --dbprefix) + shift DBPREFIX="${1}" shift ;; - -C | --dbcollation) shift + -C | --dbcollation) + shift DBCOLLATION="${1}" shift ;; - -f | --dbfile) shift + -f | --dbfile) + shift DBFILE="${1}" shift ;; - -g | --dbprivileges) shift + -g | --dbprivileges) + shift DBPRIVILEGES="${1}" shift ;; - -H | --dbhost) shift + -H | --dbhost) + shift DBHOST=${1} shift ;; - -n | --dbname) shift + -n | --dbname) + shift DBNAME="${1}" shift ;; - -p | --dbpass) shift + -p | --dbpass) + shift DBPASS="${1}" shift ;; - -P | --dbport) shift + -P | --dbport) + shift DBPORT=${1} shift ;; - -q | --dbquery) shift + -q | --dbquery) + shift DBQUERY="${1}" shift ;; - -u | --dbuser) shift + -u | --dbuser) + shift DBUSER="${1}" shift ;; - -x | --extra-args) shift + -x | --extra-args) + shift EXTRA_ARGS="${1}" shift ;; - -D | --dry-run) shift + -D | --dry-run) + shift DRYRUN=true ;; - -r | --root) shift + -r | --root) + shift USEROOT=true ;; - -V | --verbose) shift + -V | --verbose) + shift VERBOSE=true ;; - -h | --help) shift + -h | --help) + shift # Bypass args. BYPASSED_ARGS="${BYPASSED_ARGS} --help" ;; - -v | --version) shift + -v | --version) + shift # Bypass args. BYPASSED_ARGS="${BYPASSED_ARGS} --version" ;; - --) shift + --) + shift break ;; *) - fail "Invalid argument: ${1}" + fail "unrecognized option '${1}'" exit 1 ;; esac @@ -860,8 +804,8 @@ function db_ops() { } ## -# Main DB CLI Wrapper -# +# Main Database CLI Wrapper +## function init_lemper_db() { # Check command line arguments. if [[ -n "${1}" ]]; then diff --git a/lib/lemper-manage.sh b/lib/lemper-manage.sh index e025696c..697088bc 100755 --- a/lib/lemper-manage.sh +++ b/lib/lemper-manage.sh @@ -23,93 +23,20 @@ PROG_VER="2.x.x" CMD_PARENT="lemper-cli" CMD_NAME="manage" -# Test mode. -DRYRUN="false" - -# Color decorator. -RED=91 -GREEN=92 -YELLOW=93 - -## -# Helper Functions -# -function begin_color() { - color="${1}" - echo -e -n "\e[${color}m" -} - -function end_color() { - echo -e -n "\e[0m" -} - -function echo_color() { - color="${1}" - shift - begin_color "${color}" - echo "$@" - end_color -} - -function error() { - echo_color "${RED}" -n "Error: " >&2 - echo "$@" >&2 -} - -# Prints an error message and exits with an error code. -function fail() { - error "$@" - echo "For usage information, run this script with --help" >&2 - exit 1 -} - -function status() { - echo_color "${GREEN}" "$@" -} - -function warning() { - echo_color "${YELLOW}" "$@" -} - -function success() { - echo_color "${GREEN}" -n "Success: " >&2 - echo "$@" >&2 -} - -function info() { - echo_color "${YELLOW}" -n "Info: " >&2 - echo "$@" >&2 -} - -# Run command -function run() { - if "${DRYRUN}"; then - echo_color "${YELLOW}" -n "would run " - echo "$@" - else - if ! "$@"; then - local CMDSTR="$*" - error "Failure running '${CMDSTR}', exiting." - exit 1 - fi - fi -} - -# May need to run this as sudo! -if [[ "$(id -u)" -ne 0 ]]; then - error "This command can only be run by root." +# Make sure only root can access and not direct access. +if ! declare -F "requires_root" &>/dev/null; then + echo "Direct access to this script is not permitted." exit 1 fi - ## # Main Functions -# +## ## # Show usage # output to STDERR. -# +## function show_usage() { cat <<- EOL ${CMD_PARENT} ${CMD_NAME} ${PROG_VER} @@ -171,7 +98,7 @@ EOL ## # Enable vhost. -# +## function enable_vhost() { # Verify user input hostname (domain name) local DOMAIN=${1} @@ -192,7 +119,7 @@ function enable_vhost() { ## # Disable vhost. -# +## function disable_vhost() { # Verify user input hostname (domain name) local DOMAIN=${1} @@ -213,7 +140,7 @@ function disable_vhost() { ## # Remove vhost. -# +## function remove_vhost() { # Verify user input hostname (domain name) local DOMAIN=${1} @@ -320,7 +247,7 @@ function remove_vhost() { ## # Enable fail2ban for virtual host. -# +## function enable_fail2ban() { # Verify user input hostname (domain name) local DOMAIN=${1} @@ -358,8 +285,8 @@ EOL } ## -# Enable fail2ban for virtual host. -# +# Disable fail2ban for virtual host. +## function disable_fail2ban() { # Verify user input hostname (domain name) local DOMAIN=${1} @@ -378,7 +305,7 @@ function disable_fail2ban() { ## # Enable Nginx's fastcgi cache. -# +## function enable_fastcgi_cache() { # Verify user input hostname (domain name) local DOMAIN=${1} @@ -394,18 +321,18 @@ function enable_fastcgi_cache() { # enable fastcgi_cache conf run sed -i "s|#include\ /etc/nginx/includes/fastcgi_cache.conf|include\ /etc/nginx/includes/fastcgi_cache.conf|g" \ "/etc/nginx/sites-available/${DOMAIN}.conf" + + # Reload Nginx. + reload_nginx else info "FastCGI cache is not enabled. There is no cached configuration." exit 1 fi - - # Reload Nginx. - reload_nginx } ## # Disable Nginx's fastcgi cache. -# +## function disable_fastcgi_cache() { # Verify user input hostname (domain name) local DOMAIN=${1} @@ -421,18 +348,18 @@ function disable_fastcgi_cache() { # enable fastcgi_cache conf run sed -i "s|^\ include\ /etc/nginx/includes/fastcgi_cache.conf|\ #include\ /etc/nginx/includes/fastcgi_cache.conf|g" \ "/etc/nginx/sites-available/${DOMAIN}.conf" + + # Reload Nginx. + reload_nginx else info "FastCGI cache is not enabled. There is no cached configuration." exit 1 fi - - # Reload Nginx. - reload_nginx } ## # Enable Nginx's Mod PageSpeed. -# +## function enable_mod_pagespeed() { # Verify user input hostname (domain name) local DOMAIN=${1} @@ -441,7 +368,7 @@ function enable_mod_pagespeed() { echo "Enabling Mod PageSpeed for ${DOMAIN}..." if [[ -f /etc/nginx/includes/mod_pagespeed.conf && -f /etc/nginx/modules-enabled/50-mod-pagespeed.conf ]]; then - # enable mod pagespeed + # Enable mod pagespeed. run sed -i "s|#include\ /etc/nginx/mod_pagespeed|include\ /etc/nginx/mod_pagespeed|g" /etc/nginx/nginx.conf run sed -i "s|#include\ /etc/nginx/includes/mod_pagespeed.conf|include\ /etc/nginx/includes/mod_pagespeed.conf|g" \ "/etc/nginx/sites-available/${DOMAIN}.conf" @@ -450,25 +377,25 @@ function enable_mod_pagespeed() { run sed -i "s|#pagespeed\ Disallow|pagespeed\ Disallow|g" "/etc/nginx/sites-available/${DOMAIN}.conf" run sed -i "s|#pagespeed\ Domain|pagespeed\ Domain|g" "/etc/nginx/sites-available/${DOMAIN}.conf" - # If SSL enabled, ensure to also to enable PageSpeed related vars. + # If SSL enabled, ensure to also enable PageSpeed related vars. #if grep -qwE "^\ include\ /etc/nginx/includes/ssl.conf" "/etc/nginx/sites-available/${DOMAIN}.conf"; then # run sed -i "s/#pagespeed\ FetchHttps/pagespeed\ FetchHttps/g" \ # "/etc/nginx/sites-available/${DOMAIN}.conf" # run sed -i "s/#pagespeed\ MapOriginDomain/pagespeed\ MapOriginDomain/g" \ # "/etc/nginx/sites-available/${DOMAIN}.conf" #fi + + # Reload Nginx. + reload_nginx else info "Mod PageSpeed is not enabled. NGiNX must be installed with PageSpeed module." exit 1 fi - - # Reload Nginx. - reload_nginx } ## # Disable Nginx's Mod PageSpeed. -# +## function disable_mod_pagespeed() { # Verify user input hostname (domain name) local DOMAIN=${1} @@ -492,18 +419,18 @@ function disable_mod_pagespeed() { # run sed -i "s/^\ pagespeed\ MapOriginDomain/\ #pagespeed\ MapOriginDomain/g" \ # "/etc/nginx/sites-available/${DOMAIN}.conf" #fi + + # Reload Nginx. + reload_nginx else info "Mod PageSpeed is not enabled. NGiNX must be installed with PageSpeed module." exit 1 fi - - # Reload Nginx. - reload_nginx } ## # Enable HTTPS (HTTP over SSL). -# +## function enable_ssl() { # Verify user input hostname (domain name). local DOMAIN=${1} @@ -591,18 +518,15 @@ EOL reload_nginx else warning -e "\nOops, Nginx HTTPS server block already exists. Please inspect manually for further action!" - exit 1 fi else info "Updating HTTPS config in dry run mode." fi - - exit 0 } ## # Disable HTTPS (HTTP over SSL). -# +## function disable_ssl() { # Verify user input hostname (domain name) local DOMAIN=${1} @@ -630,13 +554,11 @@ function disable_ssl() { else info "Disabling HTTPS config in dry run mode." fi - - exit 0 } ## # Disable HTTPS and remove Let's Encrypt SSL certificate. -# +## function remove_ssl() { # Verify user input hostname (domain name) local DOMAIN=${1} @@ -660,6 +582,8 @@ function remove_ssl() { else fail "Certbot executable binary not found. Install it first!" fi + + reload_nginx else info "SSL certificate removed in dry run mode." fi @@ -667,7 +591,7 @@ function remove_ssl() { ## # Renew Let's Encrypt SSL certificate. -# +## function renew_ssl() { # Verify user input hostname (domain name) local DOMAIN=${1} @@ -701,16 +625,16 @@ function renew_ssl() { else info "Certificate file not found. May be your SSL is not activated yet." fi + + reload_nginx else info "Renew SSL certificate in dry run mode." fi - - exit 0 } ## # Enable Brotli compression module. -# +## function enable_brotli() { local DOMAIN=${1} verify_vhost "${DOMAIN}" @@ -748,7 +672,7 @@ function enable_brotli() { ## # Enable Gzip compression module, # enabled by default. -# +## function enable_gzip() { local DOMAIN=${1} verify_vhost "${DOMAIN}" @@ -785,7 +709,7 @@ function enable_gzip() { ## # Disable Gzip/Brotli compression module -# +## function disable_compression() { local DOMAIN=${1} verify_vhost "${DOMAIN}" @@ -805,7 +729,7 @@ function disable_compression() { ## # Verify if virtual host exists. -# +## function verify_vhost() { if [[ -z "${1}" ]]; then error "Virtual host (vhost) or domain name is required." @@ -826,13 +750,13 @@ function verify_vhost() { ## # Reload NGiNX safely. -# +## function reload_nginx() { # Reload Nginx echo "Reloading NGiNX configuration..." if [[ -e /var/run/nginx.pid ]]; then - if nginx -t 2>/dev/null > /dev/null; then + if nginx -t > /dev/null 2>&1; then service nginx reload -s > /dev/null 2>&1 else error "Configuration couldn't be validated. Please correct the error below:"; @@ -866,7 +790,7 @@ function reload_nginx() { ## # Main Manage CLI Wrapper -# +## function init_lemper_manage() { OPTS=$(getopt -o c:d:e:f:p:r:s:bghv \ -l enable:,disable:,remove:,enable-fail2ban:,disable-fail2ban:,enable-fastcgi-cache:,disable-fastcgi-cache: \ @@ -917,18 +841,22 @@ function init_lemper_manage() { ;; -s | --enable-ssl) enable_ssl "${2}" + exit shift 2 ;; --disable-ssl) disable_ssl "${2}" + exit shift 2 ;; --remove-ssl) remove_ssl "${2}" + exit shift 2 ;; --renew-ssl) renew_ssl "${2}" + exit shift 2 ;; -b | --enable-brotli) diff --git a/lib/lemper-site.sh b/lib/lemper-site.sh new file mode 100755 index 00000000..4c3363db --- /dev/null +++ b/lib/lemper-site.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash + +# +-------------------------------------------------------------------------+ +# | Lemper Site - Simple LEMPer Stack Site (vhost) Manager | +# +-------------------------------------------------------------------------+ +# | Copyright (c) 2014-2021 MasEDI.Net (https://masedi.net/lemper) | +# +-------------------------------------------------------------------------+ +# | This source file is subject to the GNU General Public License | +# | that is bundled with this package in the file LICENSE.md. | +# | | +# | If you did not receive a copy of the license and are unable to | +# | obtain it through the world-wide-web, please send an email | +# | to license@lemper.cloud so we can send you a copy immediately. | +# +-------------------------------------------------------------------------+ +# | Authors: Edi Septriyanto | +# +-------------------------------------------------------------------------+ + +set -e + +# Make sure only root can access and not direct access. +if [[ "$(type -t requires_root)" != "function" ]]; then + echo "Direct access to this script is not permitted." + exit 1 +fi + +# Version control. +#PROG_NAME=$(basename "$0") +#PROG_VER="2.x.x" +CMD_PARENT="lemper-cli" +CMD_NAME="site" + +if [ -z "${CLI_PLUGINS_DIR}" ]; then + CLI_PLUGINS_DIR="/etc/lemper/cli-plugins" +fi + +function site_subcmd_help() { + cmd_help + exit 0 +} + +function site_subcmd_version() { + cmd_version + exit 0 +} + +## +# LEMPer CLI 'site' subcommand wrapper. +# +# Usage: +# lemper-cli site [options] [...] +## +function init_lemper_site() { + # Check command line arguments. + if [[ -n "${1}" ]]; then + CMD="${1}" + shift # Pass the remaining arguments to the next subcommand. + + case ${CMD} in + help | -h | --help) + site_subcmd_help + exit 0 + ;; + version | -v | --version) + site_subcmd_version + exit 0 + ;; + *) + if declare -f "site_subcmd_${CMD}" &>/dev/null 2>&1; then + # Run subcommand function if exists. + "site_subcmd_${CMD}" "$@" + exit 0 + elif [[ -x "${CLI_PLUGINS_DIR}/lemper-site-${CMD}" ]]; then + # Source the plugin executable file. + # shellcheck disable=SC1090 + . "${CLI_PLUGINS_DIR}/lemper-site-${CMD}" "$@" + exit 0 + else + echo "${CMD_PARENT} ${CMD_NAME}: '${CMD}' is not ${CMD_NAME} subcommand." + echo "See '${CMD_PARENT} ${CMD_NAME} --help' for more information." + exit 1 + fi + ;; + esac + else + echo "${CMD_PARENT} ${CMD_NAME}: missing required arguments." + echo "See '${CMD_PARENT} ${CMD_NAME} --help' for more information." + exit 1 + fi +} + +# Start running things from a call at the end so if this script is executed +# after a partial download it doesn't do anything. +init_lemper_site "$@" diff --git a/remove.sh b/remove.sh index f0e362e8..a93a6464 100755 --- a/remove.sh +++ b/remove.sh @@ -36,7 +36,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" # Make sure only supported distribution can run this installer script. preflight_system_check diff --git a/scripts/cleanup_server.sh b/scripts/cleanup_server.sh index 806a8c5d..32f5fdcc 100755 --- a/scripts/cleanup_server.sh +++ b/scripts/cleanup_server.sh @@ -21,7 +21,7 @@ else fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" # Make sure only supported distribution can run this installer script. preflight_system_check diff --git a/scripts/helper.sh b/scripts/helper.sh index 0443304d..b1b1041b 100644 --- a/scripts/helper.sh +++ b/scripts/helper.sh @@ -65,6 +65,16 @@ function fail() { exit 1 } +function success() { + echo_color "${GREEN}" -n "Success: " >&2 + echo "$@" >&2 +} + +function info() { + echo_color "${YELLOW}" -n "Info: " >&2 + echo "$@" >&2 +} + function status() { echo_color "${GREEN}" "$@" } @@ -73,14 +83,32 @@ function warning() { echo_color "${YELLOW}" "$@" } -function success() { - echo_color "${GREEN}" -n "Success: " >&2 - echo "$@" >&2 +function echo_ok() { + echo_color "${GREEN}" "$@" } -function info() { - echo_color "${YELLOW}" -n "Info: " >&2 - echo "$@" >&2 +function echo_warn() { + echo_color "${YELLOW}" "$@" +} + +function echo_err() { + echo_color "${RED}" "$@" +} + +# Make sure only root can run LEMPer script. +function requires_root() { + if [[ "$(id -u)" -ne 0 ]]; then + if ! hash sudo 2>/dev/null; then + echo "Installer script must be run as 'root' or with sudo." + exit 1 + else + #echo "Switching to root user to run installer script." + sudo -E "$0" "$@" + exit 0 + fi + fi + + #success "Root privileges granted." } # Run command @@ -298,14 +326,6 @@ function validate_fqdn() { fi } -# Make sure only root can run LEMPer script. -function requires_root() { - if [[ "$(id -u)" -ne 0 ]]; then - error "This command can only be run by root." - exit 1 - fi -} - # Get general distribution name. function get_distrib_name() { if [[ -f "/etc/os-release" ]]; then diff --git a/scripts/install_certbotle.sh b/scripts/install_certbotle.sh index ea4b4f1a..13142317 100755 --- a/scripts/install_certbotle.sh +++ b/scripts/install_certbotle.sh @@ -14,7 +14,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" # Make sure only supported distribution can run this installer script. preflight_system_check @@ -22,14 +22,18 @@ preflight_system_check # Install Certbot Let's Encrypt. function init_certbotle_install() { if [[ "${AUTO_INSTALL}" == true ]]; then - DO_INSTALL_CERTBOT="y" + if [[ "${INSTALL_CERTBOT}" == true ]]; then + DO_INSTALL_CERTBOT="y" + else + DO_INSTALL_CERTBOT="n" + fi else while [[ "${DO_INSTALL_CERTBOT}" != "y" && "${DO_INSTALL_CERTBOT}" != "n" ]]; do read -rp "Do you want to install Certbot Let's Encrypt client? [y/n]: " -i y -e DO_INSTALL_CERTBOT done fi - if [[ ${DO_INSTALL_CERTBOT} == y* && ${INSTALL_CERTBOT} == true ]]; then + if [[ ${DO_INSTALL_CERTBOT} == y* || ${DO_INSTALL_CERTBOT} == Y* ]]; then echo "Installing Certbot Let's Encrypt client..." DISTRIB_NAME=${DISTRIB_NAME:-$(get_distrib_name)} diff --git a/scripts/install_dependencies.sh b/scripts/install_dependencies.sh index fd40d0c0..b9138077 100755 --- a/scripts/install_dependencies.sh +++ b/scripts/install_dependencies.sh @@ -14,7 +14,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" # Make sure only supported distribution can run this installer script. preflight_system_check diff --git a/scripts/install_fail2ban.sh b/scripts/install_fail2ban.sh index 0a08034d..863b7835 100755 --- a/scripts/install_fail2ban.sh +++ b/scripts/install_fail2ban.sh @@ -14,7 +14,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" # Make sure only supported distribution can run this installer script. preflight_system_check diff --git a/scripts/install_imagemagick.sh b/scripts/install_imagemagick.sh index fbb07e00..e12dc848 100755 --- a/scripts/install_imagemagick.sh +++ b/scripts/install_imagemagick.sh @@ -14,7 +14,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" # Make sure only supported distribution can run this installer script. preflight_system_check diff --git a/scripts/install_mailer.sh b/scripts/install_mailer.sh index dd5e039d..ebeee804 100755 --- a/scripts/install_mailer.sh +++ b/scripts/install_mailer.sh @@ -14,7 +14,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" # Make sure only supported distribution can run this installer script. preflight_system_check diff --git a/scripts/install_mariadb.sh b/scripts/install_mariadb.sh index 477c2be5..d1af2193 100755 --- a/scripts/install_mariadb.sh +++ b/scripts/install_mariadb.sh @@ -14,7 +14,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" # Make sure only supported distribution can run this installer script. preflight_system_check diff --git a/scripts/install_memcached.sh b/scripts/install_memcached.sh index 728205a0..efa6cf8a 100755 --- a/scripts/install_memcached.sh +++ b/scripts/install_memcached.sh @@ -14,7 +14,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" # Make sure only supported distribution can run this installer script. preflight_system_check diff --git a/scripts/install_mongodb.sh b/scripts/install_mongodb.sh index dea1dc24..ef8d27c6 100755 --- a/scripts/install_mongodb.sh +++ b/scripts/install_mongodb.sh @@ -3,7 +3,7 @@ # MongoDB installer # Ref : https://www.linode.com/docs/databases/mongodb/install-mongodb-on-ubuntu-16-04 # Min. Requirement : GNU/Linux Ubuntu 18.04 -# Last Build : 11/12/2021 +# Last Build : 24/12/2021 # Author : MasEDI.Net (me@masedi.net) # Since Version : 1.0.0 @@ -15,23 +15,20 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" # Make sure only supported distribution can run this installer script. preflight_system_check +DISTRIB_NAME=${DISTRIB_NAME:-$(get_distrib_name)} +RELEASE_NAME=${RELEASE_NAME:-$(get_release_name)} +MONGODB_VERSION=${MONGODB_VERSION:-"5.0"} +[[ "${RELEASE_NAME}" == "jessie" || "${RELEASE_NAME}" == "xenial" ]] && MONGODB_VERSION="4.4" + ## # Add MongoDB repository. -# +## function add_mongodb_repo() { - echo "Adding MongoDB ${MONGODB_VERSION} repository..." - - DISTRIB_NAME=${DISTRIB_NAME:-$(get_distrib_name)} - RELEASE_NAME=${RELEASE_NAME:-$(get_release_name)} - MONGODB_VERSION=${MONGODB_VERSION:-"5.0"} - - [[ "${RELEASE_NAME}" == "jessie" || "${RELEASE_NAME}" == "xenial" ]] && MONGODB_VERSION="4.4" - local DISTRIB_ARCH case ${ARCH} in i386 | i486| i586 | i686) @@ -53,20 +50,30 @@ function add_mongodb_repo() { case ${DISTRIB_NAME} in debian) - if [ ! -f "/etc/apt/sources.list.d/mongodb-org-${MONGODB_VERSION}-${RELEASE_NAME}.list" ]; then + if [[ ! -f "/etc/apt/sources.list.d/mongodb-org-${MONGODB_VERSION}-${RELEASE_NAME}.list" ]]; then + echo "Adding MongoDB repository key..." + + run bash -c "wget -qO - 'https://www.mongodb.org/static/pgp/server-${MONGODB_VERSION}.asc' | apt-key add -" + + echo "Adding MongoDB repository..." + run touch "/etc/apt/sources.list.d/mongodb-org-${MONGODB_VERSION}-${RELEASE_NAME}.list" run bash -c "echo 'deb [ arch=${DISTRIB_ARCH} ] https://repo.mongodb.org/apt/debian ${RELEASE_NAME}/mongodb-org/${MONGODB_VERSION} main' > /etc/apt/sources.list.d/mongodb-org-${MONGODB_VERSION}-${RELEASE_NAME}.list" - run bash -c "wget -qO - 'https://www.mongodb.org/static/pgp/server-${MONGODB_VERSION}.asc' | apt-key add -" run apt-get update -qq -y else info "MongoDB ${MONGODB_VERSION} repository already exists." fi ;; ubuntu) - if [ ! -f "/etc/apt/sources.list.d/mongodb-org-${MONGODB_VERSION}-${RELEASE_NAME}.list" ]; then + if [[ ! -f "/etc/apt/sources.list.d/mongodb-org-${MONGODB_VERSION}-${RELEASE_NAME}.list" ]]; then + echo "Adding MongoDB repository key..." + + run bash -c "wget -qO - 'https://www.mongodb.org/static/pgp/server-${MONGODB_VERSION}.asc' | apt-key add -" + + echo "Adding MongoDB repository..." + run touch "/etc/apt/sources.list.d/mongodb-org-${MONGODB_VERSION}-${RELEASE_NAME}.list" run bash -c "echo 'deb [ arch=${DISTRIB_ARCH} ] https://repo.mongodb.org/apt/ubuntu ${RELEASE_NAME}/mongodb-org/${MONGODB_VERSION} multiverse' > /etc/apt/sources.list.d/mongodb-org-${MONGODB_VERSION}-${RELEASE_NAME}.list" - run bash -c "wget -qO - 'https://www.mongodb.org/static/pgp/server-${MONGODB_VERSION}.asc' | apt-key add -" run apt-get update -qq -y else info "MongoDB ${MONGODB_VERSION} repository already exists." @@ -103,21 +110,46 @@ function init_mongodb_install() { # Add repository. add_mongodb_repo - echo "Installing MongoDB server.." + echo "Installing MongoDB server..." - run apt-get install -qq -y libbson-1.0 libmongoc-1.0-0 \ - mongodb-org mongodb-org-server mongodb-org-shell mongodb-org-tools + run apt-get install -qq -y libbson-1.0 libmongoc-1.0-0 mongodb-org mongodb-org-server \ + mongodb-org-shell mongodb-org-tools mongodb-org-mongos mongodb-database-tools \ + mongodb-org-database-tools-extra mongodb-mongosh - # Enable in start-up - run systemctl start mongod.service - run systemctl enable mongod + if [[ "${DRYRUN}" != true ]]; then + # Enable in start-up. + echo "Enable MongoDB systemd service..." - if [[ "${DRYRUN}" == true ]]; then - info "MongoDB server installed in dry run mode." - else - echo "MongoDB installation completed." - echo "After installation finished, you can add a MongoDB administrative user. Example command lines below:"; - cat <<- EOL + run systemctl start mongod.service + run systemctl enable mongod + + if [[ $(systemctl is-active mongod) == 'active' ]]; then + echo "MongoDB server is running." + else + echo "MongoDB server is not running." + fi + + # Add MongoDB default admin user. + if [[ -n $(command -v mongosh) && $(pgrep -c mongod) -gt 0 ]]; then + echo "Final test MongoDB service..." + + sleep 3 # Wait for MongoDB to completely start. + + MONGODB_ADMIN_USER=${MONGODB_ADMIN_USER:-"lemperdb"} + MONGODB_ADMIN_PASSWORD=${MONGODB_ADMIN_PASSWORD:-"$(openssl rand -base64 64 | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)"} + + run mongosh admin \ + --eval "\"db.createUser({'user': '${MONGODB_ADMIN_USER}', 'pwd': '${MONGODB_ADMIN_PASSWORD}', 'roles':[{'role': 'root', 'db': 'admin'}]});\"" + + # Save config. + save_config -e "MONGODB_HOST=127.0.0.1\nMONGODB_PORT=27017\nMONGODB_ADMIN_USER=${MONGODB_ADMIN_USER}\nMONGODB_ADMIN_PASS=${MONGODB_ADMIN_PASSWORD}" + + # Save log. + save_log -e "MongoDB default admin user is enabled, here is your admin credentials:\nAdmin username: ${MONGODB_ADMIN_USER} | Admin password: ${MONGODB_ADMIN_PASSWORD}\nSave this credentials and use it to authenticate your MongoDB connection." + else + echo "MongoDB installation completed with errors on start-up, please check the log file." + echo -e "After installation finished, you can add a MongoDB administrative user.\nExample command lines below:"; + cat <<- EOL mongosh > use admin @@ -134,29 +166,16 @@ mongosh -u admin -p --authenticationDatabase user-data > db.exampleCollection.find({"name" : "John Doe"}) EOL - - # Add MongoDB default admin user. - if [[ -n $(command -v mongosh) ]]; then - echo "Adding MongoDB default admin user.." - - MONGODB_ADMIN_USER=${MONGODB_ADMIN_USER:-"lemperdb"} - MONGODB_ADMIN_PASSWORD=${MONGODB_ADMIN_PASSWORD:-"$(openssl rand -base64 64 | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)"} - - run mongosh admin --eval "\"db.createUser({'user': '${MONGODB_ADMIN_USER}', 'pwd': '${MONGODB_ADMIN_PASSWORD}', 'roles':[{'role': 'root', 'db': 'admin'}]});\"" - - # Save config. - save_config -e "MONGODB_HOST=127.0.0.1\nMONGODB_PORT=27017\nMONGODB_ADMIN_USER=${MONGODB_ADMIN_USER}\nMONGODB_ADMIN_PASS=${MONGODB_ADMIN_PASSWORD}" - - # Save log. - save_log -e "MongoDB default admin user is enabled, here is your admin credentials:\nAdmin username: ${MONGODB_ADMIN_USER} | Admin password: ${MONGODB_ADMIN_PASSWORD}\nSave this credentials and use it to authenticate your MongoDB connection." fi + else + info "MongoDB server installed in dry run mode." fi else info "MongoDB server installation skipped." fi } -echo "[MongoDB Server Installation]" +echo "[MongoDB ${MONGODB_VERSION} Server Installation]" # Start running things from a call at the end so if this script is executed # after a partial download it doesn't do anything. diff --git a/scripts/install_nginx.sh b/scripts/install_nginx.sh index 798d16ab..99176625 100755 --- a/scripts/install_nginx.sh +++ b/scripts/install_nginx.sh @@ -14,7 +14,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" # Make sure only supported distribution can run this installer script. preflight_system_check diff --git a/scripts/install_phalcon.sh b/scripts/install_phalcon.sh index 19de1919..c16f51bc 100755 --- a/scripts/install_phalcon.sh +++ b/scripts/install_phalcon.sh @@ -14,7 +14,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" # Make sure only supported distribution can run this installer script. preflight_system_check diff --git a/scripts/install_php.sh b/scripts/install_php.sh index 5450d9b0..9941d33d 100755 --- a/scripts/install_php.sh +++ b/scripts/install_php.sh @@ -2,7 +2,7 @@ # PHP Installer # Min. Requirement : GNU/Linux Ubuntu 18.04 -# Last Build : 11/12/2021 +# Last Build : 09/01/2022 # Author : MasEDI.Net (me@masedi.net) # Since Version : 1.0.0 @@ -14,7 +14,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" # Make sure only supported distribution can run this installer script. preflight_system_check @@ -195,6 +195,14 @@ function install_php() { run mkdir -p /var/log/php fi + if [[ ! -d "/home/${LEMPER_USERNAME}/logs/php" ]]; then + run mkdir -p "/home/${LEMPER_USERNAME}/logs/php" + fi + + # Log rotation. + run cp -f "etc/logrotate.d/php${PHPv}-fpm" /etc/logrotate.d/ && \ + run chmod 0644 "/etc/logrotate.d/php${PHPv}-fpm" + # Optimize PHP & FPM configuration. optimize_php_fpm "${PHPv}" fi @@ -253,6 +261,12 @@ function optimize_php_fpm() { run cp -f "etc/php/${PHPv}/fpm/php.ini" "/etc/php/${PHPv}/fpm/" else if [[ "${DRYRUN}" != true ]]; then + if [[ "${ENVIRONMENT}" =~ "prod" ]]; then + OVT="${OVT:-"0"}" + else + OVT="${OVT:-"1"}" + fi + cat >> "/etc/php/${PHPv}/fpm/php.ini" <> /etc/sysctl.conf <\ 'redispasswordhere'|'auth'\ =>\ '${REDIS_PASSWORD}'|g" includes/config.inc.php - fi - else - run cd redisadmin && \ - run "${COMPOSER_BIN}" -q update + if [[ "${REDIS_REQUIRE_PASSWORD}" == true ]]; then + run sed -i "s|//'auth'\ =>\ 'redispasswordhere'|'auth'\ =>\ '${REDIS_PASSWORD}'|g" includes/config.inc.php fi - - run cd "${CURRENT_DIR}" || return 1 + else + run cd redisadmin && \ + run "${COMPOSER_BIN}" -q update fi + run cd "${CURRENT_DIR}" || return 1 + [ -f /usr/share/nginx/html/lcp/redisadmin/index.php ] && echo_ok "OK" + + # Assign ownership properly. run chown -hR www-data:www-data /usr/share/nginx/html - if [[ -x /usr/local/bin/lemper-cli && -d /usr/share/nginx/html/lcp ]]; then - success "LEMPer CLI & web tools successfully installed." - fi + #if [[ -x /usr/local/bin/lemper-cli && -d /usr/share/nginx/html/lcp ]]; then + # success "LEMPer CLI & web tools successfully installed." + #fi } echo "[LEMPer CLI & Web Tools Installation]" diff --git a/scripts/install_vsftpd.sh b/scripts/install_vsftpd.sh index c67c42a0..9dd247e6 100755 --- a/scripts/install_vsftpd.sh +++ b/scripts/install_vsftpd.sh @@ -14,7 +14,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" # Make sure only supported distribution can run this installer script. preflight_system_check @@ -118,7 +118,7 @@ function init_vsftpd_install() { elif [[ -f "${LIB_GNU_DIR}/libcap.so" ]]; then run ln -s "${LIB_GNU_DIR}/libcap.so" "${LIB_DIR}/libcap.so" else - error "Cannot find libcap.so file." + echo "Cannot find libcap.so file." fi local CURRENT_DIR && \ @@ -135,11 +135,21 @@ function init_vsftpd_install() { run cd "${BUILD_DIR}" && \ run wget -q "${VSFTPD_ZIP_URL}" && \ run tar -zxf "${VSFTPD_FILENAME}" && \ - run cd vsftpd-*/ && \ + run cd vsftpd-*/ || return 1 + + # If SSL Enabled, modify the builddefs.h file. + if [[ "${VSFTPD_SSL_ENABLE}" == true ]]; then + run sed -i 's/\#undef\ VSF_BUILD_SSL/\#define\ VSF_BUILD_SSL/g' ./builddefs.h + fi + run make && \ run make install && \ run ldconfig /usr/local/lib && \ run cd "${CURRENT_DIR}" || return 1 + + # Move executable to /usr/sbin. + [ -x /usr/local/sbin/vsftpd ] && \ + run mv /usr/local/sbin/vsftpd /usr/sbin/ ;; *) # Skip installation. @@ -182,9 +192,13 @@ pasv_max_port=50000 user_sub_token=$USER local_root=/home/$USER -rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem -rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key -ssl_enable=Yes +EOL + + # Enable SSL. + if [[ "${VSFTPD_SSL_ENABLE}" == true ]]; then + cat >> /etc/vsftpd.conf <. -# -# This project would not be possible without help from: -# Matthew Montgomery Paul Kehrer Dave Burgess -# Jonathan Hinds Mike Jackson Nils Breunese -# Shawn Ashlee Luuk Vosslamber Ville Skytta -# Trent Hornibrook Jason Gill Mark Imbriaco -# Greg Eden Aubin Galinotti Giovanni Bechis -# Bill Bradford Ryan Novosielski Michael Scheidell -# Blair Christensen Hans du Plooy Victor Trac -# Everett Barnes Tom Krouper Gary Barrueto -# Simon Greenaway Adam Stein Isart Montane -# Baptiste M. Cole Turner Major Hayden -# Joe Ashcraft Jean-Marie Renouard -# -# Inspired by Matthew Montgomery's tuning-primer.sh script: -# http://forge.mysql.com/projects/view.php?id=44 -# - -# --------------------------------------------------------------------------- -# BEGIN TEXT TEMPLATE MODULE -# --------------------------------------------------------------------------- -# Text::Template.pm -# -# Fill in `templates' -# -# Copyright 2013 M. J. Dominus. -# You may copy and distribute this program under the -# same terms as Perl itself. -# If in doubt, write to mjd-perl-template+@plover.com for a license. -# -# Version 1.46 - -package Text::Template; -require 5.004; -use Exporter; -#use no strict; -@ISA = qw(Exporter); -@EXPORT_OK = qw(fill_in_file fill_in_string TTerror); -use vars '$ERROR'; -use strict; - -$Text::Template::VERSION = '1.46'; -my %GLOBAL_PREPEND = ('Text::Template' => ''); - -sub Version { - $Text::Template::VERSION; -} - -sub _param { - my $kk; - my ($k, %h) = @_; - for $kk ($k, "\u$k", "\U$k", "-$k", "-\u$k", "-\U$k") { - return $h{$kk} if exists $h{$kk}; - } - return; -} - -sub always_prepend -{ - my $pack = shift; - my $old = $GLOBAL_PREPEND{$pack}; - $GLOBAL_PREPEND{$pack} = shift; - $old; -} - -{ - my %LEGAL_TYPE; - BEGIN { - %LEGAL_TYPE = map {$_=>1} qw(FILE FILEHANDLE STRING ARRAY); - } - sub new { - my $pack = shift; - my %a = @_; - my $stype = uc(_param('type', %a) || "FILE"); - my $source = _param('source', %a); - my $untaint = _param('untaint', %a); - my $prepend = _param('prepend', %a); - my $alt_delim = _param('delimiters', %a); - my $broken = _param('broken', %a); - unless (defined $source) { - require Carp; - Carp::croak("Usage: $ {pack}::new(TYPE => ..., SOURCE => ...)"); - } - unless ($LEGAL_TYPE{$stype}) { - require Carp; - Carp::croak("Illegal value `$stype' for TYPE parameter"); - } - my $self = {TYPE => $stype, - PREPEND => $prepend, - UNTAINT => $untaint, - BROKEN => $broken, - (defined $alt_delim ? (DELIM => $alt_delim) : ()), - }; - # Under 5.005_03, if any of $stype, $prepend, $untaint, or $broken - # are tainted, all the others become tainted too as a result of - # sharing the expression with them. We install $source separately - # to prevent it from acquiring a spurious taint. - $self->{SOURCE} = $source; - - bless $self => $pack; - return unless $self->_acquire_data; - - $self; - } -} - -# Convert template objects of various types to type STRING, -# in which the template data is embedded in the object itself. -sub _acquire_data { - my ($self) = @_; - my $type = $self->{TYPE}; - if ($type eq 'STRING') { - # nothing necessary - } elsif ($type eq 'FILE') { - my $data = _load_text($self->{SOURCE}); - unless (defined $data) { - # _load_text already set $ERROR - return undef; - } - if ($self->{UNTAINT} && _is_clean($self->{SOURCE})) { - _unconditionally_untaint($data); - } - $self->{TYPE} = 'STRING'; - $self->{FILENAME} = $self->{SOURCE}; - $self->{SOURCE} = $data; - } elsif ($type eq 'ARRAY') { - $self->{TYPE} = 'STRING'; - $self->{SOURCE} = join '', @{$self->{SOURCE}}; - } elsif ($type eq 'FILEHANDLE') { - $self->{TYPE} = 'STRING'; - local $/; - my $fh = $self->{SOURCE}; - my $data = <$fh>; # Extra assignment avoids bug in Solaris perl5.00[45]. - if ($self->{UNTAINT}) { - _unconditionally_untaint($data); - } - $self->{SOURCE} = $data; - } else { - # This should have been caught long ago, so it represents a - # drastic `can't-happen' sort of failure - my $pack = ref $self; - die "Can only acquire data for $pack objects of subtype STRING, but this is $type; aborting"; - } - $self->{DATA_ACQUIRED} = 1; -} - -sub source { - my ($self) = @_; - $self->_acquire_data unless $self->{DATA_ACQUIRED}; - return $self->{SOURCE}; -} - -sub set_source_data { - my ($self, $newdata) = @_; - $self->{SOURCE} = $newdata; - $self->{DATA_ACQUIRED} = 1; - $self->{TYPE} = 'STRING'; - 1; -} - -sub compile { - my $self = shift; - - return 1 if $self->{TYPE} eq 'PREPARSED'; - - return undef unless $self->_acquire_data; - unless ($self->{TYPE} eq 'STRING') { - my $pack = ref $self; - # This should have been caught long ago, so it represents a - # drastic `can't-happen' sort of failure - die "Can only compile $pack objects of subtype STRING, but this is $self->{TYPE}; aborting"; - } - - my @tokens; - my $delim_pats = shift() || $self->{DELIM}; - - - - my ($t_open, $t_close) = ('{', '}'); - my $DELIM; # Regex matches a delimiter if $delim_pats - if (defined $delim_pats) { - ($t_open, $t_close) = @$delim_pats; - $DELIM = "(?:(?:\Q$t_open\E)|(?:\Q$t_close\E))"; - @tokens = split /($DELIM|\n)/, $self->{SOURCE}; - } else { - @tokens = split /(\\\\(?=\\*[{}])|\\[{}]|[{}\n])/, $self->{SOURCE}; - } - my $state = 'TEXT'; - my $depth = 0; - my $lineno = 1; - my @content; - my $cur_item = ''; - my $prog_start; - while (@tokens) { - my $t = shift @tokens; - next if $t eq ''; - if ($t eq $t_open) { # Brace or other opening delimiter - if ($depth == 0) { - push @content, [$state, $cur_item, $lineno] if $cur_item ne ''; - $cur_item = ''; - $state = 'PROG'; - $prog_start = $lineno; - } else { - $cur_item .= $t; - } - $depth++; - } elsif ($t eq $t_close) { # Brace or other closing delimiter - $depth--; - if ($depth < 0) { - $ERROR = "Unmatched close brace at line $lineno"; - return undef; - } elsif ($depth == 0) { - push @content, [$state, $cur_item, $prog_start] if $cur_item ne ''; - $state = 'TEXT'; - $cur_item = ''; - } else { - $cur_item .= $t; - } - } elsif (!$delim_pats && $t eq '\\\\') { # precedes \\\..\\\{ or \\\..\\\} - $cur_item .= '\\'; - } elsif (!$delim_pats && $t =~ /^\\([{}])$/) { # Escaped (literal) brace? - $cur_item .= $1; - } elsif ($t eq "\n") { # Newline - $lineno++; - $cur_item .= $t; - } else { # Anything else - $cur_item .= $t; - } - } - - if ($state eq 'PROG') { - $ERROR = "End of data inside program text that began at line $prog_start"; - return undef; - } elsif ($state eq 'TEXT') { - push @content, [$state, $cur_item, $lineno] if $cur_item ne ''; - } else { - die "Can't happen error #1"; - } - - $self->{TYPE} = 'PREPARSED'; - $self->{SOURCE} = \@content; - 1; -} - -sub prepend_text { - my ($self) = @_; - my $t = $self->{PREPEND}; - unless (defined $t) { - $t = $GLOBAL_PREPEND{ref $self}; - unless (defined $t) { - $t = $GLOBAL_PREPEND{'Text::Template'}; - } - } - $self->{PREPEND} = $_[1] if $#_ >= 1; - return $t; -} - -sub fill_in { - my $fi_self = shift; - my %fi_a = @_; - - unless ($fi_self->{TYPE} eq 'PREPARSED') { - my $delims = _param('delimiters', %fi_a); - my @delim_arg = (defined $delims ? ($delims) : ()); - $fi_self->compile(@delim_arg) - or return undef; - } - - my $fi_varhash = _param('hash', %fi_a); - my $fi_package = _param('package', %fi_a) ; - my $fi_broken = - _param('broken', %fi_a) || $fi_self->{BROKEN} || \&_default_broken; - my $fi_broken_arg = _param('broken_arg', %fi_a) || []; - my $fi_safe = _param('safe', %fi_a); - my $fi_ofh = _param('output', %fi_a); - my $fi_eval_package; - my $fi_scrub_package = 0; - my $fi_filename = _param('filename') || $fi_self->{FILENAME} || 'template'; - - my $fi_prepend = _param('prepend', %fi_a); - unless (defined $fi_prepend) { - $fi_prepend = $fi_self->prepend_text; - } - - if (defined $fi_safe) { - $fi_eval_package = 'main'; - } elsif (defined $fi_package) { - $fi_eval_package = $fi_package; - } elsif (defined $fi_varhash) { - $fi_eval_package = _gensym(); - $fi_scrub_package = 1; - } else { - $fi_eval_package = caller; - } - - my $fi_install_package; - if (defined $fi_varhash) { - if (defined $fi_package) { - $fi_install_package = $fi_package; - } elsif (defined $fi_safe) { - $fi_install_package = $fi_safe->root; - } else { - $fi_install_package = $fi_eval_package; # The gensymmed one - } - _install_hash($fi_varhash => $fi_install_package); - } - - if (defined $fi_package && defined $fi_safe) { - no strict 'refs'; - # Big fat magic here: Fix it so that the user-specified package - # is the default one available in the safe compartment. - *{$fi_safe->root . '::'} = \%{$fi_package . '::'}; # LOD - } - - my $fi_r = ''; - my $fi_item; - foreach $fi_item (@{$fi_self->{SOURCE}}) { - my ($fi_type, $fi_text, $fi_lineno) = @$fi_item; - if ($fi_type eq 'TEXT') { - $fi_self->append_text_to_output( - text => $fi_text, - handle => $fi_ofh, - out => \$fi_r, - type => $fi_type, - ); - } elsif ($fi_type eq 'PROG') { - no strict; - my $fi_lcomment = "#line $fi_lineno $fi_filename"; - my $fi_progtext = - "package $fi_eval_package; $fi_prepend;\n$fi_lcomment\n$fi_text;"; - my $fi_res; - my $fi_eval_err = ''; - if ($fi_safe) { - $fi_safe->reval(q{undef $OUT}); - $fi_res = $fi_safe->reval($fi_progtext); - $fi_eval_err = $@; - my $OUT = $fi_safe->reval('$OUT'); - $fi_res = $OUT if defined $OUT; - } else { - my $OUT; - $fi_res = eval $fi_progtext; - $fi_eval_err = $@; - $fi_res = $OUT if defined $OUT; - } - - # If the value of the filled-in text really was undef, - # change it to an explicit empty string to avoid undefined - # value warnings later. - $fi_res = '' unless defined $fi_res; - - if ($fi_eval_err) { - $fi_res = $fi_broken->(text => $fi_text, - error => $fi_eval_err, - lineno => $fi_lineno, - arg => $fi_broken_arg, - ); - if (defined $fi_res) { - $fi_self->append_text_to_output( - text => $fi_res, - handle => $fi_ofh, - out => \$fi_r, - type => $fi_type, - ); - } else { - return $fi_res; # Undefined means abort processing - } - } else { - $fi_self->append_text_to_output( - text => $fi_res, - handle => $fi_ofh, - out => \$fi_r, - type => $fi_type, - ); - } - } else { - die "Can't happen error #2"; - } - } - - _scrubpkg($fi_eval_package) if $fi_scrub_package; - defined $fi_ofh ? 1 : $fi_r; -} - -sub append_text_to_output { - my ($self, %arg) = @_; - - if (defined $arg{handle}) { - print { $arg{handle} } $arg{text}; - } else { - ${ $arg{out} } .= $arg{text}; - } - - return; -} - -sub fill_this_in { - my $pack = shift; - my $text = shift; - my $templ = $pack->new(TYPE => 'STRING', SOURCE => $text, @_) - or return undef; - $templ->compile or return undef; - my $result = $templ->fill_in(@_); - $result; -} - -sub fill_in_string { - my $string = shift; - my $package = _param('package', @_); - push @_, 'package' => scalar(caller) unless defined $package; - Text::Template->fill_this_in($string, @_); -} - -sub fill_in_file { - my $fn = shift; - my $templ = Text::Template->new(TYPE => 'FILE', SOURCE => $fn, @_) - or return undef; - $templ->compile or return undef; - my $text = $templ->fill_in(@_); - $text; -} - -sub _default_broken { - my %a = @_; - my $prog_text = $a{text}; - my $err = $a{error}; - my $lineno = $a{lineno}; - chomp $err; -# $err =~ s/\s+at .*//s; - "Program fragment delivered error ``$err''"; -} - -sub _load_text { - my $fn = shift; - local *F; - unless (open F, $fn) { - $ERROR = "Couldn't open file $fn: $!"; - return undef; - } - local $/; - ; -} - -sub _is_clean { - my $z; - eval { ($z = join('', @_)), eval '#' . substr($z,0,0); 1 } # LOD -} - -sub _unconditionally_untaint { - for (@_) { - ($_) = /(.*)/s; - } -} - -{ - my $seqno = 0; - sub _gensym { - __PACKAGE__ . '::GEN' . $seqno++; - } - sub _scrubpkg { - my $s = shift; - $s =~ s/^Text::Template:://; - no strict 'refs'; - my $hash = $Text::Template::{$s."::"}; - foreach my $key (keys %$hash) { - undef $hash->{$key}; - } - } -} - -# Given a hashful of variables (or a list of such hashes) -# install the variables into the specified package, -# overwriting whatever variables were there before. -sub _install_hash { - my $hashlist = shift; - my $dest = shift; - if (UNIVERSAL::isa($hashlist, 'HASH')) { - $hashlist = [$hashlist]; - } - my $hash; - foreach $hash (@$hashlist) { - my $name; - foreach $name (keys %$hash) { - my $val = $hash->{$name}; - no strict 'refs'; - local *SYM = *{"$ {dest}::$name"}; - if (! defined $val) { - delete ${"$ {dest}::"}{$name}; - } elsif (ref $val) { - *SYM = $val; - } else { - *SYM = \$val; - } - } - } -} - -sub TTerror { $ERROR } - -1; -# --------------------------------------------------------------------------- -# END TEXT TEMPLATE MODULE -# --------------------------------------------------------------------------- - - -package main; -use strict; -use warnings; -use diagnostics; -use File::Spec; -use Getopt::Long; -use File::Basename; -use Cwd 'abs_path'; - -# Set up a few variables for use in the script -my $tunerversion = "1.6.0"; -my ( @adjvars, @generalrec ); - -# Set defaults -my %opt = ( - "silent" => 0, - "nobad" => 0, - "nogood" => 0, - "noinfo" => 0, - "debug" => 0, - "nocolor" => 0, - "forcemem" => 0, - "forceswap" => 0, - "host" => 0, - "socket" => 0, - "port" => 0, - "user" => 0, - "pass" => 0, - "skipsize" => 0, - "checkversion" => 0, - "buffers" => 0, - "passwordfile" => 0, - "outputfile" => 0, - "dbstat" => 0, - "idxstat" => 0, - "skippassword" => 0, - "noask" => 0, - "template" => 0, - "reportfile" => 0 -); - -# Gather the options from the command line -GetOptions( - \%opt, 'nobad', 'nogood', 'noinfo', - 'debug', 'nocolor', 'forcemem=i', 'forceswap=i', - 'host=s', 'socket=s', 'port=i', 'user=s', - 'pass=s', 'skipsize', 'checkversion', 'mysqladmin=s', - 'mysqlcmd=s', 'help', 'buffers', 'skippassword', - 'passwordfile=s', 'outputfile=s', 'silent', 'dbstat', - 'idxstat', 'noask', 'template=s', 'reportfile=s' -); - -if ( defined $opt{'help'} && $opt{'help'} == 1 ) { usage(); } - -sub usage { - - # Shown with --help option passed - print " MySQLTuner $tunerversion - MySQL High Performance Tuning Script\n" - . " Bug reports, feature requests, and downloads at http://mysqltuner.com/\n" - . " Maintained by Major Hayden (major\@mhtx.net) - Licensed under GPL\n" - . "\n" - . " Important Usage Guidelines:\n" - . " To run the script with the default options, run the script without arguments\n" - . " Allow MySQL server to run for at least 24-48 hours before trusting suggestions\n" - . " Some routines may require root level privileges (script will provide warnings)\n" - . " You must provide the remote server's total memory when connecting to other servers\n" - . "\n" - . " Connection and Authentication\n" - . " --host Connect to a remote host to perform tests (default: localhost)\n" - . " --socket Use a different socket for a local connection\n" - . " --port Port to use for connection (default: 3306)\n" - . " --user Username to use for authentication\n" - . " --pass Password to use for authentication\n" - . " --mysqladmin Path to a custom mysqladmin executable\n" - . " --mysqlcmd Path to a custom mysql executable\n" . "\n" - . " --noask Dont ask password if needed\n" . "\n" - . " Performance and Reporting Options\n" - . " --skipsize Don't enumerate tables and their types/sizes (default: on)\n" - . " (Recommended for servers with many tables)\n" - . " --skippassword Don't perform checks on user passwords(default: off)\n" - . " --checkversion Check for updates to MySQLTuner (default: don't check)\n" - . " --forcemem Amount of RAM installed in megabytes\n" - . " --forceswap Amount of swap memory configured in megabytes\n" - . " --passwordfile Path to a password file list(one password by line)\n" - . " Output Options:\n" - . " --silent Don't output anything on screen\n" - . " --nogood Remove OK responses\n" - . " --nobad Remove negative/suggestion responses\n" - . " --noinfo Remove informational responses\n" - . " --debug Print debug information\n" - . " --dbstat Print database information\n" - . " --idxstat Print index information\n" - . " --nocolor Don't print output in color\n" - . " --buffers Print global and per-thread buffer values\n" - . " --outputfile Path to a output txt file\n" . "\n" - . " --reportfile Path to a report txt file\n" . "\n" - . " --template Path to a template file\n" . "\n"; - exit 0; -} - -my $devnull = File::Spec->devnull(); -my $basic_password_files = - ( $opt{passwordfile} eq "0" ) - ? abs_path( dirname(__FILE__) ) . "/basic_passwords.txt" - : abs_path( $opt{passwordfile} ); - -# for RPM distributions -$basic_password_files = "/usr/share/mysqltuner/basic_passwords.txt" - unless -f "$basic_password_files"; - -# -my $outputfile = undef; -$outputfile = abs_path( $opt{outputfile} ) unless $opt{outputfile} eq "0"; - -my $fh = undef; -open( $fh, '>', $outputfile ) - or die("Fail opening $outputfile") - if defined($outputfile); -$opt{nocolor} = 1 if defined($outputfile); - -# Setting up the colors for the print styles -my $good = ( $opt{nocolor} == 0 ) ? "[\e[0;32mOK\e[0m]" : "[OK]"; -my $bad = ( $opt{nocolor} == 0 ) ? "[\e[0;31m!!\e[0m]" : "[!!]"; -my $info = ( $opt{nocolor} == 0 ) ? "[\e[0;34m--\e[0m]" : "[--]"; -my $deb = ( $opt{nocolor} == 0 ) ? "[\e[0;31mDG\e[0m]" : "[DG]"; - -# Super sturucture containing all informations -my %result; - -# Functions that handle the print styles -sub prettyprint { - print $_[0] . "\n" unless $opt{'silent'}; - print $fh $_[0] . "\n" if defined($fh); -} -sub goodprint { prettyprint $good. " " . $_[0] unless ( $opt{nogood} == 1 ); } -sub infoprint { prettyprint $info. " " . $_[0] unless ( $opt{noinfo} == 1 ); } -sub badprint { prettyprint $bad. " " . $_[0] unless ( $opt{nobad} == 1 ); } -sub debugprint { prettyprint $deb. " " . $_[0] unless ( $opt{debug} == 0 ); } -sub redwrap { - return ( $opt{nocolor} == 0 ) ? "\e[0;31m" . $_[0] . "\e[0m" : $_[0]; -} - -sub greenwrap { - return ( $opt{nocolor} == 0 ) ? "\e[0;32m" . $_[0] . "\e[0m" : $_[0]; -} - -# Calculates the parameter passed in bytes, and then rounds it to one decimal place -sub hr_bytes { - my $num = shift; - if ( $num >= ( 1024**3 ) ) { #GB - return sprintf( "%.1f", ( $num / ( 1024**3 ) ) ) . "G"; - } - elsif ( $num >= ( 1024**2 ) ) { #MB - return sprintf( "%.1f", ( $num / ( 1024**2 ) ) ) . "M"; - } - elsif ( $num >= 1024 ) { #KB - return sprintf( "%.1f", ( $num / 1024 ) ) . "K"; - } - else { - return $num . "B"; - } -} - -# Calculates the parameter passed in bytes, and then rounds it to the nearest integer -sub hr_bytes_rnd { - my $num = shift; - if ( $num >= ( 1024**3 ) ) { #GB - return int( ( $num / ( 1024**3 ) ) ) . "G"; - } - elsif ( $num >= ( 1024**2 ) ) { #MB - return int( ( $num / ( 1024**2 ) ) ) . "M"; - } - elsif ( $num >= 1024 ) { #KB - return int( ( $num / 1024 ) ) . "K"; - } - else { - return $num . "B"; - } -} - -# Calculates the parameter passed to the nearest power of 1000, then rounds it to the nearest integer -sub hr_num { - my $num = shift; - if ( $num >= ( 1000**3 ) ) { # Billions - return int( ( $num / ( 1000**3 ) ) ) . "B"; - } - elsif ( $num >= ( 1000**2 ) ) { # Millions - return int( ( $num / ( 1000**2 ) ) ) . "M"; - } - elsif ( $num >= 1000 ) { # Thousands - return int( ( $num / 1000 ) ) . "K"; - } - else { - return $num; - } -} - -# Calculate Percentage -sub percentage { - my $value = shift; - my $total = shift; - $total = 0 unless defined $total; - return 100, 00 if $total == 0; - return sprintf( "%.2f", ( $value * 100 / $total ) ); -} - -# Calculates uptime to display in a more attractive form -sub pretty_uptime { - my $uptime = shift; - my $seconds = $uptime % 60; - my $minutes = int( ( $uptime % 3600 ) / 60 ); - my $hours = int( ( $uptime % 86400 ) / (3600) ); - my $days = int( $uptime / (86400) ); - my $uptimestring; - if ( $days > 0 ) { - $uptimestring = "${days}d ${hours}h ${minutes}m ${seconds}s"; - } - elsif ( $hours > 0 ) { - $uptimestring = "${hours}h ${minutes}m ${seconds}s"; - } - elsif ( $minutes > 0 ) { - $uptimestring = "${minutes}m ${seconds}s"; - } - else { - $uptimestring = "${seconds}s"; - } - return $uptimestring; -} - -# Retrieves the memory installed on this machine -my ( $physical_memory, $swap_memory, $duflags ); - -sub os_setup { - - sub memerror { - badprint -"Unable to determine total memory/swap; use '--forcemem' and '--forceswap'"; - exit 1; - } - my $os = `uname`; - $duflags = ( $os =~ /Linux/ ) ? '-b' : ''; - if ( $opt{'forcemem'} > 0 ) { - $physical_memory = $opt{'forcemem'} * 1048576; - infoprint "Assuming $opt{'forcemem'} MB of physical memory"; - if ( $opt{'forceswap'} > 0 ) { - $swap_memory = $opt{'forceswap'} * 1048576; - infoprint "Assuming $opt{'forceswap'} MB of swap space"; - } - else { - $swap_memory = 0; - badprint - "Assuming 0 MB of swap space (use --forceswap to specify)"; - } - } - else { - if ( $os =~ /Linux/ ) { - $physical_memory = - `grep -i memtotal: /proc/meminfo | awk '{print \$2}'` - or memerror; - $physical_memory *= 1024; - - $swap_memory = - `grep -i swaptotal: /proc/meminfo | awk '{print \$2}'` - or memerror; - $swap_memory *= 1024; - } - elsif ( $os =~ /Darwin/ ) { - $physical_memory = `sysctl -n hw.memsize` or memerror; - $swap_memory = - `sysctl -n vm.swapusage | awk '{print \$3}' | sed 's/\..*\$//'` - or memerror; - } - elsif ( $os =~ /NetBSD|OpenBSD|FreeBSD/ ) { - $physical_memory = `sysctl -n hw.physmem` or memerror; - if ( $physical_memory < 0 ) { - $physical_memory = `sysctl -n hw.physmem64` or memerror; - } - $swap_memory = - `swapctl -l | grep '^/' | awk '{ s+= \$2 } END { print s }'` - or memerror; - } - elsif ( $os =~ /BSD/ ) { - $physical_memory = `sysctl -n hw.realmem` or memerror; - $swap_memory = - `swapinfo | grep '^/' | awk '{ s+= \$2 } END { print s }'`; - } - elsif ( $os =~ /SunOS/ ) { - $physical_memory = - `/usr/sbin/prtconf | grep Memory | cut -f 3 -d ' '` - or memerror; - chomp($physical_memory); - $physical_memory = $physical_memory * 1024 * 1024; - } - elsif ( $os =~ /AIX/ ) { - $physical_memory = - `lsattr -El sys0 | grep realmem | awk '{print \$2}'` - or memerror; - chomp($physical_memory); - $physical_memory = $physical_memory * 1024; - $swap_memory = `lsps -as | awk -F"(MB| +)" '/MB /{print \$2}'` - or memerror; - chomp($swap_memory); - $swap_memory = $swap_memory * 1024 * 1024; - } - } - debugprint "Physical Memory: $physical_memory"; - debugprint "Swap Memory: $swap_memory"; - chomp($physical_memory); - chomp($swap_memory); - chomp($os); - $result{'OS'}{'OS Type'} = $os; - $result{'OS'}{'Physical Memory'}{'bytes'} = $physical_memory; - $result{'OS'}{'Physical Memory'}{'pretty'} = hr_bytes($physical_memory); - $result{'OS'}{'Swap Memory'}{'bytes'} = $swap_memory; - $result{'OS'}{'Swap Memory'}{'pretty'} = hr_bytes($swap_memory); - -} - -# Checks for updates to MySQLTuner -sub validate_tuner_version { - if ($opt{checkversion} eq 0) { - infoprint "Skipped version check for MySQLTuner script"; - return; - } - - my $update; - my $url = "https://raw.githubusercontent.com/major/MySQLTuner-perl/master/mysqltuner.pl"; - my $httpcli=`which curl`; - chomp($httpcli); - if ( 1 != 1 and defined($httpcli) and -e "$httpcli" ) { - debugprint "$httpcli is available."; - - debugprint "$httpcli --connect-timeout 5 -silent '$url' 2>/dev/null | grep 'my \$tunerversion'| cut -d\\\" -f2"; - $update = `$httpcli --connect-timeout 5 -silent '$url' 2>/dev/null | grep 'my \$tunerversion'| cut -d\\\" -f2`; - chomp($update); - debugprint "VERSION: $update"; - - - compare_tuner_version($update); - return; - } - - - $httpcli=`which wget`; - chomp($httpcli); - if ( defined($httpcli) and -e "$httpcli" ) { - debugprint "$httpcli is available."; - - debugprint "$httpcli -e timestamping=off -T 5 -O - '$url' 2>$devnull| grep 'my \$tunerversion'| cut -d\\\" -f2"; - $update = `$httpcli -e timestamping=off -T 5 -O - '$url' 2>$devnull| grep 'my \$tunerversion'| cut -d\\\" -f2`; - chomp($update); - compare_tuner_version($update); - return; - } - debugprint "curl and wget are not avalaible."; - infoprint "Unable to check for the latest MySQLTuner version"; -} - -sub compare_tuner_version { - my $remoteversion=shift; - debugprint "Remote data: $remoteversion"; - #exit 0; - if ($remoteversion ne $tunerversion) { - badprint "There is a new version of MySQLTuner available ($remoteversion)"; - return; - } - goodprint "You have the latest version of MySQLTuner($tunerversion)"; - return; -} - -# Checks to see if a MySQL login is possible -my ( $mysqllogin, $doremote, $remotestring, $mysqlcmd, $mysqladmincmd ); - -sub mysql_setup { - $doremote = 0; - $remotestring = ''; - if ( $opt{mysqladmin} ) { - $mysqladmincmd = $opt{mysqladmin}; - } - else { - $mysqladmincmd = `which mysqladmin`; - } - chomp($mysqladmincmd); - if ( !-e $mysqladmincmd && $opt{mysqladmin} ) { - badprint "Unable to find the mysqladmin command you specified: " - . $mysqladmincmd . ""; - exit 1; - } - elsif ( !-e $mysqladmincmd ) { - badprint - "Couldn't find mysqladmin in your \$PATH. Is MySQL installed?"; - exit 1; - } - if ( $opt{mysqlcmd} ) { - $mysqlcmd = $opt{mysqlcmd}; - } - else { - $mysqlcmd = `which mysql`; - } - chomp($mysqlcmd); - if ( !-e $mysqlcmd && $opt{mysqlcmd} ) { - badprint "Unable to find the mysql command you specified: " - . $mysqlcmd . ""; - exit 1; - } - elsif ( !-e $mysqlcmd ) { - badprint "Couldn't find mysql in your \$PATH. Is MySQL installed?"; - exit 1; - } - $mysqlcmd =~ s/\n$//g; - my $mysqlclidefaults=`$mysqlcmd --print-defaults`; - debugprint "MySQL Client: $mysqlclidefaults"; - if ( $mysqlclidefaults=~/auto-vertical-output/ ) { - badprint "Avoid auto-vertical-output in configuration file(s) for MySQL like"; - exit 1; - } - - debugprint "MySQL Client: $mysqlcmd"; - - # Are we being asked to connect via a socket? - if ( $opt{socket} ne 0 ) { - $remotestring = " -S $opt{socket}"; - } - - # Are we being asked to connect to a remote server? - if ( $opt{host} ne 0 ) { - chomp( $opt{host} ); - $opt{port} = ( $opt{port} eq 0 ) ? 3306 : $opt{port}; - - # If we're doing a remote connection, but forcemem wasn't specified, we need to exit - if ( $opt{'forcemem'} eq 0 ) { - badprint - "The --forcemem option is required for remote connections"; - exit 1; - } - infoprint "Performing tests on $opt{host}:$opt{port}"; - $remotestring = " -h $opt{host} -P $opt{port}"; - $doremote = 1; - } - - # Did we already get a username and password passed on the command line? - if ( $opt{user} ne 0 and $opt{pass} ne 0 ) { - $mysqllogin = "-u $opt{user} -p'$opt{pass}'" . $remotestring; - my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; - if ( $loginstatus =~ /mysqld is alive/ ) { - goodprint - "Logged in using credentials passed on the command line"; - return 1; - } - else { - badprint - "Attempted to use login credentials, but they were invalid"; - exit 1; - } - } - my $svcprop = `which svcprop 2>/dev/null`; - if ( substr( $svcprop, 0, 1 ) =~ "/" ) { - - # We are on solaris - ( my $mysql_login = -`svcprop -p quickbackup/username svc:/network/mysql-quickbackup:default` - ) =~ s/\s+$//; - ( my $mysql_pass = -`svcprop -p quickbackup/password svc:/network/mysql-quickbackup:default` - ) =~ s/\s+$//; - if ( substr( $mysql_login, 0, 7 ) ne "svcprop" ) { - - # mysql-quickbackup is installed - $mysqllogin = "-u $mysql_login -p$mysql_pass"; - my $loginstatus = `mysqladmin $mysqllogin ping 2>&1`; - if ( $loginstatus =~ /mysqld is alive/ ) { - goodprint - "Logged in using credentials from mysql-quickbackup."; - return 1; - } - else { - badprint -"Attempted to use login credentials from mysql-quickbackup, but they failed."; - exit 1; - } - } - } - elsif ( -r "/etc/psa/.psa.shadow" and $doremote == 0 ) { - - # It's a Plesk box, use the available credentials - $mysqllogin = "-u admin -p`cat /etc/psa/.psa.shadow`"; - my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; - unless ( $loginstatus =~ /mysqld is alive/ ) { - badprint -"Attempted to use login credentials from Plesk, but they failed."; - exit 1; - } - } - elsif ( -r "/usr/local/directadmin/conf/mysql.conf" and $doremote == 0 ) { - - # It's a DirectAdmin box, use the available credentials - my $mysqluser = - `cat /usr/local/directadmin/conf/mysql.conf | egrep '^user=.*'`; - my $mysqlpass = - `cat /usr/local/directadmin/conf/mysql.conf | egrep '^passwd=.*'`; - - $mysqluser =~ s/user=//; - $mysqluser =~ s/[\r\n]//; - $mysqlpass =~ s/passwd=//; - $mysqlpass =~ s/[\r\n]//; - - $mysqllogin = "-u $mysqluser -p$mysqlpass"; - - my $loginstatus = `mysqladmin ping $mysqllogin 2>&1`; - unless ( $loginstatus =~ /mysqld is alive/ ) { - badprint -"Attempted to use login credentials from DirectAdmin, but they failed."; - exit 1; - } - } - elsif ( -r "/etc/mysql/debian.cnf" and $doremote == 0 ) { - - # We have a debian maintenance account, use it - $mysqllogin = "--defaults-file=/etc/mysql/debian.cnf"; - my $loginstatus = `$mysqladmincmd $mysqllogin ping 2>&1`; - if ( $loginstatus =~ /mysqld is alive/ ) { - goodprint - "Logged in using credentials from debian maintenance account."; - return 1; - } - else { - badprint -"Attempted to use login credentials from debian maintenance account, but they failed."; - exit 1; - } - } - else { - - # It's not Plesk or debian, we should try a login - debugprint "$mysqladmincmd $remotestring ping 2>&1"; - my $loginstatus = `$mysqladmincmd $remotestring ping 2>&1`; - if ( $loginstatus =~ /mysqld is alive/ ) { - - # Login went just fine - $mysqllogin = " $remotestring "; - - # Did this go well because of a .my.cnf file or is there no password set? - my $userpath = `printenv HOME`; - if ( length($userpath) > 0 ) { - chomp($userpath); - } - unless ( -e "${userpath}/.my.cnf" or -e "${userpath}/.mylogin.cnf" ) - { - badprint -"Successfully authenticated with no password - SECURITY RISK!"; - } - return 1; - } - else { - if ( $opt{'noask'}==1 ) { - badprint "Attempted to use login credentials, but they were invalid"; - exit 1; - } - - print STDERR "Please enter your MySQL administrative login: "; - my $name = <>; - print STDERR "Please enter your MySQL administrative password: "; - system("stty -echo >$devnull 2>&1"); - my $password = <>; - system("stty echo >$devnull 2>&1"); - chomp($password); - chomp($name); - $mysqllogin = "-u $name"; - - if ( length($password) > 0 ) { - $mysqllogin .= " -p'$password'"; - } - $mysqllogin .= $remotestring; - my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; - if ( $loginstatus =~ /mysqld is alive/ ) { - print STDERR ""; - if ( !length($password) ) { - - # Did this go well because of a .my.cnf file or is there no password set? - my $userpath = `ls -d ~`; - chomp($userpath); - unless ( -e "$userpath/.my.cnf" ) { - badprint -"Successfully authenticated with no password - SECURITY RISK!"; - } - } - return 1; - } - else { - badprint " Attempted to use login credentials, but they were invalid."; - exit 1; - } - exit 1; - } - } -} - -# MySQL Request Array -sub select_array { - my $req = shift; - debugprint "PERFORM: $req "; - my @result = `$mysqlcmd $mysqllogin -Bse "$req"`; - chomp(@result); - return @result; -} - -# MySQL Request one -sub select_one { - my $req = shift; - debugprint "PERFORM: $req "; - my $result = `$mysqlcmd $mysqllogin -Bse "$req"`; - chomp($result); - return $result; -} - -sub get_tuning_info { - my @infoconn = select_array "\\s"; - my ( $tkey, $tval ); - @infoconn = - grep { !/Threads:/ and !/Connection id:/ and !/pager:/ and !/Using/ } - @infoconn; - foreach my $line (@infoconn) { - if ( $line =~ /\s*(.*):\s*(.*)/ ) { - debugprint "$1 => $2"; - $tkey = $1; - $tval = $2; - chomp($tkey); - chomp($tval); - $result{'MySQL Client'}{$tkey} = $tval; - } - } - $result{'MySQL Client'}{'Client Path'} = $mysqlcmd; - $result{'MySQL Client'}{'Admin Path'} = $mysqladmincmd; - $result{'MySQL Client'}{'Authentication Info'} = $mysqllogin; - -} - -# Populates all of the variable and status hashes -my ( %mystat, %myvar, $dummyselect, %myrepl, %myslaves ); - -sub get_all_vars { - - # We need to initiate at least one query so that our data is useable - $dummyselect = select_one "SELECT VERSION()"; - debugprint "VERSION: " . $dummyselect . ""; - $result{'MySQL Client'}{'Version'} = $dummyselect; - my @mysqlvarlist = select_array "SHOW /*!50000 GLOBAL */ VARIABLES"; - foreach my $line (@mysqlvarlist) { - $line =~ /([a-zA-Z_]*)\s*(.*)/; - $myvar{$1} = $2; - $result{'Variables'}{$1} = $2; - debugprint "V: $1 = $2"; - } - - my @mysqlstatlist = select_array "SHOW /*!50000 GLOBAL */ STATUS"; - foreach my $line (@mysqlstatlist) { - $line =~ /([a-zA-Z_]*)\s*(.*)/; - $mystat{$1} = $2; - $result{'Status'}{$1} = $2; - debugprint "S: $1 = $2"; - } - - # Workaround for MySQL bug #59393 wrt. ignore-builtin-innodb - if ( ( $myvar{'ignore_builtin_innodb'} || "" ) eq "ON" ) { - $myvar{'have_innodb'} = "NO"; - } - - # have_* for engines is deprecated and will be removed in MySQL 5.6; - # check SHOW ENGINES and set corresponding old style variables. - # Also works around MySQL bug #59393 wrt. skip-innodb - my @mysqlenginelist = select_array "SHOW ENGINES"; - foreach my $line (@mysqlenginelist) { - if ( $line =~ /^([a-zA-Z_]+)\s+(\S+)/ ) { - my $engine = lc($1); - - if ( $engine eq "federated" || $engine eq "blackhole" ) { - $engine .= "_engine"; - } - elsif ( $engine eq "berkeleydb" ) { - $engine = "bdb"; - } - my $val = ( $2 eq "DEFAULT" ) ? "YES" : $2; - $myvar{"have_$engine"} = $val; - $result{'Storage Engines'}{$engine} = $2; - } - } - - my @mysqlslave = select_array "SHOW SLAVE STATUS\\G"; - - foreach my $line (@mysqlslave) { - if ( $line =~ /\s*(.*):\s*(.*)/ ) { - debugprint "$1 => $2"; - $myrepl{"$1"} = $2; - $result{'Replication'}{'Status'}{$1} = $2; - } - } - - #print Dumper(%myrepl); - #exit 0; - my @mysqlslaves = select_array "SHOW SLAVE HOSTS"; - my @lineitems = (); - foreach my $line (@mysqlslaves) { - debugprint "L: $line "; - @lineitems = split /\s+/, $line; - $myslaves{ $lineitems[0] } = $line; - $result{'Replication'}{'Slaves'}{ $lineitems[0] } = $lineitems[4]; - } -} - -sub get_basic_passwords { - my $file = shift; - open( FH, "< $file" ) or die "Can't open $file for read: $!"; - my @lines = ; - close FH or die "Cannot close $file: $!"; - return @lines; -} - -sub security_recommendations { - prettyprint -"\n-------- Security Recommendations -------------------------------------------"; - if ( $opt{skippassword} eq 1 ) { - infoprint "Skipped due to --skippassword option"; - return; - } - - # Looking for Anonymous users - my @mysqlstatlist = select_array -"SELECT CONCAT(user, '\@', host) FROM mysql.user WHERE TRIM(USER) = '' OR USER IS NULL"; - if (@mysqlstatlist) { - foreach my $line ( sort @mysqlstatlist ) { - chomp($line); - badprint "User '" . $line . "' is an anonymous account."; - } - push( @generalrec, - "Remove Anonymous User accounts - there are " - . scalar(@mysqlstatlist) - . " Anonymous accounts." ); - } - else { - goodprint "There is no anonymous account in all database users"; - } - - # Looking for Empty Password - @mysqlstatlist = select_array -"SELECT CONCAT(user, '\@', host) FROM mysql.user WHERE password = '' OR password IS NULL"; - if (@mysqlstatlist) { - foreach my $line ( sort @mysqlstatlist ) { - chomp($line); - badprint "User '" . $line . "' has no password set."; - } - push( @generalrec, -"Set up a Password for user with the following SQL statement ( SET PASSWORD FOR 'user'\@'SpecificDNSorIp' = PASSWORD('secure_password'); )" - ); - } - else { - goodprint "All database users have passwords assigned"; - } - - # Looking for User with user/ uppercase /capitalise user as password - @mysqlstatlist = select_array -"SELECT CONCAT(user, '\@', host) FROM mysql.user WHERE CAST(password as Binary) = PASSWORD(user) OR CAST(password as Binary) = PASSWORD(UPPER(user)) OR CAST(password as Binary) = PASSWORD(UPPER(LEFT(User, 1)) + SUBSTRING(User, 2, LENGTH(User)))"; - if (@mysqlstatlist) { - foreach my $line ( sort @mysqlstatlist ) { - chomp($line); - badprint "User '" . $line . "' has user name as password."; - } - push( @generalrec, -"Set up a Secure Password for user\@host ( SET PASSWORD FOR 'user'\@'SpecificDNSorIp' = PASSWORD('secure_password'); )" - ); - } - - @mysqlstatlist = select_array - "SELECT CONCAT(user, '\@', host) FROM mysql.user WHERE HOST='%'"; - if (@mysqlstatlist) { - foreach my $line ( sort @mysqlstatlist ) { - chomp($line); - badprint "User '" . $line . "' hasn't specific host restriction."; - } - push( @generalrec, - "Restrict Host for user\@% to user\@SpecificDNSorIp" ); - } - - unless ( -f $basic_password_files ) { - badprint "There is not basic password file list !"; - return; - } - - my @passwords = get_basic_passwords $basic_password_files; - infoprint "There is " - . scalar(@passwords) - . " basic passwords in the list."; - my $nbins = 0; - my $passreq; - if (@passwords) { - foreach my $pass (@passwords) { - $pass =~ s/\s//g; - chomp($pass); - - # Looking for User with user/ uppercase /capitalise weak password - @mysqlstatlist = - select_array -"SELECT CONCAT(user, '\@', host) FROM mysql.user WHERE password = PASSWORD('" - . $pass - . "') OR password = PASSWORD(UPPER('" - . $pass - . "')) OR password = PASSWORD(UPPER(LEFT('" - . $pass - . "', 1)) + SUBSTRING('" - . $pass - . "', 2, LENGTH('" - . $pass . "')))"; - debugprint "There is " . scalar(@mysqlstatlist) . " items."; - if (@mysqlstatlist) { - foreach my $line (@mysqlstatlist) { - chomp($line); - badprint "User '" . $line - . "' is using weak pasword: $pass in a lower, upper or capitalize derivated version."; - $nbins++; - } - } - } - } - if ( $nbins > 0 ) { - push( @generalrec, $nbins . " user(s) used basic or weaked password." ); - } -} - -sub get_replication_status { - prettyprint -"\n-------- Replication Metrics -------------------------------------------------"; - - if ( scalar( keys %myslaves ) == 0 ) { - infoprint "No replication slave(s) for this server."; - } - else { - infoprint "This server is acting as master for " - . scalar( keys %myslaves ) - . " server(s)."; - } - - if ( scalar( keys %myrepl ) == 0 and scalar( keys %myslaves ) == 0 ) { - infoprint "This is a standalone server.."; - return; - } - if ( scalar( keys %myrepl ) == 0 ) { - infoprint "No replication setup for this server."; - return; - } - my ($io_running) = $myrepl{'Slave_IO_Running'}; - debugprint "IO RUNNING: $io_running "; - my ($sql_running) = $myrepl{'Slave_SQL_Running'}; - debugprint "SQL RUNNING: $sql_running "; - my ($seconds_behind_master) = $myrepl{'Seconds_Behind_Master'}; - debugprint "SECONDS : $seconds_behind_master "; - - if ( defined($io_running) - and ( $io_running !~ /yes/i or $sql_running !~ /yes/i ) ) - { - badprint -"This replication slave is not running but seems to be configurated."; - } - if ( defined($io_running) - && $io_running =~ /yes/i - && $sql_running =~ /yes/i ) - { - if ( $myvar{'read_only'} eq 'OFF' ) { - badprint -"This replication slave is running with the read_only option disabled."; - } - else { - goodprint -"This replication slave is running with the read_only option enabled."; - } - if ( $seconds_behind_master > 0 ) { - badprint -"This replication slave is lagging and slave has $seconds_behind_master second(s) behind master host."; - } - else { - goodprint "This replication slave is uptodate with master."; - } - } -} - -# Checks for supported or EOL'ed MySQL versions -my ( $mysqlvermajor, $mysqlverminor, $mysqlvermicro ); - -sub validate_mysql_version { - ( $mysqlvermajor, $mysqlverminor, $mysqlvermicro ) = - $myvar{'version'} =~ /^(\d+)(?:\.(\d+)|)(?:\.(\d+)|)/; - $mysqlverminor ||= 0; - $mysqlvermicro ||= 0; - if ( !mysql_version_ge( 5, 1 ) ) { - badprint "Your MySQL version " - . $myvar{'version'} - . " is EOL software! Upgrade soon!"; - } - elsif ( ( mysql_version_ge(6) and mysql_version_le(9) ) or mysql_version_ge(12) ) { - badprint "Currently running unsupported MySQL version " - . $myvar{'version'} . ""; - } else { - goodprint "Currently running supported MySQL version " - . $myvar{'version'} . ""; - } -} - -# Checks if MySQL version is greater than equal to (major, minor, micro) -sub mysql_version_ge { - my ( $maj, $min, $mic ) = @_; - $min ||= 0; - $mic ||= 0; - return $mysqlvermajor > $maj - || $mysqlvermajor == $maj - && ( $mysqlverminor > $min - || $mysqlverminor == $min && $mysqlvermicro >= $mic ); -} - -# Checks if MySQL version is lower than equal to (major, minor, micro) -sub mysql_version_le { - my ( $maj, $min, $mic ) = @_; - $min ||= 0; - $mic ||= 0; - return $mysqlvermajor < $maj - || $mysqlvermajor == $maj - && ( $mysqlverminor < $min - || $mysqlverminor == $min && $mysqlvermicro <= $mic ); -} - -# Checks for 32-bit boxes with more than 2GB of RAM -my ($arch); - -sub check_architecture { - if ( $doremote eq 1 ) { return; } - if ( `uname` =~ /SunOS/ && `isainfo -b` =~ /64/ ) { - $arch = 64; - goodprint "Operating on 64-bit architecture"; - } - elsif ( `uname` !~ /SunOS/ && `uname -m` =~ /64/ ) { - $arch = 64; - goodprint "Operating on 64-bit architecture"; - } - elsif ( `uname` =~ /AIX/ && `bootinfo -K` =~ /64/ ) { - $arch = 64; - goodprint "Operating on 64-bit architecture"; - } - elsif ( `uname` =~ /NetBSD|OpenBSD/ && `sysctl -b hw.machine` =~ /64/ ) { - $arch = 64; - goodprint "Operating on 64-bit architecture"; - } - elsif ( `uname` =~ /FreeBSD/ && `sysctl -b hw.machine_arch` =~ /64/ ) { - $arch = 64; - goodprint "Operating on 64-bit architecture"; - } - elsif ( `uname` =~ /Darwin/ && `uname -m` =~ /Power Macintosh/ ) { - -# Darwin box.local 9.8.0 Darwin Kernel Version 9.8.0: Wed Jul 15 16:57:01 PDT 2009; root:xnu1228.15.4~1/RELEASE_PPC Power Macintosh - $arch = 64; - goodprint "Operating on 64-bit architecture"; - } - elsif ( `uname` =~ /Darwin/ && `uname -m` =~ /x86_64/ ) { - -# Darwin gibas.local 12.3.0 Darwin Kernel Version 12.3.0: Sun Jan 6 22:37:10 PST 2013; root:xnu-2050.22.13~1/RELEASE_X86_64 x86_64 - $arch = 64; - goodprint "Operating on 64-bit architecture"; - } - else { - $arch = 32; - if ( $physical_memory > 2147483648 ) { - badprint -"Switch to 64-bit OS - MySQL cannot currently use all of your RAM"; - } - else { - goodprint - "Operating on 32-bit architecture with less than 2GB RAM"; - } - } - $result{'OS'}{'Architecture'} = "$arch bits"; -} - -# Start up a ton of storage engine counts/statistics -my ( %enginestats, %enginecount, $fragtables ); - -sub check_storage_engines { - if ( $opt{skipsize} eq 1 ) { - prettyprint -"\n-------- Storage Engine Statistics -------------------------------------------"; - infoprint "Skipped due to --skipsize option"; - return; - } - prettyprint -"\n-------- Storage Engine Statistics -------------------------------------------"; - - my $engines; - if ( mysql_version_ge( 5, 1, 5 ) ) { - my @engineresults = select_array -"SELECT ENGINE,SUPPORT FROM information_schema.ENGINES WHERE ENGINE NOT IN ('performance_schema','MyISAM','MERGE','MEMORY') ORDER BY ENGINE ASC"; - foreach my $line (@engineresults) { - my ( $engine, $engineenabled ); - ( $engine, $engineenabled ) = $line =~ /([a-zA-Z_]*)\s+([a-zA-Z]+)/; - $result{'Engine'}{$engine}{'Enabled'} = $engineenabled; - $engines .= - ( $engineenabled eq "YES" || $engineenabled eq "DEFAULT" ) - ? greenwrap "+" . $engine . " " - : redwrap "-" . $engine . " "; - } - } - else { - $engines .= - ( defined $myvar{'have_archive'} && $myvar{'have_archive'} eq "YES" ) - ? greenwrap "+Archive " - : redwrap "-Archive "; - $engines .= - ( defined $myvar{'have_bdb'} && $myvar{'have_bdb'} eq "YES" ) - ? greenwrap "+BDB " - : redwrap "-BDB "; - $engines .= - ( defined $myvar{'have_federated_engine'} - && $myvar{'have_federated_engine'} eq "YES" ) - ? greenwrap "+Federated " - : redwrap "-Federated "; - $engines .= - ( defined $myvar{'have_innodb'} && $myvar{'have_innodb'} eq "YES" ) - ? greenwrap "+InnoDB " - : redwrap "-InnoDB "; - $engines .= - ( defined $myvar{'have_isam'} && $myvar{'have_isam'} eq "YES" ) - ? greenwrap "+ISAM " - : redwrap "-ISAM "; - $engines .= - ( defined $myvar{'have_aria'} && $myvar{'have_aria'} eq "YES" ) - ? greenwrap "+Aria " - : redwrap "-Aria "; - $engines .= - ( defined $myvar{'have_ndbcluster'} - && $myvar{'have_ndbcluster'} eq "YES" ) - ? greenwrap "+NDBCluster " - : redwrap "-NDBCluster "; - } - - my @dblist = grep {$_ ne 'lost+found' } select_array "SHOW DATABASES"; - - $result{'Databases'}{'List'} = [@dblist]; - infoprint "Status: $engines"; - if ( mysql_version_ge( 5, 1, 5 ) ) { - -# MySQL 5 servers can have table sizes calculated quickly from information schema - my @templist = select_array -"SELECT ENGINE,SUM(DATA_LENGTH+INDEX_LENGTH),COUNT(ENGINE),SUM(DATA_LENGTH),SUM(INDEX_LENGTH) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema', 'performance_schema', 'mysql') AND ENGINE IS NOT NULL GROUP BY ENGINE ORDER BY ENGINE ASC;"; - - my ( $engine, $size, $count, $dsize, $isize ); - foreach my $line (@templist) { - ( $engine, $size, $count, $dsize, $isize ) = - $line =~ /([a-zA-Z_]*)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/; - if ( !defined($size) ) { next; } - $enginestats{$engine} = $size; - $enginecount{$engine} = $count; - $result{'Engine'}{$engine}{'Table Number'} = $count; - $result{'Engine'}{$engine}{'Total Size'} = $size; - $result{'Engine'}{$engine}{'Data Size'} = $dsize; - $result{'Engine'}{$engine}{'Index Size'} = $isize; - } - $fragtables = select_one -"SELECT COUNT(TABLE_NAME) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema','performance_schema', 'mysql') AND Data_free > 0 AND NOT ENGINE='MEMORY'"; - chomp($fragtables); - $result{'Tables'}{'Fragmented tables'} = - [ select_array -"SELECT CONCAT(CONCAT(TABLE_SCHEMA, '.'), TABLE_NAME) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema','performance_schema', 'mysql') AND Data_free > 0 AND NOT ENGINE='MEMORY'" - ]; - - } - else { - - # MySQL < 5 servers take a lot of work to get table sizes - my @tblist; - - # Now we build a database list, and loop through it to get storage engine stats for tables - foreach my $db (@dblist) { - chomp($db); - if ( $db eq "information_schema" - or $db eq "performance_schema" - or $db eq "mysql" - or $db eq "lost+found" ) - { - next; - } - my @ixs = ( 1, 6, 9 ); - if ( !mysql_version_ge( 4, 1 ) ) { - - # MySQL 3.23/4.0 keeps Data_Length in the 5th (0-based) column - @ixs = ( 1, 5, 8 ); - } - push( @tblist, - map { [ (split)[@ixs] ] } - select_array "SHOW TABLE STATUS FROM \\\`$db\\\`" ); - } - - # Parse through the table list to generate storage engine counts/statistics - $fragtables = 0; - foreach my $tbl (@tblist) { - debugprint "Data dump ". Dumper (@$tbl); - my ( $engine, $size, $datafree ) = @$tbl; - next if $engine eq 'NULL'; - $size=0 if $size eq 'NULL'; - $datafree=0 if $datafree eq 'NULL'; - if ( defined $enginestats{$engine} ) { - $enginestats{$engine} += $size; - $enginecount{$engine} += 1; - } - else { - $enginestats{$engine} = $size; - $enginecount{$engine} = 1; - } - if ( $datafree > 0 ) { - $fragtables++; - } - } - } - while ( my ( $engine, $size ) = each(%enginestats) ) { - infoprint "Data in $engine tables: " - . hr_bytes_rnd($size) - . " (Tables: " - . $enginecount{$engine} . ")" . ""; - } - - # If the storage engine isn't being used, recommend it to be disabled - if ( !defined $enginestats{'InnoDB'} - && defined $myvar{'have_innodb'} - && $myvar{'have_innodb'} eq "YES" ) - { - badprint "InnoDB is enabled but isn't being used"; - push( @generalrec, - "Add skip-innodb to MySQL configuration to disable InnoDB" ); - } - if ( !defined $enginestats{'BerkeleyDB'} - && defined $myvar{'have_bdb'} - && $myvar{'have_bdb'} eq "YES" ) - { - badprint "BDB is enabled but isn't being used"; - push( @generalrec, - "Add skip-bdb to MySQL configuration to disable BDB" ); - } - if ( !defined $enginestats{'ISAM'} - && defined $myvar{'have_isam'} - && $myvar{'have_isam'} eq "YES" ) - { - badprint "MYISAM is enabled but isn't being used"; - push( @generalrec, -"Add skip-isam to MySQL configuration to disable ISAM (MySQL > 4.1.0)" - ); - } - - # Fragmented tables - if ( $fragtables > 0 ) { - badprint "Total fragmented tables: $fragtables"; - push( @generalrec, - "Run OPTIMIZE TABLE to defragment tables for better performance" ); - } - else { - goodprint "Total fragmented tables: $fragtables"; - } - - # Auto increments - my %tblist; - - # Find the maximum integer - my $maxint = select_one "SELECT ~0"; - $result{'MaxInt'} = $maxint; - -# Now we use a database list, and loop through it to get storage engine stats for tables - foreach my $db (@dblist) { - chomp($db); - - if ( !$tblist{$db} ) { - $tblist{$db} = (); - } - - if ( $db eq "information_schema" ) { next; } - my @ia = ( 0, 10 ); - if ( !mysql_version_ge( 4, 1 ) ) { - - # MySQL 3.23/4.0 keeps Data_Length in the 5th (0-based) column - @ia = ( 0, 9 ); - } - push( - @{ $tblist{$db} }, - map { [ (split)[@ia] ] } - select_array "SHOW TABLE STATUS FROM \\\`$db\\\`" - ); - } - - my @dbnames = keys %tblist; - - foreach my $db (@dbnames) { - foreach my $tbl ( @{ $tblist{$db} } ) { - my ( $name, $autoincrement ) = @$tbl; - - if ( $autoincrement =~ /^\d+?$/ ) { - my $percent = percentage( $autoincrement, $maxint ); - $result{'PctAutoIncrement'}{"$db.$name"} = $percent; - if ( $percent >= 75 ) { - badprint -"Table '$db.$name' has an autoincrement value near max capacity ($percent%)"; - } - } - } - } - -} - -my %mycalc; - -sub calculations { - if ( $mystat{'Questions'} < 1 ) { - badprint - "Your server has not answered any queries - cannot continue..."; - exit 2; - } - - # Per-thread memory - if ( mysql_version_ge(4) ) { - $mycalc{'per_thread_buffers'} = - $myvar{'read_buffer_size'} + - $myvar{'read_rnd_buffer_size'} + - $myvar{'sort_buffer_size'} + - $myvar{'thread_stack'} + - $myvar{'join_buffer_size'}; - } - else { - $mycalc{'per_thread_buffers'} = - $myvar{'record_buffer'} + - $myvar{'record_rnd_buffer'} + - $myvar{'sort_buffer'} + - $myvar{'thread_stack'} + - $myvar{'join_buffer_size'}; - } - $mycalc{'total_per_thread_buffers'} = - $mycalc{'per_thread_buffers'} * $myvar{'max_connections'}; - $mycalc{'max_total_per_thread_buffers'} = - $mycalc{'per_thread_buffers'} * $mystat{'Max_used_connections'}; - - # Server-wide memory - $mycalc{'max_tmp_table_size'} = - ( $myvar{'tmp_table_size'} > $myvar{'max_heap_table_size'} ) - ? $myvar{'max_heap_table_size'} - : $myvar{'tmp_table_size'}; - $mycalc{'server_buffers'} = - $myvar{'key_buffer_size'} + $mycalc{'max_tmp_table_size'}; - $mycalc{'server_buffers'} += - ( defined $myvar{'innodb_buffer_pool_size'} ) - ? $myvar{'innodb_buffer_pool_size'} - : 0; - $mycalc{'server_buffers'} += - ( defined $myvar{'innodb_additional_mem_pool_size'} ) - ? $myvar{'innodb_additional_mem_pool_size'} - : 0; - $mycalc{'server_buffers'} += - ( defined $myvar{'innodb_log_buffer_size'} ) - ? $myvar{'innodb_log_buffer_size'} - : 0; - $mycalc{'server_buffers'} += - ( defined $myvar{'query_cache_size'} ) ? $myvar{'query_cache_size'} : 0; - $mycalc{'server_buffers'} += - ( defined $myvar{'aria_pagecache_buffer_size'} ) - ? $myvar{'aria_pagecache_buffer_size'} - : 0; - -# Global memory -# Max used memory is memory used by MySQL based on Max_used_connections -# This is the max memory used theorically calculated with the max concurrent connection number reached by mysql - $mycalc{'max_used_memory'} = - $mycalc{'server_buffers'} + $mycalc{"max_total_per_thread_buffers"}; - $mycalc{'pct_max_used_memory'} = - percentage( $mycalc{'max_used_memory'}, $physical_memory ); - -# Total possible memory is memory needed by MySQL based on max_connections -# This is the max memory MySQL can theorically used if all connections allowed has opened by mysql - $mycalc{'max_peak_memory'} = - $mycalc{'server_buffers'} + $mycalc{'total_per_thread_buffers'}; - $mycalc{'pct_max_physical_memory'} = - percentage( $mycalc{'max_peak_memory'}, $physical_memory ); - - debugprint "Max Used Memory: " - . hr_bytes( $mycalc{'max_used_memory'} ) . ""; - debugprint "Max Used Percentage RAM: " - . $mycalc{'pct_max_used_memory'} . "%"; - - debugprint "Max Peak Memory: " - . hr_bytes( $mycalc{'max_peak_memory'} ) . ""; - debugprint "Max Peak Percentage RAM: " - . $mycalc{'pct_max_physical_memory'} . "%"; - - # Slow queries - $mycalc{'pct_slow_queries'} = - int( ( $mystat{'Slow_queries'} / $mystat{'Questions'} ) * 100 ); - - # Connections - $mycalc{'pct_connections_used'} = int( - ( $mystat{'Max_used_connections'} / $myvar{'max_connections'} ) * 100 ); - $mycalc{'pct_connections_used'} = - ( $mycalc{'pct_connections_used'} > 100 ) - ? 100 - : $mycalc{'pct_connections_used'}; - - # Aborted Connections - $mycalc{'pct_connections_aborted'} = - percentage( $mystat{'Aborted_connects'}, $mystat{'Connections'} ); - debugprint "Aborted_connects: " . $mystat{'Aborted_connects'} . ""; - debugprint "Connections: " . $mystat{'Connections'} . ""; - debugprint "pct_connections_aborted: " - . $mycalc{'pct_connections_aborted'} . ""; - - # Key buffers - if ( mysql_version_ge( 4, 1 ) && $myvar{'key_buffer_size'} > 0 ) { - $mycalc{'pct_key_buffer_used'} = sprintf( - "%.1f", - ( - 1 - ( - ( - $mystat{'Key_blocks_unused'} * - $myvar{'key_cache_block_size'} - ) / $myvar{'key_buffer_size'} - ) - ) * 100 - ); - } - else { - $mycalc{'pct_key_buffer_used'} = 0; - } - - if ( $mystat{'Key_read_requests'} > 0 ) { - $mycalc{'pct_keys_from_mem'} = sprintf( - "%.1f", - ( - 100 - ( - ( $mystat{'Key_reads'} / $mystat{'Key_read_requests'} ) * - 100 - ) - ) - ); - } - else { - $mycalc{'pct_keys_from_mem'} = 0; - } - if ( defined $mystat{'Aria_pagecache_read_requests'} - && $mystat{'Aria_pagecache_read_requests'} > 0 ) - { - $mycalc{'pct_aria_keys_from_mem'} = sprintf( - "%.1f", - ( - 100 - ( - ( - $mystat{'Aria_pagecache_reads'} / - $mystat{'Aria_pagecache_read_requests'} - ) * 100 - ) - ) - ); - } - else { - $mycalc{'pct_aria_keys_from_mem'} = 0; - } - - if ( $mystat{'Key_write_requests'} > 0 ) { - $mycalc{'pct_wkeys_from_mem'} = sprintf( - "%.1f", - ( - 100 - ( - ( $mystat{'Key_writes'} / $mystat{'Key_write_requests'} ) * - 100 - ) - ) - ); - } - else { - $mycalc{'pct_wkeys_from_mem'} = 0; - } - - if ( $doremote eq 0 and !mysql_version_ge(5) ) { - my $size = 0; - $size += (split)[0] - for -`find $myvar{'datadir'} -name "*.MYI" 2>&1 | xargs du -L $duflags 2>&1`; - $mycalc{'total_myisam_indexes'} = $size; - $mycalc{'total_aria_indexes'} = 0; - } - elsif ( mysql_version_ge(5) ) { - $mycalc{'total_myisam_indexes'} = select_one -"SELECT IFNULL(SUM(INDEX_LENGTH),0) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema') AND ENGINE = 'MyISAM';"; - $mycalc{'total_aria_indexe'} = select_one -"SELECT IFNULL(SUM(INDEX_LENGTH),0) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema') AND ENGINE = 'Aria';"; - } - if ( defined $mycalc{'total_myisam_indexes'} - and $mycalc{'total_myisam_indexes'} == 0 ) - { - $mycalc{'total_myisam_indexes'} = "fail"; - } - elsif ( defined $mycalc{'total_myisam_indexes'} ) { - chomp( $mycalc{'total_myisam_indexes'} ); - } - if ( defined $mycalc{'total_aria_indexes'} - and $mycalc{'total_aria_indexes'} == 0 ) - { - $mycalc{'total_aria_indexes'} = "fail"; - } - elsif ( defined $mycalc{'total_aria_indexes'} ) { - chomp( $mycalc{'total_aria_indexes'} ); - } - - # Query cache - if ( mysql_version_ge(4) ) { - $mycalc{'query_cache_efficiency'} = sprintf( - "%.1f", - ( - $mystat{'Qcache_hits'} / - ( $mystat{'Com_select'} + $mystat{'Qcache_hits'} ) - ) * 100 - ); - if ( $myvar{'query_cache_size'} ) { - $mycalc{'pct_query_cache_used'} = sprintf( - "%.1f", - 100 - ( - $mystat{'Qcache_free_memory'} / $myvar{'query_cache_size'} - ) * 100 - ); - } - if ( $mystat{'Qcache_lowmem_prunes'} == 0 ) { - $mycalc{'query_cache_prunes_per_day'} = 0; - } - else { - $mycalc{'query_cache_prunes_per_day'} = int( - $mystat{'Qcache_lowmem_prunes'} / ( $mystat{'Uptime'} / 86400 ) - ); - } - } - - # Sorting - $mycalc{'total_sorts'} = $mystat{'Sort_scan'} + $mystat{'Sort_range'}; - if ( $mycalc{'total_sorts'} > 0 ) { - $mycalc{'pct_temp_sort_table'} = int( - ( $mystat{'Sort_merge_passes'} / $mycalc{'total_sorts'} ) * 100 ); - } - - # Joins - $mycalc{'joins_without_indexes'} = - $mystat{'Select_range_check'} + $mystat{'Select_full_join'}; - $mycalc{'joins_without_indexes_per_day'} = - int( $mycalc{'joins_without_indexes'} / ( $mystat{'Uptime'} / 86400 ) ); - - # Temporary tables - if ( $mystat{'Created_tmp_tables'} > 0 ) { - if ( $mystat{'Created_tmp_disk_tables'} > 0 ) { - $mycalc{'pct_temp_disk'} = int( - ( - $mystat{'Created_tmp_disk_tables'} / - $mystat{'Created_tmp_tables'} - ) * 100 - ); - } - else { - $mycalc{'pct_temp_disk'} = 0; - } - } - - # Table cache - if ( $mystat{'Opened_tables'} > 0 ) { - $mycalc{'table_cache_hit_rate'} = - int( $mystat{'Open_tables'} * 100 / $mystat{'Opened_tables'} ); - } - else { - $mycalc{'table_cache_hit_rate'} = 100; - } - - # Open files - if ( $myvar{'open_files_limit'} > 0 ) { - $mycalc{'pct_files_open'} = - int( $mystat{'Open_files'} * 100 / $myvar{'open_files_limit'} ); - } - - # Table locks - if ( $mystat{'Table_locks_immediate'} > 0 ) { - if ( $mystat{'Table_locks_waited'} == 0 ) { - $mycalc{'pct_table_locks_immediate'} = 100; - } - else { - $mycalc{'pct_table_locks_immediate'} = int( - $mystat{'Table_locks_immediate'} * 100 / ( - $mystat{'Table_locks_waited'} + - $mystat{'Table_locks_immediate'} - ) - ); - } - } - - # Thread cache - $mycalc{'thread_cache_hit_rate'} = - int( 100 - - ( ( $mystat{'Threads_created'} / $mystat{'Connections'} ) * 100 ) ); - - # Other - if ( $mystat{'Connections'} > 0 ) { - $mycalc{'pct_aborted_connections'} = - int( ( $mystat{'Aborted_connects'} / $mystat{'Connections'} ) * 100 ); - } - if ( $mystat{'Questions'} > 0 ) { - $mycalc{'total_reads'} = $mystat{'Com_select'}; - $mycalc{'total_writes'} = - $mystat{'Com_delete'} + - $mystat{'Com_insert'} + - $mystat{'Com_update'} + - $mystat{'Com_replace'}; - if ( $mycalc{'total_reads'} == 0 ) { - $mycalc{'pct_reads'} = 0; - $mycalc{'pct_writes'} = 100; - } - else { - $mycalc{'pct_reads'} = int( - ( - $mycalc{'total_reads'} / - ( $mycalc{'total_reads'} + $mycalc{'total_writes'} ) - ) * 100 - ); - $mycalc{'pct_writes'} = 100 - $mycalc{'pct_reads'}; - } - } - - # InnoDB - if ( $myvar{'have_innodb'} eq "YES" ) { - $mycalc{'innodb_log_size_pct'} = - ( $myvar{'innodb_log_file_size'} * 100 / - $myvar{'innodb_buffer_pool_size'} ); - } - ( - $mystat{'Innodb_buffer_pool_read_requests'}, - $mystat{'Innodb_buffer_pool_reads'} - ) - = ( 1, 1 ) - unless defined $mystat{'Innodb_buffer_pool_reads'}; - $mycalc{'pct_read_efficiency'} = percentage( - ( - $mystat{'Innodb_buffer_pool_read_requests'} - - $mystat{'Innodb_buffer_pool_reads'} - ), - $mystat{'Innodb_buffer_pool_read_requests'} - ) if defined $mystat{'Innodb_buffer_pool_read_requests'}; - debugprint "pct_read_efficiency: " . $mycalc{'pct_read_efficiency'} . ""; - debugprint "Innodb_buffer_pool_reads: " - . $mystat{'Innodb_buffer_pool_reads'} . ""; - debugprint "Innodb_buffer_pool_read_requests: " - . $mystat{'Innodb_buffer_pool_read_requests'} . ""; - ( - $mystat{'Innodb_buffer_pool_write_requests'}, - $mystat{'Innodb_buffer_pool_writes'} - ) - = ( 1, 1 ) - unless defined $mystat{'Innodb_buffer_pool_writes'}; - $mycalc{'pct_write_efficiency'} = percentage( - ( - $mystat{'Innodb_buffer_pool_write_requests'} - - $mystat{'Innodb_buffer_pool_writes'} - ), - $mystat{'Innodb_buffer_pool_write_requests'} - ) if defined $mystat{'Innodb_buffer_pool_write_requests'}; - debugprint "pct_write_efficiency: " . $mycalc{'pct_read_efficiency'} . ""; - debugprint "Innodb_buffer_pool_writes: " - . $mystat{'Innodb_buffer_pool_writes'} . ""; - debugprint "Innodb_buffer_pool_write_requests: " - . $mystat{'Innodb_buffer_pool_write_requests'} . ""; - $mycalc{'pct_innodb_buffer_used'} = percentage( - ( - $mystat{'Innodb_buffer_pool_pages_total'} - - $mystat{'Innodb_buffer_pool_pages_free'} - ), - $mystat{'Innodb_buffer_pool_pages_total'} - ) if defined $mystat{'Innodb_buffer_pool_pages_total'}; - - # Binlog Cache - if ( $myvar{'log_bin'} ne 'OFF' ) { - $mycalc{'pct_binlog_cache'} = percentage( - $mystat{'Binlog_cache_use'} - $mystat{'Binlog_cache_disk_use'}, - $mystat{'Binlog_cache_use'} ); - } -} - -sub mysql_stats { - prettyprint -"\n-------- Performance Metrics -------------------------------------------------"; - - # Show uptime, queries per second, connections, traffic stats - my $qps; - if ( $mystat{'Uptime'} > 0 ) { - $qps = sprintf( "%.3f", $mystat{'Questions'} / $mystat{'Uptime'} ); - } - push( @generalrec, - "MySQL started within last 24 hours - recommendations may be inaccurate" - ) if ( $mystat{'Uptime'} < 86400 ); - infoprint "Up for: " - . pretty_uptime( $mystat{'Uptime'} ) . " (" - . hr_num( $mystat{'Questions'} ) . " q [" - . hr_num($qps) - . " qps], " - . hr_num( $mystat{'Connections'} ) - . " conn," . " TX: " - . hr_num( $mystat{'Bytes_sent'} ) - . ", RX: " - . hr_num( $mystat{'Bytes_received'} ) . ")"; - infoprint "Reads / Writes: " - . $mycalc{'pct_reads'} . "% / " - . $mycalc{'pct_writes'} . "%"; - - # Binlog Cache - if ( $myvar{'log_bin'} eq 'OFF' ) { - infoprint "Binary logging is disabled"; - } - else { - infoprint "Binary logging is enabled (GTID MODE: " - . ( defined( $myvar{'gtid_mode'} ) ? $myvar{'gtid_mode'} : "OFF" ) - . ")"; - } - - # Memory usage - infoprint "Total buffers: " - . hr_bytes( $mycalc{'server_buffers'} ) - . " global + " - . hr_bytes( $mycalc{'per_thread_buffers'} ) - . " per thread ($myvar{'max_connections'} max threads)"; - - if ( $opt{buffers} ne 0 ) { - infoprint "Global Buffers"; - infoprint " +-- Key Buffer: " - . hr_bytes( $myvar{'key_buffer_size'} ) . ""; - infoprint " +-- Max Tmp Table: " - . hr_bytes( $mycalc{'max_tmp_table_size'} ) . ""; - - if ( defined $myvar{'query_cache_type'} ) { - infoprint "Query Cache Buffers"; - infoprint " +-- Query Cache: " - . $myvar{'query_cache_type'} . " - " - . ( - $myvar{'query_cache_type'} eq 0 | - $myvar{'query_cache_type'} eq 'OFF' ? "DISABLED" - : ( $myvar{'query_cache_type'} eq 1 ? "ALL REQUESTS" - : "ON DEMAND" ) - ) . ""; - infoprint " +-- Query Cache Size: " - . hr_bytes( $myvar{'query_cache_size'} ) . ""; - } - - infoprint "Per Thread Buffers"; - infoprint " +-- Read Buffer: " - . hr_bytes( $myvar{'read_buffer_size'} ) . ""; - infoprint " +-- Read RND Buffer: " - . hr_bytes( $myvar{'read_rnd_buffer_size'} ) . ""; - infoprint " +-- Sort Buffer: " - . hr_bytes( $myvar{'sort_buffer_size'} ) . ""; - infoprint " +-- Thread stack: " - . hr_bytes( $myvar{'thread_stack'} ) . ""; - infoprint " +-- Join Buffer: " - . hr_bytes( $myvar{'join_buffer_size'} ) . ""; - if ( $myvar{'log_bin'} ne 'OFF' ) { - infoprint "Binlog Cache Buffers"; - infoprint " +-- Binlog Cache: " - . hr_bytes( $myvar{'binlog_cache_size'} ) . ""; - } - } - - if ( $arch - && $arch == 32 - && $mycalc{'max_used_memory'} > 2 * 1024 * 1024 * 1024 ) - { - badprint -"Allocating > 2GB RAM on 32-bit systems can cause system instability"; - badprint "Maximum reached memory usage: " - . hr_bytes( $mycalc{'max_used_memory'} ) - . " ($mycalc{'pct_max_used_memory'}% of installed RAM)"; - } - elsif ( $mycalc{'pct_max_used_memory'} > 85 ) { - badprint "Maximum reached memory usage: " - . hr_bytes( $mycalc{'max_used_memory'} ) - . " ($mycalc{'pct_max_used_memory'}% of installed RAM)"; - } - else { - goodprint "Maximum reached memory usage: " - . hr_bytes( $mycalc{'max_used_memory'} ) - . " ($mycalc{'pct_max_used_memory'}% of installed RAM)"; - } - - if ( $mycalc{'pct_max_physical_memory'} > 85 ) { - badprint "Maximum possible memory usage: " - . hr_bytes( $mycalc{'max_peak_memory'} ) - . " ($mycalc{'pct_max_physical_memory'}% of installed RAM)"; - push( @generalrec, - "Reduce your overall MySQL memory footprint for system stability" ); - } - else { - goodprint "Maximum possible memory usage: " - . hr_bytes( $mycalc{'max_peak_memory'} ) - . " ($mycalc{'pct_max_physical_memory'}% of installed RAM)"; - } - - # Slow queries - if ( $mycalc{'pct_slow_queries'} > 5 ) { - badprint "Slow queries: $mycalc{'pct_slow_queries'}% (" - . hr_num( $mystat{'Slow_queries'} ) . "/" - . hr_num( $mystat{'Questions'} ) . ")"; - } - else { - goodprint "Slow queries: $mycalc{'pct_slow_queries'}% (" - . hr_num( $mystat{'Slow_queries'} ) . "/" - . hr_num( $mystat{'Questions'} ) . ")"; - } - if ( $myvar{'long_query_time'} > 10 ) { - push( @adjvars, "long_query_time (<= 10)" ); - } - if ( defined( $myvar{'log_slow_queries'} ) ) { - if ( $myvar{'log_slow_queries'} eq "OFF" ) { - push( @generalrec, - "Enable the slow query log to troubleshoot bad queries" ); - } - } - - # Connections - if ( $mycalc{'pct_connections_used'} > 85 ) { - badprint -"Highest connection usage: $mycalc{'pct_connections_used'}% ($mystat{'Max_used_connections'}/$myvar{'max_connections'})"; - push( @adjvars, - "max_connections (> " . $myvar{'max_connections'} . ")" ); - push( @adjvars, - "wait_timeout (< " . $myvar{'wait_timeout'} . ")", - "interactive_timeout (< " . $myvar{'interactive_timeout'} . ")" ); - push( @generalrec, -"Reduce or eliminate persistent connections to reduce connection usage" - ); - } - else { - goodprint -"Highest usage of available connections: $mycalc{'pct_connections_used'}% ($mystat{'Max_used_connections'}/$myvar{'max_connections'})"; - } - - # Aborted Connections - if ( $mycalc{'pct_connections_aborted'} > 3 ) { - badprint -"Aborted connections: $mycalc{'pct_connections_aborted'}% ($mystat{'Aborted_connects'}/$mystat{'Connections'})"; - push( @generalrec, - "Reduce or eliminate unclosed connections and network issues" ); - } - else { - goodprint -"Aborted connections: $mycalc{'pct_connections_aborted'}% ($mystat{'Aborted_connects'}/$mystat{'Connections'})"; - } - - # Query cache - if ( !mysql_version_ge(4) ) { - - # MySQL versions < 4.01 don't support query caching - push( @generalrec, - "Upgrade MySQL to version 4+ to utilize query caching" ); - } - elsif ( $myvar{'query_cache_size'} < 1 ) { - badprint "Query cache is disabled"; - push( @adjvars, "query_cache_size (>= 8M)" ); - } - elsif ( $myvar{'query_cache_type'} eq "OFF" ) { - badprint "Query cache is disabled"; - push( @adjvars, "query_cache_type (=1)" ); - } - elsif ( $mystat{'Com_select'} == 0 ) { - badprint - "Query cache cannot be analyzed - no SELECT statements executed"; - } - else { - if ( $mycalc{'query_cache_efficiency'} < 20 ) { - badprint - "Query cache efficiency: $mycalc{'query_cache_efficiency'}% (" - . hr_num( $mystat{'Qcache_hits'} ) - . " cached / " - . hr_num( $mystat{'Qcache_hits'} + $mystat{'Com_select'} ) - . " selects)"; - push( @adjvars, - "query_cache_limit (> " - . hr_bytes_rnd( $myvar{'query_cache_limit'} ) - . ", or use smaller result sets)" ); - } - else { - goodprint - "Query cache efficiency: $mycalc{'query_cache_efficiency'}% (" - . hr_num( $mystat{'Qcache_hits'} ) - . " cached / " - . hr_num( $mystat{'Qcache_hits'} + $mystat{'Com_select'} ) - . " selects)"; - } - if ( $mycalc{'query_cache_prunes_per_day'} > 98 ) { - badprint -"Query cache prunes per day: $mycalc{'query_cache_prunes_per_day'}"; - if ( $myvar{'query_cache_size'} >= 128 * 1024 * 1024 ) { - push( @generalrec, -"Increasing the query_cache size over 128M may reduce performance" - ); - push( @adjvars, - "query_cache_size (> " - . hr_bytes_rnd( $myvar{'query_cache_size'} ) - . ") [see warning above]" ); - } - else { - push( @adjvars, - "query_cache_size (> " - . hr_bytes_rnd( $myvar{'query_cache_size'} ) - . ")" ); - } - } - else { - goodprint -"Query cache prunes per day: $mycalc{'query_cache_prunes_per_day'}"; - } - } - - # Sorting - if ( $mycalc{'total_sorts'} == 0 ) { - - # For the sake of space, we will be quiet here - # No sorts have run yet - } - elsif ( $mycalc{'pct_temp_sort_table'} > 10 ) { - badprint - "Sorts requiring temporary tables: $mycalc{'pct_temp_sort_table'}% (" - . hr_num( $mystat{'Sort_merge_passes'} ) - . " temp sorts / " - . hr_num( $mycalc{'total_sorts'} ) - . " sorts)"; - push( @adjvars, - "sort_buffer_size (> " - . hr_bytes_rnd( $myvar{'sort_buffer_size'} ) - . ")" ); - push( @adjvars, - "read_rnd_buffer_size (> " - . hr_bytes_rnd( $myvar{'read_rnd_buffer_size'} ) - . ")" ); - } - else { - goodprint - "Sorts requiring temporary tables: $mycalc{'pct_temp_sort_table'}% (" - . hr_num( $mystat{'Sort_merge_passes'} ) - . " temp sorts / " - . hr_num( $mycalc{'total_sorts'} ) - . " sorts)"; - } - - # Joins - if ( $mycalc{'joins_without_indexes_per_day'} > 250 ) { - badprint - "Joins performed without indexes: $mycalc{'joins_without_indexes'}"; - push( @adjvars, - "join_buffer_size (> " - . hr_bytes( $myvar{'join_buffer_size'} ) - . ", or always use indexes with joins)" ); - push( @generalrec, - "Adjust your join queries to always utilize indexes" ); - } - else { - - # For the sake of space, we will be quiet here - # No joins have run without indexes - } - - # Temporary tables - if ( $mystat{'Created_tmp_tables'} > 0 ) { - if ( $mycalc{'pct_temp_disk'} > 25 - && $mycalc{'max_tmp_table_size'} < 256 * 1024 * 1024 ) - { - badprint - "Temporary tables created on disk: $mycalc{'pct_temp_disk'}% (" - . hr_num( $mystat{'Created_tmp_disk_tables'} ) - . " on disk / " - . hr_num( $mystat{'Created_tmp_tables'} ) - . " total)"; - push( @adjvars, - "tmp_table_size (> " - . hr_bytes_rnd( $myvar{'tmp_table_size'} ) - . ")" ); - push( @adjvars, - "max_heap_table_size (> " - . hr_bytes_rnd( $myvar{'max_heap_table_size'} ) - . ")" ); - push( @generalrec, -"When making adjustments, make tmp_table_size/max_heap_table_size equal" - ); - push( @generalrec, - "Reduce your SELECT DISTINCT queries which have no LIMIT clause" ); - } - elsif ($mycalc{'pct_temp_disk'} > 25 - && $mycalc{'max_tmp_table_size'} >= 256 * 1024 * 1024 ) - { - badprint - "Temporary tables created on disk: $mycalc{'pct_temp_disk'}% (" - . hr_num( $mystat{'Created_tmp_disk_tables'} ) - . " on disk / " - . hr_num( $mystat{'Created_tmp_tables'} ) - . " total)"; - push( @generalrec, - "Temporary table size is already large - reduce result set size" - ); - push( @generalrec, - "Reduce your SELECT DISTINCT queries without LIMIT clauses" ); - } - else { - goodprint - "Temporary tables created on disk: $mycalc{'pct_temp_disk'}% (" - . hr_num( $mystat{'Created_tmp_disk_tables'} ) - . " on disk / " - . hr_num( $mystat{'Created_tmp_tables'} ) - . " total)"; - } - } - else { - - # For the sake of space, we will be quiet here - # No temporary tables have been created - } - - # Thread cache - if ( $myvar{'thread_cache_size'} eq 0 ) { - badprint "Thread cache is disabled"; - push( @generalrec, "Set thread_cache_size to 4 as a starting value" ); - push( @adjvars, "thread_cache_size (start at 4)" ); - } - else { - if ( $mycalc{'thread_cache_hit_rate'} <= 50 ) { - badprint - "Thread cache hit rate: $mycalc{'thread_cache_hit_rate'}% (" - . hr_num( $mystat{'Threads_created'} ) - . " created / " - . hr_num( $mystat{'Connections'} ) - . " connections)"; - push( @adjvars, - "thread_cache_size (> $myvar{'thread_cache_size'})" ); - } - else { - goodprint - "Thread cache hit rate: $mycalc{'thread_cache_hit_rate'}% (" - . hr_num( $mystat{'Threads_created'} ) - . " created / " - . hr_num( $mystat{'Connections'} ) - . " connections)"; - } - } - - # Table cache - my $table_cache_var = ""; - if ( $mystat{'Open_tables'} > 0 ) { - if ( $mycalc{'table_cache_hit_rate'} < 20 ) { - badprint "Table cache hit rate: $mycalc{'table_cache_hit_rate'}% (" - . hr_num( $mystat{'Open_tables'} ) - . " open / " - . hr_num( $mystat{'Opened_tables'} ) - . " opened)"; - if ( mysql_version_ge( 5, 1 ) ) { - $table_cache_var = "table_open_cache"; - } - else { - $table_cache_var = "table_cache"; - } - - push( @adjvars, - $table_cache_var . " (> " . $myvar{$table_cache_var} . ")" ); - push( @generalrec, - "Increase " - . $table_cache_var - . " gradually to avoid file descriptor limits" ); - push( @generalrec, - "Read this before increasing " - . $table_cache_var - . " over 64: http://bit.ly/1mi7c4C" ); - push( @generalrec, - "Beware that open_files_limit (" - . $myvar{'open_files_limit'} - . ") variable " ); - push( @generalrec, - "should be greater than $table_cache_var ( " - . $myvar{$table_cache_var} - . ")" ); - } - else { - goodprint "Table cache hit rate: $mycalc{'table_cache_hit_rate'}% (" - . hr_num( $mystat{'Open_tables'} ) - . " open / " - . hr_num( $mystat{'Opened_tables'} ) - . " opened)"; - } - } - - # Open files - if ( defined $mycalc{'pct_files_open'} ) { - if ( $mycalc{'pct_files_open'} > 85 ) { - badprint "Open file limit used: $mycalc{'pct_files_open'}% (" - . hr_num( $mystat{'Open_files'} ) . "/" - . hr_num( $myvar{'open_files_limit'} ) . ")"; - push( @adjvars, - "open_files_limit (> " . $myvar{'open_files_limit'} . ")" ); - } - else { - goodprint "Open file limit used: $mycalc{'pct_files_open'}% (" - . hr_num( $mystat{'Open_files'} ) . "/" - . hr_num( $myvar{'open_files_limit'} ) . ")"; - } - } - - # Table locks - if ( defined $mycalc{'pct_table_locks_immediate'} ) { - if ( $mycalc{'pct_table_locks_immediate'} < 95 ) { - badprint -"Table locks acquired immediately: $mycalc{'pct_table_locks_immediate'}%"; - push( @generalrec, - "Optimize queries and/or use InnoDB to reduce lock wait" ); - } - else { - goodprint -"Table locks acquired immediately: $mycalc{'pct_table_locks_immediate'}% (" - . hr_num( $mystat{'Table_locks_immediate'} ) - . " immediate / " - . hr_num( $mystat{'Table_locks_waited'} + - $mystat{'Table_locks_immediate'} ) - . " locks)"; - } - } - - # Binlog cache - if ( defined $mycalc{'pct_binlog_cache'} ) { - if ( $mycalc{'pct_binlog_cache'} < 90 - && $mystat{'Binlog_cache_use'} > 0 ) - { - badprint "Binlog cache memory access: " - . $mycalc{'pct_binlog_cache'} . "% ( " - . ( - $mystat{'Binlog_cache_use'} - $mystat{'Binlog_cache_disk_use'} ) - . " Memory / " - . $mystat{'Binlog_cache_use'} - . " Total)"; - push( @generalrec, - "Increase binlog_cache_size (Actual value: " - . $myvar{'binlog_cache_size'} - . ") " ); - push( @adjvars, - "binlog_cache_size (" - . hr_bytes( $myvar{'binlog_cache_size'} + 16 * 1024 * 1024 ) - . " ) " ); - } - else { - goodprint "Binlog cache memory access: " - . $mycalc{'pct_binlog_cache'} . "% ( " - . ( - $mystat{'Binlog_cache_use'} - $mystat{'Binlog_cache_disk_use'} ) - . " Memory / " - . $mystat{'Binlog_cache_use'} - . " Total)"; - debugprint "Not enought data to validate binlog cache size\n" - if $mystat{'Binlog_cache_use'} < 10; - } - } - - # Performance options - if ( !mysql_version_ge( 5, 1 ) ) { - push( @generalrec, "Upgrade to MySQL 5.5+ to use asynchrone write" ); - } - elsif ( $myvar{'concurrent_insert'} eq "OFF" ) { - push( @generalrec, "Enable concurrent_insert by setting it to 'ON'" ); - } - elsif ( $myvar{'concurrent_insert'} eq 0 ) { - push( @generalrec, "Enable concurrent_insert by setting it to 1" ); - } -} - -# Recommandations for MyISAM -sub mysql_myisam { - prettyprint -"\n-------- MyISAM Metrics -----------------------------------------------------"; - - # AriaDB - - # Key buffer usage - if ( defined( $mycalc{'pct_key_buffer_used'} ) ) { - if ( $mycalc{'pct_key_buffer_used'} < 90 ) { - badprint "Key buffer used: $mycalc{'pct_key_buffer_used'}% (" - . hr_num( $myvar{'key_buffer_size'} * - $mycalc{'pct_key_buffer_used'} / - 100 ) - . " used / " - . hr_num( $myvar{'key_buffer_size'} ) - . " cache)"; - -#push(@adjvars,"key_buffer_size (\~ ".hr_num( $myvar{'key_buffer_size'} * $mycalc{'pct_key_buffer_used'} / 100).")"); - } - else { - goodprint "Key buffer used: $mycalc{'pct_key_buffer_used'}% (" - . hr_num( $myvar{'key_buffer_size'} * - $mycalc{'pct_key_buffer_used'} / - 100 ) - . " used / " - . hr_num( $myvar{'key_buffer_size'} ) - . " cache)"; - } - } - else { - - # No queries have run that would use keys - debugprint "Key buffer used: $mycalc{'pct_key_buffer_used'}% (" - . hr_num( - $myvar{'key_buffer_size'} * $mycalc{'pct_key_buffer_used'} / 100 ) - . " used / " - . hr_num( $myvar{'key_buffer_size'} ) - . " cache)"; - } - - # Key buffer - if ( !defined( $mycalc{'total_myisam_indexes'} ) and $doremote == 1 ) { - push( @generalrec, - "Unable to calculate MyISAM indexes on remote MySQL server < 5.0.0" - ); - } - elsif ( $mycalc{'total_myisam_indexes'} =~ /^fail$/ ) { - badprint - "Cannot calculate MyISAM index size - re-run script as root user"; - } - elsif ( $mycalc{'total_myisam_indexes'} == "0" ) { - badprint - "None of your MyISAM tables are indexed - add indexes immediately"; - } - else { - if ( $myvar{'key_buffer_size'} < $mycalc{'total_myisam_indexes'} - && $mycalc{'pct_keys_from_mem'} < 95 ) - { - badprint "Key buffer size / total MyISAM indexes: " - . hr_bytes( $myvar{'key_buffer_size'} ) . "/" - . hr_bytes( $mycalc{'total_myisam_indexes'} ) . ""; - push( @adjvars, - "key_buffer_size (> " - . hr_bytes( $mycalc{'total_myisam_indexes'} ) - . ")" ); - } - else { - goodprint "Key buffer size / total MyISAM indexes: " - . hr_bytes( $myvar{'key_buffer_size'} ) . "/" - . hr_bytes( $mycalc{'total_myisam_indexes'} ) . ""; - } - if ( $mystat{'Key_read_requests'} > 0 ) { - if ( $mycalc{'pct_keys_from_mem'} < 95 ) { - badprint - "Read Key buffer hit rate: $mycalc{'pct_keys_from_mem'}% (" - . hr_num( $mystat{'Key_read_requests'} ) - . " cached / " - . hr_num( $mystat{'Key_reads'} ) - . " reads)"; - } - else { - goodprint - "Read Key buffer hit rate: $mycalc{'pct_keys_from_mem'}% (" - . hr_num( $mystat{'Key_read_requests'} ) - . " cached / " - . hr_num( $mystat{'Key_reads'} ) - . " reads)"; - } - } - else { - - # No queries have run that would use keys - debugprint "Key buffer size / total MyISAM indexes: " - . hr_bytes( $myvar{'key_buffer_size'} ) . "/" - . hr_bytes( $mycalc{'total_myisam_indexes'} ) . ""; - } - if ( $mystat{'Key_write_requests'} > 0 ) { - if ( $mycalc{'pct_wkeys_from_mem'} < 95 ) { - badprint - "Write Key buffer hit rate: $mycalc{'pct_wkeys_from_mem'}% (" - . hr_num( $mystat{'Key_write_requests'} ) - . " cached / " - . hr_num( $mystat{'Key_writes'} ) - . " writes)"; - } - else { - goodprint - "Write Key buffer hit rate: $mycalc{'pct_wkeys_from_mem'}% (" - . hr_num( $mystat{'Key_write_requests'} ) - . " cached / " - . hr_num( $mystat{'Key_writes'} ) - . " writes)"; - } - } - else { - - # No queries have run that would use keys - debugprint - "Write Key buffer hit rate: $mycalc{'pct_wkeys_from_mem'}% (" - . hr_num( $mystat{'Key_write_requests'} ) - . " cached / " - . hr_num( $mystat{'Key_writes'} ) - . " writes)"; - } - } -} - -# Recommandations for Ariadb -sub mysql_ariadb { - prettyprint -"\n-------- AriaDB Metrics -----------------------------------------------------"; - - # AriaDB - unless ( defined $myvar{'have_aria'} - && $myvar{'have_aria'} eq "YES" - && defined $enginestats{'Aria'} ) - { - infoprint "AriaDB is disabled."; - return; - } - infoprint "AriaDB is enabled."; - - # Aria pagecache - if ( !defined( $mycalc{'total_aria_indexes'} ) and $doremote == 1 ) { - push( @generalrec, - "Unable to calculate Aria indexes on remote MySQL server < 5.0.0" ); - } - elsif ( $mycalc{'total_aria_indexes'} =~ /^fail$/ ) { - badprint - "Cannot calculate Aria index size - re-run script as root user"; - } - elsif ( $mycalc{'total_aria_indexes'} == "0" ) { - badprint - "None of your Aria tables are indexed - add indexes immediately"; - } - else { - if ( - $myvar{'aria_pagecache_buffer_size'} < $mycalc{'total_aria_indexes'} - && $mycalc{'pct_aria_keys_from_mem'} < 95 ) - { - badprint "Aria pagecache size / total Aria indexes: " - . hr_bytes( $myvar{'aria_pagecache_buffer_size'} ) . "/" - . hr_bytes( $mycalc{'total_aria_indexes'} ) . ""; - push( @adjvars, - "aria_pagecache_buffer_size (> " - . hr_bytes( $mycalc{'total_aria_indexes'} ) - . ")" ); - } - else { - goodprint "Aria pagecache size / total Aria indexes: " - . hr_bytes( $myvar{'aria_pagecache_buffer_size'} ) . "/" - . hr_bytes( $mycalc{'total_aria_indexes'} ) . ""; - } - if ( $mystat{'Aria_pagecache_read_requests'} > 0 ) { - if ( $mycalc{'pct_aria_keys_from_mem'} < 95 ) { - badprint -"Aria pagecache hit rate: $mycalc{'pct_aria_keys_from_mem'}% (" - . hr_num( $mystat{'Aria_pagecache_read_requests'} ) - . " cached / " - . hr_num( $mystat{'Aria_pagecache_reads'} ) - . " reads)"; - } - else { - goodprint -"Aria pagecache hit rate: $mycalc{'pct_aria_keys_from_mem'}% (" - . hr_num( $mystat{'Aria_pagecache_read_requests'} ) - . " cached / " - . hr_num( $mystat{'Aria_pagecache_reads'} ) - . " reads)"; - } - } - else { - - # No queries have run that would use keys - } - } -} - -# Recommandations for Innodb -sub mysql_innodb { - prettyprint -"\n-------- InnoDB Metrics -----------------------------------------------------"; - - # InnoDB - unless ( defined $myvar{'have_innodb'} - && $myvar{'have_innodb'} eq "YES" - && defined $enginestats{'InnoDB'} ) - { - infoprint "InnoDB is disabled."; - if ( mysql_version_ge( 5, 5 ) ) { - badprint -"InnoDB Storage engine is disabled. InnoDB is the default storage engine"; - } - return; - } - infoprint "InnoDB is enabled."; - - if ( $opt{buffers} ne 0 ) { - infoprint "InnoDB Buffers"; - if ( defined $myvar{'innodb_buffer_pool_size'} ) { - infoprint " +-- InnoDB Buffer Pool: " - . hr_bytes( $myvar{'innodb_buffer_pool_size'} ) . ""; - } - if ( defined $myvar{'innodb_buffer_pool_instances'} ) { - infoprint " +-- InnoDB Buffer Pool Instances: " - . $myvar{'innodb_buffer_pool_instances'} . ""; - } - if ( defined $myvar{'innodb_additional_mem_pool_size'} ) { - infoprint " +-- InnoDB Additional Mem Pool: " - . hr_bytes( $myvar{'innodb_additional_mem_pool_size'} ) . ""; - } - if ( defined $myvar{'innodb_log_buffer_size'} ) { - infoprint " +-- InnoDB Log Buffer: " - . hr_bytes( $myvar{'innodb_log_buffer_size'} ) . ""; - } - if ( defined $mystat{'Innodb_buffer_pool_pages_free'} ) { - infoprint " +-- InnoDB Log Buffer Free: " - . hr_bytes( $mystat{'Innodb_buffer_pool_pages_free'} ) . ""; - } - if ( defined $mystat{'Innodb_buffer_pool_pages_total'} ) { - infoprint " +-- InnoDB Log Buffer Used: " - . hr_bytes( $mystat{'Innodb_buffer_pool_pages_total'} ) . ""; - } - } - - # InnoDB Buffer Pull Size - if ( $myvar{'innodb_buffer_pool_size'} > $enginestats{'InnoDB'} ) { - goodprint "InnoDB buffer pool / data size: " - . hr_bytes( $myvar{'innodb_buffer_pool_size'} ) . "/" - . hr_bytes( $enginestats{'InnoDB'} ) . ""; - } - else { - badprint "InnoDB buffer pool / data size: " - . hr_bytes( $myvar{'innodb_buffer_pool_size'} ) . "/" - . hr_bytes( $enginestats{'InnoDB'} ) . ""; - push( @adjvars, - "innodb_buffer_pool_size (>= " - . hr_bytes_rnd( $enginestats{'InnoDB'} ) - . ") if possible." ); - } - - # InnoDB Buffer Pull Instances (MySQL 5.6.6+) - if ( defined( $myvar{'innodb_buffer_pool_instances'} ) ) { - - # Bad Value if > 64 - if ( $myvar{'innodb_buffer_pool_instances'} > 64 ) { - badprint "InnoDB buffer pool instances: " - . $myvar{'innodb_buffer_pool_instances'} . ""; - push( @adjvars, "innodb_buffer_pool_instances (<= 64)" ); - } - - # InnoDB Buffer Pull Size > 1Go - if ( $myvar{'innodb_buffer_pool_size'} > 1024 * 1024 * 1024 ) { - -# InnoDB Buffer Pull Size / 1Go = InnoDB Buffer Pull Instances limited to 64 max. - - # InnoDB Buffer Pull Size > 64Go - my $max_innodb_buffer_pool_instances = - int( $myvar{'innodb_buffer_pool_size'} / ( 1024 * 1024 * 1024 ) ); - $max_innodb_buffer_pool_instances = 64 - if ( $max_innodb_buffer_pool_instances > 64 ); - - if ( $myvar{'innodb_buffer_pool_instances'} != - $max_innodb_buffer_pool_instances ) - { - badprint "InnoDB buffer pool instances: " - . $myvar{'innodb_buffer_pool_instances'} . ""; - push( @adjvars, - "innodb_buffer_pool_instances(=" - . $max_innodb_buffer_pool_instances - . ")" ); - } - else { - goodprint "InnoDB buffer pool instances: " - . $myvar{'innodb_buffer_pool_instances'} . ""; - } - - # InnoDB Buffer Pull Size < 1Go - } - else { - if ( $myvar{'innodb_buffer_pool_instances'} != 1 ) { - badprint -"InnoDB buffer pool <= 1G and innodb_buffer_pool_instances(!=1)."; - push( @adjvars, "innodb_buffer_pool_instances (=1)" ); - } - else { - goodprint "InnoDB buffer pool instances: " - . $myvar{'innodb_buffer_pool_instances'} . ""; - } - } - } - - # InnoDB Used Buffer Pool - if ( defined $mycalc{'pct_innodb_buffer_used'} - && $mycalc{'pct_innodb_buffer_used'} < 80 ) - { - badprint "InnoDB Used buffer: " - . $mycalc{'pct_innodb_buffer_used'} . "% (" - . ( $mystat{'Innodb_buffer_pool_pages_total'} - - $mystat{'Innodb_buffer_pool_pages_free'} ) - . " used/ " - . $mystat{'Innodb_buffer_pool_pages_total'} - . " total)"; - } - else { - goodprint "InnoDB Used buffer: " - . $mycalc{'pct_innodb_buffer_used'} . "% (" - . ( $mystat{'Innodb_buffer_pool_pages_total'} - - $mystat{'Innodb_buffer_pool_pages_free'} ) - . " used/ " - . $mystat{'Innodb_buffer_pool_pages_total'} - . " total)"; - } - - # InnoDB Read efficency - if ( defined $mycalc{'pct_read_efficiency'} - && $mycalc{'pct_read_efficiency'} < 90 ) - { - badprint "InnoDB Read buffer efficiency: " - . $mycalc{'pct_read_efficiency'} . "% (" - . ( $mystat{'Innodb_buffer_pool_read_requests'} - - $mystat{'Innodb_buffer_pool_reads'} ) - . " hits/ " - . $mystat{'Innodb_buffer_pool_read_requests'} - . " total)"; - } - else { - goodprint "InnoDB Read buffer efficiency: " - . $mycalc{'pct_read_efficiency'} . "% (" - . ( $mystat{'Innodb_buffer_pool_read_requests'} - - $mystat{'Innodb_buffer_pool_reads'} ) - . " hits/ " - . $mystat{'Innodb_buffer_pool_read_requests'} - . " total)"; - } - - # InnoDB Write efficiency - if ( defined $mycalc{'pct_write_efficiency'} - && $mycalc{'pct_write_efficiency'} < 90 ) - { - badprint "InnoDB Write buffer efficiency: " - . $mycalc{'pct_write_efficiency'} . "% (" - . ( $mystat{'Innodb_buffer_pool_write_requests'} - - $mystat{'Innodb_buffer_pool_writes'} ) - . " hits/ " - . $mystat{'Innodb_buffer_pool_write_requests'} - . " total)"; - } - else { - goodprint "InnoDB Write buffer efficiency: " - . $mycalc{'pct_write_efficiency'} . "% (" - . ( $mystat{'Innodb_buffer_pool_write_requests'} - - $mystat{'Innodb_buffer_pool_writes'} ) - . " hits/ " - . $mystat{'Innodb_buffer_pool_write_requests'} - . " total)"; - } - - # InnoDB Log Waits - if ( defined $mystat{'Innodb_log_waits'} - && $mystat{'Innodb_log_waits'} > 0 ) - { - badprint "InnoDB log waits: " - . percentage( $mystat{'Innodb_log_waits'}, - $mystat{'Innodb_log_writes'} ) - . "% (" - . $mystat{'Innodb_log_waits'} - . " waits / " - . $mystat{'Innodb_log_writes'} - . " writes)"; - push( @adjvars, - "innodb_log_buffer_size (>= " - . hr_bytes_rnd( $myvar{'innodb_log_buffer_size'} ) - . ")" ); - } - else { - goodprint "InnoDB log waits: " - . percentage( $mystat{'Innodb_log_waits'}, - $mystat{'Innodb_log_writes'} ) - . "% (" - . $mystat{'Innodb_log_waits'} - . " waits / " - . $mystat{'Innodb_log_writes'} - . " writes)"; - } - $result{'Calculations'} = {%mycalc}; -} - -# Recommandations for MySQL Databases -sub mysql_databases { - return if ( $opt{dbstat} == 0 ); - - prettyprint -"\n-------- Database Metrics ------------------------------------------------"; - unless ( mysql_version_ge( 5, 5 ) ) { - infoprint -"Skip Database metrics from information schema missing in this version"; - return; - } - - my @dblist = select_array("SHOW DATABASES;"); - infoprint "There is " . scalar(@dblist) . " Database(s)."; - my @totaldbinfo = split /\s/, - select_one( -"SELECT SUM(TABLE_ROWS), SUM(DATA_LENGTH), SUM(INDEX_LENGTH) , SUM(DATA_LENGTH+INDEX_LENGTH) FROM information_schema.TABLES;" - ); - infoprint "All Databases:"; - infoprint " +-- ROWS : " - . ( $totaldbinfo[0] eq 'NULL' ? 0 : $totaldbinfo[0] ) . ""; - infoprint " +-- DATA : " - . hr_bytes( $totaldbinfo[1] ) . "(" - . percentage( $totaldbinfo[1], $totaldbinfo[3] ) . "%)"; - infoprint " +-- INDEX: " - . hr_bytes( $totaldbinfo[2] ) . "(" - . percentage( $totaldbinfo[2], $totaldbinfo[3] ) . "%)"; - infoprint " +-- SIZE : " . hr_bytes( $totaldbinfo[3] ) . ""; - - badprint "Index size is larger than data size \n" - if $totaldbinfo[1] < $totaldbinfo[2]; - - $result{'Databases'}{'All databases'}{'Rows'} = - ( $totaldbinfo[0] eq 'NULL' ? 0 : $totaldbinfo[0] ); - $result{'Databases'}{'All databases'}{'Data Size'} = $totaldbinfo[1]; - $result{'Databases'}{'All databases'}{'Data Pct'} = - percentage( $totaldbinfo[1], $totaldbinfo[3] ) . "%"; - $result{'Databases'}{'All databases'}{'Index Size'} = $totaldbinfo[2]; - $result{'Databases'}{'All databases'}{'Index Pct'} = - percentage( $totaldbinfo[2], $totaldbinfo[3] ) . "%"; - $result{'Databases'}{'All databases'}{'Total Size'} = $totaldbinfo[3]; - - foreach (@dblist) { - chomp($_); - if ( $_ eq "information_schema" - or $_ eq "performance_schema" - or $_ eq "mysql" - or $_ eq "" ) - { - next; - } - - my @dbinfo = split /\s/, - select_one( -"SELECT TABLE_SCHEMA, SUM(TABLE_ROWS), SUM(DATA_LENGTH), SUM(INDEX_LENGTH) , SUM(DATA_LENGTH+INDEX_LENGTH), COUNT(DISTINCT ENGINE) FROM information_schema.TABLES WHERE TABLE_SCHEMA='$_' GROUP BY TABLE_SCHEMA ORDER BY TABLE_SCHEMA" - ); - next unless defined $dbinfo[0]; - infoprint "Database: " . $dbinfo[0] . ""; - infoprint " +-- ROWS : " - . ( !defined( $dbinfo[1] ) or $dbinfo[1] eq 'NULL' ? 0 : $dbinfo[1] ) - . ""; - infoprint " +-- DATA : " - . hr_bytes( $dbinfo[2] ) . "(" - . percentage( $dbinfo[2], $dbinfo[4] ) . "%)"; - infoprint " +-- INDEX: " - . hr_bytes( $dbinfo[3] ) . "(" - . percentage( $dbinfo[3], $dbinfo[4] ) . "%)"; - infoprint " +-- TOTAL: " . hr_bytes( $dbinfo[4] ) . ""; - badprint "Index size is larger than data size for $dbinfo[0] \n" - if $dbinfo[2] < $dbinfo[3]; - badprint "There " . $dbinfo[5] . " storage engines. Be careful \n" - if $dbinfo[5] > 1; - $result{'Databases'}{ $dbinfo[0] }{'Rows'} = $dbinfo[1]; - $result{'Databases'}{ $dbinfo[0] }{'Data Size'} = $dbinfo[2]; - $result{'Databases'}{ $dbinfo[0] }{'Data Pct'} = - percentage( $dbinfo[2], $dbinfo[4] ) . "%"; - $result{'Databases'}{ $dbinfo[0] }{'Index Size'} = $dbinfo[3]; - $result{'Databases'}{ $dbinfo[0] }{'Index Pct'} = - percentage( $dbinfo[3], $dbinfo[4] ) . "%"; - $result{'Databases'}{ $dbinfo[0] }{'Total Size'} = $dbinfo[4]; - } -} - -# Recommandations for MySQL Databases -sub mysql_indexes { - return if ( $opt{idxstat} == 0 ); - - prettyprint -"\n-------- Indexes Metrics -------------------------------------------------"; - unless ( mysql_version_ge( 5, 5 ) ) { - infoprint -"Skip Index metrics from information schema missing in this version"; - return; - } - my $selIdxReq = <<'ENDSQL'; -SELECT - CONCAT(CONCAT(t.TABLE_SCHEMA, '.'),t.TABLE_NAME) AS 'table' - , CONCAT(CONCAT(CONCAT(s.INDEX_NAME, '('),s.COLUMN_NAME), ')') AS 'index' - , s.SEQ_IN_INDEX AS 'seq' - , s2.max_columns AS 'maxcol' - , s.CARDINALITY AS 'card' - , t.TABLE_ROWS AS 'est_rows' - , ROUND(((s.CARDINALITY / IFNULL(t.TABLE_ROWS, 0.01)) * 100), 2) AS 'sel' -FROM INFORMATION_SCHEMA.STATISTICS s - INNER JOIN INFORMATION_SCHEMA.TABLES t - ON s.TABLE_SCHEMA = t.TABLE_SCHEMA - AND s.TABLE_NAME = t.TABLE_NAME - INNER JOIN ( - SELECT - TABLE_SCHEMA - , TABLE_NAME - , INDEX_NAME - , MAX(SEQ_IN_INDEX) AS max_columns - FROM INFORMATION_SCHEMA.STATISTICS - WHERE TABLE_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema') - GROUP BY TABLE_SCHEMA, TABLE_NAME, INDEX_NAME - ) AS s2 - ON s.TABLE_SCHEMA = s2.TABLE_SCHEMA - AND s.TABLE_NAME = s2.TABLE_NAME - AND s.INDEX_NAME = s2.INDEX_NAME -WHERE t.TABLE_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema') -AND t.TABLE_ROWS > 10 -AND s.CARDINALITY IS NOT NULL -AND (s.CARDINALITY / IFNULL(t.TABLE_ROWS, 0.01)) < 8.00 -ORDER BY sel -LIMIT 10; -ENDSQL - my @idxinfo = select_array($selIdxReq); - infoprint "Worst selectivity indexes:"; - foreach (@idxinfo) { - debugprint "$_"; - my @info = split /\s/; - infoprint "Index: " . $info[1] . ""; - - infoprint " +-- COLUNM : " . $info[0] . ""; - infoprint " +-- NB SEQS : " . $info[2] . " sequence(s)"; - infoprint " +-- NB COLS : " . $info[3] . " column(s)"; - infoprint " +-- CARDINALITY : " . $info[4] . " distinct values"; - infoprint " +-- NB ROWS : " . $info[5] . " rows"; - infoprint " +-- SELECTIVITY : " . $info[6] . "%"; - - $result{'Indexes'}{ $info[1] }{'Colunm'} = $info[0]; - $result{'Indexes'}{ $info[1] }{'Sequence number'} = $info[2]; - $result{'Indexes'}{ $info[1] }{'Number of collunm'} = $info[3]; - $result{'Indexes'}{ $info[1] }{'Cardianality'} = $info[4]; - $result{'Indexes'}{ $info[1] }{'Row number'} = $info[5]; - $result{'Indexes'}{ $info[1] }{'Selectivity'} = $info[6]; - if ( $info[6] < 25 ) { - badprint "$info[1] has a low selectivity"; - } - } - - return - unless ( defined( $myvar{'performance_schema'} ) - and $myvar{'performance_schema'} eq 'ON' ); - - $selIdxReq = <<'ENDSQL'; -SELECT CONCAT(CONCAT(object_schema,'.'),object_name) AS 'table', index_name -FROM performance_schema.table_io_waits_summary_by_index_usage -WHERE index_name IS NOT NULL -AND count_star =0 -AND index_name <> 'PRIMARY' -AND object_schema != 'mysql' -ORDER BY count_star, object_schema, object_name; -ENDSQL - @idxinfo = select_array($selIdxReq); - infoprint "Unsused indexes:"; - push( @generalrec, "Remove unused indexes." ) if ( scalar(@idxinfo) > 0 ); - foreach (@idxinfo) { - debugprint "$_"; - my @info = split /\s/; - badprint "Index: $info[1] on $info[0] is not used."; - push @{ $result{'Indexes'}{'Unused Indexes'} }, - $info[0] . "." . $info[1]; - } -} - -# Take the two recommendation arrays and display them at the end of the output -sub make_recommendations { - prettyprint -"\n-------- Recommendations -----------------------------------------------------"; - if ( @generalrec > 0 ) { - prettyprint "General recommendations:"; - foreach (@generalrec) { prettyprint " " . $_ . ""; } - } - if ( @adjvars > 0 ) { - prettyprint "Variables to adjust:"; - if ( $mycalc{'pct_max_physical_memory'} > 90 ) { - prettyprint - " *** MySQL's maximum memory usage is dangerously high ***\n" - . " *** Add RAM before increasing MySQL buffer variables ***"; - } - foreach (@adjvars) { prettyprint " " . $_ . ""; } - } - if ( @generalrec == 0 && @adjvars == 0 ) { - prettyprint - "No additional performance recommendations are available."; - } -} - -sub close_outputfile { - close($fh) if defined($fh); -} - -sub headerprint { - prettyprint - " >> MySQLTuner $tunerversion - Major Hayden \n" - . " >> Bug reports, feature requests, and downloads at http://mysqltuner.com/\n" - . " >> Run with '--help' for additional options and output filtering"; -} - -sub string2file { - my $filename=shift; - my $content=shift; - open my $fh, q(>), $filename - or die "Unable to open $filename in write mode. please check permissions for this file or directory"; - print $fh $content if defined($content); - close $fh; - debugprint $content if ($opt{'debug'}); -} - -sub file2array { - my $filename = shift; - debugprint "* reading $filename" if ($opt{'debug'}); - my $fh; - open( $fh, q(<), "$filename" ) - or die "Couldn't open $filename for reading: $!\n"; - my @lines = <$fh>; - close($fh); - return @lines; -} - -sub file2string { - return join ( '', file2array(@_) ); -} - -my $templateModel; -if ($opt{'template'} ne 0 ) { - $templateModel=file2string ($opt{'template'}); -}else { - # DEFAULT REPORT TEMPLATE - $templateModel=<<'END_TEMPLATE'; - - - - Report - - - - -

Result output

-
-{$data}
-
- - - -END_TEMPLATE -} -sub dump_result { - if ($opt{'debug'}) { - use Data::Dumper qw/Dumper/; - $Data::Dumper::Pair = " : "; - debugprint Dumper( \%result ); - } - - debugprint "HTML REPORT: $opt{'reportfile'}"; - if ($opt{'reportfile'} ne 0 ) { - use Data::Dumper qw/Dumper/; - $Data::Dumper::Pair = " : "; - my $vars= {'data' => Dumper( \%result ) }; - - my $template = Text::Template->new(TYPE => 'STRING', PREPEND => q{;}, SOURCE => $templateModel) - or die "Couldn't construct template: $Text::Template::ERROR"; - open my $fh, q(>), $opt{'reportfile'} - or die "Unable to open $opt{'reportfile'} in write mode. please check permissions for this file or directory"; - $template->fill_in(HASH =>$vars, OUTPUT=>$fh ); - close $fh; - } -} - -# --------------------------------------------------------------------------- -# BEGIN 'MAIN' -# --------------------------------------------------------------------------- -headerprint # Header Print -mysql_setup; # Gotta login first -validate_tuner_version; # Check last version -os_setup; # Set up some OS variables -get_all_vars; # Toss variables/status into hashes -get_tuning_info; # Get information about the tuning connexion -validate_mysql_version; # Check current MySQL version -check_architecture; # Suggest 64-bit upgrade -check_storage_engines; # Show enabled storage engines -mysql_databases; # Show informations about databases -mysql_indexes; # Show informations about indexes -security_recommendations; # Display some security recommendations -calculations; # Calculate everything we need -mysql_stats; # Print the server stats -mysql_myisam; # Print MyISAM stats -mysql_innodb; # Print InnoDB stats -mysql_ariadb; # Print AriaDB stats -get_replication_status; # Print replication info -make_recommendations; # Make recommendations based on stats -dump_result; # Dump result if debug is on -close_outputfile; # Close reportfile if needed - -# --------------------------------------------------------------------------- -# END 'MAIN' -# --------------------------------------------------------------------------- -1; - -__END__ - -=pod - -=encoding UTF-8 - -=head1 NAME - - MySQLTuner 1.6.0 - MySQL High Performance Tuning Script - -=head1 IMPORTANT USAGE GUIDELINES - -To run the script with the default options, run the script without arguments -Allow MySQL server to run for at least 24-48 hours before trusting suggestions -Some routines may require root level privileges (script will provide warnings) -You must provide the remote server's total memory when connecting to other servers - -=head1 CONNECTION AND AUTHENTIFICATION - - --host Connect to a remote host to perform tests (default: localhost) - --socket Use a different socket for a local connection - --port Port to use for connection (default: 3306) - --user Username to use for authentication - --pass Password to use for authentication - --mysqladmin Path to a custom mysqladmin executable - --mysqlcmd Path to a custom mysql executable - -=head1 PERFORMANCE AND REPORTING OPTIONS - - --skipsize Don't enumerate tables and their types/sizes (default: on) - (Recommended for servers with many tables) - --skippassword Don't perform checks on user passwords(default: off) - --checkversion Check for updates to MySQLTuner (default: don't check) - --forcemem Amount of RAM installed in megabytes - --forceswap Amount of swap memory configured in megabytes - --passwordfile Path to a password file list(one password by line) - -=head1 OUTPUT OPTIONS - --silent Don't output anything on screen - --nogood Remove OK responses - --nobad Remove negative/suggestion responses - --noinfo Remove informational responses - --debug Print debug information - --dbstat Print database information - --idxstat Print index information - --nocolor Don't print output in color - --buffers Print global and per-thread buffer values - --outputfile Path to a output txt file - --reportfile Path to a report txt file - --template Path to a template file - -=head1 PERLDOC - -You can find documentation for this module with the perldoc command. - - perldoc mysqltuner - -=head2 INTENALS - -L - - Internal documentation - -=head1 AUTHORS - -Major Hayden - major@mhtx.net - -=head1 CONTRIBUTORS - -=over 4 - -=item * - -Matthew Montgomery - -=item * - -Paul Kehrer - -=item * - -Dave Burgess - -=item * - -Jonathan Hinds - -=item * - -Mike Jackson - -=item * - -Nils Breunese - -=item * - -Shawn Ashlee - -=item * - -Luuk Vosslamber - -=item * - -Ville Skytta - -=item * - -Trent Hornibrook - -=item * - -Jason Gill - -=item * - -Mark Imbriaco - -=item * - -Greg Eden - -=item * - -Aubin Galinotti - -=item * - -Giovanni Bechis - -=item * - -Bill Bradford - -=item * - -Ryan Novosielski - -=item * - -Michael Scheidell - -=item * - -Blair Christensen - -=item * - -Hans du Plooy - -=item * - -Victor Trac - -=item * - -Everett Barnes - -=item * - -Tom Krouper - -=item * - -Gary Barrueto - -=item * - -Simon Greenaway - -=item * - -Adam Stein - -=item * - -Isart Montane - -=item * - -Baptiste M. - -=item * - -Cole Turner - -=item * - -Major Hayden - -=item * - -Joe Ashcraft - -=item * - -Jean-Marie Renouard - -=back - -=head1 SUPPORT - - -Bug reports, feature requests, and downloads at http://mysqltuner.com/ - -Bug tracker can be found at https://github.com/major/MySQLTuner-perl/issues - -Maintained by Major Hayden (major\@mhtx.net) - Licensed under GPL - -=head1 SOURCE CODE - -L - - git clone -q https://github.com/major/MySQLTuner-perl.git - -=head1 COPYRIGHT AND LICENSE - -Copyright (C) 2006-2015 Major Hayden - major@mhtx.net - -For the latest updates, please visit http://mysqltuner.com/ - -Git repository available at http://github.com/major/MySQLTuner-perl - -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, see . - -=cut - -# Local variables: -# indent-tabs-mode: t -# cperl-indent-level: 8 -# perl-indent-level: 8 -# End: diff --git a/scripts/remove_certbotle.sh b/scripts/remove_certbotle.sh index d10b8417..2b9fddc1 100755 --- a/scripts/remove_certbotle.sh +++ b/scripts/remove_certbotle.sh @@ -14,7 +14,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" function init_certbotle_removal() { if dpkg-query -l | awk '/certbot/ { print $2 }' | grep -qwE "^certbot$"; then diff --git a/scripts/remove_fail2ban.sh b/scripts/remove_fail2ban.sh index 90b46d7d..989dfd80 100755 --- a/scripts/remove_fail2ban.sh +++ b/scripts/remove_fail2ban.sh @@ -14,7 +14,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" function init_fail2ban_removal() { # Stop fail2ban process. diff --git a/scripts/remove_mailer.sh b/scripts/remove_mailer.sh index 676f3776..ddd31377 100755 --- a/scripts/remove_mailer.sh +++ b/scripts/remove_mailer.sh @@ -14,4 +14,4 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" diff --git a/scripts/remove_mariadb.sh b/scripts/remove_mariadb.sh index f47ffbf0..a334f9a0 100755 --- a/scripts/remove_mariadb.sh +++ b/scripts/remove_mariadb.sh @@ -14,7 +14,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" function mariadb_remove_config() { # Remove MariaDB server config files. diff --git a/scripts/remove_memcached.sh b/scripts/remove_memcached.sh index 18b30036..da1ebdaf 100755 --- a/scripts/remove_memcached.sh +++ b/scripts/remove_memcached.sh @@ -14,7 +14,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" function init_memcached_removal() { # Stop Memcached server process. diff --git a/scripts/remove_mongodb.sh b/scripts/remove_mongodb.sh index 67c17dad..5c398fdf 100755 --- a/scripts/remove_mongodb.sh +++ b/scripts/remove_mongodb.sh @@ -2,7 +2,7 @@ # MongoDB Uninstaller # Min. Requirement : GNU/Linux Ubuntu 18.04 -# Last Build : 10/12/2021 +# Last Build : 24/12/2021 # Author : MasEDI.Net (me@masedi.net) # Since Version : 1.0.0 @@ -14,28 +14,43 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" + +# Make sure only supported distribution can run this installer script. +preflight_system_check + +DISTRIB_NAME=${DISTRIB_NAME:-$(get_distrib_name)} +RELEASE_NAME=${RELEASE_NAME:-$(get_release_name)} +MONGODB_VERSION=${MONGODB_VERSION:-"5.0"} +[[ "${RELEASE_NAME}" == "jessie" || "${RELEASE_NAME}" == "xenial" ]] && MONGODB_VERSION="4.4" + +function remove_mongodb_repo() { + echo "Removing MongoDB repository..." + + if [[ -f "/etc/apt/sources.list.d/mongodb-org-${MONGODB_VERSION}-${RELEASE_NAME}.list" ]]; then + run rm -f "/etc/apt/sources.list.d/mongodb-org-${MONGODB_VERSION}-${RELEASE_NAME}.list" + run apt-get update -qq -y + fi + + echo "Removing MongoDB repository key..." + + run bash -c "wget -qO - 'https://www.mongodb.org/static/pgp/server-${MONGODB_VERSION}.asc' | apt-key del -" +} function init_mongodb_removal() { # Stop MongoDB server process. if [[ $(pgrep -c mongod) -gt 0 ]]; then + echo "Stopping MongoDB server..." + run systemctl stop mongod fi if dpkg-query -l | awk '/mongodb/ { print $2 }' | grep -qwE "^mongodb"; then - echo "Found MongoDB package installation. Removing..." + echo "Removing MongoDB packages..." # Remove MongoDB server. #shellcheck disable=SC2046 - #run apt-get purge -qq -y $(dpkg-query -l | awk '/mongodb/ { print $2 }') - run apt-get purge -qq -y mongodb-org mongodb-org-server mongodb-org-shell mongodb-org-tools - - if [[ "${FORCE_REMOVE}" == true ]]; then - run rm -f /etc/apt/sources.list.d/mongodb-org-* - fi - - # Remove MongoDB config files. - warning "!! This action is not reversible !!" + run apt-get purge -qq -y $(dpkg-query -l | awk '/mongodb/ { print $2 }') if [[ "${AUTO_REMOVE}" == true ]]; then if [[ "${FORCE_REMOVE}" == true ]]; then @@ -44,27 +59,35 @@ function init_mongodb_removal() { REMOVE_MONGOD_CONFIG="n" fi else + # Remove MongoDB config files. + warning "!! This action is not reversible !!" + while [[ "${REMOVE_MONGOD_CONFIG}" != "y" && "${REMOVE_MONGOD_CONFIG}" != "n" ]]; do read -rp "Remove MongoDB database and configuration files? [y/n]: " -e REMOVE_MONGOD_CONFIG done fi if [[ "${REMOVE_MONGOD_CONFIG}" == Y* || "${REMOVE_MONGOD_CONFIG}" == y* ]]; then - [ -f /etc/mongod.conf ] && run rm -fr /etc/mongod.conf + echo "Removing MongoDB data & configs..." + + [ -f /etc/mongod.conf ] && run rm -f /etc/mongod.conf [ -d /var/lib/mongodb ] && run rm -fr /var/lib/mongodb - echo "All your MongoDB database and configuration files deleted permanently." + #echo "All your MongoDB database and configuration files deleted permanently." + + # Remove MongoDB repository. + remove_mongodb_repo fi else echo "MongoDB package not found, possibly installed from source." - echo "Remove it manually!!" MONGOD_BIN=$(command -v mongod) - echo "MongoDB server binary executable: ${MONGOD_BIN}" + echo "MongoDB server executable binary: ${MONGOD_BIN}" + echo "You could remove it manually!" fi - # Final test. + # Final check. if [[ "${DRYRUN}" != true ]]; then if [[ -z $(command -v mongod) ]]; then success "MongoDB server removed succesfully." @@ -76,7 +99,7 @@ function init_mongodb_removal() { fi } -echo "Uninstalling MongoDB server..." +echo "[MongoDB ${MONGODB_VERSION} Server Removal]" if [[ -n $(command -v mongod) ]]; then if [[ "${AUTO_REMOVE}" == true ]]; then diff --git a/scripts/remove_nginx.sh b/scripts/remove_nginx.sh index add88613..71b74569 100755 --- a/scripts/remove_nginx.sh +++ b/scripts/remove_nginx.sh @@ -14,7 +14,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" # Remove nginx. function init_nginx_removal() { diff --git a/scripts/remove_php.sh b/scripts/remove_php.sh index 651eea3f..c94caa60 100755 --- a/scripts/remove_php.sh +++ b/scripts/remove_php.sh @@ -14,7 +14,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" ## # Remove PHP & FPM installation from system. diff --git a/scripts/remove_redis.sh b/scripts/remove_redis.sh index b7748a7c..246618a5 100755 --- a/scripts/remove_redis.sh +++ b/scripts/remove_redis.sh @@ -14,7 +14,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" function init_redis_removal() { # Stop Redis server process. diff --git a/scripts/remove_vsftpd.sh b/scripts/remove_vsftpd.sh index b2919ad1..73777ae6 100755 --- a/scripts/remove_vsftpd.sh +++ b/scripts/remove_vsftpd.sh @@ -14,7 +14,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" function init_vsftpd_removal() { # Stop VSFTPD process. diff --git a/scripts/secure_server.sh b/scripts/secure_server.sh index eea58d21..beae00b9 100755 --- a/scripts/secure_server.sh +++ b/scripts/secure_server.sh @@ -14,7 +14,7 @@ if [[ "$(type -t run)" != "function" ]]; then fi # Make sure only root can run this installer script. -requires_root +requires_root "$@" ## # Securing SSH server.