#!/usr/bin/perl use strict; =FILE INFO BLOCK # FILE : drupal_file_update.pl # VERSION : 1.0.0 # CREATED ON : 2014-04-18 # MODIFIED : 2014-04-18 # AUTHOR : http://site.thisisjusthowidoit.com/category/programming-scripting # LICENSE : GPL (http://www.gnu.org/licenses/gpl.html) # DESCRIPTION : Update Drupal Files fast and easy from SSH command line # INSTALL : Edit the variables below, Upload this script to a 'private' dir on your server, one that is NOT accessable from a web-browser. Log into your server via SSH and run the script. # DISCLAIMER :The steps/code given have worked for me with out a problem, but i will not be held responsible for how you use the information or what the out come of the use does for you. This script will do the following: - Check the Drupal site for its current release version - Display your Drupal version Give you the following options from the command promt: - Download the latest Drupal files from the Drupal site - Back up your current Drupal files - Back up your current Drupal SQL database - Extract the latest downloaded files to your site ** Then you need to run Drupals update.php This script only backs up your files and dbase along with downloading and installing Drupal files BUT it does not run any SQl-database update, you NEED to do that from your Drupal update.php file =cut use POSIX qw(strftime); use File::Copy "cp"; my $username = $ENV{LOGNAME} || $ENV{USER} || getpwuid($<); # used for easy dir path =Start of config settings The two main settings you will need to change are: $drupalRootPath $drupalRootURL You also might want to customize: @preserveFile @nobackup =cut # FULL Drupal ROOT Path with-out ending slash # You should only need to change the end part of this string, /drupal/site, to reflect your site structure my $drupalRootPath = "/home/$username/public_html/drupal/site"; # FULL Drupal ROOT URL with-out ending slash my $drupalRootURL = 'http://localhost/drupal/site'; # Full DIR path to save TAR backups # This path will also be used to temperaraly save the newly downloaded Drupal Update .tar file # Dir needs to be writable my $backupSavePath= "$drupalRootPath/backups"; # File(s) you want/need to preserve after your update is complete # File(s) here will be copied to a temp dir *before* the update starts # and after the update is done, they will be copied back. # Ive added some code to my .htaccess and want to preserve it, hence the option. my @preserveFile = ('.htaccess', 'robots.txt', ); # Files and Dirs that you do not want to backup # Relative path to Drupal root for files and dirs that will NOT be backed up # Being this script is only for Updates, i do not see the need to waist space and back up everything, # so i exclude images and since i placed my 'backup' folder in-side my Drupal install, i opmit the backup dir also my @nobackup = ('backups', 'sites/default/files/*.jpg', 'sites/default/files/*.JPG', 'sites/default/files/images', 'sites/default/files/myuploads/', 'sites/default/files/pictures', 'sites/default/files/brilliant_gallery_temp', ); # Current Date and Time used in backup file names my $timeDateNow = strftime("%Y-%m-%d_%H.%M.%S", gmtime(time)); # File name for Drupal Files Backup, if is in the string it will be replaced with local Drupal Ver my $drupalFileBackup = "Drupal-_Files_$timeDateNow.tar"; # File name for Drupal SQL Backup my $drupalSQLBackup = "Drupal-_SQL_$timeDateNow.sql"; =The following settings deal with connecting to and downloading from the Drupal site. They should be fine as they are, there varibules for flexability =cut # Main url to the Drupal Project page my $drupalProjectURL = "https://drupal.org/project/drupal"; # URL to download the Drupal Project files from *with-out* file, needs trailing slash my $drupalFTPurl = 'http://ftp.drupal.org/files/projects/'; # File name used to download from Drupal FTP, notice the tag, this is *needed*, follow this format my $drupalUpdate_filename = 'drupal-.tar.gz'; =End Of Config... The scripts core code is below. There should be no need to edit anything below here, but if you want to play, have fun :) =cut my $updateVersion = '0'; # used for a sanity check in the checkDrupalSiteForUpdateVersion() sub, no need to edit my $localVersion = '0'; # used and set through out script, no need to edit my $dashs = '-' x 75; startUpdate(); sub startUpdate{ p("\n$dashs You should log into your Drupal sites Admin now (before continuing). $drupalRootURL/?q=admin $dashs"); e() if(!askyn("This file update script works on yes/no questions. There are 5 question. 1) Start/Check Drupal versions 2) Download new Drupal files 3) Back up your current files 4) Backup your MySQL 5) Install new Drupal files. $dashs [1] Would you like to start the version check ?")); my $drupalVer = checkDrupalSiteForUpdateVersion(); $localVersion = checkLocalDrupalCHANGELOGForVersion(); p("\n$dashs Your Drupal Version = $localVersion Available Drupal Download Version = $drupalVer $dashs"); print "1) Start update ?\n2) Display files in backup dir\n3) Exit script\nSelect Option: "; my $result = ; if ($result =~ /^-?\d+\.?\d*$/) { if ($result == 1) { # continue } elsif($result == 2) { displayBackupFiles(); return startUpdate(); } elsif($result == 3) { e(); } else { return startUpdate(); } } else { return startUpdate(); } dirChecks(); # run this first to make sure all dir exists as they should $drupalFileBackup =~ s/\/$localVersion/; $drupalSQLBackup =~ s/\/$localVersion/; $drupalUpdate_filename =~ s/\/$drupalVer/; $drupalFTPurl = $drupalFTPurl . $drupalUpdate_filename; download_drupalProject();# download new Drupal files backup_files(); # backup Drupal files backup_sql(); #backup Drupals SQL extractDrupalUpdateFiles(); #backup Drupals SQL } sub extractDrupalUpdateFiles{ return '' if(!askyn("[5] Extract new Drupal files to your site ?")); # check to make sure the Drupal Update files are here if (!-f "$backupSavePath/$drupalUpdate_filename") { er("Can not find: $backupSavePath/$drupalUpdate_filename"); } # copy over 'preserveFile' files _cd("$drupalRootPath"); ptitle("Preserving Files:"); foreach my $save (@preserveFile){ p("$save"); cp("$drupalRootPath/$save","$backupSavePath/$save") } my $tar="$backupSavePath/$drupalUpdate_filename"; ptitle("Extracting- $drupalFileBackup"); my $cmd="tar -xzf '$tar' -C $drupalRootPath --strip-components=1 --overwrite 2>&1"; p("\nWORKING: $cmd"); system($cmd); ptitle("Re-Preserving Files:"); foreach my $save (@preserveFile){ p("$save"); cp("$backupSavePath/$save", "$drupalRootPath/$save"); unlink("$backupSavePath/$save"); } write_backup_dir_htaccess(); ptitle("File Update Complete:\nYou now NEED to run your Drupal Update\n$drupalRootURL/update.php"); p("DONE\n\n"); } sub write_backup_dir_htaccess{ open(my $fh, '>', "$backupSavePath/.htaccess") or die "Could not open file '$backupSavePath/.htaccess' $!"; print $fh "Allow from none\nDeny from all\n"; close $fh; } sub displayBackupFiles{ ptitle("File list from: $backupSavePath"); opendir (DIR, $backupSavePath) or er("Can not open: $backupSavePath"); while (my $file = readdir(DIR)) { next if ($file =~ m/^\./); print "$file\n"; } closedir(DIR); } sub download_drupalProject{ return '' if(!askyn("[2] Download new $updateVersion Drupal files ?")); # download new Drupal files ptitle("Downloading: $drupalFTPurl"); my $cmd = "wget -P $backupSavePath $drupalFTPurl"; system($cmd); p("done:"); } sub checkDrupalSiteForUpdateVersion{ # used for a sanity loop in the startUpdate() sub # so not to keep downloading the Drupal Project Page below return $updateVersion if($updateVersion ne '0'); ptitle("Downloading the Drupal Project page to get current ver number"); my $page = '' ; system("wget $drupalProjectURL --no-check-certificate"); my $oFile = "drupal"; open FILE, $oFile or er("Can not open: $oFile"); my ($info); while () { $page .= $_; } close(FILE); unlink('drupal'); p('Done:'); # search the Drupal Project and retreive the latest version number # Recommended releases # Development releases $page = getBetween($page, 'Recommended releases', 'Development releases'); $updateVersion = getBetween($page, '-release-notes">', '<'); return $updateVersion; } sub checkLocalDrupalCHANGELOGForVersion{ my $oFile = "$drupalRootPath/CHANGELOG.txt"; open FILE, $oFile or er("Can not open: $oFile"); my ($info); while () { chomp; $info .= $_; } close(FILE); my $ver = getBetween($info, 'Drupal ', ','); # grab the $database array from the settings files return $ver; } sub backup_sql{ return '' if(!askyn("[4] Backup your current Drupal SQL database ?")); my %sql_DatabaseInfo = readSettingsFile(); # Check is the Drupal database driver is MySQL # If not, give options # This script is only set up to backup MySQL if($sql_DatabaseInfo{'driver'} ne 'mysql'){ e() if(!askyn("Drupal SQL driver is not MySQL, this script can only Backup MySQL databases. Continue Drupal Update with out backing up your Database ?")); } ptitle("Backing up Drupal's SQL ($backupSavePath/$drupalSQLBackup)"); my $cmd="mysqldump --opt -h$sql_DatabaseInfo{'host'} -u$sql_DatabaseInfo{'username'} -p$sql_DatabaseInfo{'password'} --databases $sql_DatabaseInfo{'database'} > '$backupSavePath/$drupalSQLBackup'"; p("\nWORKING: mysqldump --opt --databases $sql_DatabaseInfo{'database'} > '$backupSavePath/$drupalSQLBackup'"); system($cmd); p("Complete:"); } sub readSettingsFile{ my $oFile = "$drupalRootPath/sites/default/settings.php"; ptitle("Accessing Drupal's settings.php file for SQL info \n ($oFile)"); if (!-f "$oFile") { p("ERROR: Can not read Drupal settings.php file:"); p("$oFile"); p("No way to retrieve Drupal SQL username/password .etc"); es(); } else { ok(); } open FILE, $oFile or er("Can not open: $oFile"); my ($info); while () { chomp; $info .= $_; } close(FILE); my $tmp = getBetween($info, '\$databases \=', '\);'); # grab the $database array from the settings files my %ret=(); $ret{'database'} = getBetween($tmp, '\'database\' \=\> \'', '\''); $ret{'username'} = getBetween($tmp, '\'username\' \=\> \'', '\''); $ret{'password'} = getBetween($tmp, '\'password\' \=\> \'', '\''); $ret{'host'} = getBetween($tmp, '\'host\' \=\> \'', '\''); $ret{'port'} = getBetween($tmp, '\'port\' \=\> \'', '\''); $ret{'driver'} = getBetween($tmp, '\'driver\' \=\> \'', '\''); $ret{'prefix'} = getBetween($tmp, '\'prefix\' \=\> \'', '\''); if (!$ret{'database'}) { p("ERROR: \$databases() array key 'database' is empty in file\n$oFile"); p("can not retrieve Drupals SQL info"); es(); } return %ret; } sub backup_files{ return '' if(!askyn("[3] Backup your current Drupal files ?")); my $exclude = "--exclude=$backupSavePath "; # pull all 'nobackup' paths to a single string for tar foreach my $exclude_paths (@nobackup){ $exclude .= "--exclude=$exclude_paths "; } ptitle("Creating tar backup of dir ($drupalRootPath)"); _cd("$drupalRootPath"); # change to Drupal Root Dir #p("Exclude string: $exclude"); my $tarSave="$backupSavePath/$drupalFileBackup"; # Drupal$localVersion my $cmd="tar -czf $tarSave . $exclude 2>&1"; p("\nWORKING: $cmd"); system($cmd); p("\nFILE BACK UP DONE: ($drupalFileBackup)"); } sub dirChecks{ # Check the that Drupal Root Path ptitle("Checking that Drupal Root Path exist ($drupalRootPath)"); if (!-d "$drupalRootPath") { p("Drupal Root Path Does Not Exist:"); p("$drupalRootPath"); es(); } else { ok(); } ptitle("Checking that backup dir exist ($backupSavePath)"); if (-d "$backupSavePath") { ok(); } else { p("No dir ($backupSavePath)"); p("Creating dir ($backupSavePath)"); unless(mkdir $backupSavePath) { er("Unable to create $backupSavePath"); es(); } } write_backup_dir_htaccess(); # Check if script can write to backup dir ptitle("Check if script can write to backup dir ($backupSavePath)"); if (-w "$drupalRootPath") { ok(); } else { er("Not Writeble"); es(); } } sub e{ p("Exiting Script, bye..."); exit 0; } sub askyn{ my $str = shift; p("\n$dashs\n$str\n$dashs"); print "1) Yes\n2) No\nSelect Option: "; my $result = ; chop($result); if ($result eq "y" || $result eq "yes") { return 1; } if ($result eq "n" || $result eq "no" || $result eq "e" || $result eq "exit") { return 0; } if ($result =~ /^-?\d+\.?\d*$/) { if ($result == 1) { return 1; } elsif($result == 2) { return 0; } else { return askyn($str); } } else { return askyn($str); } } sub ptitle{ my $str = shift; print "\n:: $str ::\n$dashs\n"; } sub p{ my $str = shift; print "$str\n"; } sub es{ p("\nExiting Script Because or Errors\n"); e(); } sub er{ my $str = shift; p("\nERROR: $str"); es(); } sub ok{ p("OK:"); } sub _cd{ my $str = shift; p("Changing into dir ($str)"); if (!-d "$str") { er("Dir does not exist ($str)"); es(); } chdir($str); } sub getBetween{ my ($content, $first, $second) = @_; return '' if(!$content); my @tmp = $content =~ m/$first(.*?)$second/is; $tmp[0] = trim($tmp[0]); return $tmp[0] ; } sub trim{ my ($findName) = @_; $findName=~s/^\s+// if($findName); $findName=~s/\s+$// if($findName); return $findName; } exit 0;