Posts Tagged ‘bash’

285 WordPress Sites, upgraded in 11 minutes – and they weren’t MultiSite

Sunday, February 12th, 2012

A number of our clients run WordPress, but, for some reason, keeping them updated is a problem. Sites are uploaded and run on autopilot and are forgotten… until they are hacked. Last week a client asked why his WordPress 2.8 site was hacked. WordPress 2.8 was released in June 2009 with 25 WordPress releases since. We checked a few of his sites and found a few different versions running, but, how many other clients were running old WordPress versions? The results were shocking.

Finding WordPress sites on shared storage

First, we need to find each of the individual client’s WordPress installations.

find /var/www -type f -wholename \*wp-includes/version.php|awk '{ print "grep -H \"wp_version =\" " $1 }' | sh > /var/tmp/wpversions

From this, with a little cut and sort trickery, we end up with the following histogram:

35 3.1
29 2.8.5
25 3.2.1
24 2.9.2
20 2.8.4
19 3.0.1
16 2.8.2
16 2.1
15 2.6
13 2.7.1
8 3.3.1
6 2.9.1
6 2.8
6 2.3
5 3.3
5 2.7
4 3.1.2
4 2.8.1
3 3.1.1
3 3.0.5
3 3.0.4
3 2.8.6
2 3.1.3
2 3.0
2 2.0.3
1 3.1.4
1 2.9
1 2.6.3
1 2.5.1
1 2.3.3
1 2.3.2
1 2.2.2
1 2.2.1
1 2.2
1 2.1.3
1 2.0.5
1 2.0.4

Yes, we have 2 2.0.3 installations in production use out of 285 sites. Of them, 8, or, less than 3% are running the current version, 3.3.1.

Clearly this is a problem.

We have a few options, one of which is to utilize the upgrade process inside WordPress which requires us to communicate with each client, or, write a quick script to give us admin privileges to do the upgrade. Or, we could use bash.

The magic

Our filesystem structure is set up so that each user has their own UID/GID, and the paths where the domains are located are fairly static. However, the script just takes the path of the wp-content/version.php file, strips off the correct pieces, copies the uncompressed WordPress .tar.gz file, changes ownership from root to the user that owns the directory.

There are two variables that need to be set:

WORDPRESS_TMPDIR – set this to the directory where you have untarred and ungzipped the WordPress archive

BASE_PATH – set this to the machine’s root path.

The script

#!/bin/bash

# cd34, 20120212
# 
# find /var/www -type f -wholename \*wp-includes/version.php|awk '{ print "grep -H \"wp_version =\" " $1 }' | sh > /var/tmp/wpversions
# 
# if you want to really save time:
# awk < /var/tmp/wpversion '{ print "/path/to/wpu.sh " $1 }' | sh -x

# set this to match your temporary directory location for WordPress
WORDPRESS_TMPDIR=/var/tmp/wordpress
# wget -O /var/tmp http://wordpress.org/latest.tar.gz
# cd /var/tmp
# tar xzf latest.tar.gz

#set this to signify the base path of your machine's web root
BASE_PATH=/var/www

if [ "X" == "$1X" ];
then
  echo "Needs a pathname for the version.php file"
  echo
  echo "$0 /var/www/domain.com/wp-includes/version.php"
  echo
  echo "You can include data after version.php, i.e. :$version from find command"
else
  WP_INCLUDE_PATH=$1
  WP_PATH=${WP_INCLUDE_PATH%%/wp-includes/version.php*}
  DOMAIN=${WP_PATH##$BASE_PATH/}

  TMP=`stat $WP_PATH|grep Uid:`
  TMP_GID=${TMP##*Gid: ( }
  DGID=${TMP_GID%%/*}
  TMP_UID=${TMP##*Uid: ( }
  DUID=${TMP_UID%%/*}

  `cp -Rp $WORDPRESS_TMPDIR/* $WP_PATH`
  `chown -R --from=root $DUID.$DGID $WP_PATH`
  `/usr/bin/wget -q -O /dev/null "http://$DOMAIN/wp-admin/upgrade.php?step=1"`
  echo "Upgraded: http://$DOMAIN"
fi

The code for this and a few other tools that I’ve written can be found at cd34-tools, hosted on code.google.com

Reading a file in bash

Thursday, April 15th, 2010

While working on a maintenance script, I’ve found myself using bash a bit more than using perl for many tasks. While I don’t mind writing in perl or python, sometimes it just seems like overkill.

I needed to read one line of a /proc/loadavg for a monitoring system and was using some code that had been written by one of our programmers in 2002 or so. The old code was written using three scripts, two of which were called from cron. The first script was written in perl, actually included /proc/loadavg as chomp($value=`cat /proc/loadvg`);

Ugh!

Then some math was done, and a decision made of what values to write for nagios’s local process to report back. Suffice it to say that the math done then involved executing a shell script from time to time based on some condition checks and a separate cron that cleaned up the status for nagios.

A literal translation of the script from perl to bash ended up with a snippet of code that looked like this:

#!/bin/bash

  LOAD=`cat /proc/loadavg`
  LOADAVG=${LOAD%%.*}

echo $LOADAVG

We’ll spare the details regarding how badly the existing script converted LOADAVG to an int in perl. While this method works, it does cause a process to be forked needlessly to read /proc/loadavg.

A quick rewrite and we end up with:

#!/bin/bash

  read LOAD < /proc/loadavg
  LOADAVG=${LOAD%%.*}

echo $LOADAVG

While this doesn't seem overly bad for a process executed once a minute, it does cause a larger problem when someone copies that snippet of code and uses it elsewhere.

If we modify the code slightly to:

#!/bin/bash

for i in {1..10000}
do
  LOAD=`cat /proc/loadavg`
  LOADAVG=${LOAD%%.*}
done

echo $LOADAVG

and

#!/bin/bash

for i in {1..10000}
do
  read LOAD < /proc/loadavg
  LOADAVG=${LOAD%%.*}
done

echo $LOADAVG

and run the following benchmark:

root@tsavo:/home/cd34# time ./s.sh 
0

real	0m13.641s
user	0m0.636s
sys	0m2.304s
root@tsavo:/home/cd34# time ./t.sh 
0

real	0m0.485s
user	0m0.332s
sys	0m0.148s

we can see that there is a substantial impact to doing things.

Taking the integer portion of a floating point number also yields a number of terrible solutions involving sed, awk and cut — all forking a separate process or two.

Entries (RSS) and Comments (RSS).
Cluster host: li