How to quickly update WHMCS on WiredTree hosting and others.

NOTE: This script has been updated to remove the premature deletion of the install directory.

Over the last month or two, WHMCS has been updated many times. This is a GOOD THING, but it can be annoying to go through the normal update process every week or two.

So, I created a small function that I’ve added to /etc/bashrc on my server.

It does the following:
1. Verifies the script is running from the right server.
2. Changes directories to the location where I have WHMCS installed.
3. Removes any files and folders from a previous update
4. Downloads the latest update from the password protected WiredTree repository
5. Unpacks it
6. Renames the admin directory to a new name for security purposes (you may need to do the cron dir too)
7. Changes the owner and group of the new files to be the one required for this website.
8. Copies in all of the new files, overwriting what is there.

So updates now take a few seconds rather than 20 distracting minutes. Maybe you will find this useful.

Here is the function I added to /etc/bashrc:


function upwhmcs(){
  curhostname=$(hostname -s)

  if [ "${curhostname}" == "$desiredhost" ]; then
    rm -Rf whmcs
    rm -f
    unzip -o
    cd whmcs
    cp -Rf * ../www
    cd ../www
  echo "Wrong Server. Update skipped"


To use that listing you need to replace the following text
REPLACE_WITH_HOSTNAME – Replace this with what you get when you run ‘hostname -s’ from the command line.
REPLACE_WITH_DIRNAME – Replace this text with the directory of your whmcs installation. Maybe it is /home/whmcs/ or /home/hosting/ or /home/sales/
REPLACE_WITH_USERNAME – This should become the username that WiredTree gave you to login and get WHMCS updates
REPLACE_WITH_PASSWORD – This should become the password that WiredTree gave you to login and get WHMCS updates
REPLACE_WITH_ADMIN_DIR_NAME – This is the custom name of the admin directory on your server. If you just use admin, delete this mv line entirely.
REPLACE_WITH_DIROWNER – This is quite likely the same value as REPLACE_WITH_DIRNAME

This script assumes that you have WHMCS installed as its own website/subdomain. You can probably adjust it to your needs if you run it in a sub-directory. If you make this customization, send it back to me and I’ll add it to the article.

After the upgrade process, you must visit the site, follow the update steps and then go back and delete the “install” directory from the installation.  That should do it!

How To: List the lines that exist in one file and not another in Linux or Windows Command Line

This solution uses grep.

Grep is a search tool that exists by default in just about every Linux installation. You can also search Google for Windows Grep.

for a Demo, Here is FileA.txt

1. In Both
2. In Both
3. In Both
4. In File A Only
5. In Both
0. Out of Order In Both
-1.  Out of Order In A Only
In A only

Here is FileB.txt

1. In Both
2. In Both
3. In Both
4. In File B Only
5. In Both
0. Out of Order In Both
-1.  Out of Order In B Only
In B only

To show lines that only exist in File B and not in A. Do this:

# grep -v -xFf FileA.txt FileB.txt

4. In File B Only
-1.  Out of Order In B Only
In B only

To show lines that only exist in File A and not in B. Do this:

# grep -v -xFf FileB.txt FileA.txt

4. In File A Only
-1.  Out of Order In A Only
In A only


In windows, Grep you can drop the capital F from the command line. But it really shouldn’t matter.

The command line arguments are:

-v = Reverse the compare so you get what doesn’t match instead of what does

-x = Compare entire lines at a time instead of characters

-F = Do not parse the contents of each line for any mid-line regex commands

-f = Compare the specified files


Hope that helps someone!

HOW TO: Take a quick glance at errors from a project.

PHP records all errors to a file named error_log

I needed to look at the status of a project on two servers to see what errors were being thrown.

This command line shows the top 20 errors and how often they occurred within the last 1000 errors..
tail -1000 error_log | awk -F ‘]’ ‘{print $4}’| sort | uniq -c | sort -nr | head -20

Depending upon the load on your server, that could report on the errors in the last half hour, or across several days. In either case, it will give you a quick idea on where you should clean your code of warnings and notices and where you need to do some fixing… Just remember there’s no time line involved in the report. So the error messages could be from something already fixed.

HOW TO: Find the IP address of a domain from the local server before propagation

Have you ever wanted to know the ip address assigned to a domain according to your local server? I had this problem. I needed to get an IP address of a domain before the domain propagated. I was writing a custom cpanel postwwwacct script. For some very, very odd reason, cpanel will tell you IF you have a dedicated ip address for the domain, but not what it is. Fortunately postwwwacct occurs after the bind of the ip address.

I thought I would have to use a grep and awk to parse the IP address out of the .db file in /var/named but fortunately it occurred to me that I could just do a simple dig call within my script and get the value back. That’s straight forward and returns only the information I want to have.

So here is a perl script that does this:

$ip=`dig \@$ns $domain A +short`;
if ( $ip == -1 )
  print "command failed: $!\n";
  printf "The result is: " . $ip;


You will of course have to replace with the url for the name server running on your server.
The backticks (“) indicate you need to run a command line and return the stdout results. (And yes, I think backticks sound gross but that’s what they are called.)

Also note the @ is a special character and must be escaped with a \.

Hope this helps!

HOW TO: Dump or Backup all MySQL databases to separate files

I needed to transfer about 40 databases from a new clients server over to my hosting platform.

