All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.aoindustries.aoserv.daemon.client.AOServDaemonConnector Maven / Gradle / Ivy

There is a newer version: 1.90.0
Show newest version
/*
 * aoserv-daemon-client - Java client for the AOServ Daemon.
 * Copyright (C) 2001-2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020  AO Industries, Inc.
 *     [email protected]
 *     7262 Bull Pen Cir
 *     Mobile, AL 36695
 *
 * This file is part of aoserv-daemon-client.
 *
 * aoserv-daemon-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * aoserv-daemon-client is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with aoserv-daemon-client.  If not, see .
 */
package com.aoindustries.aoserv.daemon.client;

import com.aoindustries.aoserv.client.backup.MysqlReplication;
import com.aoindustries.aoserv.client.email.InboxAttributes;
import com.aoindustries.aoserv.client.infrastructure.VirtualServer;
import com.aoindustries.aoserv.client.linux.PosixPath;
import com.aoindustries.aoserv.client.monitoring.AlertLevel;
import com.aoindustries.aoserv.client.mysql.Database.CheckTableResult;
import com.aoindustries.aoserv.client.mysql.Database.Engine;
import com.aoindustries.aoserv.client.mysql.Database.TableStatus;
import com.aoindustries.aoserv.client.mysql.Server;
import com.aoindustries.aoserv.client.mysql.Table_Name;
import com.aoindustries.aoserv.client.pki.Certificate;
import com.aoindustries.aoserv.client.schema.AoservProtocol;
import com.aoindustries.io.stream.StreamableInput;
import com.aoindustries.io.stream.StreamableOutput;
import com.aoindustries.lang.NullArgumentException;
import com.aoindustries.net.HostAddress;
import com.aoindustries.net.InetAddress;
import com.aoindustries.net.Port;
import com.aoindustries.util.BufferManager;
import com.aoindustries.util.Tuple2;
import com.aoindustries.validation.ValidationException;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A AOServDaemonConnector provides the
 * connections between a client and a server-control daemon.
 *
 * @author  AO Industries, Inc.
 */
final public class AOServDaemonConnector {

	private static final Logger logger = Logger.getLogger(AOServDaemonConnector.class.getName());

	/**
	 * Each unique connector is only created once.
	 */
	private static final List connectors = new ArrayList<>();

	/**
	 * The hostname to connect to.
	 */
	final HostAddress hostname;

	/**
	 * The local IP address to connect from.
	 */
	final InetAddress local_ip;

	/**
	 * The port to connect to.
	 */
	final Port port;

	/**
	 * The protocol used.
	 */
	final String protocol;

	/**
	 * The key to connect with.
	 */
	final String key;

	final int poolSize;

	final long maxConnectionAge;

	final String trustStore;

	final String trustStorePassword;

	private final AOServDaemonConnectionPool pool;

	/**
	 * Creates a new AOServConnector.
	 */
	private AOServDaemonConnector(
		HostAddress hostname,
		InetAddress local_ip,
		Port port,
		String protocol,
		String key,
		int poolSize,
		long maxConnectionAge,
		String trustStore,
		String trustStorePassword
	) {
		if(port.getProtocol() != com.aoindustries.net.Protocol.TCP) throw new IllegalArgumentException("Only TCP supported: " + port);
		this.hostname=hostname;
		this.local_ip=local_ip;
		this.port=port;
		this.protocol=protocol;
		this.key=key;
		this.poolSize=poolSize;
		this.maxConnectionAge=maxConnectionAge;
		this.trustStore=trustStore;
		this.trustStorePassword=trustStorePassword;
		this.pool = new AOServDaemonConnectionPool(this, logger);
	}

