alanthing

Linux and other things

OS X 10.7 Lion Development: MacPorts

This post originally featured on the Echo & Co. blog.

OS X Lion comes with most of the tools you would need to do “MAMP” (Mac OS X, Apache, MySQL/MariaDB, PHP) development, as outlined in my previous posts once you add a database. So then why would you want to use MacPorts? Setting your development environment up in MacPorts isolates the binaries, libraries, and configuration files, completely separate from the existing OS X install (with the exception of startup scripts). You can also tweak the configuration files on your own, apply your own patches, and apply updates that MacPorts may get before Apple pushes them. It will take more time because you’ll be compiling everything, but you have all of the control. Read on for how to get things set up.

Prerequisites

We’ll going to be compiling a lot of source code so you’ll need to have Xcode 4 installed. I initially tried with the OS X GCC Installer but eventually ran into a problem where a portfile was expecting an Xcode binary to check to a prerequisite. I’m sure some hacking could’ve resolved it to avoid having to install the very large Xcode package, but at the end of the day it’s better to just know that it’ll work as expected.

Once you have Xcode 4 installed, you’ll need to install MacPorts. It’s a simple click-through installer. Come back to this guide once MacPorts is ready.

Note that for all commands before that are starting with a $, the dollar sign is showing a command-line prompt in Terminal, and you should not actually type it as part of the commands.

Install packages

Update the ports tree to get the latest from the MacPorts server in case the installer had an older tree:

1
sudo /opt/local/bin/port selfupdate

All of the packages that we need can be installed with a single command:

1
sudo /opt/local/bin/port install apache2 php5 php5-mysql mysql5-server php5-gd php5-mbstring php5-apc lynx phpmyadmin

Compiling all of these tools will take a while, so grab a book and come back when the command is completed.

Configure PHP

The following will set up PHP with some development-friendly settings.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ sudo cp -av /opt/local/etc/php5/php.ini-development /opt/local/etc/php5/php.ini
$ sudo sed -i "" 's/\(default_socket[ ]\{0,1\}=\)/\1 \/opt\/local\/var\/run\/mysql5\/mysqld.sock/g' /opt/local/etc/php5/php.ini
$ sudo sh -c "cat >> /opt/local/etc/php5/php.ini <<'EOF'


;;
;; User customizations below
;;

; Original - memory_limit = 128M
memory_limit = 196M
; Original - post_max_size = 8M
post_max_size = 200M
; Original - upload_max_filesize = 2M
upload_max_filesize = 100M
; Original - default_socket_timeout = 60
default_socket_timeout = 600
; Original - max_execution_time = 30
max_execution_time = 300
; Original - max_input_time = 60
max_input_time = 600
; Original - ;date.timezone =
date.timezone = 'America/New_York'
EOF"

Now configure the APC extension (installed above with php5-apc) which will increase PHP performance dramatically.

1
2
3
4
5
6
7
8
9
10
$ sudo sh -c "cat > /opt/local/var/db/php5/apc-config.ini <<'EOF'
[apc]
apc.enabled = 1
apc.shm_segments = 1
apc.shm_size = 96M
apc.cache_by_default = 1
apc.stat = 1
apc.rfc1867 = 1
apc.stat = 7200
EOF"

Configure Apache

Start by setting up PHP to work with Apache properly:

1
2
3
4
5
6
7
8
9
10
11
$ sudo cp -av /opt/local/apache2/conf/httpd.conf /opt/local/apache2/conf/httpd.conf-default
$ sudo /opt/local/apache2/bin/apxs -a -e -n "php5" /opt/local/apache2/modules/libphp5.so
$ sudo bash -c "cat >> /opt/local/apache2/conf/httpd.conf <<'EOF'

## Add mod_php information
Include conf/extra/mod_php.conf
# Add index.php to the list of files that will be served as directory indexes.
<IfModule dir_module>
  DirectoryIndex index.php index.html
</IfModule>
EOF"

Now we’ll move httpd-vhosts.conf to ~/Sites for easy editing of new virtual hosts, as well as create a ~/Sites/logs folder:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ sudo mv -v /opt/local/apache2/conf/extra/httpd-vhosts.conf /opt/local/apache2/conf/extra/httpd-vhosts.conf-default </br>
$ [ ! -d ~/Sites ] && mkdir -v ~/Sites
$ cp -av /opt/local/apache2/conf/extra/httpd-vhosts.conf-default ~/Sites/httpd-vhosts.conf
$ sudo ln -s ~/Sites/httpd-vhosts.conf /opt/local/apache2/conf/extra/httpd-vhosts.conf
$ sudo sed -i "" 's/\#\(.*httpd-vhosts\.conf\)/\1/' /opt/local/apache2/conf/httpd.conf
$ [ ! -d ~/Sites/logs ] && mkdir ~/Sites/logs
$ sudo perl -pi -e 'BEGIN{undef $/;} s/\<VirtualHost .*\n//sg;' ${USERHOME}/Sites/httpd-vhosts.conf </br>
$ sudo sh -c "cat >> ~/Sites/httpd-vhosts.conf <<'EOF' </br>
 </br>
## Change /Users/name to the path of your home folder </br>
#<VirtualHost *:80> </br>
#  ServerName project.local </br>
#  CustomLog "/Users/name/Sites/logs/project.local-access_log" combined </br>
#  ErrorLog "/Users/name/Sites/logs/project.local-error_log" </br>
#  DocumentRoot "/Users/name/Sites/project.local" </br>
#</VirtualHost> </br>
EOF"

Allow userdirs so the MacPorts Apache “feels” more like the built-in Apache:

1
sudo sed -i "" 's/\#\(.*httpd-userdir\.conf\)/\1/' /opt/local/apache2/conf/httpd.conf

Start Apache and set it to load on boot:

1
sudo /opt/local/bin/port load apache2

Configure MySQL

Set up the configuration file:

1
2
sudo cp -av /opt/local/share/mysql5/mysql/my-small.cnf /opt/local/etc/mysql5/my.cnf
sudo sed -i "" 's/max_allowed_packet = 1.*M/max_allowed_packet = 1G/g' /opt/local/etc/mysql5/my.cnf

Initialize MySQL and run the secure installation script:

1
2
3
sudo -u _mysql /opt/local/bin/mysql_install_db5
sudo /opt/local/bin/port load mysql5-server
sudo /opt/local/lib/mysql5/bin/mysql_secure_installation