When doing a couple database exports, I might use PHPMyAdmin to do the export. Heck, it’s convenient because I usually want to look around at the same time. For doing a straight export, mysqldump is a great program that is even easier than phpmyadmin. There’s really nothing to it! (NOTE: For large databases with a LARGE number of tables you need to add –skip-lock-tables)

mysqldump –all-databases > dbdump.sql

If you want to get fancy, you can even compress the file

mysqldump –all-databases | gzip -9 > dbdump.sql.gz

But I wanted to go one step further. I wanted every database to have a separate file, compressed and correctly named.

I was shocked how simple it was to write. First I had to ask mysql for a list of the databases. Then I needed extract just the database name, removing the table borders. Then I needed to tell mysqldump to use each database name in the export source and destination.

In no time I had a 3 line script that was incredibly powerful.

I give you

for database in $(mysql -e "show databases"|awk -F " " '{print $1}'); do
  mysqldump $database | gzip -9 > $database.sql.gz



Note that you may need to provide the -u and -p parameters (username and password).

That’s all there is to it!

The answer to: How do you set directories to 755 and files to 644?

AKA: Choosing and setting safe file permissions for a WordPress install.


If you have ever watched a WordPress security presentation, you’ve heard the advice:

Generally speaking, directories should be 755 and files should be 644.

But the presenter never tells you how to change these settings for all the directories and files in the WordPress install. I’m guilty of the same thing in my presentation.  After all, Bash doesn’t go over well in front of general audiences.

But how do you change all the directories and files to have the settings that you want? You’re certainly not going to go around and manually change all of the permissions file by file & directory by directory!  And if you did, would you still be able to upload files and upgrade your plugins with those permissions? Probably not.

I thought I would share how I handle this on my servers.  My method is not the most refined, and does have some minor issues, but it works and gets me 95% to where I should be.

I have this function built into my .bashrc file:

function defaultr()
find . -type d | xargs chmod -v 755
find . -type f | xargs chmod -v 644
find . -type d -iname uploads | xargs chmod -Rv 774
find . -type d -iname blogs.dir | xargs chmod -Rv 774
find . -type d -iname cache | xargs chmod -Rv 774
find . -type d -iname themes | xargs chmod -Rv 774
find . -type d -iname plugins | xargs chmod -Rv 774
find . -type d -iname upgrade | xargs chmod -Rv 774

It is meant to be run from a www or public_html directory with WordPress installed under it.  This will follow the basic rule, and modify the special directories so that you can upload, upgrade and use a caching tool.  Additionally, it works for WordPress with multi-site either enabled or disabled.
Now, if you wanted to have some real fun, you could add lock and unlock functions so that there can be no changes to your plugins and themes unless you want them to be changed and then your site IS secure.

function lock()
find . -type f -iname wp-config.php | xargs chmod -Rv 644
find . -type d -iname themes | xargs chmod -Rv 754
find . -type d -iname plugins | xargs chmod -Rv 754
find . -type d -iname upgrade | xargs chmod -Rv 754

function unlock()
find . -type f -iname wp-config.php | xargs chmod -Rv 774
find . -type d -iname themes | xargs chmod -Rv 774
find . -type d -iname plugins | xargs chmod -Rv 774
find . -type d -iname upgrade | xargs chmod -Rv 774

Of course that means you need to be confident and not panic when you see messages like “Cannot update/modify/create x” and remember to unlock things before making those changes, but largely, you can set and forget these things until you are ready to apply updates.

One final note. The numbers in these examples are suitable for many shared hosts. If you pay more than $10/month for hosting, chances are you will be able to changes those numbers to better suit your needs. When I lock the files down, I use 544 and sometimes lower.

In any case, this is a good baseline for your functions and will get the job done.  If you have a similar/better technique, I’d love to hear it.

(BTW have you figured out where the flaws are?)


  • I should mention that the settings here are verbose.  You can change the -Rv to -Rcf or just -Rf and get the same results less space taken up.
  • If you get an error like “chmod: missing operand after ‘744’” This is “normal”. It just means that your install does not have one of the directories. Maybe you’ve never upgraded a plugin and the upgrade directory doesn’t exist. Or you have never run multisite and blogs.dir does’t exist.  You can either remove these lines or create empty directories, if the errors bother you.
  • I’ve added echo lines so I can determine which commands create errors

In Linux, how do you unzip all the zip files in a directory?

This is a simple issue that trips me up every now and then.  Let’s say you have a directory of a couple dozen themes all in zip files.  It would be a pain to type “unzip” for every single one, but when you do an “unzip *.zip” you get something like:

[root@hosting ~]# unzip *.zip
caution: filename not matched:
caution: filename not matched:
caution: filename not matched:

You see the problem is that as command line runs, the * is processed redundantly via both unzip and the *nix CLI. In the end, it tries to extract from the first file it finds, all of the remaining files in the directory by name.

The trick to fix this is simple. Escape the * with a back slash when making the call. Then the * is passed as a character to unzip and nothing more.  Like so:

[root@hosting ~]# unzip \*.zip

inflating: …

Escaping symbols and aliases is important in other circumstances as well.  Every now and then a Linux command simply won’t work because the CLI has replaced text mid command with other text.  I once couldn’t FTP in somewhere because the password was replaced before FTP could actually run. Escaping the symbols in the password involved would have allowed the connection to work..