	/**
	 * Copies a home directory.
	 *
	 * @param  username  the username to copy the home directory of
	 * @param  to_connector  the connector to send the data to
	 *
	 * @return  the number of bytes transferred
	 */
	public long copyHomeDirectory(com.aoindustries.aoserv.client.linux.User.Name username, AOServDaemonConnector to_connector) throws IOException, SQLException {
		// Establish the connection to the source
		AOServDaemonConnection sourceConn=getConnection();
		try {
			StreamableOutput sourceOut = sourceConn.getRequestOut(AOServDaemonProtocol.TAR_HOME_DIRECTORY);
			sourceOut.writeUTF(username.toString());
			sourceOut.flush();

			StreamableInput sourceIn=sourceConn.getResponseIn();

			// Establish the connection to the destination
			AOServDaemonConnection destConn=to_connector.getConnection();
			try {
				StreamableOutput destOut = destConn.getRequestOut(AOServDaemonProtocol.UNTAR_HOME_DIRECTORY);
				destOut.writeUTF(username.toString());

				long byteCount=0;
				int sourceCode;
				byte[] buff=BufferManager.getBytes();
				try {
					while((sourceCode=sourceIn.read())==AOServDaemonProtocol.NEXT) {
						int len=sourceIn.readShort();
						byteCount+=len;
						sourceIn.readFully(buff, 0, len);
						destOut.writeByte(AOServDaemonProtocol.NEXT);
						destOut.writeShort(len);
						destOut.write(buff, 0, len);
					}
				} finally {
					BufferManager.release(buff, false);
				}
				if (sourceCode != AOServDaemonProtocol.DONE) {
					if (sourceCode == AOServDaemonProtocol.IO_EXCEPTION) {
						String message=sourceIn.readUTF();
						destOut.writeByte(AOServDaemonProtocol.IO_EXCEPTION);
						destOut.writeUTF(message);
						destOut.flush();
						throw new IOException(message);
					} else if (sourceCode == AOServDaemonProtocol.SQL_EXCEPTION) {
						String message=sourceIn.readUTF();
						destOut.writeByte(AOServDaemonProtocol.SQL_EXCEPTION);
						destOut.writeUTF(message);
						destOut.flush();
						throw new SQLException(message);
					} else throw new IOException("Unknown result: " + sourceCode);
				}
				destOut.writeByte(AOServDaemonProtocol.DONE);
				destOut.flush();

				StreamableInput destIn=destConn.getResponseIn();
				int destResult=destIn.read();
				if(destResult!=AOServDaemonProtocol.DONE) {
					if (destResult == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(destIn.readUTF());
					else if (destResult == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(destIn.readUTF());
					else throw new IOException("Unknown result: " + destResult);
				}

				return byteCount;
			} catch(IOException err) {
				destConn.close();
				throw err;
			} finally {
				to_connector.releaseConnection(destConn);
			}
		} catch(IOException err) {
			sourceConn.close();
			throw err;
		} finally {
			releaseConnection(sourceConn);
		}
	}

	@FunctionalInterface
	public static interface DumpSizeCallback {
		/**
		 * Called once the dump size is known and before
		 * the stream is written to.
		 *
		 * @param  dumpSize  The number of bytes that will be transferred or {@code -1} if unknown
		 */
		void onDumpSize(long dumpSize) throws IOException;
	}

	public void dumpMySQLDatabase(
		int pkey,
		boolean gzip,
		DumpSizeCallback onDumpSize,
		StreamableOutput masterOut
	) throws IOException, SQLException {
		transferStream(AOServDaemonProtocol.DUMP_MYSQL_DATABASE, pkey, gzip, onDumpSize, masterOut);
	}

	public void dumpPostgresDatabase(
		int pkey,
		boolean gzip,
		DumpSizeCallback onDumpSize,
		StreamableOutput masterOut
	) throws IOException, SQLException {
		transferStream(AOServDaemonProtocol.DUMP_POSTGRES_DATABASE, pkey, gzip, onDumpSize, masterOut);
	}

	public String getAutoresponderContent(PosixPath path) throws IOException, SQLException {
		// Establish the connection to the server
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.GET_AUTORESPONDER_CONTENT);
			out.writeUTF(path.toString());
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int code=in.read();
			if(code==AOServDaemonProtocol.DONE) return in.readUTF();
			if (code == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
			if (code == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
			throw new IOException("Unknown result: " + code);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	public int getConcurrency() {
		return pool.getConcurrency();
	}

	/**
	 * Allocates a connection to the server.  These connections must later be
	 * released with the releaseConnection method.  Connection
	 * pooling is obtained this way.  These connections may be over any protocol,
	 * so they may only be used for one client/server exchange at a time.
	 */
	public AOServDaemonConnection getConnection() throws IOException {
		try {
			return pool.getConnection();
		} catch(IOException err) {
			logger.log(Level.INFO, "IOException while trying to get a connection to server from "+local_ip+" to "+hostname+":"+port, err);
			throw err;
		}
	}

	/**
	 * Allocates a connection to the server.  These connections must later be
	 * released with the releaseConnection method.  Connection
	 * pooling is obtained this way.  These connections may be over any protocol,
	 * so they may only be used for one client/server exchange at a time.
	 */
	public AOServDaemonConnection getConnection(int maxConnections) throws IOException {
		return pool.getConnection(maxConnections);
	}

	public int getConnectionCount() {
		return pool.getConnectionCount();
	}

	/**
	 * Gets the default AOServConnector as defined in the
	 * client.properties resource.  Each possible
	 * protocol is tried, in order, until a successful connection is
	 * made.  If no connection is made, an IOException
	 * is thrown.
	 */
	public synchronized static AOServDaemonConnector getConnector(
		HostAddress hostname,
		InetAddress local_ip,
		Port port,
		String protocol,
		String key,
		int poolSize,
		long maxConnectionAge,
		String trustStore,
		String trustStorePassword
	) {
		NullArgumentException.checkNotNull(hostname, "hostname");
		NullArgumentException.checkNotNull(local_ip, "local_ip");
		NullArgumentException.checkNotNull(protocol, "protocol");

		int size=connectors.size();
		for(int c=0;c getEncryptedLinuxAccountPassword(com.aoindustries.aoserv.client.linux.User.Name username) throws IOException, SQLException {
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.GET_ENCRYPTED_LINUX_ACCOUNT_PASSWORD);
			out.writeUTF(username.toString());
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int result = in.read();
			if (result == AOServDaemonProtocol.DONE) {
				String encryptedPassword = in.readUTF();
				Integer changedDate;
				if(conn.protocolVersion.compareTo(AOServDaemonProtocol.Version.VERSION_1_80_1) >= 0) {
					int i = in.readCompressedInt();
					changedDate = i==-1 ? null : i;
				} else {
					changedDate = null;
				}
				return new Tuple2<>(encryptedPassword, changedDate);
			}
			else if (result == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
			else if (result == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
			else throw new IOException("Unknown result: " + result);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	public long[] getImapFolderSizes(com.aoindustries.aoserv.client.linux.User.Name username, String[] folderNames) throws IOException, SQLException {
		// Establish the connection to the server
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.GET_IMAP_FOLDER_SIZES);
			out.writeUTF(username.toString());
			out.writeCompressedInt(folderNames.length);
			for(String folderName : folderNames) {
				out.writeUTF(folderName);
			}
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int code=in.read();
			if(code==AOServDaemonProtocol.DONE) {
				long[] sizes=new long[folderNames.length];
				for(int c=0;c= 0) {
				daemonOut.writeUTF(serverName.toString());
			}
			daemonOut.writeCompressedInt(port.getPort());
			daemonOut.flush();

			StreamableInput in=conn.getResponseIn();
			int code=in.read();
			if(code==AOServDaemonProtocol.NEXT) {
				return new MysqlReplication.SlaveStatus(
					in.readNullUTF(),
					in.readNullUTF(),
					in.readNullUTF(),
					in.readNullUTF(),
					in.readNullUTF(),
					in.readNullUTF(),
					in.readNullUTF(),
					in.readNullUTF(),
					in.readNullUTF(),
					in.readNullUTF(),
					in.readNullUTF(),
					in.readNullUTF(),
					in.readNullUTF(),
					in.readNullUTF()
				);
			} else if(code==AOServDaemonProtocol.DONE) {
				return null;
			} else if(code == AOServDaemonProtocol.IO_EXCEPTION) {
				throw new IOException(in.readUTF());
			} else if (code == AOServDaemonProtocol.SQL_EXCEPTION) {
				throw new SQLException(in.readUTF());
			} else {
				throw new IOException("Unknown result: " + code);
			}
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	public List getMySQLTableStatus(
		PosixPath failoverRoot,
		int nestedOperatingSystemVersion,
		Server.Name serverName,
		Port port,
		com.aoindustries.aoserv.client.mysql.Database.Name databaseName
	) throws IOException, SQLException {
		if(port.getProtocol() != com.aoindustries.net.Protocol.TCP) throw new IllegalArgumentException("Only TCP supported: " + port);
		// Establish the connection to the server
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput daemonOut = conn.getRequestOut(AOServDaemonProtocol.GET_MYSQL_TABLE_STATUS);
			daemonOut.writeUTF(failoverRoot==null ? "" : failoverRoot.toString());
			daemonOut.writeCompressedInt(nestedOperatingSystemVersion);
			if(conn.protocolVersion.compareTo(AOServDaemonProtocol.Version.VERSION_1_84_11) >= 0) {
				daemonOut.writeUTF(serverName.toString());
			}
			daemonOut.writeCompressedInt(port.getPort());
			daemonOut.writeUTF(databaseName.toString());
			daemonOut.flush();

			StreamableInput in=conn.getResponseIn();
			int code=in.read();
			if(code==AOServDaemonProtocol.NEXT) {
				try {
					int size = in.readCompressedInt();
					List tableStatuses = new ArrayList<>(size);
					for(int c=0;c checkMySQLTables(
		PosixPath failoverRoot,
		int nestedOperatingSystemVersion,
		Server.Name serverName,
		Port port,
		com.aoindustries.aoserv.client.mysql.Database.Name databaseName,
		List tableNames
	) throws IOException, SQLException {
		if(port.getProtocol() != com.aoindustries.net.Protocol.TCP) throw new IllegalArgumentException("Only TCP supported: " + port);
		// Establish the connection to the server
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput daemonOut = conn.getRequestOut(AOServDaemonProtocol.CHECK_MYSQL_TABLES);
			daemonOut.writeUTF(failoverRoot==null ? "" : failoverRoot.toString());
			daemonOut.writeCompressedInt(nestedOperatingSystemVersion);
			if(conn.protocolVersion.compareTo(AOServDaemonProtocol.Version.VERSION_1_84_11) >= 0) {
				daemonOut.writeUTF(serverName.toString());
			}
			daemonOut.writeCompressedInt(port.getPort());
			daemonOut.writeUTF(databaseName.toString());
			int numTables = tableNames.size();
			daemonOut.writeCompressedInt(numTables);
			for(int c=0;c checkTableResults = new ArrayList<>(size);
				for(int c=0;c= 0) {
				out.writeCompressedInt(changedDate==null ? -1 : changedDate);
			}
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int result = in.read();
			if (result != AOServDaemonProtocol.DONE) {
				if (result == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
				else if (result == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
				else throw new IOException("Unknown result: " + result);
			}
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	/**
	 * Sets the password for a LinuxServerAccount.
	 */
	public void setLinuxServerAccountPassword(com.aoindustries.aoserv.client.linux.User.Name username, String plain_password) throws IOException, SQLException {
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.SET_LINUX_SERVER_ACCOUNT_PASSWORD);
			out.writeUTF(username.toString());
			out.writeUTF(plain_password);
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int result = in.read();
			if (result != AOServDaemonProtocol.DONE) {
				if (result == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
				else if (result == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
				else throw new IOException("Unknown result: " + result);
			}
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	/**
	 * Sets the password for a MySQLServerUser.
	 */
	public void setMySQLUserPassword(int mysqlServer, com.aoindustries.aoserv.client.mysql.User.Name username, String password) throws IOException, SQLException {
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.SET_MYSQL_USER_PASSWORD);
			out.writeCompressedInt(mysqlServer);
			out.writeUTF(username.toString());
			out.writeBoolean(password!=null); if(password!=null) out.writeUTF(password);
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int result = in.read();
			if (result != AOServDaemonProtocol.DONE) {
				if (result == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
				else if (result == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
				else throw new IOException("Unknown result: " + result);
			}
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	/**
	 * Sets the password for a PostgresServerUser.
	 */
	public void setPostgresUserPassword(int pkey, String password) throws IOException, SQLException {
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.SET_POSTGRES_USER_PASSWORD);
			out.writeCompressedInt(pkey);
			out.writeBoolean(password!=null); if(password!=null) out.writeUTF(password);
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int result = in.read();
			if (result != AOServDaemonProtocol.DONE) {
				if (result == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
				else if (result == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
				else throw new IOException("Unknown result: " + result);
			}
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	public void startApache() throws IOException, SQLException {
		controlProcess(AOServDaemonProtocol.START_APACHE);
	}

	public void startCron() throws IOException, SQLException {
		controlProcess(AOServDaemonProtocol.START_CRON);
	}

	/**
	 * Starts a distribution verification.
	 */
	public void startDistro(boolean includeUser) throws IOException, SQLException {
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.START_DISTRO);
			out.writeBoolean(includeUser);
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int result = in.read();
			if (result != AOServDaemonProtocol.DONE) {
				if (result == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
				else if (result == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
				else throw new IOException("Unknown result: " + result);
			}
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	/**
	 * Starts a Java VM.
	 */
	public String startJVM(int httpdSite) throws IOException, SQLException {
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.START_JVM);
			out.writeCompressedInt(httpdSite);
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int result = in.read();
			if (result == AOServDaemonProtocol.DONE) return in.readBoolean()?in.readUTF():null;
			else if (result == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
			else if (result == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
			else throw new IOException("Unknown result: " + result);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	public void startMySQL(int mysqlServer) throws IOException, SQLException {
		controlProcess(AOServDaemonProtocol.START_MYSQL, mysqlServer);
	}

	public void startPostgreSQL(int pkey) throws IOException, SQLException {
		controlProcess(AOServDaemonProtocol.START_POSTGRESQL, pkey);
	}

	public void startXfs() throws IOException, SQLException {
		controlProcess(AOServDaemonProtocol.START_XFS);
	}

	public void startXvfb() throws IOException, SQLException {
		controlProcess(AOServDaemonProtocol.START_XVFB);
	}

	public void stopApache() throws IOException, SQLException {
		controlProcess(AOServDaemonProtocol.STOP_APACHE);
	}

	public void stopCron() throws IOException, SQLException {
		controlProcess(AOServDaemonProtocol.STOP_CRON);
	}

	/**
	 * Stops a Java VM.
	 */
	public String stopJVM(int httpdSite) throws IOException, SQLException {
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.STOP_JVM);
			out.writeCompressedInt(httpdSite);
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int result = in.read();
			if (result == AOServDaemonProtocol.DONE) return in.readBoolean()?in.readUTF():null;
			else if (result == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
			else if (result == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
			else throw new IOException("Unknown result: " + result);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	public void stopMySQL(int mysqlServer) throws IOException, SQLException {
		controlProcess(AOServDaemonProtocol.STOP_MYSQL, mysqlServer);
	}

	public void stopPostgreSQL(int pkey) throws IOException, SQLException {
		controlProcess(AOServDaemonProtocol.STOP_POSTGRESQL, pkey);
	}

	public void stopXfs() throws IOException, SQLException {
		controlProcess(AOServDaemonProtocol.STOP_XFS);
	}

	public void stopXvfb() throws IOException, SQLException {
		controlProcess(AOServDaemonProtocol.STOP_XVFB);
	}

	@Override
	public String toString() {
		return getClass().getName()+"?hostname="+hostname+"&local_ip="+local_ip+"&port="+port+"&protocol="+protocol;
	}

	private void transferStream(
		int command,
		int param1,
		boolean gzip,
		DumpSizeCallback onDumpSize,
		StreamableOutput masterOut
	) throws IOException, SQLException {
		AOServDaemonConnection conn=getConnection();
		try {
			if(gzip && conn.protocolVersion.compareTo(AOServDaemonProtocol.Version.VERSION_1_80_0) < 0) {
				throw new IOException(
					"Gzip compression requires AOServ Daemon version "
						+ AOServDaemonProtocol.Version.VERSION_1_80_0
						+ " or higher.  Current version is " + conn.protocolVersion + '.');
			}
			StreamableOutput out = conn.getRequestOut(command);
			out.writeCompressedInt(param1);
			if(conn.protocolVersion.compareTo(AOServDaemonProtocol.Version.VERSION_1_80_0) >= 0) {
				out.writeBoolean(gzip);
			}
			out.flush();

			transferStream0(conn, onDumpSize, masterOut);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	/* Unused 2017-03-20
	private void transferStream(
		int command,
		String param1,
		StreamableOutput masterOut
	) throws IOException, SQLException {
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out=conn.getOutputStream();
			out.writeCompressedInt(command);
			out.writeUTF(param1);
			out.flush();

			transferStream0(conn, masterOut);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}
	 */

	/* Unused 2017-03-20
	private void transferStream(
		int command,
		String param1,
		StreamableOutput masterOut,
		long skipBytes
	) throws IOException, SQLException {
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out=conn.getOutputStream();
			out.writeCompressedInt(command);
			out.writeUTF(param1);
			out.writeLong(skipBytes);
			out.flush();

			/*if(reporter!=null) {
				long fileSize=conn.getResponseIn().readLong();
				reporter.setTotalSize(fileSize);
				reporter.setFinishedSize(skipBytes);
			}* /
			transferStream0(conn, masterOut);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}
	 */

	private void transferStream0(
		AOServDaemonConnection conn,
		DumpSizeCallback onDumpSize,
		StreamableOutput masterOut
	) throws IOException, SQLException {
		StreamableInput in=conn.getResponseIn();
		long dumpSize;
		if(conn.protocolVersion.compareTo(AOServDaemonProtocol.Version.VERSION_1_80_0) >= 0) {
			dumpSize = in.readLong();
		} else {
			dumpSize = -1;
		}
		if(dumpSize < -1) throw new IOException("dumpSize < -1: " + dumpSize);
		if(onDumpSize != null) onDumpSize.onDumpSize(dumpSize);
		long bytesRead = 0;
		{
			int code;
			byte[] buff=BufferManager.getBytes();
			try {
				while((code=in.read())==AOServDaemonProtocol.NEXT) {
					int len=in.readShort();
					bytesRead += len;
					if(dumpSize != -1 && bytesRead > dumpSize) throw new IOException("Too many bytes read: " + bytesRead + " > " + dumpSize);
					in.readFully(buff, 0, len);
					masterOut.writeByte(AoservProtocol.NEXT);
					masterOut.writeShort(len);
					masterOut.write(buff, 0, len);
					//if(reporter!=null) reporter.addFinishedSize(len);
				}
			} finally {
				BufferManager.release(buff, false);
			}
			if (code != AOServDaemonProtocol.DONE) {
				if (code == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
				else if (code == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
				else throw new IOException("Unknown result: " + code);
			}
		}
		if(dumpSize != -1 && bytesRead < dumpSize) throw new IOException("Too few bytes read: " + bytesRead + " < " + dumpSize);
	}

	private void waitFor(int taskCode) throws IOException, SQLException {
		AOServDaemonConnection conn = getConnection();
		try {
			StreamableOutput out;
			if(conn.protocolVersion.compareTo(AOServDaemonProtocol.Version.VERSION_1_80_0) < 0) {
				// Older protocol use a single WAIT_FOR_REBUILD with a follow-up table ID.
				// Table IDs can change over time, so the new protocol uses distinct task codes for each type of wait.
				// Find the table ID consistent with schema version 1.77
				final int tableId;
				switch(taskCode) {
					case AOServDaemonProtocol.WAIT_FOR_HTTPD_SITE_REBUILD :
						tableId = AOServDaemonProtocol.OLD_HTTPD_SITES_TABLE_ID;
						break;
					case AOServDaemonProtocol.WAIT_FOR_LINUX_ACCOUNT_REBUILD :
						tableId = AOServDaemonProtocol.OLD_LINUX_ACCOUNTS_TABLE_ID;
						break;
					case AOServDaemonProtocol.WAIT_FOR_MYSQL_DATABASE_REBUILD :
						tableId = AOServDaemonProtocol.OLD_MYSQL_DATABASES_TABLE_ID;
						break;
					case AOServDaemonProtocol.WAIT_FOR_MYSQL_DB_USER_REBUILD :
						tableId = AOServDaemonProtocol.OLD_MYSQL_DB_USERS_TABLE_ID;
						break;
					case AOServDaemonProtocol.WAIT_FOR_MYSQL_USER_REBUILD :
						tableId = AOServDaemonProtocol.OLD_MYSQL_USERS_TABLE_ID;
						break;
					case AOServDaemonProtocol.WAIT_FOR_POSTGRES_DATABASE_REBUILD :
						tableId = AOServDaemonProtocol.OLD_POSTGRES_DATABASES_TABLE_ID;
						break;
					case AOServDaemonProtocol.WAIT_FOR_POSTGRES_SERVER_REBUILD :
						tableId = AOServDaemonProtocol.OLD_POSTGRES_SERVERS_TABLE_ID;
						break;
					case AOServDaemonProtocol.WAIT_FOR_POSTGRES_USER_REBUILD :
						tableId = AOServDaemonProtocol.OLD_POSTGRES_USERS_TABLE_ID;
						break;
					default :
						throw new IOException("Unexpected taskCode: " + taskCode);

				}
				out = conn.getRequestOut(AOServDaemonProtocol.OLD_WAIT_FOR_REBUILD);
				out.writeCompressedInt(tableId);
			} else {
				out = conn.getRequestOut(taskCode);
			}
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int result = in.read();
			if (result != AOServDaemonProtocol.DONE) {
				if (result == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
				else if (result == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
				else throw new IOException("Unknown result: " + result);
			}
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	public void waitForHttpdSiteRebuild() throws IOException, SQLException {
		waitFor(AOServDaemonProtocol.WAIT_FOR_HTTPD_SITE_REBUILD);
	}

	public void waitForLinuxAccountRebuild() throws IOException, SQLException {
		waitFor(AOServDaemonProtocol.WAIT_FOR_LINUX_ACCOUNT_REBUILD);
	}

	public void waitForMySQLDatabaseRebuild() throws IOException, SQLException {
		waitFor(AOServDaemonProtocol.WAIT_FOR_MYSQL_DATABASE_REBUILD);
	}

	public void waitForMySQLDBUserRebuild() throws IOException, SQLException {
		waitFor(AOServDaemonProtocol.WAIT_FOR_MYSQL_DB_USER_REBUILD);
	}

	public void waitForMySQLServerRebuild() throws IOException, SQLException {
		waitFor(AOServDaemonProtocol.WAIT_FOR_MYSQL_SERVER_REBUILD);
	}

	public void waitForMySQLUserRebuild() throws IOException, SQLException {
		waitFor(AOServDaemonProtocol.WAIT_FOR_MYSQL_USER_REBUILD);
	}

	public void waitForPostgresDatabaseRebuild() throws IOException, SQLException {
		waitFor(AOServDaemonProtocol.WAIT_FOR_POSTGRES_DATABASE_REBUILD);
	}

	public void waitForPostgresServerRebuild() throws IOException, SQLException {
		waitFor(AOServDaemonProtocol.WAIT_FOR_POSTGRES_SERVER_REBUILD);
	}

	public void waitForPostgresUserRebuild() throws IOException, SQLException {
		waitFor(AOServDaemonProtocol.WAIT_FOR_POSTGRES_USER_REBUILD);
	}

	/**
	 * Gets the error handler for this and its underlying connection pool.
	 */
	Logger getLogger() {
		return logger;
	}

	/**
	 * Gets a 3ware RAID report.
	 *
	 * @return  the report
	 */
	public String get3wareRaidReport() throws IOException, SQLException {
		// Establish the connection to the server
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.GET_3WARE_RAID_REPORT);
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int code=in.read();
			if(code==AOServDaemonProtocol.DONE) return in.readUTF();
			if (code == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
			if (code == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
			throw new IOException("Unknown result: " + code);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	/**
	 * Gets the UPS status.
	 *
	 * @return  the report
	 */
	public String getUpsStatus() throws IOException, SQLException {
		// Establish the connection to the server
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.GET_UPS_STATUS);
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int code=in.read();
			if(code==AOServDaemonProtocol.DONE) return in.readUTF();
			if (code == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
			if (code == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
			throw new IOException("Unknown result: " + code);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	/**
	 * Gets a /proc/mdstat report.
	 *
	 * @return  the report
	 */
	public String getMdStatReport() throws IOException, SQLException {
		// Establish the connection to the server
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.GET_MD_STAT_REPORT);
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int code=in.read();
			if(code==AOServDaemonProtocol.DONE) return in.readUTF();
			if (code == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
			if (code == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
			throw new IOException("Unknown result: " + code);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	/**
	 * Gets a MD mismatch report.
	 *
	 * @return  the report
	 */
	public String getMdMismatchReport() throws IOException, SQLException {
		// Establish the connection to the server
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.GET_MD_MISMATCH_REPORT);
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int code=in.read();
			if(code==AOServDaemonProtocol.DONE) return in.readUTF();
			if (code == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
			if (code == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
			throw new IOException("Unknown result: " + code);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	/**
	 * Gets a DRBD report.
	 *
	 * @return  the report
	 */
	public String getDrbdReport() throws IOException, SQLException {
		// Establish the connection to the server
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.GET_DRBD_REPORT);
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int code=in.read();
			if(code==AOServDaemonProtocol.DONE) return in.readUTF();
			if (code == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
			if (code == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
			throw new IOException("Unknown result: " + code);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	public Tuple2 getFailoverFileReplicationActivity(int replication) throws IOException, SQLException {
		// Establish the connection to the server
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.GET_FAILOVER_FILE_REPLICATION_ACTIVITY);
			out.writeCompressedInt(replication);
			out.flush();

			StreamableInput in = conn.getResponseIn();
			int code=in.read();
			if(code == AOServDaemonProtocol.DONE)          return new Tuple2<>(in.readLong(), in.readUTF());
			if(code == AOServDaemonProtocol.IO_EXCEPTION)  throw new IOException(in.readUTF());
			if(code == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
			throw new IOException("Unknown result: " + code);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	/**
	 * Gets a LVM report.
	 *
	 * @return  the report
	 */
	public String[] getLvmReport() throws IOException, SQLException {
		// Establish the connection to the server
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.GET_LVM_REPORT);
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int code=in.read();
			if(code==AOServDaemonProtocol.DONE) {
				return new String[] {
					in.readUTF(),
					in.readUTF(),
					in.readUTF()
				};
			}
			if (code == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
			if (code == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
			throw new IOException("Unknown result: " + code);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	/**
	 * Gets a hard drive temperature report.
	 *
	 * @return  the report
	 */
	public String getHddTempReport() throws IOException, SQLException {
		// Establish the connection to the server
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.GET_HDD_TEMP_REPORT);
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int code=in.read();
			if(code==AOServDaemonProtocol.DONE) return in.readUTF();
			if (code == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
			if (code == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
			throw new IOException("Unknown result: " + code);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	/**
	 * Gets a hard drive model report.
	 *
	 * @return  the report
	 */
	public String getHddModelReport() throws IOException, SQLException {
		// Establish the connection to the server
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.GET_HDD_MODEL_REPORT);
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int code=in.read();
			if(code==AOServDaemonProtocol.DONE) return in.readUTF();
			if (code == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
			if (code == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
			throw new IOException("Unknown result: " + code);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	/**
	 * Gets a filesystems CSV report.
	 *
	 * @return  the report
	 */
	public String getFilesystemsCsvReport() throws IOException, SQLException {
		// Establish the connection to the server
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.GET_FILESYSTEMS_CSV_REPORT);
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int code=in.read();
			if(code==AOServDaemonProtocol.DONE) return in.readUTF();
			if (code == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
			if (code == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
			throw new IOException("Unknown result: " + code);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	/**
	 * Gets a load average report.
	 *
	 * @return  the report
	 */
	public String getLoadAvgReport() throws IOException, SQLException {
		// Establish the connection to the server
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.GET_AO_SERVER_LOADAVG_REPORT);
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int code=in.read();
			if(code==AOServDaemonProtocol.DONE) return in.readUTF();
			if (code == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
			if (code == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
			throw new IOException("Unknown result: " + code);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	/**
	 * Gets a meminfo report.
	 *
	 * @return  the report
	 */
	public String getMemInfoReport() throws IOException, SQLException {
		// Establish the connection to the server
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.GET_AO_SERVER_MEMINFO_REPORT);
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int code=in.read();
			if(code==AOServDaemonProtocol.DONE) return in.readUTF();
			if (code == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
			if (code == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
			throw new IOException("Unknown result: " + code);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	/**
	 * Checks a port from the server point of view.
	 *
	 * @return  the result
	 */
	public String checkPort(
		InetAddress ipAddress,
		Port port,
		String appProtocol,
		String monitoringParameters
	) throws IOException, SQLException {
		// Establish the connection to the server
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.CHECK_PORT);
			out.writeUTF(ipAddress.toString());
			out.writeCompressedInt(port.getPort());
			if(conn.protocolVersion.compareTo(AOServDaemonProtocol.Version.VERSION_1_80_0) < 0) {
				// Old protocol transferred lowercase
				out.writeUTF(port.getProtocol().name().toLowerCase(Locale.ROOT));
			} else {
				out.writeEnum(port.getProtocol());
			}
			out.writeUTF(appProtocol);
			out.writeUTF(monitoringParameters);
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int code=in.read();
			if(code==AOServDaemonProtocol.DONE) return in.readUTF();
			if (code == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
			if (code == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
			throw new IOException("Unknown result: " + code);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	/**
	 * Checks for a SMTP blacklist from the server point of view.
	 *
	 * @return  the status line
	 */
	public String checkSmtpBlacklist(InetAddress sourceIp, InetAddress connectIp) throws IOException, SQLException {
		// Establish the connection to the server
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.CHECK_SMTP_BLACKLIST);
			out.writeUTF(sourceIp.toString());
			out.writeUTF(connectIp.toString());
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int code=in.read();
			if(code==AOServDaemonProtocol.DONE) return in.readUTF();
			if (code == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
			if (code == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
			throw new IOException("Unknown result: " + code);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	public List checkSslCertificate(int sslCertificate, boolean allowCached) throws IOException, SQLException {
		// Establish the connection to the server
		AOServDaemonConnection conn = getConnection();
		try {
			if(conn.protocolVersion.compareTo(AOServDaemonProtocol.Version.VERSION_1_81_10) < 0) {
				return Collections.singletonList(new Certificate.Check(
						"Daemon Protocol",
						conn.protocolVersion.toString(),
						AlertLevel.UNKNOWN,
						"Protocol version does not support checking SSL certificates, please installed AOServ Daemon >= " + AOServDaemonProtocol.Version.VERSION_1_81_10
					)
				);
			} else {
				StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.CHECK_SSL_CERTIFICATE);
				out.writeCompressedInt(sslCertificate);
				if(conn.protocolVersion.compareTo(AOServDaemonProtocol.Version.VERSION_1_83_0) >= 0) {
					out.writeBoolean(allowCached);
				}
				out.flush();

				StreamableInput in = conn.getResponseIn();
				int code = in.read();
				if(code == AOServDaemonProtocol.NEXT) {
					int size = in.readCompressedInt();
					List results = new ArrayList<>(size);
					for(int i = 0; i < size; i++) {
						results.add(new Certificate.Check(
								in.readUTF(),
								in.readUTF(),
								AlertLevel.valueOf(in.readUTF()),
								in.readNullUTF()
							)
						);
					}
					return results;
				}
				if(code == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
				if(code == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
				throw new IOException("Unknown result: " + code);
			}
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	/**
	 * Gets the current system time.
	 *
	 * @return  the report
	 */
	public long getSystemTimeMillis() throws IOException, SQLException {
		// Establish the connection to the server
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.GET_AO_SERVER_SYSTEM_TIME_MILLIS);
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int code=in.read();
			if(code==AOServDaemonProtocol.DONE) return in.readLong();
			if (code == AOServDaemonProtocol.IO_EXCEPTION) throw new IOException(in.readUTF());
			if (code == AOServDaemonProtocol.SQL_EXCEPTION) throw new SQLException(in.readUTF());
			throw new IOException("Unknown result: " + code);
		} catch(IOException err) {
			conn.close();
			throw err;
		} finally {
			releaseConnection(conn);
		}
	}

	/**
	 * Gets the list of servers configured to auto-start in /etc/xen/auto.
	 */
	public Set getXenAutoStartLinks() throws IOException, SQLException {
		// Establish the connection to the server
		AOServDaemonConnection conn=getConnection();
		try {
			StreamableOutput out = conn.getRequestOut(AOServDaemonProtocol.GET_XEN_AUTO_START_LINKS);
			out.flush();

			StreamableInput in=conn.getResponseIn();
			int code=in.read();
			if(code==AOServDaemonProtocol.DONE) {
				int numLinks = in.readCompressedInt();
				Set links = new LinkedHashSet<>(numLinks*4/3+1);
				for(int i=0; i




© 2015 - 2025 Weber Informatics LLC | Privacy Policy