mysql_service.inc 6.24 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 drop_database($name) {
17
    return $this->query("DROP DATABASE `%s`", $name);
18 19 20 21
  }


  function create_database($name) {
22
    return $this->query("CREATE DATABASE `%s`", $name);  
23 24 25 26 27 28 29 30 31 32 33 34 35 36
  }

  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;
  }
37 38

  /**
39
   * Verifies that provision can grant privileges to a user on a database.
40 41
   *
   * @return
42
   *   TRUE if the check was successful.
43 44
   */
  function can_grant_privileges() {
45 46 47 48 49 50 51
    $dbname   = drush_get_option('aegir_db_prefix', 'site_');
    $user     = $dbname . '_user';
    $password = $dbname . '_password';
    $host     = $dbname . '_host';
    if ($status = $this->grant($dbname, $user, $password, $host)) {
      $this->revoke($dbname, $user, $host);
    }
52 53
    return $status;
  }
54 55 56 57 58 59 60

  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
61 62 63 64 65 66 67 68 69 70 71 72 73
    $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;
74 75
        }
      }
drumm's avatar
drumm committed
76 77 78 79 80
    }
    if (!$grant_found) {
      $success = $this->query("DROP USER `%s`@`%s`", $username, $host) && $success;
    }
    return $success;
81 82 83 84 85 86 87 88
  }


  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
89
    $success = $this->safe_shell_exec($cmd, $db_host, $db_user, $db_passwd, $dump_file);
90 91

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

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


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

116
  function generate_dump() {
drumm's avatar
drumm committed
117 118
    // Aet the umask to 077 so that the dump itself is generated so it's
    // non-readable by the webserver.
119
    umask(0077);
drumm's avatar
drumm committed
120
    // Mixed copy-paste of drush_shell_exec and provision_shell_exec.
121
    $cmd = sprintf("mysqldump --defaults-file=/dev/fd/3 --single-transaction --quick %s | sed 's|/\\*!50001 CREATE ALGORITHM=UNDEFINED \\*/|/\\*!50001 CREATE \\*/|g; s|/\\*!50017 DEFINER=`[^`]*`@`[^`]*`\s*\\*/||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
122
    $success = $this->safe_shell_exec($cmd, drush_get_option('db_host'), urldecode(drush_get_option('db_user')), urldecode(drush_get_option('db_passwd')));
123

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

drumm's avatar
drumm committed
131 132 133 134
  /**
   * 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)
135 136 137 138 139 140 141
   *
   * 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
142
   */
Adrian Rossouw's avatar
Adrian Rossouw committed
143
  function safe_shell_exec($cmd, $db_host, $db_user, $db_passwd, $dump_file = null) {
144 145 146 147
   $mycnf = sprintf('[client]
host=%s
user=%s
password=%s
148 149
port=%s
', $db_host, $db_user, $db_passwd, $this->server->db_port);
150

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

drumm's avatar
drumm committed
153 154 155 156 157 158
   $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
159
   $pipes = array();
drumm's avatar
drumm committed
160
   $process = proc_open($cmd, $descriptorspec, $pipes);
drumm's avatar
drumm committed
161
   $this->safe_shell_exec_output = '';
drumm's avatar
drumm committed
162 163 164 165
   if (is_resource($process)) {
     fwrite($pipes[3], $mycnf);
     fclose($pipes[3]);

drumm's avatar
drumm committed
166
     $this->safe_shell_exec_output = stream_get_contents($pipes[1]) . stream_get_contents($pipes[2]);
drumm's avatar
drumm committed
167 168 169 170 171 172 173 174 175 176 177
     // "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);
178 179
  }
}