backup.class.inc.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. <?php
  2. // Copyright (C) 2010-2012 Combodo SARL
  3. //
  4. // This program is free software; you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation; version 3 of the License.
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with this program; if not, write to the Free Software
  15. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. class BackupException extends Exception
  17. {
  18. }
  19. class DBBackup
  20. {
  21. protected $sDBHost;
  22. protected $sDBUser;
  23. protected $sDBPwd;
  24. protected $sDBName;
  25. protected $sDBSubName;
  26. /**
  27. * Connects to the database to backup
  28. * By default, connects to the current MetaModel (must be loaded)
  29. *
  30. * @param sDBHost string Database host server
  31. * @param $sDBUser string User login
  32. * @param $sDBPwd string User password
  33. * @param $sDBName string Database name
  34. * @param $sDBSubName string Prefix to the tables of itop in the database
  35. */
  36. public function __construct($sDBHost = null, $sDBUser = null, $sDBPwd = null, $sDBName = null, $sDBSubName = null)
  37. {
  38. if (is_null($sDBHost))
  39. {
  40. $this->sDBHost = MetaModel::GetConfig()->GetDBHost();
  41. $this->sDBUser = MetaModel::GetConfig()->GetDBUser();
  42. $this->sDBPwd = MetaModel::GetConfig()->GetDBPwd();
  43. $this->sDBName = MetaModel::GetConfig()->GetDBName();
  44. $this->sDBSubName = MetaModel::GetConfig()->GetDBSubName();
  45. }
  46. else
  47. {
  48. $this->sDBHost = $sDBHost;
  49. $this->sDBUser = $sDBUser;
  50. $this->sDBPwd = $sDBPwd;
  51. $this->sDBName = $sDBName;
  52. $this->sDBSubName = $sDBSubName;
  53. }
  54. }
  55. /**
  56. * Create a normalized backup name, depending on the current date/time and Database
  57. * @param sNameSpec string Name and path, eventually containing itop placeholders + time formatting specs
  58. */
  59. public function MakeName($sNameSpec = "__DB__-%Y-%m-%d")
  60. {
  61. $sFileName = $sNameSpec;
  62. $sFileName = str_replace('__HOST__', $this->sDBHost, $sFileName);
  63. $sFileName = str_replace('__DB__', $this->sDBName, $sFileName);
  64. $sFileName = str_replace('__SUBNAME__', $this->sDBSubName, $sFileName);
  65. // Transform %Y, etc.
  66. $sFileName = strftime($sFileName);
  67. return $sFileName;
  68. }
  69. public function CreateZip($sZipFile, $sSourceConfigFile = null)
  70. {
  71. $sDataFile = tempnam(SetupUtils::GetTmpDir(), 'itop-');
  72. SetupPage::log("Info - Data file: '$sDataFile'");
  73. if (is_null($sSourceConfigFile))
  74. {
  75. $sSourceConfigFile = MetaModel::GetConfig()->GetLoadedFile();
  76. }
  77. $this->DoBackup($sDataFile);
  78. $this->DoZip($sDataFile, $sSourceConfigFile, $sZipFile);
  79. unlink($sDataFile);
  80. }
  81. protected static function EscapeShellArg($sValue)
  82. {
  83. // Note: See comment from the 23-Apr-2004 03:30 in the PHP documentation
  84. // It suggests to rely on pctnl_* function instead of using escapeshellargs
  85. return escapeshellarg($sValue);
  86. }
  87. /**
  88. * Create a backup file
  89. */
  90. public function DoBackup($sBackupFileName)
  91. {
  92. $sHost = self::EscapeShellArg($this->sDBHost);
  93. $sUser = self::EscapeShellArg($this->sDBUser);
  94. $sPwd = self::EscapeShellArg($this->sDBPwd);
  95. $sDBName = self::EscapeShellArg($this->sDBName);
  96. $sTables = '';
  97. if ($this->sDBSubName != '')
  98. {
  99. // This instance of iTop uses a prefix for the tables, so there may be other tables in the database
  100. // Let's explicitely list all the tables and views to dump
  101. $aTables = $this->EnumerateTables();
  102. if (count($aTables) == 0)
  103. {
  104. // No table has been found with the given prefix
  105. throw new BackupException("No table has been found with the given prefix");
  106. }
  107. $aEscapedTables = array();
  108. foreach($aTables as $sTable)
  109. {
  110. $aEscapedTables[] = self::EscapeShellArg($sTable);
  111. }
  112. $sTables = implode(' ', $aEscapedTables);
  113. }
  114. $sMySQLBinDir = utils::ReadParam('mysql_bindir', '', true);
  115. if (empty($sMySQLBinDir))
  116. {
  117. $sMySQLDump = 'mysqldump';
  118. }
  119. else
  120. {
  121. $sMySQLDump = '"'.$sMySQLBinDir.'/mysqldump"';
  122. }
  123. // Store the results in a temporary file
  124. $sTmpFileName = self::EscapeShellArg($sBackupFileName);
  125. $sCommand = "$sMySQLDump --opt --default-character-set=utf8 --add-drop-database --host=$sHost --user=$sUser --password=$sPwd --result-file=$sTmpFileName $sDBName $sTables 2>&1";
  126. $sCommandDisplay = "$sMySQLDump --opt --default-character-set=utf8 --add-drop-database --host=$sHost --user=xxxxx --password=xxxxx --result-file=$sTmpFileName $sDBName $sTables";
  127. // Now run the command for real
  128. SetupPage::log("Info - Executing command: $sCommandDisplay");
  129. $aOutput = array();
  130. $iRetCode = 0;
  131. exec($sCommand, $aOutput, $iRetCode);
  132. if ($iRetCode != 0)
  133. {
  134. SetupPage::log("Error - retcode=".$iRetCode."\n");
  135. throw new BackupException("Failed to execute mysqldump. Return code: $iRetCode");
  136. }
  137. foreach($aOutput as $sLine)
  138. {
  139. SetupPage::log("Info - mysqldump said: $sLine");
  140. }
  141. }
  142. /**
  143. * Helper to create a ZIP out of a data file and the configuration file
  144. */
  145. protected function DoZip($sDataFile, $sConfigFile, $sZipArchiveFile)
  146. {
  147. if (!is_file($sConfigFile))
  148. {
  149. throw new BackupException("Configuration file '$sConfigFile' does not exist or could not be read");
  150. }
  151. // Make sure the target path exists
  152. $sZipDir = dirname($sZipArchiveFile);
  153. SetupUtils::builddir($sZipDir);
  154. $oZip = new ZipArchive();
  155. $res = $oZip->open($sZipArchiveFile, ZipArchive::CREATE | ZipArchive::OVERWRITE);
  156. if ($res === TRUE)
  157. {
  158. $oZip->addFile($sDataFile, 'itop-dump.sql');
  159. $oZip->addFile($sConfigFile, 'config-itop.php');
  160. if ($oZip->close())
  161. {
  162. SetupPage::log("Info - Archive: $sZipArchiveFile created");
  163. }
  164. else
  165. {
  166. SetupPage::log("Error - Failed to save zip archive: $sZipArchiveFile");
  167. throw new BackupException("Failed to save zip archive: $sZipArchiveFile");
  168. }
  169. }
  170. else
  171. {
  172. SetupPage::log("Error - Failed to create zip archive: $sZipArchiveFile.");
  173. throw new BackupException("Failed to create zip archive: $sZipArchiveFile.");
  174. }
  175. }
  176. /**
  177. * Helper to download the file directly from the browser
  178. */
  179. public function DownloadBackup($sFile)
  180. {
  181. $oP = new ajax_page('backup');
  182. $oP->SetContentType("multipart/x-zip");
  183. $oP->SetContentDisposition('inline', basename($sFile));
  184. $oP->add(file_get_contents($sFile));
  185. $oP->output();
  186. }
  187. /**
  188. * Helper to open a Database connection
  189. */
  190. protected function DBConnect()
  191. {
  192. $oMysqli = new mysqli($this->sDBHost, $this->sDBUser, $this->sDBPwd);
  193. if ($oMysqli->connect_errno)
  194. {
  195. throw new BackupException("Cannot connect to the MySQL server '$this->sDBHost' (".$mysqli->connect_errno . ") ".$mysqli->connect_error);
  196. }
  197. if (!$oMysqli->select_db($this->sDBName))
  198. {
  199. throw new BackupException("The database '$this->sDBName' does not seem to exist");
  200. }
  201. return $oMysqli;
  202. }
  203. /**
  204. * Helper to enumerate the tables of the database
  205. */
  206. protected function EnumerateTables()
  207. {
  208. $oMysqli = $this->DBConnect();
  209. if ($this->sDBSubName != '')
  210. {
  211. $oResult = $oMysqli->query("SHOW TABLES LIKE '{$this->sDBSubName}%'");
  212. }
  213. else
  214. {
  215. $oResult = $oMysqli->query("SHOW TABLES");
  216. }
  217. if (!$oResult)
  218. {
  219. throw new BackupException("Failed to execute the SHOW TABLES query: ".$oMysqli->error);
  220. }
  221. $aTables = array();
  222. while ($aRow = $oResult->fetch_row())
  223. {
  224. $aTables[] = $aRow[0];
  225. }
  226. return $aTables;
  227. }
  228. }
  229. ?>