Configure phpMyAdmin

Add a config file for Apache and reload Apache:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ sudo sh -c "cat >> /opt/local/apache2/conf/httpd.conf <<'EOF'

# phpMyAdmin
Include conf/extra/phpmyadmin.conf
EOF"
$ sudo sh -c "cat >> /opt/local/apache2/conf/extra/phpmyadmin.conf <<'EOF'
AliasMatch ^/phpmyadmin(?:/)?(/.*)?$ /opt/local/www/phpmyadmin$1
AliasMatch ^/phpMyAdmin(?:/)?(/.*)?$ /opt/local/www/phpmyadmin$1

<Directory "/opt/local/www/phpmyadmin">
 Options -Indexes
 AllowOverride None
 Order allow,deny
 Allow from all

 LanguagePriority en de es fr ja ko pt-br ru
 ForceLanguagePriority Prefer Fallback
</Directory>
EOF"
$ sudo /opt/local/bin/port unload apache2
$ sudo /opt/local/bin/port load apache2

Basic set up of phpMyAdmin:

1
2
3
sudo sed -i "" "s/blowfish_secret\'\] = \'/blowfish_secret\'\] = \'`cat /dev/urandom | strings | grep -o '[[:alnum:]]' | head -n 50 | tr -d '\n'`/" /opt/local/www/phpmyadmin/config.inc.php
sudo /opt/local/bin/mysql5 -uroot -proot < /opt/local/www/phpmyadmin/scripts/create_tables.sql
sudo /opt/local/bin/mysql5 -uroot -proot -e"CREATE USER 'pma'@'localhost' IDENTIFIED BY 'pmapass'; GRANT SELECT, INSERT, DELETE, UPDATE ON phpmyadmin.* TO pma@localhost;"

Hooray!

Now you can edit ~/Sites/httpd-vhosts.conf and add a new virtual host. You’d need to reload Apache after doing so by running sudo port unload apache2 && sudo port load apache2. phpMyAdmin or the mysql5 binary should provide you a way to create new databases and database users and you can then set up a local site.

If you find MacPorts too heavy a separation from OSX, or too slow to compile, you should check out my previous blog posts on setting up a MAMP environment with as many built-in tools as possible only by adding a MySQL installer or MySQL/MariaDB via Homebrew.

OS X 10.7 Lion Development: Native Apache & PHP With Homebrew MySQL or MariaDB

This post originally featured on the Echo & Co. blog.

OS X Lion ships with Apache and PHP, which both require a little bit of tweaking to get fully-functional for “MAMP” local development. The one thing Lion does not ship with is a database. This will be very similar to my previous post on local development but this time we’ll be using Homebrew to install either MySQL or MariaDB for the database. Since we’ll be using a compiler for Homebrew, I’ll also cover how to add APC and other PECL modules that you can add to OS X.

Note that for all commands before that are starting with a $, the dollar sign is showing a command-line prompt in Terminal, and you should not actually type it as part of the commands.

Apache

We’ll set things up so we won’t need sudo often in the future, and so we can manage multiple VirtualHosts. We’ll keep the Apache information and our website roots in ~/Sites, and Apache logs in ~/Sites/logs

1
2
3
4
5
[ ! -d ~/Sites ] && mkdir ~/Sites
touch ~/Sites/httpd-vhosts.conf
sudo ln -s ~/Sites/httpd-vhosts.conf /etc/apache2/other
mkdir ~/Sites/logs
chmod 0777 ~/Sites/logs

Edit the new ~/Sites/httpd-vhosts.conf file and add the following. Note that you’ll need to change all instances of “/Users/name” to your actual home folder path.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 
# Use name-based virtual hosting. 
# 
NameVirtualHost *:80

# 
# Set up permissions for VirtualHosts in ~/Sites 
# 
<Directory "/Users/name/Sites">
    Options Indexes FollowSymLinks MultiViews
    AllowOverride All
    Order allow,deny
    Allow from all
</Directory>

# For http://localhost in the OS X default location 
<VirtualHost _default_:80>
    ServerName localhost
    DocumentRoot /Library/WebServer/Documents
</VirtualHost>

# 
# VirtualHosts below 
# 

## Template 
#<VirtualHost *:80> 
#    ServerName domain.local 
#    CustomLog "/Users/name/Sites/logs/domain.local-access_log" combined 
#    ErrorLog "/Users/name/Sites/logs/domain.local-error_log" 
#    DocumentRoot "/Users/name/Sites/domain.local" 
#</VirtualHost>

Launch System Preferences and go to Sharing and toggle Web Sharing off and on so it’s started and reloaded with the new settings. Then click on the blue underlined link under “Your computer’s website is available at this address:” to ensure Apache is working correctly, and you should see text saying “It works!”

To add a site, duplicate the <VirtualHost> section under the Template, remove the comments, and edit appropriately. Edit /etc/hosts (or use Gas Mask) and add an entry for 127.0.0.1 for your project.local, and make the appropriate change in the conf file, along with the correct DocumentRoot and Log file locations. See Apache’s documentation for more information.

PHP

OS X has shipped with PHP for quite some time, but not had it enabled by default. We’ll enable mod_php for Apache, and also set up an /etc/php.ini file based on the default one shipped with Lion with some development-friendly changes. Change your timezone as needed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ sudo sh -c "grep php /etc/apache2/httpd.conf|grep LoadModule|cut -d'#' -f2 > /etc/apache2/other/php5-loadmodule.conf"
$ sudo cp -a /etc/php.ini.default /etc/php.ini
$ sudo sh -c "cat >> /etc/php.ini <<'EOF' 


;; 
;; User customizations below 
;; 

; Original - memory_limit = 128M 
memory_limit = 196M 
; Original - post_max_size = 8M 
post_max_size = 200M 
; Original - upload_max_filesize = 2M 
upload_max_filesize = 100M 
; Original - default_socket_timeout = 60 
default_socket_timeout = 600 
; Original - max_execution_time = 30 
max_execution_time = 300 
; Original - max_input_time = 60 
max_input_time = 600 
; Original - display_errors = Off 
display_errors = on 
; Original - display_startup_errors = Off 
display_startup_errors = on 
; Original - ;date.timezone = 
date.timezone = 'America/New_York' 
EOF"

