mysql_service.inc 6.01 KB
Newer Older
1 2 3 4
<?php
// $Id$


5

6 7
// extends the pdo implementation
class provisionService_db_mysql extends provisionService_db_pdo {
8
   public $PDO_type = 'mysql';
9

10 11 12 13 14
   protected $has_port = TRUE;

   function default_port() {
     return 3306;
   }
15 16

  function database_exists($name) {
17 18
    // An underscore in a LIKE clause is a single character wildcard, escape it.
    $name = str_replace('_', '\_', $name);
19 20 21 22
    $result = $this->query("SHOW DATABASES LIKE '%s'", $name);
    if ($result) {
      return $result->fetchColumn(0);
    }
23 24 25 26
  }


  function drop_database($name) {
27
    return $this->query("DROP DATABASE `%s`", $name);
28 29 30 31
  }


  function create_database($name) {
32
    return $this->query("CREATE DATABASE `%s`", $name);  
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
  }

  function can_create_database() {
    $test = drush_get_option('aegir_db_prefix', 'site_') .'test';
    $this->create_database($test);

    if ($this->database_exists($test)) {
      if (!$this->drop_database($test)) {
        drush_log(dt("Failed to drop database @dbname", array('@dbname' => $test)), 'warning');
      }
      return TRUE;
    }
    return FALSE;
  }
 

  function grant($name, $username, $password, $host = '') {
    $host = ($host) ? $host : '%';
    return $this->query("GRANT ALL PRIVILEGES ON `%s`.* TO `%s`@`%s` IDENTIFIED BY '%s'", $name, $username, $host, $password);
  }

  function revoke($name, $username, $host = '') {
drumm's avatar
drumm committed
55 56 57 58 59 60 61 62 63 64 65 66 67
    $host = ($host) ? $host : '%';
    $success = $this->query("REVOKE ALL PRIVILEGES ON `%s`.* FROM `%s`@`%s`", $name, $username, $host);

    // check if there are any privileges left for the user
    $grants = $this->query("SHOW GRANTS FOR `%s`@`%s`", $username, $host);
    $grant_found = FALSE;
    if ($grants) {
      while ($grant = $grants->fetch()) {
        // those are empty grants: just the user line
        if (!preg_match("/^GRANT USAGE ON /", array_pop($grant))) {
          // real grant, we shouldn't remove the user
          $grant_found = TRUE;
          break;
68 69
        }
      }
drumm's avatar
drumm committed
70 71 72 73 74
    }
    if (!$grant_found) {
      $success = $this->query("DROP USER `%s`@`%s`", $username, $host) && $success;
    }
    return $success;
75 76 77 78 79 80 81 82
  }


