commit 4de105a2dd0ba4064a64cc37ad5b9d2c0dfb23d4 Author: Matthew Saunders Brown Date: Wed Apr 7 09:43:20 2021 -0700 first commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..156bfd4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +MIT License Copyright (c) 2021 Matthew Saunders Brown + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b01622b --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +# Wordpress Tools + +A couple of handy tools for virtualhost servers with wordpress sites. + +bash completion for wp-cli +a systemd cron for running wp crons. individual sites don't have to set their own cron job or rely on lazy cron +wp-create-db-user-from-config.sh extracts DB config info from wp-config.php, useful for creating dbs & users for sites that are being migrated +wp-installer.sh automated wp installer + +## Install +``` +cd /usr/local/src +git clone https://git.stack-source.com/msb/wordpress-tools.git +cd wordpress-tools +cp bash_completion.d/wp-cli /etc/bash_completion.d/ +chmod 644 /etc/bash_completion.d/wp-cli +chmod 755 /usr/local/bin/wp-* +cp bin/wp-* /usr/local/bin + +cp sbin/wp-cron.sh /usr/local/sbin/ +chmod 755 /usr/local/sbin/wp-cron.sh +cp systemd/wp-cron.* /usr/lib/systemd/system/ +chmod 644 /usr/lib/systemd/system/wp-cron.* + +systemctl daemon-reload +systemctl start wp-cron.timer +``` diff --git a/bash_completion.d/wp-cli b/bash_completion.d/wp-cli new file mode 100644 index 0000000..4e0a3ec --- /dev/null +++ b/bash_completion.d/wp-cli @@ -0,0 +1,23 @@ +# bash completion for the `wp` command + +_wp_complete() { + local OLD_IFS="$IFS" + local cur=${COMP_WORDS[COMP_CWORD]} + + IFS=$'\n'; # want to preserve spaces at the end + local opts="$(wp cli completions --line="$COMP_LINE" --point="$COMP_POINT")" + + if [[ "$opts" =~ \\s* ]] + then + COMPREPLY=( $(compgen -f -- $cur) ) + elif [[ $opts = "" ]] + then + COMPREPLY=( $(compgen -f -- $cur) ) + else + COMPREPLY=( ${opts[*]} ) + fi + + IFS="$OLD_IFS" + return 0 +} +complete -o nospace -F _wp_complete wp diff --git a/bin/wp-create-db-user-from-config.sh b/bin/wp-create-db-user-from-config.sh new file mode 100755 index 0000000..e99a754 --- /dev/null +++ b/bin/wp-create-db-user-from-config.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# +# wordpress-tools +# https://git.stack-source.com/msb/wordpress-tools +# MIT License Copyright (c) 2021 Matthew Saunders Brown + +# require root +if [ "${EUID}" -ne 0 ]; then + echo "This script must be run as root" + exit +fi + +VHOST=$1 + +if [ ! -f /srv/$VHOST/html/wp-config.php ]; then + echo "Config file /srv/$VHOST/html/wp-config.php does not exist." + exit +fi + +# set username +VHOST_USER=$(stat -c '%U' /srv/$VHOST) + +cd /srv/$VHOST/html/ +DB_NAME=`wp --allow-root config get DB_NAME` +DB_USER=`wp --allow-root config get DB_USER` +DB_PASSWORD=`wp --allow-root config get DB_PASSWORD` +DB_HOST=`wp --allow-root config get DB_HOST` + +#if [ $DB_HOST == '127.0.0.1' ]; then +# DB_HOST=localhost +#fi + +if [ -d /var/lib/mysql/$DB_NAME ]; then + echo "NOTICE: $DB_NAME already exists" + echo " before proceeding do manual checks for db user" + echo +else + echo mysqladmin create $DB_NAME +fi +echo mysql -e \"CREATE USER \'$DB_USER\'@\'$DB_HOST\' IDENTIFIED BY \'$DB_PASSWORD\'\;\" +echo mysql -e \"GRANT ALL PRIVILEGES ON $DB_NAME.* TO \'$DB_USER\'@\'$DB_HOST\'\;\" diff --git a/bin/wp-installer.sh b/bin/wp-installer.sh new file mode 100755 index 0000000..e3e5325 --- /dev/null +++ b/bin/wp-installer.sh @@ -0,0 +1,364 @@ +#!/bin/bash +# +# wordpress-tools +# https://git.stack-source.com/msb/wordpress-tools +# MIT License Copyright (c) 2021 Matthew Saunders Brown + +# require root +if [ "${EUID}" -ne 0 ]; then + echo "This script must be run as root" + exit +fi + +virtualhost_basedir="/srv/www" +virtualhost_htdocsdir="html" + +declare -a input_keys_index +input_keys_index=(v r t e u p m d n w x s) + +declare -A input_keys_array +input_keys_array[v]="VirtualHost" +input_keys_array[r]="URL" +input_keys_array[t]="Site Title" +input_keys_array[e]="Admin Email" +input_keys_array[u]="Admin Username" +input_keys_array[p]="Admin Password" +input_keys_array[m]="MySQL Hostname" +input_keys_array[d]="MySQL Database" +input_keys_array[n]="MySQL Username" +input_keys_array[w]="MySQL Password" +input_keys_array[x]="MySQL DB Prefix" +input_keys_array[s]="Shell Username" + +declare -A input_values_array + +help() +{ + thisfilename=$(basename -- "$0") + echo "$thisfilename - Install WordPress to a specific VirtualHost." + echo "" + echo "Usage: $thisfilename [-v VirtualHost] [-r URL] [-t Site Title] [-e Admin Email] [-u Admin Username] [-p Admin Password] [-h MySQL Hostname] [-d MySQL Database] [-n MySQL Username] [-w MySQL Password] [-x MySQL DB Prefix] [-s Shell Username]" + echo "" + echo " -v VirtualHost VirtualHost that WordPress install is for, only option that is required - all others have automatic defaults." + echo " -r URL Site URL for WordPress install, defaults to VirtualHost." + echo " -t Site Title Site Title for WordPress install, defaults to VirtualHost." + echo " -e Admin Email Email address for WordPress admin user, defaults to 'Admin username'@'VirtualHost'" + echo " -u Admin Username Username for WordPress admin user, defaults to Shell Username" + echo " -p Admin Password Password for WordPress admin user, random password is generated if not set." + echo " -m MySQL Hostname MySQL Hostname, defaults to 127.0.0.1" + echo " -d MySQL Database MySQL Database, defaults to 'VirtualHost' with dots & dashes changed from symbols to words if not specified." + echo " -n MySQL Username MySQL Username, defaults to 'Shell Username'@'VirtualHost' if not specified." + echo " -w MySQL Password MySQL Hostname, random password is generated if not set." + echo " -x MySQL DB Prefix WordPress db table prefix, defaults to wp_" + echo " -s Shell Username System user that owns website files for this VirtualHost, autodetected and reset if not specified correctly." + echo " -f Force install - skips install confirmation step, for automated installs." + echo " -h Print this help." + echo "" + echo " You will be prompted to any options that were not" + echo " specified on the command line and given a chance" + echo " to confirm before performing the install unless the" + echo " -f option is specified." + exit +} + +# force (-f) defaults to false +force=false + +# set any options that were passed +while getopts "v:r:t:e:u:p:m:d:n:w:x:s:fh" opt; do + case "${opt}" in + h ) + help + exit;; + v ) + input_values_array[v]=${OPTARG} + ;; + r ) + input_values_array[r]=${OPTARG} + ;; + t ) + input_values_array[t]=${OPTARG} + ;; + e ) + input_values_array[e]=${OPTARG} + ;; + u ) + input_values_array[u]=${OPTARG} + ;; + p ) + input_values_array[p]=${OPTARG} + ;; + m ) + input_values_array[m]=${OPTARG} + ;; + d ) + input_values_array[d]=${OPTARG} + ;; + n ) + input_values_array[n]=${OPTARG} + ;; + w ) + input_values_array[w]=${OPTARG} + ;; + x ) + input_values_array[x]=${OPTARG} + ;; + s ) + input_values_array[s]=${OPTARG} + ;; + f ) + force=true + ;; + \? ) + echo "Invalid option: $OPTARG" 1>&2 + exit 1;; + : ) + echo "Invalid option: $OPTARG requires an argument" 1>&2 + exit 1;; + esac +done + +# v - virtualhost +function validate_v() { + if [ -d "$virtualhost_basedir/${input_values_array[v]}" ]; then + if [ -d "$virtualhost_basedir/${input_values_array[v]}/$virtualhost_htdocsdir" ]; then + # make sure DocumentRoot is empty (except for possible index.html file) + htdocsdir_file_count=`/usr/bin/ls $virtualhost_basedir/${input_values_array[v]}/$virtualhost_htdocsdir|grep -v index.html|wc -l` + if [ $htdocsdir_file_count -gt 0 ]; then + echo "$virtualhost_basedir/${input_values_array[v]}/$virtualhost_htdocsdir appears to contain an existing website." + echo "Manually remove all files before attempting to install WordPress" + exit + fi + else + echo "${input_values_array[v]} is installed, but DocumentRoot for ${input_values_array[v]} does not exist." + echo "$virtualhost_basedir/${input_values_array[v]}/$virtualhost_htdocsdir" + exit + fi + else + echo "VirtualHost ${input_values_array[v]} is not installed on this server." + echo "$virtualhost_basedir/${input_values_array[v]}/$virtualhost_htdocsdir" + unset input_values_array[v] + fi +} +if [ -n "${input_values_array[v]}" ] ; then + validate_v +fi +if [ -z "${input_values_array[v]}" ] ; then + while [ -z "${input_values_array[v]}" ]; do + read -p "Enter the VirtualHost for WordPress install: " virtualhost + if [ -n "$virtualhost" ] ; then + input_values_array[v]=$virtualhost + validate_v + fi + done +fi + +# s - Shell Username +function validate_s() { + # force shell user value, user can't override + shell_username=$(stat -c '%U' $virtualhost_basedir/${input_values_array[v]}) + if [[ ${input_values_array[s]} != $shell_username ]]; then + echo "ERROR: Incorrect Shell Username, forcing value reset." + input_values_array[s]=$shell_username + fi +} +if [ -z "${input_values_array[s]}" ] ; then + input_values_array[s]=$(stat -c '%U' $virtualhost_basedir/${input_values_array[v]}) +fi +if [ -n "${input_values_array[e]}" ] ; then + validate_s +fi + +# u - URL +function validate_u() { + if [[ ${input_values_array[r]} != *"${input_values_array[v]}"* ]] ; then + echo "URL (${input_values_array[r]}) does not appear to be based on VirtualHost (${input_values_array[v]})" + unset input_values_array[r] + fi +} +if [ -z "${input_values_array[r]}" ] ; then + input_values_array[r]="www.${input_values_array[v]}" +fi +if [ -n "${input_values_array[r]}" ] ; then + validate_u +fi + +# t - Site Title +if [ -z "${input_values_array[t]}" ] ; then + input_values_array[t]="${input_values_array[v]}" +fi + +# e - Admin Email +function validate_e() { + regex="^[a-z0-9!#\$%&'*+/=?^_\`{|}~-]+(\.[a-z0-9!#$%&'*+/=?^_\`{|}~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]([a-z0-9-]*[a-z0-9])?\$" + if ! [[ ${input_values_array[e]} =~ $regex ]] ; then + read -p "ERROR: Invalid email address, please re-enter: " admin_email + if [ -n "$admin_email" ] ; then + input_values_array[e]=$admin_email + validate_e + fi + fi +} +if [ -z "${input_values_array[e]}" ] ; then + input_values_array[e]="${input_values_array[s]}@${input_values_array[v]}" +fi +if [ -n "${input_values_array[e]}" ] ; then + validate_e +fi + +# u - Admin Username +if [ -z "${input_values_array[u]}" ] ; then + input_values_array[u]=${input_values_array[s]} +fi + +# p - Admin Password +if [ -z "${input_values_array[p]}" ] ; then + input_values_array[p]=`pwgen -1 8` +fi + +# m - MySQL Hostname +if [ -z "${input_values_array[m]}" ] ; then + input_values_array[m]="127.0.0.1" +fi + +# d - MySQL Database +if [ -z "${input_values_array[d]}" ] ; then + mysql_database=${input_values_array[v]} + mysql_database=${mysql_database//./dot} + mysql_database=${mysql_database//-/dash} + input_values_array[d]=$mysql_database +fi + +# n - MySQL Username +if [ -z "${input_values_array[n]}" ] ; then + input_values_array[n]="${input_values_array[s]}@${input_values_array[v]}" +fi + +# w - MySQL Password +function validate_w() { + mysql_username_count=`mysql -s -N -e "SELECT COUNT(*) FROM mysql.user WHERE User='${input_values_array[n]}'"` + if [[ "$mysql_username_count" -gt 0 ]]; then + mysql_password_verification=`mysql -s -A -N -e "SELECT COUNT(1) Password_is_OK FROM mysql.user WHERE user='${input_values_array[n]}' AND password=PASSWORD('${input_values_array[w]}')"` + if [[ "$mysql_password_verification" -ne 1 ]]; then + read -p "ERROR: Invalid MySQL Password for existing MySQL User ${input_values_array[n]}, please enter current password: " mysql_password + if [ -n "$mysql_password" ] ; then + input_values_array[w]=$mysql_password + restart_script + fi + fi + fi +} +if [ -z "${input_values_array[w]}" ] ; then + input_values_array[w]=`pwgen -1 16` +fi + +# x - MySQL DB Prefix +function validate_x() { + if mysql -e "USE ${input_values_array[d]}" 2> /dev/null; then + mysql_db_prefix=`echo ${input_values_array[x]} |sed 's|_|\\\_|g'` + mysql_db_prefix_count=`mysql -s -N -e "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '${input_values_array[d]}' AND TABLE_NAME LIKE '$mysql_db_prefix%'"` + if [[ "$mysql_db_prefix_count" -gt 0 ]]; then + read -p "One or more MySQL tables with DB Prefix '${input_values_array[x]}' already exist, please enter new prefix: " mysql_db_prefix + if [ -n "$mysql_db_prefix" ] ; then + input_values_array[x]=$mysql_db_prefix + restart_script + fi + fi + fi +} +if [ -z "${input_values_array[x]}" ] ; then + input_values_array[x]="wp_" +fi +# if [ -n "${input_values_array[x]}" ] ; then +# validate_x +# fi + +# display confirmation +function display_confirmation() { + echo + for key in "${!input_keys_index[@]}" ; do + value=${input_keys_index[$key]} + echo "$value - ${input_keys_array[$value]}: ${input_values_array[$value]}" + done + echo + echo "i - install now with these settings" + echo "q - quit without installing" + echo + read -p "Select option to edit or install/quit: " option + echo +} + +function restart_script() { + # set command line args + cli_args= + for key in "${!input_values_array[@]}" ; do + value=${input_values_array[$key]} + cli_args="$cli_args -$key '$value'" + done +# echo "${0} $cli_args" + echo + eval ${0} $cli_args + exit +} + +if [[ "$force" == "false" ]]; then + display_confirmation +else + option="i" +fi + +if [[ "$option" == "i" ]]; then + + # validate MySQL DB Prefix + validate_x + # validate MySQL User Password + validate_w + + echo + echo "Installing WP for VirtualHost ${input_values_array[v]} now..." + + # create database if it doesn't already exist + if ! mysql -e "USE ${input_values_array[d]}" 2> /dev/null; then + mysqladmin create ${input_values_array[d]} + fi + + # create mysql user if they don't already exist + if [[ "$mysql_username_count" -eq 0 ]]; then + mysql -e "CREATE USER '${input_values_array[n]}'@'${input_values_array[m]}' IDENTIFIED BY '${input_values_array[w]}'" + fi + + # add db privileges for mysql user if no already enabled + if ! mysql --host=${input_values_array[m]} --user=${input_values_array[n]} --password=${input_values_array[w]} -e "USE ${input_values_array[d]}" 2> /dev/null; then + mysql -e "GRANT ALL PRIVILEGES ON ${input_values_array[d]}.* TO '${input_values_array[n]}'@'${input_values_array[m]}'" + mysqladmin flush-privileges + fi + + # switch to Shell User and install WordPress +sudo -u ${input_values_array[s]} --shell /bin/bash << EOF +cd $virtualhost_basedir/${input_values_array[v]}/$virtualhost_htdocsdir +wp core download +wp config create --dbhost="${input_values_array[m]}" --dbname=${input_values_array[d]} --dbuser="${input_values_array[n]}" --dbpass="${input_values_array[w]}" --dbprefix="${input_values_array[x]}" +wp config set DISALLOW_FILE_EDIT true +wp config set DISABLE_WP_CRON true +wp core install --url="${input_values_array[u]}" --title="${input_values_array[v]}" --admin_user=${input_values_array[u]} --admin_password="${input_values_array[p]}" --admin_email=${input_values_array[e]} +echo "apache_modules:" > wp-cli.local.yml +echo " - mod_rewrite" >> wp-cli.local.yml +wp rewrite flush --hard +rm wp-cli.local.yml +EOF + + if [ -f "$virtualhost_basedir/${input_values_array[v]}/$virtualhost_htdocsdir/index.html" ]; then + /usr/bin/rm $virtualhost_basedir/${input_values_array[v]}/$virtualhost_htdocsdir/index.html + fi + +elif [[ "$option" == "q" ]]; then + echo "Quiting without installing." +elif [[ " ${input_keys_index[@]} " =~ " $option " ]]; then + read -p "Enter new value for ${input_keys_array[$option]} (${input_values_array[$option]}): " new_value + if [ -n "$new_value" ] ; then + input_values_array[$option]=$new_value + fi + restart_script +else + echo "ERROR: Invalid entry, try again." +fi diff --git a/sbin/wp-cron.sh b/sbin/wp-cron.sh new file mode 100755 index 0000000..95b1cc9 --- /dev/null +++ b/sbin/wp-cron.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# +# wordpress-tools +# https://git.stack-source.com/msb/wordpress-tools +# MIT License Copyright (c) 2021 Matthew Saunders Brown + +# must be root, will su to vhost users for wp cron runs +if [ "$USER" != "root" ]; then + exec sudo $0 +fi + +declare -a virtualhostExclusionArray +# optionally add virtualhosts to virtualhostExclusionArray to have them skipped + +# create virtualhostArray +cd /srv/www +virtualhostArray=(`ls -1|grep -v ^html$`) + +for VHOST in "${virtualhostArray[@]}" +do + # check if VHOST has been added to exclustion array + if [[ ! " ${virtualhostExclusionArray[@]} " =~ " ${VHOST} " ]]; then + # basic check for WP install + if [ -f /srv/www/$VHOST/html/wp-config.php ]; then + VHOST_USER=$(stat -c '%U' /srv/www/$VHOST) + # confirm that WP really is installed + if $(su -c "wp core is-installed --path=/srv/www/$VHOST/html/" $VHOST_USER); then + # run cron for VHOST as VHOST_USER + /usr/bin/logger --tag wp-cron "running wp-cli cron for $VHOST as $VHOST_USER" + su -c "/usr/local/bin/wp cron event run --due-now --quiet --path=/srv/www/$VHOST/html/" $VHOST_USER + fi + fi + fi +done + diff --git a/systemd/wp-cron.service b/systemd/wp-cron.service new file mode 100644 index 0000000..382f81b --- /dev/null +++ b/systemd/wp-cron.service @@ -0,0 +1,13 @@ +# wordpress-tools +# https://git.stack-source.com/msb/wordpress-tools +# MIT License Copyright (c) 2021 Matthew Saunders Brown +# +[Unit] +Description=WP CLI cron + +[Service] +Type=simple +ExecStart=/usr/local/sbin/wp-cron.sh + +[Install] +WantedBy=multi-user.target diff --git a/systemd/wp-cron.timer b/systemd/wp-cron.timer new file mode 100644 index 0000000..2a1c277 --- /dev/null +++ b/systemd/wp-cron.timer @@ -0,0 +1,13 @@ +# wordpress-tools +# https://git.stack-source.com/msb/wordpress-tools +# MIT License Copyright (c) 2021 Matthew Saunders Brown +# +[Unit] +Description=WP CLI cron for all virtualhosts every 15 minutes + +[Timer] +OnCalendar=*:0/15 +Unit=wp-cron.service + +[Install] +WantedBy=multi-user.target