Optional: Lion ships with PEAR but not installed, whereas Snow Leopard it was installed. This will install PEAR from the phar archive, upgrade it, and add ‘pear’ and ‘pecl’ aliases to your shell.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ sudo /usr/bin/php /usr/lib/php/install-pear-nozlib.phar
$ cat >> ~/.bashrc <<'EOF' 

alias pear="php /usr/lib/php/pear/pearcmd.php" 
alias pecl="php /usr/lib/php/pear/peclcmd.php" 
EOF
$ . ~/.bashrc
$ sudo pear channel-update pear.php.net
$ sudo pecl channel-update pecl.php.net
$ sudo pear upgrade --force pear
$ sudo pear upgrade
$ sudo pecl upgrade
$ sudo sh -c "cat >> /etc/php.ini <<'EOF' 
; Original - ;include_path = ".:/php/includes" 
include_path = ".:/usr/lib/php/pear" 
EOF"

We’ll cover APC and other PECL modules after we have Homebrew up and running.

Toggle Web Sharing in System Preferences > Sharing for the new PHP options to take effect.

Homebrew

Hop onto the Mac App Store and download Xcode 4. It’s free! When it’s finished downloading, you’ll have an Application called Install Xcode in the Applications folder, so run that and click through and you’ll be all set. If it seems like overkill since we’re not covering iOS or Mac app development, Xcode ships with the compiler we need for Homebrew and installing PECL modules.

To install Homebrew, go to https://github.com/mxcl/homebrew/wiki/installation and run the ruby script in Terminal. If you get the error message: Error: Cannot write to /usr/local, run:

1
2
sudo chmod g+w /usr/local
sudo chgrp staff /usr/local

MariaDB / MySQL

MariaDB is a fork of MySQL that has additional features and is a drop-in replacement for MySQL. MariaDB is supported by Drupal 7, and works with Drupal 6 as well. Make the choice to go with either MariaDB or MySQL and continue; I’ll cover how to setup and use both.

MySQL

To install MySQL, simply run:

1
brew install mysql

cmake is a dependency of MySQL, and cmake needs java, so if you get a pop-up asking you to install a Java runtime, be sure to click Install and proceed. Grab a book because compiling MySQL and its dependencies will take several minutes. Now, to configure MySQL (includes raising packet limits for easier use in a non-production scenario) and start:

1
2
3
4
5
unset TMPDIR
mysql_install_db --verbose --user=`whoami` --basedir="$(brew --prefix mysql)" --datadir=/usr/local/var/mysql --tmpdir=/tmp
cp $(brew --prefix mysql)/support-files/my-small.cnf /usr/local/var/mysql/my.cnf
sed -i "" 's/max_allowed_packet = 1.*M/max_allowed_packet = 2G/g' /usr/local/var/mysql/my.cnf
[ ! -d ~/Library/LaunchAgents ] && mkdir ~/Library/LaunchAgents

MariaDB

To install MariaDB, run:

1
brew install mariadb pidof

pidof is referenced in some of the MariaDB scripts but is not listed as a prerequisite in MariaDB’s Homebrew formula, so we need to install it. To configure and start:

1
2
3
4
5
unset TMPDIR
mysql_install_db --verbose --user=`whoami` --basedir="$(brew --prefix mariadb)" --datadir=/usr/local/var/mysql --tmpdir=/tmp
cp $(brew --prefix mariadb)/share/mysql/my-small.cnf /usr/local/var/mysql/my.cnf
sed -i "" 's/max_allowed_packet = 1.*M/max_allowed_packet = 2G/g' /usr/local/var/mysql/my.cnf
[ ! -d ~/Library/LaunchAgents ] && mkdir ~/Library/LaunchAgents

APC and other PECL modules

PECL modules require compiling, which is why I excluded it from my previous blog post because I did not require Xcode to be installed. An easy one is uploadprogress which is often recommended for Drupal development:

1
2
3
4
5
6
$ sudo pecl install uploadprogress
$ sudo sh -c "cat >> /etc/php.ini <<'EOF' 

; Enable PECL extension uploadprogress 
extension=uploadprogress.so 
EOF"

APC is easy too, but requires you to install pcre libraries. With Homebrew, this is easy, and we’ll add some basic APC parameters:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ brew install pcre
$ sudo pecl install apc
$ sudo sh -c "cat >> /etc/php.ini <<'EOF' 

; Enable PECL extension APC 
extension=apc.so 
apc.enabled = 1 
apc.shm_segments = 1 
apc.shm_size = 96M 
apc.cache_by_default = 1 
apc.stat = 1 
apc.rfc1867 = 1 
EOF"

Non-PECL extensions, like mcrypt, require some additional work. Check out this guide for building mcrypt.

Make PHP and MariaDB / MySQL Play Nice

If you were to run php -i|egrep 'mysql.*default_socket' you would see that PHP was compiled to expect the MySQL socket file in /var/mysql, but MariaDB / MySQL will place it in /tmp. This easiest fix is to tell PHP to look in /tmp:

1
sudo sed -i "" 's|/var/mysql/mysql\.sock|/tmp/mysql.sock|g' /etc/php.ini

Toggle Web Sharing in System Preferences > Sharing for the new PHP options to take effect.

Hooray!

You should now be all set to keep adding more VirtualHosts in httpd-vhosts.conf and begin development on your local machine. Stay tuned for instructions on how to use MacPorts!

Update: If you’re interested in leveraging more from Homebrew, like replacing Apache and PHP all from Homebrew, check out the homebrew-alt repository on GitHub. Newer versions of Homebrew allow you to install formulae from URLs or alternate folders, and homebrew-alt provides formulae that replace built-in OS X applications (something official formulae will not do). For example, you could install PHP with PHP-FPM, something the OS X-provided PHP does not offer. Have fun exploring!

OS X 10.7 Lion Development: Native MAMP With MySQL Installer

This post originally featured on the Echo & Co. blog.

With the release of Lion, there are some subtle differences to setting up a local MAMP (Mac OS X, Apache, MySQL, PHP) environment compared to Snow Leopard. In an effort to keep this from being overly wordy and just get to the good stuff, we’ll dive right in, so read on to get started.

Note that for all commands before that are starting with a $, the dollar sign is showing a command-line prompt in Terminal, and you should not actually type it as part of the commands.

Apache

We’ll set things up so we won’t need sudo often in the future, and so we can manage multiple VirtualHosts. We’ll keep the Apache information and our website roots in ~/Sites, and Apache logs in ~/Sites/logs