  function import_dump($dump_file, $creds) {
    extract($creds);

    $cmd = sprintf("mysql --defaults-file=/dev/fd/3 %s", escapeshellcmd($db_name));

Adrian Rossouw's avatar
Adrian Rossouw committed
83
    $success = $this->safe_shell_exec($cmd, $db_host, $db_user, $db_passwd, $dump_file);
84 85

    drush_log(sprintf("Importing database using command: %s", $cmd));
86

Adrian Rossouw's avatar
Adrian Rossouw committed
87
    if (!$success) {
drumm's avatar
drumm committed
88
      drush_set_error('PROVISION_DB_IMPORT_FAILED', dt("Database import failed: %output", array('%output' => $this->safe_shell_exec_output)));
89 90
    }
  }
91
  
92
  function grant_host(provisionContext_server $server) {
93
    $command = sprintf('mysql -u intntnllyInvalid -h %s -P %s -e ""',
94 95 96 97 98
      escapeshellarg($this->server->remote_host),
      escapeshellarg($this->server->db_port));


    $server->shell_exec($command);
99 100 101
    if (preg_match("/Access denied for user 'intntnllyInvalid'@'([^']*)'/", implode('', drush_shell_exec_output()), $match)) {
      return $match[1];
    }
102 103 104
    elseif (preg_match("/Host '([^']*)' is not allowed to connect to/", implode('', drush_shell_exec_output()), $match)) {
      return $match[1];
    }
105 106 107
    else {
      return drush_set_error('PROVISION_DB_CONNECT_FAIL', dt('Dummy connection failed to fail: %msg', array('%msg' => join("\n", drush_shell_exec_output()))));
    }
108 109
  }

110
  function generate_dump() {
drumm's avatar
drumm committed
111 112
    // Aet the umask to 077 so that the dump itself is generated so it's
    // non-readable by the webserver.
113
    umask(0077);
drumm's avatar
drumm committed
114
    // Mixed copy-paste of drush_shell_exec and provision_shell_exec.
115
    $cmd = sprintf("mysqldump --defaults-file=/dev/fd/3 %s | sed 's|/\\*!50001 CREATE ALGORITHM=UNDEFINED \\*/|/\\*!50001 CREATE \\*/|g; s|/\\*!50017 DEFINER=`[^`]*`@`[^`]*` \\*/||g' | sed '/\\*!50013 DEFINER=.*/ d' > %s/database.sql", escapeshellcmd(drush_get_option('db_name')), escapeshellcmd(d()->site_path));
Adrian Rossouw's avatar
Adrian Rossouw committed
116
    $success = $this->safe_shell_exec($cmd, drush_get_option('db_host'), urldecode(drush_get_option('db_user')), urldecode(drush_get_option('db_passwd')));
117

Adrian Rossouw's avatar
Adrian Rossouw committed
118
    if (!$success && !drush_get_option('force', false)) {
drumm's avatar
drumm committed
119
      drush_set_error('PROVISION_BACKUP_FAILED', dt('Could not generate database backup from mysqldump. (error: %msg)', array('%msg' => $this->safe_shell_exec_output)));
120
    }
drumm's avatar
drumm committed
121
    // Reset the umask to normal permissions.
122 123 124
    umask(0022);
  }

drumm's avatar
drumm committed
125 126 127 128
  /**
   * We go through all this trouble to hide the password from the commandline,
   * it's the most secure way (apart from writing a temporary file, which would
   * create conflicts in parallel runs)
129 130 131 132 133 134 135
   *
   * XXX: this needs to be refactored so it:
   *  - works even if /dev/fd/3 doesn't exit
   *  - has a meaningful name (we're talking about reading and writing
   * dumps here, really, or at least call mysql and mysqldump, not
   * just any command)
   *  - can be pushed upstream to drush (http://drupal.org/node/671906)
drumm's avatar
drumm committed
136
   */
Adrian Rossouw's avatar
Adrian Rossouw committed
137
  function safe_shell_exec($cmd, $db_host, $db_user, $db_passwd, $dump_file = null) {
138 139 140 141
   $mycnf = sprintf('[client]
host=%s
user=%s
password=%s
142 143
port=%s
', $db_host, $db_user, $db_passwd, $this->server->db_port);
144

Adrian Rossouw's avatar
Adrian Rossouw committed
145 146
   $stdin_spec = (!is_null($dump_file)) ? array("file", $dump_file, "r") : array("pipe", "r");

drumm's avatar
drumm committed
147 148 149 150 151 152
   $descriptorspec = array(
     0 => $stdin_spec,
     1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
     2 => array("pipe", "w"),  // stderr is a file to write to
     3 => array("pipe", "r"),  // fd3 is our special file descriptor where we pass credentials
   );
anarcat's avatar
anarcat committed
153
   $pipes = array();
drumm's avatar
drumm committed
154
   $process = proc_open($cmd, $descriptorspec, $pipes);
drumm's avatar
drumm committed
155
   $this->safe_shell_exec_output = '';
drumm's avatar
drumm committed
156 157 158 159
   if (is_resource($process)) {
     fwrite($pipes[3], $mycnf);
     fclose($pipes[3]);

drumm's avatar
drumm committed
160
     $this->safe_shell_exec_output = stream_get_contents($pipes[1]) . stream_get_contents($pipes[2]);
drumm's avatar
drumm committed
161 162 163 164 165 166 167 168 169 170 171
     // "It is important that you close any pipes before calling
     // proc_close in order to avoid a deadlock"
     fclose($pipes[1]);
     fclose($pipes[2]);
     $return_value = proc_close($process);
   }
   else {
     // XXX: failed to execute? unsure when this happens
     $return_value = -1;
   }
   return ($return_value == 0);
172 173
  }
}