backup.class.inc.php 7.4 KB

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