1
2
3
4
5
$ [ ! -d ~/Sites ] && mkdir ~/Sites
$ touch ~/Sites/httpd-vhosts.conf
$ sudo ln -s ~/Sites/httpd-vhosts.conf /etc/apache2/other
$ mkdir ~/Sites/logs
$ chmod 0777 ~/Sites/logs

Edit the new ~/Sites/httpd-vhosts.conf file and add the following. Note that you’ll need to change all instances of “/Users/name” to your actual home folder path.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 
# Use name-based virtual hosting. 
# 
NameVirtualHost *:80

# 
# Set up permissions for VirtualHosts in ~/Sites 
# 
<Directory "/Users/name/Sites">
    Options Indexes FollowSymLinks MultiViews
    AllowOverride All
    Order allow,deny
    Allow from all
</Directory>

# For http://localhost in the OS X default location 
<VirtualHost _default_:80>
    ServerName localhost
    DocumentRoot /Library/WebServer/Documents
</VirtualHost>

# 
# VirtualHosts below 
# 

## Template 
#<VirtualHost *:80> 
#    ServerName domain.local 
#    CustomLog "/Users/name/Sites/logs/domain.local-access_log" combined 
#    ErrorLog "/Users/name/Sites/logs/domain.local-error_log" 
#    DocumentRoot "/Users/name/Sites/domain.local" 
#</VirtualHost>

Launch System Preferences and go to Sharing and toggle Web Sharing off and on so it’s started and reloaded with the new settings. Then click on the blue underlined link under “Your computer’s website is available at this address:” to ensure Apache is working correctly, and you should see text saying “It works!”

To add a site, duplicate the <VirtualHost> section under the Template, remove the comments, and edit appropriately. Edit /etc/hosts (or use Gas Mask) and add an entry for 127.0.0.1 for your project.local, and make the appropriate change in the conf file, along with the correct DocumentRoot and Log file locations. See Apache’s documentation for more information.

PHP

OS X has shipped with PHP for quite some time, but not had it enabled by default. We’ll enable mod_php for Apache, and also set up an /etc/php.ini file based on the default one shipped with Lion with some development-friendly changes. Change your timezone as needed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ sudo bash -c "grep php /etc/apache2/httpd.conf|grep LoadModule|cut -d'#' -f2 > /etc/apache2/other/php5-loadmodule.conf"
$ sudo cp -a /etc/php.ini.default /etc/php.ini
$ sudo bash -c "cat >> /etc/php.ini <<'EOF' 


;; 
;; User customizations below 
;; 

; Original - memory_limit = 128M 
memory_limit = 196M 
; Original - post_max_size = 8M 
post_max_size = 200M 
; Original - upload_max_filesize = 2M 
upload_max_filesize = 100M 
; Original - default_socket_timeout = 60 
default_socket_timeout = 600 
; Original - max_execution_time = 30 
max_execution_time = 300 
; Original - max_input_time = 60 
max_input_time = 600 
; Original - display_errors = Off 
display_errors = on 
; Original - display_startup_errors = Off 
display_startup_errors = on 
; Original - ;date.timezone = 
date.timezone = 'America/New_York' 
EOF"

Optional: Lion ships with PEAR but not installed, whereas Snow Leopard it was installed. This will install PEAR from the phar archive, upgrade it, and add pear and pecl aliases to your shell.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ sudo /usr/bin/php /usr/lib/php/install-pear-nozlib.phar
$ cat >> ~/.bashrc <<'EOF' 

alias pear="php /usr/lib/php/pear/pearcmd.php" 
alias pecl="php /usr/lib/php/pear/peclcmd.php" 
EOF
$ . ~/.bashrc
$ sudo pear channel-update pear.php.net
$ sudo pecl channel-update pecl.php.net
$ sudo pear upgrade --force pear
$ sudo pear upgrade
$ sudo pecl upgrade
$ sudo sh -c "cat >> /etc/php.ini <<'EOF' 
; Original - ;include_path = ".:/php/includes" 
include_path = ".:/usr/lib/php/pear" 
EOF"

Note that installing most pecl modules will require having a compiler installed via Xcode. I’ll be addressing installing uploadprogress and APC in a later post, since this walkthrough requires no compiling.

Toggle Web Sharing in System Preferences > Sharing for the new PHP options to take effect.

MySQL

MySQL is the only thing not shipped with OS X that we need for our development environment. Go to the download site for OS X pre-built binaries at http://dev.mysql.com/downloads/mysql/index.html#macosx-dmg and choose the DMG Archive most appropriate for your system. As of this writing, the most recent version says OS X 10.6, but it will work on 10.7 as well.

Open the downloaded disk image and begin by installing the mysql-5.x.x-osx10.6-x86_64.pkg file. Once the Installer script is completed, install the Preference Pane by double-clicking on MySQL.prefPane. If you are not sure where to install the preference pane, choose “Install for this user only.”

Optional: If you wish to have MySQL start on boot, you do need to additionally install the MySQLStartupItem.pkg file. The preference pane has a toggle for starting on boot, but it will not work until you install the pkg and you run the following in Terminal:

1
sudo chown -R root:wheel /Library/StartupItems/MySQLCOM

Open System Preferences and MySQL and start the database by clicking the Start MySQL Server button.

Optional: The MySQL installer installed its files to /usr/local/mysql, and the binaries are in /usr/local/mysql/bin which is not in the $PATH of any user by default. Rather than edit the $PATH shell variable, we add symbolic links in /usr/local/bin (a location already in $PATH) to a few MySQL binaries. Add more or omit more binaries as desired:

1
2
3
4
5
6
[ ! -d /usr/local/bin ] && sudo mkdir -p /usr/local && sudo chmod 0777 /usr/local && mkdir /usr/local/bin && sudo chmod 0755 /usr/local
cd /usr/local/bin
ln -s /usr/local/mysql/bin/mysql
ln -s /usr/local/mysql/bin/mysqladmin
ln -s /usr/local/mysql/bin/mysqldump
ln -s /usr/local/mysql/support-files/mysql.server

This script ships with MySQL and only needs to be run once, and it should be run with sudo. As you run it, you can accept all other defaults after you set the root user’s password:

1
sudo /usr/local/mysql/bin/mysql_secure_installation

After installing MySQL, several sample my.cnf files are created but none is placed where MySQL can find it. Start with a “small” configuration file and make a few changes to increase the max_allowed_packet variable:

