available_tables); } public function __construct($socket = '/var/lib/icinga/rw/live') { $this->assertPhpExtensionLoaded('sockets'); if ($socket[0] === '/') { if (! is_writable($socket)) { throw new \Exception(sprintf( 'Cannot write to livestatus socket "%s"', $socket )); } $this->socket_type = self::TYPE_UNIX; $this->socket_path = $socket; } else { if (! preg_match('~^tcp://([^:]+):(\d+)~', $socket, $m)) { throw new \Exception(sprintf( 'Invalid TCP socket syntax: "%s"', $socket )); } // TODO: Better syntax checks $this->socket_host = $m[1]; $this->socket_port = (int) $m[2]; $this->socket_type = self::TYPE_TCP; } } public function select() { $select = new Query($this); return $select; } public function count(Query $query) { $count = clone($query); $count->count(); \Icinga\Benchmark::measure('Sending Livestatus Count Query'); $data = $this->_fetch((string) $count); \Icinga\Benchmark::measure('Got Livestatus count result'); return $data[0][0]; } public function fetchAll(Query $query) { \Icinga\Benchmark::measure('Sending Livestatus Query'); $data = $this->_fetch((string) $query); \Icinga\Benchmark::measure('Got Livestatus Data'); if ($query->hasColumns()) { $headers = $query->getColumnAliases(); } else { $headers = array_shift($data); } $result = array(); foreach ($data as $row) { $result_row = & $result[]; $result_row = (object) array(); foreach ($row as $key => $val) { $result_row->{$headers[$key]} = $val; } } if ($query->hasOrder()) { usort($result, array($query, 'compare')); } if ($query->hasLimit()) { $result = array_slice( $result, $query->getOffset(), $query->getLimit() ); } \Icinga\Benchmark::measure('Data sorted, limits applied'); return $result; } protected function _fetch($raw_query) { $conn = $this->getConnection(); $this->writeToSocket($raw_query); $header = $this->readFromSocket(16); $status = (int) substr($header, 0, 3); $length = (int) trim(substr($header, 4)); $body = $this->readFromSocket($length); if ($status !== 200) { throw new \Exception(sprintf( 'Problem while reading %d bytes from livestatus: %s', $length, $body )); } $result = json_decode($body); if ($result === null) { throw new \Exception('Got invalid response body from livestatus'); } return $result; } protected function readFromSocket($length) { $offset = 0; $buffer = ''; while($offset < $length) { $data = socket_read($this->connection, $length - $offset); if ($data === false) { throw new \Exception(sprintf( 'Failed to read from livestatus socket: %s', socket_strerror(socket_last_error($this->connection)) )); } $size = strlen($data); $offset += $size; $buffer .= $data; if ($size === 0) { break; } } if ($offset !== $length) { throw new \Exception(sprintf( 'Got only %d instead of %d bytes from livestatus socket', $offset, $length )); } return $buffer; } protected function writeToSocket($data) { $res = socket_write($this->connection, $data); if ($res === false) { throw new \Exception('Writing to livestatus socket failed'); } return true; } protected function assertPhpExtensionLoaded($name) { if (! extension_loaded($name)) { throw new \Exception(sprintf( 'The extension "%s" is not loaded', $name )); } } protected function getConnection() { if ($this->connection === null) { if ($this->socket_type === self::TYPE_TCP) { $this->establishTcpConnection(); } else { $this->establishSocketConnection(); } } return $this->connection; } protected function establishTcpConnection() { // TODO: find a bedder place for this if (! defined('TCP_NODELAY')) { define('TCP_NODELAY', 1); } $this->connection = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); if (! @socket_connect($this->connection, $this->socket_host, $this->socket_port)) { throw new \Exception(sprintf( 'Cannot connect to livestatus TCP socket "%s:%d": %s', $this->socket_host, $this->socket_port, socket_strerror(socket_last_error($this->connection)) )); } socket_set_option($this->connection, SOL_TCP, TCP_NODELAY, 1); } protected function establishSocketConnection() { $this->connection = socket_create(AF_UNIX, SOCK_STREAM, 0); if (! socket_connect($this->connection, $this->socket_path)) { throw new \Exception(sprintf( 'Cannot connect to livestatus local socket "%s"', $this->socket_path )); } } public function disconnect() { if ($this->connection) { socket_close($this->connection); } } public function __destruct() { $this->disconnect(); } }