/* nutclient.h - definitions for nutclient C/C++ library Copyright (C) 2012 Emilien Kia This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef NUTCLIENT_HPP_SEEN #define NUTCLIENT_HPP_SEEN /* Begin of C++ nutclient library declaration */ #ifdef __cplusplus #include #include #include #include #include #include #include /* See include/common.h for details behind this */ #ifndef NUT_UNUSED_VARIABLE #define NUT_UNUSED_VARIABLE(x) (void)(x) #endif namespace nut { namespace internal { class Socket; } /* namespace internal */ class Client; class TcpClient; class Device; class Variable; class Command; /** * Basic nut exception. */ class NutException : public std::exception { public: NutException(const std::string& msg):_msg(msg){} NutException(const NutException&) = default; NutException& operator=(NutException& rhs) = default; virtual ~NutException() override; virtual const char * what() const noexcept override {return this->_msg.c_str();} virtual std::string str() const noexcept {return this->_msg;} private: std::string _msg; }; /** * System error. */ class SystemException : public NutException { public: SystemException(); SystemException(const SystemException&) = default; SystemException& operator=(SystemException& rhs) = default; virtual ~SystemException() override; private: static std::string err(); }; /** * IO oriented nut exception. */ class IOException : public NutException { public: IOException(const std::string& msg):NutException(msg){} IOException(const IOException&) = default; IOException& operator=(IOException& rhs) = default; virtual ~IOException() override; }; /** * IO oriented nut exception specialized for unknown host */ class UnknownHostException : public IOException { public: UnknownHostException():IOException("Unknown host"){} UnknownHostException(const UnknownHostException&) = default; UnknownHostException& operator=(UnknownHostException& rhs) = default; virtual ~UnknownHostException() override; }; /** * IO oriented nut exception when client is not connected */ class NotConnectedException : public IOException { public: NotConnectedException():IOException("Not connected"){} NotConnectedException(const NotConnectedException&) = default; NotConnectedException& operator=(NotConnectedException& rhs) = default; virtual ~NotConnectedException() override; }; /** * IO oriented nut exception when there is no response. */ class TimeoutException : public IOException { public: TimeoutException():IOException("Timeout"){} TimeoutException(const TimeoutException&) = default; TimeoutException& operator=(TimeoutException& rhs) = default; virtual ~TimeoutException() override; }; /** * Cookie given when performing async action, used to redeem result at a later date. */ typedef std::string TrackingID; /** * Result of an async action. */ typedef enum { UNKNOWN, PENDING, SUCCESS, INVALID_ARGUMENT, FAILURE, } TrackingResult; typedef std::string Feature; /** * A nut client is the starting point to dialog to NUTD. * It can connect to an NUTD then retrieve its device list. * Use a specific client class to connect to a NUTD. */ class Client { friend class Device; friend class Variable; friend class Command; public: virtual ~Client(); /** * Intend to authenticate to a NUTD server. * Set the username and password associated to the connection. * \param user User name. * \param passwd Password. * \todo Is his method is global to all connection protocol or is it specific to TCP ? * \note Actually, authentication fails only if already set, not if bad values are sent. */ virtual void authenticate(const std::string& user, const std::string& passwd) = 0; /** * Disconnect from the NUTD server. * \todo Is his method is global to all connection protocol or is it specific to TCP ? */ virtual void logout() = 0; /** * Device manipulations. * \see nut::Device * \{ */ /** * Retrieve a device from its name. * If the device does not exist, a bad (not ok) device is returned. * \param name Name of the device. * \return The device. */ virtual Device getDevice(const std::string& name); /** * Retrieve the list of all devices supported by UPSD server. * \return The set of supported devices. */ virtual std::set getDevices(); /** * Test if a device is supported by the NUTD server. * \param dev Device name. * \return true if supported, false otherwise. */ virtual bool hasDevice(const std::string& dev); /** * Retrieve names of devices supported by NUTD server. * \return The set of names of supported devices. */ virtual std::set getDeviceNames() = 0; /** * Retrieve the description of a device. * \param name Device name. * \return Device description. */ virtual std::string getDeviceDescription(const std::string& name) = 0; /** \} */ /** * Variable manipulations. * \see nut::Variable * \{ */ /** * Retrieve names of all variables supported by a device. * \param dev Device name * \return Variable names */ virtual std::set getDeviceVariableNames(const std::string& dev) = 0; /** * Retrieve names of read/write variables supported by a device. * \param dev Device name * \return RW variable names */ virtual std::set getDeviceRWVariableNames(const std::string& dev) = 0; /** * Test if a variable is supported by a device. * \param dev Device name * \param name Variable name * \return true if the variable is supported. */ virtual bool hasDeviceVariable(const std::string& dev, const std::string& name); /** * Retrieve the description of a variable. * \param dev Device name * \param name Variable name * \return Variable description if provided. */ virtual std::string getDeviceVariableDescription(const std::string& dev, const std::string& name) = 0; /** * Retrieve values of a variable. * \param dev Device name * \param name Variable name * \return Variable values (usually one) if available. */ virtual std::vector getDeviceVariableValue(const std::string& dev, const std::string& name) = 0; /** * Retrieve values of all variables of a device. * \param dev Device name * \return Variable values indexed by variable names. */ virtual std::map > getDeviceVariableValues(const std::string& dev); /** * Retrieve values of all variables of a set of devices. * \param devs Device names * \return Variable values indexed by variable names, indexed by device names. */ virtual std::map > > getDevicesVariableValues(const std::set& devs); /** * Intend to set the value of a variable. * \param dev Device name * \param name Variable name * \param value Variable value */ virtual TrackingID setDeviceVariable(const std::string& dev, const std::string& name, const std::string& value) = 0; /** * Intend to set the value of a variable. * \param dev Device name * \param name Variable name * \param values Vector of variable values */ virtual TrackingID setDeviceVariable(const std::string& dev, const std::string& name, const std::vector& values) = 0; /** \} */ /** * Instant command manipulations. * \see nut::Command * \{ */ /** * Retrieve names of all commands supported by a device. * \param dev Device name * \return Command names */ virtual std::set getDeviceCommandNames(const std::string& dev) = 0; /** * Test if a command is supported by a device. * \param dev Device name * \param name Command name * \return true if the command is supported. */ virtual bool hasDeviceCommand(const std::string& dev, const std::string& name); /** * Retrieve the description of a command. * \param dev Device name * \param name Command name * \return Command description if provided. */ virtual std::string getDeviceCommandDescription(const std::string& dev, const std::string& name) = 0; /** * Intend to execute a command. * \param dev Device name * \param name Command name * \param param Additional command parameter */ virtual TrackingID executeDeviceCommand(const std::string& dev, const std::string& name, const std::string& param="") = 0; /** \} */ /** * Device specific commands. * \{ */ /** * Log the current user (if authenticated) for a device. * \param dev Device name. */ virtual void deviceLogin(const std::string& dev) = 0; /** * Retrieve the number of user logged-in for the specified device. * \param dev Device name. * \return Number of logged-in users. */ virtual int deviceGetNumLogins(const std::string& dev) = 0; /* NOTE: "master" is deprecated since NUT v2.8.0 in favor of "primary". * For the sake of old/new server/client interoperability, * practical implementations should try to use one and fall * back to the other, and only fail if both return "ERR". */ virtual void deviceMaster(const std::string& dev) = 0; virtual void devicePrimary(const std::string& dev) = 0; virtual void deviceForcedShutdown(const std::string& dev) = 0; /** * Retrieve the result of a tracking ID. * \param id Tracking ID. */ virtual TrackingResult getTrackingResult(const TrackingID& id) = 0; virtual bool hasFeature(const Feature& feature); virtual bool isFeatureEnabled(const Feature& feature) = 0; virtual void setFeature(const Feature& feature, bool status) = 0; static const Feature TRACKING; protected: Client(); }; /** * TCP NUTD client. * It connect to NUTD with a TCP socket. */ class TcpClient : public Client { /* We have a number of direct-call methods we do not expose * generally, but still want covered with integration tests */ friend class NutActiveClientTest; public: /** * Construct a nut TcpClient object. * You must call one of TcpClient::connect() after. */ TcpClient(); /** * Construct a nut TcpClient object then connect it to the specified server. * \param host Server host name. * \param port Server port. */ TcpClient(const std::string& host, uint16_t port = 3493); ~TcpClient() override; /** * Connect it to the specified server. * \param host Server host name. * \param port Server port. */ void connect(const std::string& host, uint16_t port = 3493); /** * Connect to the server. * Host name and ports must have already set (usefull for reconnection). */ void connect(); /** * Test if the connection is active. * \return tru if the connection is active. */ bool isConnected()const; /** * Force the deconnection. */ void disconnect(); /** * Set the timeout in seconds. * \param timeout Timeout n seconds, negative to block operations. */ void setTimeout(time_t timeout); /** * Retrieve the timeout. * \returns Current timeout in seconds. */ time_t getTimeout()const; /** * Retriueve the host name of the server the client is connected to. * \return Server host name */ std::string getHost()const; /** * Retriueve the port of host of the server the client is connected to. * \return Server port */ uint16_t getPort()const; virtual void authenticate(const std::string& user, const std::string& passwd) override; virtual void logout() override; virtual Device getDevice(const std::string& name) override; virtual std::set getDeviceNames() override; virtual std::string getDeviceDescription(const std::string& name) override; virtual std::set getDeviceVariableNames(const std::string& dev) override; virtual std::set getDeviceRWVariableNames(const std::string& dev) override; virtual std::string getDeviceVariableDescription(const std::string& dev, const std::string& name) override; virtual std::vector getDeviceVariableValue(const std::string& dev, const std::string& name) override; virtual std::map > getDeviceVariableValues(const std::string& dev) override; virtual std::map > > getDevicesVariableValues(const std::set& devs) override; virtual TrackingID setDeviceVariable(const std::string& dev, const std::string& name, const std::string& value) override; virtual TrackingID setDeviceVariable(const std::string& dev, const std::string& name, const std::vector& values) override; virtual std::set getDeviceCommandNames(const std::string& dev) override; virtual std::string getDeviceCommandDescription(const std::string& dev, const std::string& name) override; virtual TrackingID executeDeviceCommand(const std::string& dev, const std::string& name, const std::string& param="") override; virtual void deviceLogin(const std::string& dev) override; /* FIXME: Protocol update needed to handle master/primary alias * and probably an API bump also, to rename/alias the routine. */ virtual void deviceMaster(const std::string& dev) override; virtual void devicePrimary(const std::string& dev) override; virtual void deviceForcedShutdown(const std::string& dev) override; virtual int deviceGetNumLogins(const std::string& dev) override; virtual TrackingResult getTrackingResult(const TrackingID& id) override; virtual bool isFeatureEnabled(const Feature& feature) override; virtual void setFeature(const Feature& feature, bool status) override; protected: std::string sendQuery(const std::string& req); void sendAsyncQueries(const std::vector& req); static void detectError(const std::string& req); TrackingID sendTrackingQuery(const std::string& req); std::vector get(const std::string& subcmd, const std::string& params = ""); std::vector > list(const std::string& subcmd, const std::string& params = ""); std::vector > parseList(const std::string& req); static std::vector explode(const std::string& str, size_t begin=0); static std::string escape(const std::string& str); private: std::string _host; uint16_t _port; time_t _timeout; internal::Socket* _socket; }; /** * Device attached to a client. * Device is a lightweight class which can be copied easily. */ class Device { friend class Client; friend class TcpClient; friend class TcpClientMock; #ifdef _NUTCLIENTTEST_BUILD friend class NutClientTest; #endif public: ~Device(); Device(const Device& dev); Device& operator=(const Device& dev); /** * Retrieve the name of the device. * The name is the unique id under which NUTD known the device. */ std::string getName()const; /** * Retrieve the client to which the device is attached. */ const Client* getClient()const; /** * Retrieve the client to which the device is attached. */ Client* getClient(); /** * Test if the device is valid (has a name and is attached to a client). */ bool isOk()const; /** * Test if the device is valid (has a name and is attached to a client). * @see Device::isOk() */ operator bool()const; /** * Test if the device is not valid (has no name or is not attached to any client). * @see Device::isOk() */ bool operator!()const; /** * Test if the two devices are sames (same name ad same client attached to). */ bool operator==(const Device& dev)const; /** * Comparison operator. */ bool operator<(const Device& dev)const; /** * Retrieve the description of the devce if specified. */ std::string getDescription(); /** * Intend to retrieve the value of a variable of the device. * \param name Name of the variable to get. * \return Value of the variable, if available. */ std::vector getVariableValue(const std::string& name); /** * Intend to retrieve values of all variables of the devices. * \return Map of all variables values indexed by their names. */ std::map > getVariableValues(); /** * Retrieve all variables names supported by the device. * \return Set of available variable names. */ std::set getVariableNames(); /** * Retrieve all Read/Write variables names supported by the device. * \return Set of available Read/Write variable names. */ std::set getRWVariableNames(); /** * Intend to set the value of a variable of the device. * \param name Variable name. * \param value New variable value. */ void setVariable(const std::string& name, const std::string& value); /** * Intend to set values of a variable of the device. * \param name Variable name. * \param values Vector of new variable values. */ void setVariable(const std::string& name, const std::vector& values); /** * Retrieve a Variable object representing the specified variable. * \param name Variable name. * \return Variable object. */ Variable getVariable(const std::string& name); /** * Retrieve Variable objects representing all variables available for the device. * \return Set of Variable objects. */ std::set getVariables(); /** * Retrieve Variable objects representing all Read/Write variables available for the device. * \return Set of Variable objects. */ std::set getRWVariables(); /** * Retrieve names of all commands supported by the device. * \return Set of available command names. */ std::set getCommandNames(); /** * Retrieve objects for all commands supported by the device. * \return Set of available Command objects. */ std::set getCommands(); /** * Retrieve an object representing a command of the device. * \param name Command name. * \return Command object. */ Command getCommand(const std::string& name); /** * Intend to execute a command on the device. * \param name Command name. * \param param Additional command parameter */ TrackingID executeCommand(const std::string& name, const std::string& param=""); /** * Login current client's user for the device. */ void login(); /* FIXME: Protocol update needed to handle master/primary alias * and probably an API bump also, to rename/alias the routine. */ void master(); void primary(); void forcedShutdown(); /** * Retrieve the number of logged user for the device. * \return Number of users. */ int getNumLogins(); protected: Device(Client* client, const std::string& name); private: Client* _client; std::string _name; }; /** * Variable attached to a device. * Variable is a lightweight class which can be copied easily. */ class Variable { friend class Device; friend class TcpClient; friend class TcpClientMock; #ifdef _NUTCLIENTTEST_BUILD friend class NutClientTest; #endif public: ~Variable(); Variable(const Variable& var); Variable& operator=(const Variable& var); /** * Retrieve variable name. */ std::string getName()const; /** * Retrieve the device to which the variable is attached to. */ const Device* getDevice()const; /** * Retrieve the device to which the variable is attached to. */ Device* getDevice(); /** * Test if the variable is valid (has a name and is attached to a device). */ bool isOk()const; /** * Test if the variable is valid (has a name and is attached to a device). * @see Variable::isOk() */ operator bool()const; /** * Test if the variable is not valid (has no name or is not attached to any device). * @see Variable::isOk() */ bool operator!()const; /** * Test if the two variables are sames (same name ad same device attached to). */ bool operator==(const Variable& var)const; /** * Less-than operator (based on variable name) to allow variable sorting. */ bool operator<(const Variable& var)const; /** * Intend to retrieve variable value. * \return Value of the variable. */ std::vector getValue(); /** * Intend to retireve variable description. * \return Variable description if provided. */ std::string getDescription(); /** * Intend to set a value to the variable. * \param value New variable value. */ void setValue(const std::string& value); /** * Intend to set (multiple) values to the variable. * \param values Vector of new variable values. */ void setValues(const std::vector& values); protected: Variable(Device* dev, const std::string& name); private: Device* _device; std::string _name; }; /** * Command attached to a device. * Command is a lightweight class which can be copied easily. */ class Command { friend class Device; friend class TcpClient; friend class TcpClientMock; #ifdef _NUTCLIENTTEST_BUILD friend class NutClientTest; #endif public: ~Command(); Command(const Command& cmd); Command& operator=(const Command& cmd); /** * Retrieve command name. */ std::string getName()const; /** * Retrieve the device to which the command is attached to. */ const Device* getDevice()const; /** * Retrieve the device to which the command is attached to. */ Device* getDevice(); /** * Test if the command is valid (has a name and is attached to a device). */ bool isOk()const; /** * Test if the command is valid (has a name and is attached to a device). * @see Command::isOk() */ operator bool()const; /** * Test if the command is not valid (has no name or is not attached to any device). * @see Command::isOk() */ bool operator!()const; /** * Test if the two commands are sames (same name ad same device attached to). */ bool operator==(const Command& var)const; /** * Less-than operator (based on command name) to allow comand sorting. */ bool operator<(const Command& var)const; /** * Intend to retireve command description. * \return Command description if provided. */ std::string getDescription(); /** * Intend to execute the instant command on device. * \param param Optional additional command parameter */ void execute(const std::string& param=""); protected: Command(Device* dev, const std::string& name); private: Device* _device; std::string _name; }; } /* namespace nut */ #endif /* __cplusplus */ /* End of C++ nutclient library declaration */ /* Begin of C nutclient library declaration */ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /** * Array of string manipulation functions. * \{ */ /** Array of string.*/ typedef char** strarr; /** * Alloc an array of string. */ strarr strarr_alloc(size_t count); /** * Free an array of string. */ void strarr_free(strarr arr); /** * Convert C++ types into an array of string. */ strarr stringvector_to_strarr(const std::vector& strset); strarr stringset_to_strarr(const std::set& strset); /** * Nut general client types and functions. * \{ */ /** Hidden structure representing a connection to NUTD. */ typedef void* NUTCLIENT_t; /** * Destroy a client. * \param client Nut client handle. */ void nutclient_destroy(NUTCLIENT_t client); /** * Authenticate into the server. * \param client Nut client handle. * \param login User name. * \param passwd Password. */ void nutclient_authenticate(NUTCLIENT_t client, const char* login, const char* passwd); /** * Log out from server. * \param client Nut client handle. */ void nutclient_logout(NUTCLIENT_t client); /** * Register current user on the device. * \param client Nut client handle. * \param dev Device name to test. */ void nutclient_device_login(NUTCLIENT_t client, const char* dev); /** * Retrieve the number of users registered on a device. * \param client Nut client handle. * \param dev Device name to test. */ int nutclient_get_device_num_logins(NUTCLIENT_t client, const char* dev); /** * Set current user as master user of the device. * \param client Nut client handle. * \param dev Device name to test. */ /* FIXME: Protocol update needed to handle master/primary alias * and probably an API bump also, to rename/alias the routine. */ void nutclient_device_master(NUTCLIENT_t client, const char* dev); void nutclient_device_primary(NUTCLIENT_t client, const char* dev); /** * Set the FSD flag for the device. * \param client Nut client handle. * \param dev Device name to test. */ void nutclient_device_forced_shutdown(NUTCLIENT_t client, const char* dev); /** * Retrieve the list of devices of a client. * \param client Nut client handle. * \return Array of string containing device names. Must be freed with strarr_free(strarr). */ strarr nutclient_get_devices(NUTCLIENT_t client); /** * Test if a device is supported by the client. * \param client Nut client handle. * \param dev Device name to test. * \return 1 if supported, 0 otherwise. */ int nutclient_has_device(NUTCLIENT_t client, const char* dev); /** * Intend to retrieve device description. * \param client Nut client handle. * \param dev Device name to test. * \return Description of device. Must be freed after use. */ char* nutclient_get_device_description(NUTCLIENT_t client, const char* dev); /** * Intend to retrieve device variable names. * \param client Nut client handle. * \param dev Device name. * \return Array of string containing variable names. Must be freed with strarr_free(strarr). */ strarr nutclient_get_device_variables(NUTCLIENT_t client, const char* dev); /** * Intend to retrieve device read/write variable names. * \param client Nut client handle. * \param dev Device name. * \return Array of string containing read/write variable names. Must be freed with strarr_free(strarr). */ strarr nutclient_get_device_rw_variables(NUTCLIENT_t client, const char* dev); /** * Test if a variable is supported by the device and the client. * \param client Nut client handle. * \param dev Device name. * \param var Variable name. * \return 1 if supported, 0 otherwise. */ int nutclient_has_device_variable(NUTCLIENT_t client, const char* dev, const char* var); /** * Intend to retrieve device variable description. * \param client Nut client handle. * \param dev Device name. * \param var Variable name. * \return Description of device variable. Must be freed after use. */ char* nutclient_get_device_variable_description(NUTCLIENT_t client, const char* dev, const char* var); /** * Intend to retrieve device variable values. * \param client Nut client handle. * \param dev Device name. * \param var Variable name. * \return Array of string containing variable values. Must be freed with strarr_free(strarr). */ strarr nutclient_get_device_variable_values(NUTCLIENT_t client, const char* dev, const char* var); /** * Intend to set device variable value. * \param client Nut client handle. * \param dev Device name. * \param var Variable name. * \param value Value to set. */ void nutclient_set_device_variable_value(NUTCLIENT_t client, const char* dev, const char* var, const char* value); /** * Intend to set device variable multiple values. * \param client Nut client handle. * \param dev Device name. * \param var Variable name. * \param values Values to set. The cller is responsible to free it after call. */ void nutclient_set_device_variable_values(NUTCLIENT_t client, const char* dev, const char* var, const strarr values); /** * Intend to retrieve device command names. * \param client Nut client handle. * \param dev Device name. * \return Array of string containing command names. Must be freed with strarr_free(strarr). */ strarr nutclient_get_device_commands(NUTCLIENT_t client, const char* dev); /** * Test if a command is supported by the device and the client. * \param client Nut client handle. * \param dev Device name. * \param cmd Command name. * \return 1 if supported, 0 otherwise. */ int nutclient_has_device_command(NUTCLIENT_t client, const char* dev, const char* cmd); /** * Intend to retrieve device command description. * \param client Nut client handle. * \param dev Device name. * \param cmd Command name. * \return Description of device command. Must be freed after use. */ char* nutclient_get_device_command_description(NUTCLIENT_t client, const char* dev, const char* cmd); /** * Intend to execute device command. * \param client Nut client handle. * \param dev Device name. * \param cmd Command name. */ void nutclient_execute_device_command(NUTCLIENT_t client, const char* dev, const char* cmd, const char* param=""); /** \} */ /** * Nut TCP client dedicated types and functions * \{ */ /** * Hidden structure representing a TCP connection to NUTD. * NUTCLIENT_TCP_t is back compatible to NUTCLIENT_t. */ typedef NUTCLIENT_t NUTCLIENT_TCP_t; /** * Create a client to NUTD using a TCP connection. * \param host Host name to connect to. * \param port Host port. * \return New client or nullptr if failed. */ NUTCLIENT_TCP_t nutclient_tcp_create_client(const char* host, uint16_t port); /** * Test if a nut TCP client is connected. * \param client Nut TCP client handle. * \return 1 if connected, 0 otherwise. */ int nutclient_tcp_is_connected(NUTCLIENT_TCP_t client); /** * Disconnect a nut TCP client. * \param client Nut TCP client handle. */ void nutclient_tcp_disconnect(NUTCLIENT_TCP_t client); /** * Intend to reconnect a nut TCP client. * \param client Nut TCP client handle. * \return 0 if correctly connected. * \todo Implement different error codes. */ int nutclient_tcp_reconnect(NUTCLIENT_TCP_t client); /** * Set the timeout value for the TCP connection. * \param timeout Timeout in seconds, negative for blocking. */ void nutclient_tcp_set_timeout(NUTCLIENT_TCP_t client, time_t timeout); /** * Retrieve the timeout value for the TCP connection. * \return Timeout value in seconds. */ time_t nutclient_tcp_get_timeout(NUTCLIENT_TCP_t client); /** \} */ #ifdef __cplusplus } #endif /* __cplusplus */ /* End of C nutclient library declaration */ #endif /* NUTCLIENT_HPP_SEEN */