1
2
sudo cp /usr/local/mysql/support-files/my-small.cnf /usr/local/mysql/data/my.cnf
sudo sed -i "" 's/max_allowed_packet = 1.*M/max_allowed_packet = 2G/g' /usr/local/mysql/data/my.cnf

To load in these changes, go back to the MySQL System Preferences pane, and restart the server by pressing “Stop MySQL Server” followed by “Start MySQL Server.”

Make PHP and MySQL Play Nice

If you were to run php -i|egrep 'mysql.*default_socket' you would see that PHP was compiled to expect the MySQL socket file in /var/mysql, but MySQL will place it in /tmp. This easiest fix is to tell PHP to look in /tmp:

1
sudo sed -i "" 's/\/var\/mysql\/mysql\.sock/\/tmp\/mysql\.sock/g' /etc/php.ini

Toggle Web Sharing in System Preferences > Sharing for the new PHP options to take effect.

Hooray!

You should now be all set to keep adding more VirtualHosts in httpd-vhosts.conf and begin development on your local machine. Stay tuned for instructions on how to use MariaDB or MySQL with Homebrew instead of the Oracle MySQL installer, and MacPorts!

Use Syslog Instead of Watchdog

This post originally featured on the Echo & Co. blog.

In this example, we choose one of our servers to receive the Syslog traffic and the others will send traffic to it. In a large environment, you should use a non-public-facing server to act as a syslog server for your sites, ideally using a database to store data, but we’ll just output everything to a file to keep things simple for this guide. If you decide to use a database like MySQL to store your data, this can still provide you with a starting point, but just know that using your same production database server defeats my intended purpose since I was ultimately trying to alleviate the amount of writes on the database.

Start by using yum to install rsyslog. If you’re using another distro, rsyslog is likely included in the base repositories. Also, note that CentOS 6 uses rsyslog by default so you may skip this step if it’s already installed on your system. You’ll need this on all servers running Drupal or your rsyslog server:

1
yum install rsyslog

For CentOS 5, you’ll need to unload the default syslog and switch to rsyslog. The following will ensure it starts on boot as well. Until otherwise noted, run the following commands on all servers:

1
2
3
/sbin/service syslog stop && /sbin/service rsyslog start
/sbin/chkconfig rsyslog on
/sbin/chkconfig syslog off

Rather than hacking the config file to bits, I find it easier to create an include folder and keep things organized by files. I also like to back up default files created by RPMs so if an update leaves behind rsyslog.rpmnew, you can compare it to the default to see the changes:

1
2
mkdir -v /etc/rsyslog.d
cp -av /etc/rsyslog.conf{,-default}

Add the following to /etc/rsyslog.conf below the last line containing “$ModLoad”:

