*/ class IDF_Scm_Monotone_Usher { /** * Without giving a specific state, returns an array of all servers. * When a state is given, the array contains only servers which are * in the given state. * * @param string $state One of REMOTE, ACTIVE, WAITING, SLEEPING, * STOPPING, STOPPED, SHUTTINGDOWN or SHUTDOWN * @return array */ public static function getServerList($state = null) { $conn = self::_triggerCommand("LIST $state"); if ($conn == "none") return array(); return preg_split("/[ ]/", $conn); } /** * Returns an array of all open connections to the given server, or to * any server if no server is specified. * If there are no connections to list, an empty array is returned. * * Example: * array("server1" => array( * array("address" => "192.168.1.0", "port" => "13456"), * ... * ), * "server2" => ... * ) * * @param string $server * @return array */ public static function getConnectionList($server = null) { $conn = self::_triggerCommand("LISTCONNECTIONS $server"); if ($conn == "none") return array(); $single_conns = preg_split("/[ ]/", $conn); $ret = array(); foreach ($single_conns as $conn) { preg_match("/\(\w+\)([^:]):(\d+)/", $conn, $matches); $ret[$matches[1]][] = (object)array( "server" => $matches[1], "address" => $matches[2], "port" => $matches[3], ); } return $ret; } /** * Get the status of a particular server, or of the usher as a whole if * no server is specified. * * @param string $server * @return One of REMOTE, SLEEPING, STOPPING, STOPPED for servers or * ACTIVE, WAITING, SHUTTINGDOWN or SHUTDOWN for usher itself */ public static function getStatus($server = null) { return self::_triggerCommand("STATUS $server"); } /** * Looks up the name of the server that would be used for an incoming * connection having the given host and pattern. * * @param string $host Host * @param string $pattern Branch pattern * @return server name * @throws IDF_Scm_Exception */ public static function matchServer($host, $pattern) { $ret = self::_triggerCommand("MATCH $host $pattern"); if (preg_match("/^OK: (.+)/", $ret, $m)) return $m[1]; preg_match("/^ERROR: (.+)/", $ret, $m); throw new IDF_Scm_Exception("could not match server: ".$m[1]); } /** * Prevent the given local server from receiving further connections, * and stop it once all connections are closed. The return value will * be the new status of that server: ACTIVE local servers will become * STOPPING, and WAITING and SLEEPING serveres become STOPPED. * Servers in other states are not affected. * * @param string $server * @return string State of the server after the command */ public static function stopServer($server) { return self::_triggerCommand("STOP $server"); } /** * Allow a STOPPED or STOPPING server to receive connections again. * The return value is the new status of that server: STOPPING servers * become ACTIVE, and STOPPED servers become SLEEPING. Servers in other * states are not affected. * * @param string $server * @return string State of the server after the command */ public static function startServer($server) { return self::_triggerCommand("START $server"); } /** * Immediately kill the given local server, dropping any open connections, * and prevent is from receiving new connections and restarting. The named * server will immediately change to state STOPPED. * * @param string $server * @return bool True if successful */ public static function killServer($server) { return self::_triggerCommand("KILL_NOW $server") == "ok"; } /** * Do not accept new connections for any servers, local or remote. * * @return bool True if successful */ public static function shutDown() { return self::_triggerCommand("SHUTDOWN") == "ok"; } /** * Begin accepting connections after a SHUTDOWN. * * @return bool True if successful */ public static function startUp() { return self::_triggerCommand("STARTUP") == "ok"; } /** * Reload the config file, the same as sending SIGHUP. * * @return bool True if successful (after the configuration was reloaded) */ public static function reload() { return self::_triggerCommand("RELOAD") == "ok"; } private static function _triggerCommand($cmd) { $uc = Pluf::f('mtn_usher'); if (empty($uc['host'])) { throw new IDF_Scm_Exception("usher host is empty"); } if (!preg_match('/^\d+$/', $uc['port']) || $uc['port'] == 0) { throw new IDF_Scm_Exception("usher port is invalid"); } if (empty($uc['user'])) { throw new IDF_Scm_Exception("usher user is empty"); } if (empty($uc['pass'])) { throw new IDF_Scm_Exception("usher pass is empty"); } $sock = @fsockopen($uc['host'], $uc['port'], $errno, $errstr); if (!$sock) { throw new IDF_Scm_Exception( "could not connect to usher: $errstr ($errno)" ); } fwrite($sock, "USERPASS {$uc['user']} {$uc['pass']}\n"); if (feof($sock)) { throw new IDF_Scm_Exception( "usher closed the connection - probably wrong admin ". "username or password" ); } fwrite($sock, "$cmd\n"); $out = ""; while (!feof($sock)) { $out .= fgets($sock); } fclose($sock); $out = rtrim($out); if ($out == "unknown command") { throw new IDF_Scm_Exception("unknown command: $cmd"); } return $out; } }