1
2
# Include all files in /etc/rsyslog.d
$IncludeConfig /etc/rsyslog.d/*.conf

Reload the current rsyslog configuration:

1
/sbin/service rsyslog reload

The next several steps will only need to be run on the syslog server. Back up and edit /etc/sysconfig/rsyslog to accept UDP connections from other servers. If you’re using iptables, open up port 514 on UDP.

1
cp -a /etc/sysconfig/rsyslog /etc/sysconfig/rsyslog-default

Edit /etc/sysconfig.rsyslog and change SYSLOGD_OPTIONS=“-m 0” to SYSLOGD_OPTIONS=“-m 0 -r514”

Create a root folder for website logs to go:

1
mkdir /var/log/websites

Create the following file to work with example.com (this format will allow you to create other files for anotherexample.org, etc.): /etc/rsyslog.d/example.conf. Add the following:

1
2
3
4
# example.com
$template DailyPerHostLogs-example,"/var/log/websites/%SYSLOGTAG:F,58:1%/%SYSLOGTAG:F,58:1%.%$YEAR%-%$MONTH%-%$DAY%.log"
:syslogtag, contains, "example"                    -?DailyPerHostLogs-example
& ~

This will create /var/log/websites/example/example.2011-08-17.log and new files for each day. To add other websites, duplicate the file and replace example with another string. We’ll later use “example” in the Drupal configuration for Syslog later that will correlate to the correct file.

For those curious about this configuration: a template is needed for dynamic file paths with year-month-day. The syslogtag variable will use the string sent by Drupal in the Syslog module settings. Drupal will send it as example:, so :F,58:1 will use the colon as the field separator and grab the first field. Even though it would appear as though this single template could be used on multiple sites, I found I still had to have a unique template for each website or all websites would dump into the same file (perhaps this is remedied in newer versions of rsyslog). The last line means that the syslog input will not be sent to any other syslog files.

Reload the current rsyslog configuration, and the server is set up:

1
/sbin/service rsyslog reload

On the clients, we’ll create simple .conf files for each website. Create /etc/rsyslog.d/example.conf on each additional web server containing:

1
2
# example.com
:syslogtag, contains, "example"     @192.168.1.20

Note that the IP address can also be a domain name, though I would not recommend it to keep another process from being needed for the lookup.

Reload the current rsyslog configuration, and each web client is set up:

1
/sbin/service rsyslog reload

Enable the syslog module and go to http://example.com/admin/settings/logging/syslog**. Set the Syslog Identity to match the value used in /etc/rsyslog.d/example.conf; in this case, example**. Using the rsyslog filter we’re using, it does not matter which facility we send to. Save the configuration.

rsyslog-screenshot

Go to your rsyslog server and watch the /var/log/websites/example/example.2011-08-16.log file. After verifying that the server is receiving all messages, disable the Watchdog module in **http://example.org/admin/build/modules/list**.

There are improvements that can be had here, like using TCP instead of UDP, using LogAnalyzer to filter through the files, and more. This was meant to be an introduction into getting combined text files of your watchdog output while reducing the strain on your database by not writing to it as often.

Keep Drush Up to Date

This post originally featured on the Echo & Co. blog.

UPDATE: See Three Ways to Get Drush on OS X for a newer guide.

At EchoDitto, we’re big fans of Drush. It’s installed on all of our servers and it’s a great way to perform maintenance tasks, download core and modules, and much much more. I’m not a big fan of installing from zip files though, so let’s use git to easily keep our Drush install up to date.

Before the git transition, I would use Subversion to check out the latest drush release somewhere, like /usr/local/drush (on my Mac), using the CVS clone Subversible. Today, however, I noticed that the latest tagged release Subversible has is 4.2, and the Drush project page has 4.4 ready to download. I figured now would be a good time to start using git for this and stop relying on a third-party to provide the code for Subversion.

The commands below will walk through cloning the Drush git repository and then switching to the latest release. I realize the drush self-update will soon make this irrelevant, but this method works today and is a great exercise in learning how Drupal is using git, especially important coming from Subversion.

Clone the repository. It will create the subfolder “drush” from your current working directory:

1
2
git clone --branch master http://git.drupal.org/project/drush.git
cd drush

Use pear to download includes/table.inc. This one-liner will take care of everything for you as long as you have pear installed and are in the new drush directory:

1
2
3
4
pear download Console_Table
tar zxvf `ls Console_Table-*.tgz` `ls Console_Table-*.tgz | sed -e 's/\.[a-z]\{3\}$//'`/Table.php
mv `ls Console_Table-*.tgz | sed -e 's/\.[a-z]\{3\}$//'`/Table.php includes/table.inc
chmod 0644 includes/table.inc && rm -fr Console_Table-*

At this point you may or may not have a functioning Drush installation, because you’re using the HEAD of the Drush code repository and it might contain broken code. I would recommend switching the checkout to the latest release. We’ll start by viewing all releases:

1
2
3
4
5
6
7
8
9
10
$ git tag
5.x-1.0
5.x-1.0-beta1
5.x-1.0-beta2
... snip ...
7.x-4.2
7.x-4.3
7.x-4.4
debian/4.3-1
debian/4.4-1

As of this writing, there are over 50 results from this command, but you’ll see the latest is “7.x-4.4”. If we want our local Drush install to be at 4.4, change the checkout:

1
2
3
$ git checkout 7.x-4.4
Previous HEAD position was 1532d0c... #1079434 by msonnabaum. Add .gitignore for includes/table.inc
HEAD is now at a808ff0... Updating drush version

That’s it! Drush is set up on 4.4:

1
2
$ drush --version
drush version 4.4

Let’s say 4.5 is released tomorrow and you want to change your checkout. First you’ll need to update the local git repository: git pull

View the list of tags:

1
2
3
4
5
6
7
8
9
10
11
$ git tag
5.x-1.0
5.x-1.0-beta1
5.x-1.0-beta2
... snip ...
7.x-4.3
7.x-4.4
7.x-4.5
debian/4.3-1
debian/4.4-1
debian/4.5-1

And switch your checkout to the new release: git checkout 7.x-4.5

Repeat for future releases, and enjoy! Of course, once drush self-update begins working, that will be the preferred method, but this will work today. Please questions or comments below.

Dump Each MySQL Table to a File

This post originally featured on the Echo & Co. blog.

Here’s a one-liner to dump each table in a database to it’s own .sql file. Crack open your shell of choice and follow along.

Replace the USER, PASSWORD, and DBNAME values with your own. If you’re not running this to connect to a local database, add —host=domain.tld after each password to connect to your remote server.

1
for i in $(mysql -uUSER -p'PASSWORD' --batch --skip-column-names DBNAME -e'show tables;'); do mysqldump -uUSER -p'PASSWORD' DBNAME $i > DBNAME-$i.sql; done

Users, passwords, and database names are case sensitive, I made them uppercase here to call them out. Leave a comment if you have a question or notice a problem or typo.

Fix Pear Permissions Problem on Mac OS X

This post originally featured on the Echo & Co. blog.

On Snow Leopard, you can install drush without using sudo. A dependency for installing drush is downloading a Pear library. The following example should allow you to be able to use the following commands to install drush to /usr/local/drush with a symlink in /usr/local/bin/drush, but it fails on the ‘pear download’ step, even though it’s writing to a user-writeable directory. Read on the figure out how to fix this annoying problem.

1
2
3
4
5
6
7
svn co http://subversible.svn.beanstalkapp.com/modules/drush/tags/DRUPAL-7--4-2/ /usr/local/drush 
cd /tmp 
pear download Console_Table 
tar zxvf `ls Console_Table-*.tgz` `ls Console_Table-*.tgz | sed -e 's/\.[a-z]\{3\}$//'`/Table.php 
mv `ls Console_Table-*.tgz | sed -e 's/\.[a-z]\{3\}$//'`/Table.php 
/usr/local/drush/includes/table.inc $ rm -fr Console_Table-* 
ln -s /usr/local/drush/drush /usr/local/bin/drush

This assumes 1) /usr/local exists and you can write to it, and 2) you’ve added the following to your ~/.profile (or ~/.bashrc or ~/.bash_profile):

1
2
3
# Use the built-in PEAR and PECL scripts
alias pear="/usr/bin/php /usr/lib/php/pearcmd.php"
alias pecl="/usr/bin/php /usr/lib/php/peclcmd.php"

But, OS X has a strange permissions problem when using pear for the first time. Running ‘pear download’ should not require sudo, but if you’ve never used pear before you’ll get this error:

1
2
3
4
5
6
7
8
$ pear download Console_Table 

Warning: touch(): Unable to create file /usr/lib/php/.lock because Permission denied in PEAR/Registry.php on line 835 

Warning: touch(): Unable to create file /usr/lib/php/.lock because Permission denied in /usr/lib/php/PEAR/Registry.php on line 835 
could not create lock file: fopen(/usr/lib/php/.lock): failed to open stream: No such file or directory 
invalid package name/package file "Console_Table" 
download failed

Strange, right? Administrators in OS X are in the wheel group, but /usr/lib/php is not writable by the group. Before you go changing permissions (which might not be retained after an OS X update), all you have to do is run something as root with pear:

1
2
3
4
5
6
7
8
9
$ sudo pear list
Installed packages, channel pear.php.net:
=========================================
Package          Version State
Archive_Tar      1.3.3   stable
Console_Getopt   1.2.3   stable
PEAR             1.8.0   stable
Structures_Graph 1.0.2   stable
XML_Util         1.2.1   stable

The permissions and ownership of /usr/lib/php are still the same, but there is now the file /usr/lib/php/.lock

1
2
$ ls -Al /usr/lib/php/.lock
-rw-r--r--  1 root  wheel  0 Feb 15 16:08 /usr/lib/php/.lock

You’d think that when pear has completed running the list command it would delete the .lock file, but it doesn’t. But since it’s there, the original pear download command will now run without sudo:

1
2
3
4
5
$ pear download Console_Table
downloading Console_Table-1.1.4.tgz ...
Starting to download Console_Table-1.1.4.tgz (9,369 bytes)
.....done: 9,369 bytes
File /private/tmp/Console_Table-1.1.4.tgz downloaded

Of course, running ‘pear install’ or many other commands will require sudo, but it’s silly that commands that do not write any files to /usr/lib/php or its children would require sudo.

Unrelated to the pear permissions issue, but related to my drush install script; this assumes that /usr/local and /usr/local/bin exist. By default, /usr/local/bin is in the $PATH regardless of whether or not the folder exists, but on a vanilla OS X install /usr/local does not exist. I have both Xcode and homebrew installed. I know homebrew will create /usr/local for you, but I’m not sure about Xcode. If you don’t want to have either installed but want drush installed to /usr/local, run this:

1
2
sudo mkdir -p /usr/local/bin
sudo chown -R root:wheel /usr/local

Easy PHP 5.2 RPMs on CentOS

This post originally featured on the Echo & Co. blog.

I had previously written a post on one method of upgrading PHP from 5.1 to 5.2 on CentOS and Red Hat servers by creating new RPMs. Since then, I have found a much better way to create PHP 5.2.17 (or newer) RPMs to easily upgrade (and later remove, if you want) the older version available by default. I’ll presume you have no prior experience building PHP RPMs.

We start by adding the Extra Packages for Enterprise Linux (EPEL) repository to our server. EPEL is safe to add as an additional repository and leave enabled because it provides complementary packages only and will not conflict with the CentOS or Red Hat repositories. Since their installation method may change, I won’t reproduce it here; instead head over to the installation portion of the FAQ and it’s just one line to get started with EPEL.

With EPEL enabled, we will need just one new package to build RPMs: Mock. Mock will create a chroot, download what’s necessary to build your RPM, build, and cleanup afterwards. Mock only requires a couple of python packages as dependencies; it will not require a compiler or anything like that since mock will grab them as needed during operation. I will presume you are not running as root so I will use sudo as necessary. To install mock:

1
sudo yum install mock

That’s it. You can run all mock operations with the ‘sudo’ command to run as root, but your packages will be built inside the chroot as a non-root user. Or, if you prefer to not use sudo when running mock, add your user to the mock group (where username is your user):

1
sudo /usr/sbin/usermod -G mock username

Using the source code from php.net and creating a RPM spec file from scratch would be a lot of work, so we’ll grab a source RPM for PHP 5.2. I’ve found that Remi has the most up-to-date RPM spec with the latest patches and robust install scripts. Browse Remi’s SRPMS and download the latest PHP 5.2 file. As of this writing, it’s php-5.2.17-1.remi.src.rpm. On your system, download it with wget in your home directory:

1
2
cd ~
wget http://rpms.famillecollet.com/SRPMS/php-5.2.17-1.remi.src.rpm

Now, we could just run mock and rebuild this source RPM and hopefully install the results, but it would fail due to not having sqlite2-devel installed. Remi provides sqlite2 and sqlite2-devel, so you could add Remi’s yum repository to your mock settings, but then it would grab Remi’s other updated packages like MySQL 5.5 and others during the PHP build. My goal here is to only build PHP against Base CentOS and EPEL packages. So, we have to edit the spec file and rebuild the source RPM before passing it off to mock. Don’t worry; while there are a few steps, it’s not very difficult if you follow closely.

To unpack the source RPM we just downloaded into our home directory, we’ll need to set up the .rpmmacros file in our home directory. Why? Without it, it will attempt to extract to /usr/src/redhat, and only root can use that directory, and you should never build any packages as a privileged user. We’ll create the macros file and the directories it needs from your home directory:

1
2
3
cd ~
echo '%_topdir %(echo $HOME)/rpmbuild' > ~/.rpmmacros
mkdir -p ~/rpmbuild/{SOURCES,SPECS,SRPMS}

Unpack the source RPM. This will extract the PHP 5.2.17 source and any patches and configuration files into ~/rpmbuild/SOURCES, and the spec file will be in ~/rpmbuild/SPECS:

1
rpm -ivh php-5.2.17-1.remi.src.rpm

Now we need to make a couple of changes to the spec file. We’ll change to the SPECS directory and make a copy of Remi’s spec file so we can keep it for reference:

1
2
cd ~/rpmbuild/SPECS
cp -a php52.spec php.spec

Open up php.spec in your favorite text editor and let’s have the PHP configure script use the bundled SQLite2 libraries instead of searching for ones on your system:

  1. Find the line “BuildRequires: sqlite2-devel >= 2.8.0” and delete it
  2. Find “—with-sqlite=shared,%{_prefix} \” and replace it with “—with-sqlite=shared \”
  3. Optionally: Add your own lines to the %changelog describing that you removed the sqlite2-devel build requirement
  4. Optionally: Remove the first 4 lines after “%pre common” to remove Remi’s note about their forums

It took 2 small tweaks to remove the sqlite2-devel RPM requirement not found in our standard repositories. See php.spec.diff to see only the changes we made; or php.spec for the complete file if you do not want to edit it yourself.

The hard part is now over! It’s easy from here on out. First step is to build the newly-edited spec into your own source RPM. But first, if you don’t have “rpmbuild” available, it’s a quick install via yum:

1
sudo yum install rpm-build

It’s possible your system may have already had the yum package rpm-build installed. We’ll build our source RPM with “-bs” to build the source, and “—nodeps” to prevent our system from checking all of the “Requires” in the spec since we’re just building the source:

1
2
rpmbuild --nodeps -bs php.spec
Wrote: /home/username/rpmbuild/SRPMS/php-5.2.17-1.src.rpm

rpmbuild will show you where it wrote your source RPM. All we have to do is provide it to mock; “-v” will give us verbose output and “-r epel-5-x86_64” will use the mock profile at “/etc/mock/epel-5-x86_64.cfg”. If you’re on a 32-bit system, change “epel-5-x86_64” to “epel-5-i386”:

1
2
cd ~/rpmbuild/SRPMS
mock -v -r epel-5-x86_64 php-5.2.17-1.src.rpm

The output shows you that mock is creating a chroot, downloading the bare essentials needed for a chroot and compiling tools, ccache to speed up compilation, and the packages in all “Requires” lines in the spec file. When mock is finished with the build after several minutes, you’ll see output similar to:

1
2
INFO: Done(php-5.2.17-1.src.rpm) Config(epel-5-x86_64) 10 minutes 20 seconds
INFO: Results and/or logs in: /var/lib/mock/epel-5-x86_64/result

The /var/lib/mock/epel-5-x86_64/results/ folder will contain all of your PHP RPMs, including another source RPM. You can now either install these PHP packages by hand, or you can dump them into your own Yum repository and install with yum.

TL;DR

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sudo yum install mock
sudo /usr/sbin/usermod -G mock username
cd ~
wget http://rpms.famillecollet.com/SRPMS/php-5.2.17-1.remi.src.rpm
echo '%_topdir %(echo $HOME)/rpmbuild' > ~/.rpmmacros
mkdir -p ~/rpmbuild/{SOURCES,SPECS,SRPMS}
rpm -ivh php-5.2.17-1.remi.src.rpm
cd ~/rpmbuild/SPECS
cp -a php52.spec php.spec
sed -i "/BuildRequires\: sqlite2-devel/d" php.spec
sed -i "s/--with-sqlite=shared,%{_prefix}/--with-sqlite=shared/" php.spec
sudo yum install rpm-build
rpmbuild --nodeps -bs php.spec
cd ~/rpmbuild/SRPMS
mock -v -r epel-5-x86_64 php-5.2.17-1.src.rpm

Resulting RPMs will be in /var/lib/epel-5-x86_64/results. If on a 32-bit system, replace epel-5-x86_64 with epel-5-i386.

Are Your WordPress Sites Running the Latest Core?

This post originally featured on the Echo & Co. blog.

In addition to hosting Drupal sites, we also host a number of WordPress sites. Similar to (checking all of our Drupal core versions)[http://echodittolabs.org/blog/2011/01/are-your-drupal-sites-running-latest-core], we needed an easy way to quickly see what versions we are running on all of our WordPress sites. Kudos to Ethan for getting this one kicked off; here is a bash script to check your definable $WEBHOME (where you deposit all of your WordPress webroots) and scan for WordPress versions.

Note: this requires bash, grep, wc, sed, awk, and tree. If your system doesn’t have tree, it’s usually pretty easy to get from EPEL/Homebrew/apt-get/[mac]ports/etc.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash

# Where to start scanning
WEBHOME=/var/www/vhosts

# Workaround to fix awk counting below
WEBHOMECOUNT=$(($(echo "${WEBHOME}"|grep -o "/"|wc -l| sed s/\ //g)+2))

# Other projects could use 'version.php' so we include 
# 'wp-includes/' in our search to limit it to WordPress
for i in $(tree -L 5 -if ${WEBHOME} | grep 'wp-includes/version.php'); do
  SITE=$(echo $i|awk -v count="$WEBHOMECOUNT" -F/ '{for(j=count;j<=NF-2;j++) \
        printf $j"/"}' | sed 's/.$//g')
  VERSION=$(grep "wp_version = " $i|awk -F\' '{print $2}')
  echo $SITE - $VERSION
done

As a result, you’ll see output like this:

1
2
3
4
5
$ ./wordpress-version-check.sh
speedyupdates.com - 3.0.4
littlebehind.org - 3.0.3
scaredtoupgrade.com/blog - 2.8.2
kickinitoldschool.org/web/wordpress - 2.0.2

If you think there’s a better way, or have any questions, let me know in the comments.

Are Your Drupal Sites Running the Latest Core?

This post originally featured on the Echo & Co. blog.

At EchoDitto, we host a lot of Drupal sites. Just about all of our web servers contain more than one completely separate Drupal cores since we develop most of our websites independently. We also keep an eye out on Drupal Security and when to apply core updates. Since we’re not using Aegir, nor do we run every site off of a single multi-site core install, we need a quick and easy way to see what versions we’re running.

Here’s a shell script we recently developed to report the Drupal Core versions for all of our sites under a single parent folder. It works on Drupal 4 through 7. It’s not been tested outside of our platform, or non-RHEL systems, but it should be universal as long as you have bash, find, awk, and grep. I decided not to use Drush because it would involve bootstrapping every site and invoking PHP; this is just more lightweight. Drush also would not work for Drupal 4 sites, which we surprisingly still have a couple kicking around still.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/bin/bash

# Where to start looking
WEBHOME=/var/www/vhosts

# Workaround to fix awk counting below, but it works
WEBHOMECOUNT=$(($(echo "${WEBHOME}"|grep -o "/"|wc -l| sed s/\ //g)+2))

# Change maxdepth from 5 to some other value if your webroot is not being detected
for i in $(find $WEBHOME -maxdepth 5 -type f -name system.module); do
  # Grabs only the version number from the file as described 
  # at http://drupal.org/handbook/version-info
  VERSION=$(grep "VERSION" ${i}|awk -F\' '{print $4}')

  # Drupal 4 has the system.module file in a lower directory than 5+
  if [[ $VERSION == 4* ]]; then
    SITE=$(echo $i|awk -v count="$WEBHOMECOUNT" -F/ '{for(j=count;j<=NF-2;j++) \
           printf $j"/"}' | sed 's/.$//g')
  else
    SITE=$(echo $i|awk -v count="$WEBHOMECOUNT" -F/ '{for(j=count;j<=NF-3;j++) \
           printf $j"/"}' | sed 's/.$//g')
    if [ -z $VERSION ]; then
      # Drupal 7 does not keep the version number on system.module, despite the 
      # drupal.org page. Grab from changelog (disabled b/c it includes extra info)
      #VERSION="$(sed '3q;d' ${WEBHOME}/${SITE}/CHANGELOG.txt)"
      # All versions of Drupal have the version of the module in system.info, 
      # so we grab it here for D7. The only reason I'm not using this for all 
      # versions is because of the aforementioned drupal.org page
      VERSION=$(grep "version = \"" ${WEBHOME}/${SITE}/modules/system/system.info \
                | awk -F\" '{print $2}')
    fi
  fi
  echo $SITE - $VERSION
done

As a result, you’ll see the directory tree after $WEBHOME where the root of the site is and the version number. Below is an example:

1
2
3
4
5
$ ./drupal-version-check.sh
mysite.org/www - 5.22
thissite.net - 4.7.11
subfolder/drupalftw.com - 6.12
blah.org/trunk - 7.0

If you have suggestions, please send them over. This script is still young and I’d love to see if anyone has any better ideas.