//+============================================================================
//
// file :               tango_admin.cpp
//
// description :        C++ source code for the tango_admin utility
//						This utility is a Tango database command line interface
//						Obviously, not all the database features are interfaced
//						by this tool. Only the features needed for the Debian
//						packaging have been implemented. This means:
//						- ping the database server
//						- check if a device is defined in DB
//						- check if a server is defined in DB
//						- create a server in DB
//						- delete a server from the DB
//						- create a property in DB
//						- delete a property from DB
//
// project :            TANGO
//
// author(s) :          E.Taurel
//
// Copyright (C) :      2004,2005,2006,2007,2008,2009,2010
//						European Synchrotron Radiation Facility
//                      BP 220, Grenoble 38043
//                      FRANCE
//
// This file is part of Tango.
//
// Tango 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 3 of the License, or
// (at your option) any later version.
//
// Tango 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 Tango.  If not, see <http://www.gnu.org/licenses/>.
//
//-============================================================================

#include <iostream>
#include <string>
#include <cmath>
#include <ctime>
#include <cstdlib>

 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netdb.h>

#include <tango/tango.h>
#include "anyoption.h"


int ping_database();
int check_device(const char *);
int check_device_exported(const char *);
int add_server(char *,char *,char *);
void list2vect(std::string &,std::vector<std::string> &);
int check_server(char *);
int server_list();
int server_instance_list(char *);
int delete_server(char *,bool);
int add_property(char *,char *,char *);
int delete_property(char *,char *);
int ping_network(bool);
int tac_enabled(void);
int ping_device(const std::string&);
int unexport_device(const char *);

// The min and max allowed timeouts. Used by the function
// compute_loops_and_sleep_time.
static constexpr double MIN_TIMEOUT{0.001};
static constexpr double MAX_TIMEOUT{1200.0};

static void printUsageAndExit(const std::unique_ptr<AnyOption>& opt)
{
  opt->printUsage();
  exit(-EINVAL);
}

// method: compute_loops_and_sleep_time
//
// descritpion: Compute the number of loops and the sleep time between loop
//              iterations.
// input parameters:
//  - timeout_in_s: Total duration in seconds for all loop iterations combined.
// output parameters:
//  - number_of_loops: Number of times a loop needs to run to reach a total
// run time duration of timeout_in_s with sleeps of sleep_time seconds between
// iteratons.
// - sleep_time: Time to sleep with nanosleep between loop iterations.
// return: -ERANGE if the timeout_in_s parameter does not fall within the
// expected range.
static int compute_loops_and_sleep_time(double timeout_in_s, unsigned int& number_of_loops, struct timespec& sleep_time)
{
    if((timeout_in_s < MIN_TIMEOUT) || (timeout_in_s > MAX_TIMEOUT))
    {
      std::cerr << "Error: The given timeout ("
                << timeout_in_s
                << ") does not fall in the allowed range of ["
                << MIN_TIMEOUT
                << ", "
                << MAX_TIMEOUT
                << "].\n";
        return -ERANGE;
    }

    if(timeout_in_s <= 0.01)
    {
        number_of_loops = 2;
        sleep_time.tv_sec = 0;
        sleep_time.tv_nsec = (timeout_in_s / number_of_loops * 1e9);
    }
    else if(timeout_in_s <= 0.1)
    {
        number_of_loops = 20;
        sleep_time.tv_sec = 0;
        sleep_time.tv_nsec = (timeout_in_s / number_of_loops * 1e9);
    }
    else if(timeout_in_s <= 1.0)
    {
        number_of_loops = 200;
        sleep_time.tv_sec = 0;
        sleep_time.tv_nsec = (timeout_in_s / number_of_loops * 1e9);
    }
    else
    {
        sleep_time.tv_sec = 0;
        sleep_time.tv_nsec = 0.5 * 1e9;
        number_of_loops = timeout_in_s / 0.5;
    }

    return 0;
}

// method: convert_string_to_double
//
// descritpion: Convert a std::string to a double. The input is checked to be valid.
// input parameters:
//  - value_string: String representation of a double
// output parameters:
//  - value: The value represented by a double
// return: -ERANGE if the timeout_in_s parameter does not fall within the
// expected range.
static int convert_string_to_double(const std::string& value_as_string,
    double& value)
{
    int ret{};
    try
    {
        auto converted_value{std::stod(value_as_string)};
        if(!std::isnan(converted_value))
        {
            // The value is within range, everything is OK.
            value = converted_value;
        }
        else
        {
            std::cout << "The parameter's value ("
                << value_as_string
                << ") cannot be \"Not a Number\" (NaN).\n";
            ret = -EDOM;
        }
    }
    catch(std::invalid_argument& ex)
    {
        std::cout << "The parameter's value ("
            << value_as_string
            << ") cannot be converted to a numerical value.\n";
        ret = -EINVAL;
    }
    catch(std::out_of_range& ex)
    {
        std::cout << "The parameter's value ("
            << value_as_string
            << ") cannot be converted to a numerical value that lies within "
                "the value range of the used data type (double).\n";
        ret = -ERANGE;
    }

    return ret;
}

// method: call_func
//
// description: Generic wrapper for a callable. The wrapper runs for a specified
//               duration with computed pauses that are based on the duration.
// input parameters:
//  - callable: Function to be called.
//  - timeout_in_s: A timeout or duration which limits how long this function
//                  runs.
// return: -ERANGE if the timeout_in_s parameter does not fall within the
//         expected range.
//         -ETIMEDOUT if the called function never returned 0 over the entire
//                     duration.
//         0 if the callable returned 0 itself which means success.
static int call_func(std::function<int()> callable, double timeout_in_s)
{
    struct timespec sleep_time{};
    unsigned int loops;
    bool run_forever{false};
    if(timeout_in_s >= 0)
    {
        auto err{compute_loops_and_sleep_time(timeout_in_s, loops, sleep_time)};
        if(err != 0)
        {
            return err;
        }
    }
    else
    {
        // In case the Tango DB check runs forever, set the time to wait between
        // retires to 0.5s.
        sleep_time.tv_sec = 0;
        sleep_time.tv_nsec = 1000000000UL * 0.5;
        loops = 1U;
        timeout_in_s = 1.0;
        run_forever = true;
        std::cerr << "Will try forever until successful.\n";
    }

    const double sleep_time_in_s{(sleep_time.tv_sec * 1e9 + sleep_time.tv_nsec) / 1e9};
    while((loops > 0U) && (timeout_in_s > 0.0))
    {
        if(callable() == 0)
        {
            return 0;
        }

        nanosleep(&sleep_time, nullptr);
        if(!run_forever)
        {
            timeout_in_s -= sleep_time_in_s;
            --loops;
        }
    };

    return -ETIMEDOUT;
}

static void print_error(const char *msg, const Tango::DevError &e)
{
    std::cerr << msg << ": " << e.desc << " (" << e.reason << ")\n";
}


int main(int argc,char *argv[])
{
	auto opt{std::make_unique< AnyOption >()};

//
// Add usage menu
//

	opt->addUsage("Usage: " );
 	opt->addUsage(" --help  		Prints this help " );
	opt->addUsage(" --ping-database	[max_time (s)] Ping database " );
	opt->addUsage(" --check-device <dev>    Check if the device is defined in DB");
	opt->addUsage(" --check-device-exported <dev>    Check if the device is exported in DB");
 	opt->addUsage(" --add-server <exec/inst> <class> <dev list (comma separated)>   Add a server in DB" );
 	opt->addUsage(" --delete-server <exec/inst> [--with-properties]   Delete a server from DB" );
	opt->addUsage(" --check-server <exec/inst>   Check if a device server is defined in DB");
	opt->addUsage(" --server-list  Display list of server names");
	opt->addUsage(" --server-instance-list <exec>   Display list of server instances for the given server name");
 	opt->addUsage(" --add-property <dev> <prop_name> <prop_value (comma separated for array)>    Add a device property in DB" );
	opt->addUsage(" --delete-property <dev> <prop_name>   Delete a device property from DB ");
	opt->addUsage(" --tac-enabled Check if the TAC (Tango Access Control) is enabled");
	opt->addUsage(" --ping-device <dev> [max_time (s)] Check if the device is running");
	opt->addUsage(" --ping-network [max_time (s)] [-v] Ping network ");
	opt->addUsage(" --unexport-device <dev> Unexport a device from DB.  USE WITH CARE.");

//
// Define the command line options
//

        struct Flag {
            const char* long_name;
            char short_name;
        };

        struct Option {
            const char* name;
        };

        std::vector<Flag> flags = {
            Flag{"help", 'h'},
            Flag{"ping-database", '\0'},
            Flag{"with-properties", '\0'},
            Flag{"server-list", '\0'},
            Flag{"ping-network", '\0'},
            Flag{"tac-enabled", '\0'},
        };

        std::vector<Option> options = {
            Option{"add-server"},
            Option{"delete-server"},
            Option{"add-property"},
            Option{"delete-property"},
            Option{"check-property"},
            Option{"check-device"},
            Option{"check-device-exported"},
            Option{"check-server"},
            Option{"server-instance-list"},
            Option{"ping-device"},
            Option{"unexport-device"},
        };

        for (auto &f: flags) {
            if (f.short_name != '\0')
            {
                opt->setFlag(f.long_name, f.short_name);
            }
            else
            {
                opt->setFlag(f.long_name);
            }
        }

        for (auto &o: options) {
            opt->setOption(o.name);
        }

//
// Process cmd line
//

	opt->processCommandArgs( argc, argv );

        int opt_count = 0;
        for (auto &f: flags)
        {
            if (opt->getFlag(f.long_name) ||
                (f.short_name != '\0' && opt->getFlag(f.short_name)))
            {
                opt_count += 1;
            }

        }

        for (auto &o: options)
        {
            if (opt->getValue(o.name) != nullptr)
            {
                opt_count += 1;
            }
        }

	if (opt_count != 1)
	{
            if (opt_count > 1)
            {
                std::cerr << "Can't mix options\n";
            }
		printUsageAndExit(opt);
	}

//
// --help option
//

	if (opt->getFlag("help") || opt->getFlag('h'))
	{
		printUsageAndExit(opt);
	}
//
// --ping-database option
//
	if (opt->getFlag("ping-database") == true)
	{

        if(argc != 2 && argc != 3)
        {
            std::cerr << "Bad argument number for option --ping-database\n";
            printUsageAndExit(opt);
        }

        setenv("SUPER_TANGO", "true", 1);
        // First sleep for 1 sec before trying to access the db
        // This was needed when ported to Natty (Ubuntu 11.04) in the
        // tango-db startup script. Db process did not start if tango
        // admin starts pinging db device too early !!
        struct timespec sleep_time{1, 0};
        nanosleep(&sleep_time, nullptr);

        if(argc == 2)
        {
            auto func{std::bind(ping_database)};
            return call_func(func, MAX_TIMEOUT);
        }
        else
        {
            double timeout{};
            const int ret{convert_string_to_double(argv[2], timeout)};
            if(ret != 0)
            {
                std::cerr << "Illegal parameter provided. Please try "
                "again!\n";
                return -ret;
            }

            auto func{std::bind(ping_database)};
            return call_func(func, timeout);
        }
    }
//
// --check-device option
//
	else if (opt->getValue("check-device") != nullptr)
	{
                if (argc != 3)
                {
                    std::cerr << "Bad argument number for option --check_device" << std::endl;
                    printUsageAndExit(opt);
                }

                return check_device(opt->getValue("check-device"));
	}

//
// --check-device-exported option
//
	else if (opt->getValue("check-device-exported") != nullptr)
	{
                if (argc != 3)
                {
                    std::cerr << "Bad argument number for option --check_device" << std::endl;
                    printUsageAndExit(opt);
                }

                return check_device_exported(opt->getValue("check-device-exported"));
	}

//
// --add-server option
//


	else if (opt->getValue("add-server") != nullptr)
	{
                if (argc != 5)
                {
                        std::cerr << "Bad argument number for option --add-server" << std::endl;
                        printUsageAndExit(opt);
                }

                return add_server(opt->getValue("add-server"),opt->getArgv(0),opt->getArgv(1));
	}

//
// --check-server option
//


	else if (opt->getValue("check-server") != nullptr)
	{
                    if (argc != 3)
                    {
                        std::cerr << "Bad argument number for option --check_server" << std::endl;
                        printUsageAndExit(opt);
                    }

                    return check_server(opt->getValue("check-server"));
	}

//
// --server-list option
//


	else if (opt->getFlag("server-list") == true)
	{
            if (argc != 2)
            {
                std::cerr << "Bad argument number for option --server-list" << std::endl;
                printUsageAndExit(opt);
            }

            return server_list();
	}

//
// --server-instance-list option
//


	else if (opt->getValue("server-instance-list") != nullptr)
	{
            if (argc != 3)
            {
                std::cerr << "Bad argument number for option --server-instance-list" << std::endl;
                printUsageAndExit(opt);
            }

            return server_instance_list(opt->getValue("server-instance-list"));
	}

//
// --delete-server option
//

	else if (opt->getValue("delete-server") != nullptr)
	{
            if ((argc < 3 || argc > 4) ||
                (argc == 3 && strcmp(argv[2],"--with-properties") == 0) ||
                (strcmp(opt->getValue("delete-server"),"--with-properties") == 0))
            {
                std::cerr << "Bad option delete-server usage" << std::endl;
                printUsageAndExit(opt);
            }

            if (opt->getFlag("with-properties") == true)
                return delete_server(opt->getValue("delete-server"),true);
            else
                return  delete_server(opt->getValue("delete-server"),false);
	}

//
// --add-property option
//

	else if (opt->getValue("add-property") != nullptr)
	{
                if (argc != 5)
                {
                    std::cerr << "Bad argument number for option --add-property" << std::endl;
                    printUsageAndExit(opt);
                }

                return add_property(opt->getValue("add-property"),opt->getArgv(0),opt->getArgv(1));
	}

//
// --delete-property option
//

	else if (opt->getValue("delete-property") != nullptr)
	{
            if (argc != 4)
            {
                std::cerr << "Bad argument number for option --add-property" << std::endl;
                printUsageAndExit(opt);
            }

            return delete_property(opt->getValue("delete-property"),opt->getArgv(0));
	}

//
// --ping-network option
//
    if (opt->getFlag("ping-network") == true)
    {
        bool verbose = false;
        if (argc > 4)
        {
            std::cerr << "Bad argument number for option --ping-network\n";
            printUsageAndExit(opt);
        }
        else if (argc == 4)
        {
            if (strncmp(argv[3], "-v", 2) != 0)
            {
                std::cerr << "Bad argument for option --ping-network\n";
                printUsageAndExit(opt);
            }
            else
                verbose = true;
        }
        else if (argc == 3)
        {
            if (strncmp(argv[2], "-v", 2) == 0)
            {
                verbose = true;
            }

        }

        if (argc == 2)
        {
            auto func{std::bind(ping_network, verbose)};
            return call_func(func, 0.0);
        }
        else
        {
            double timeout{};
            const int ret{convert_string_to_double(argv[2], timeout)};
            if(ret == 0)
            {
                auto func{std::bind(ping_network, verbose)};
                return call_func(func, timeout);
            }
            else
            {
                std::cerr << "Illegal parameter provided. Please try "
                "again!\n";
                return -ret;
            }
        }
    }

//
// --tac-enabled option
//

	if (opt->getFlag("tac-enabled") == true)
	{
            if (argc > 2)
            {
                std::cerr << "Bad argument number for option --tac-enabled" << std::endl;
                printUsageAndExit(opt);
            }

            return tac_enabled();
        }

//
// --ping-device option
//


    if (opt->getValue("ping-device") != nullptr)
    {
            if (argc == 3)
            {
                auto func{std::bind(ping_device, opt->getValue("ping-device"))};
                return call_func(func, MAX_TIMEOUT);
            }
            else if (argc == 4)
            {
                double timeout{};
                const int ret{convert_string_to_double(argv[3], timeout)};
                if(ret == 0)
                {
                    auto func{std::bind(ping_device, opt->getValue("ping-device"))};
                    return call_func(func, timeout);
                }
                else
                {
                    std::cerr << "Illegal parameter provided. Please try "
                    "again!\n";
                    return -ret;
                }
            }
            else
            {
                std::cerr << "Bad argument number for option --ping_device" << std::endl;
                printUsageAndExit(opt);
            }
    }

//
// --unexport-device option
//


    if (opt->getValue("unexport-device") != nullptr)
    {
        if (argc != 3)
        {
            std::cerr << "Bad argument number for option --unexport-device" << std::endl;
            printUsageAndExit(opt);
        }

        return unexport_device(opt->getValue("unexport-device"));
    }

    return -ENOTSUP;
}

//+-------------------------------------------------------------------------
//
// method : 		ping_database
//
// description : 	This function connect to the database and executes
//					one of its command in order to check the database
//					connectivity.
//
// The function returns 0 is everything is fine. Otherwise, it returns
// -ETIMEDOUT
//
//--------------------------------------------------------------------------
int ping_database()
{
    try
    {
        Tango::Database db;
        const std::string db_info{db.get_info()};
        return 0;
    }
    catch(const Tango::DevFailed& e)
    {
        // This is OK. Just try again until we reach the timeout.
    }

    return -1;
}

//+-------------------------------------------------------------------------
//
// method : 		check_device
//
// description : 	This function checks if a device is defined in the DB
//
// argument : in : 	- name : The device name
//
// The function returns 0 is the device is defined. If we can connect to the
// database, but it is not defined, it return 1. Otherwise, it returns -1
//
//--------------------------------------------------------------------------

int check_device(const char *name)
{
	try
	{
		Tango::Database db;

		std::string d_name(name);
		Tango::DbDevImportInfo dii = db.import_device(d_name);
	}
	catch (Tango::DevFailed &e)
	{
    if (strcmp(e.errors[0].reason, "DB_DeviceNotDefined") != 0)
    {
      print_error("Database query failed", e.errors[0]);
      return 1;
    }

    return -1;
	}

  return 0;
}

//+-------------------------------------------------------------------------
//
// method : 		check_device_exported
//
// description : 	This function checks if a device is exported in the DB
//
// argument : in : 	- name : The device name
//
// The function returns 0 is the device is defined and exported. Otherwise, it returns 1.
// If it fails to query the database, it returns -1.
//
//--------------------------------------------------------------------------

int check_device_exported(const char *name)
{
    Tango::Database db;
    std::string d_name(name);

    try
    {
        Tango::DbDevImportInfo dii = db.import_device(d_name);

        if (dii.exported == 1)
        {
            return 0;
        }
        else
        {
            return 1;
        }
    }
    catch (Tango::DevFailed &e)
    {
        print_error("Database query failed", e.errors[0]);
        return -1;
    }
}

//+-------------------------------------------------------------------------
//
// method : 		add_server
//
// description : 	This function adds a server definition in the DB
//
// argument : in : 	- d_name : The device server name (exec/inst)
//					- c_name : The class name
//					- d_list : The device list
//
// The function returns 0 is everything is fine. Otherwise, it returns -1
//
//--------------------------------------------------------------------------

int add_server(char *d_name,char *c_name,char *d_list)
{
//
// Check ds name syntax
//

std::string ds_name(d_name);
	std::string::size_type pos;

	pos = ds_name.find('/');
	if ((count(ds_name.begin(),ds_name.end(),'/') != 1) || pos == 0 || pos == (ds_name.size() - 1))
	{
		std::cerr << "Wrong syntax for ds name" << std::endl;
    	return -1;
	}

//
// Check class name syntax
//

	std::string class_name(c_name);
	if (count(class_name.begin(),class_name.end(),'/') != 0)
	{
		std::cerr << "Wrong syntax for class name" << std::endl;
		return -1;
	}

//
// Check device list and device syntax
//

	std::string dev_list(d_list);
	std::vector<std::string> dev_names;

	list2vect(dev_list,dev_names);

	for (unsigned int loop = 0;loop < dev_names.size();++loop)
	{
		if (count(dev_names[loop].begin(),dev_names[loop].end(),'/') != 2)
		{
			std::cerr << "Wrong syntax for device " << dev_names[loop] << std::endl;
			return -1;
		}

		std::string::size_type pos1,pos2;
		pos1 = dev_names[loop].find('/');
		pos2 = dev_names[loop].rfind('/');

		if (pos1 == 0 || pos2 == dev_names[loop].length() - 1 || pos2 == pos1 + 1)
		{
			std::cerr << "Wrong syntax for device " << dev_names[loop] << std::endl;
      return -1;
		}
	}

//
// Create server in DB
// Dont forget to add the admin device
//

	setenv("SUPER_TANGO","true",1);

	try
	{
		Tango::Database db;

		Tango::DbDevInfos ddi;
		Tango::DbDevInfo tmp_dbi;

		for (unsigned int loop = 0;loop < dev_names.size();++loop)
		{
			tmp_dbi.name = dev_names[loop];
			tmp_dbi._class = class_name;
			tmp_dbi.server = ds_name;
			ddi.push_back(tmp_dbi);
		}
		tmp_dbi.name = "dserver/" + ds_name;
		tmp_dbi._class = "DServer";
		tmp_dbi.server = ds_name;

		ddi.push_back(tmp_dbi);

		db.add_server(ds_name,ddi);
	}
	catch (Tango::DevFailed &e)
	{
    return -1;
	}

	return 0;
}

//+-------------------------------------------------------------------------
//
// method : 		check_server
//
// description : 	This function checks if a device server is defined in the DB
//
// argument : in : 	- d_name : The device server name
//
// The function returns 0 is the device is defined. Otherwise, it returns -1
//
//--------------------------------------------------------------------------

int check_server(char *d_name)
{
	std::string dev_name = "dserver/";
	std::string ds_name = d_name;

	dev_name = dev_name + ds_name;

	return check_device((char *)dev_name.c_str());
}

//+-------------------------------------------------------------------------
//
// method : 		server_list
//
// description : 	This function lists all server names
//
// The function returns 0 if at least one server is defined. Otherwise returns -1
//
//--------------------------------------------------------------------------

int server_list()
{
	try
	{
		Tango::Database db;

		std::vector<std::string> servers;
		db.get_server_name_list() >> servers;
		for(size_t idx = 0; idx < servers.size(); ++idx)
		{
			std::cout << servers[idx] << " ";
		}
		std::cout << std::endl;
	}
	catch (Tango::DevFailed &e)
	{
    return -1;
	}

	return 0;
}

//+-------------------------------------------------------------------------
//
// method : 		server_instance_list
//
// description : 	This function lists all server instances for the given
//                      server name
//
// argument : in : 	- s_name : The server name
//
// The function returns 0 is the server name is defined. Otherwise returns -1
//
//--------------------------------------------------------------------------

int server_instance_list(char *s_name)
{
	try
	{
		Tango::Database db;

		std::string server_name = s_name;
		size_t start_pos = server_name.size() + 1;
		server_name += "/*";

		std::vector<std::string> servers;
		db.get_server_list(server_name) >> servers;
		for(size_t idx = 0; idx < servers.size(); ++idx)
		{
			std::cout << servers[idx].substr(start_pos) << " ";
		}
		std::cout << std::endl;
	}
	catch (Tango::DevFailed &e)
	{
    return -1;
	}

	return 0;
}

//+-------------------------------------------------------------------------
//
// method : 		delete_server
//
// description : 	This function deletes a device server from the DB
//
// argument : in : 	- d_name : The device server name
//					- with_res : If true, also delte device properties
//
// The function returns 0 is everything is fine. Otherwise, it returns -1
//
//--------------------------------------------------------------------------

int delete_server(char *d_name,bool with_res)
{
	std::string ds_name(d_name);

//
// Check device server name syntax
//

	std::string::size_type pos;
	pos = ds_name.find('/');

	if (pos == 0 || pos == ds_name.size() - 1 ||
		count(ds_name.begin(),ds_name.end(),'/') != 1)
	{
    return -1;
	}

	int ret = check_server(d_name);
	if (ret != 0)
		return ret;

	try
	{

		Tango::Database db;

//
// If we need to remove prop
//

		if (with_res == true)
		{

//
//	First get the ds class list
//

			Tango::DbDatum db_res = db.get_device_class_list(ds_name);
			std::vector<std::string> dev_list;
			db_res >> dev_list;

//
// Get device property name for each device
//

			for (unsigned int loop = 0;loop < dev_list.size();++loop)
			{
				std::vector<std::string> prop_list;

				db.get_device_property_list(dev_list[loop],"*",prop_list);

//
// Delete all device properties
//

				if (prop_list.empty() == false)
				{
					Tango::DbData dbd;

					for (unsigned int ctr = 0;ctr < prop_list.size();++ctr)
						dbd.push_back(Tango::DbDatum(prop_list[ctr]));

					db.delete_device_property(dev_list[loop],dbd);
				}

				++loop;
			}

		}

//
// Delete device server from db
//


		db.delete_server(ds_name);
	}
	catch (Tango::DevFailed &e)
	{
    return -1;
	}

	return 0;
}

//+-------------------------------------------------------------------------
//
// method : 		add_property
//
// description : 	This function adds a device property in the DB
//
// argument : in : 	- d_name : The device name
//					- p_name : The property name
//					- p_val : The property value
//
// The function returns 0 is everything is fine. Otherwise, it returns -1
//
//--------------------------------------------------------------------------

int add_property(char *d_name,char *p_name,char *p_val)
{
//
// Check dev name syntax
//

	std::string dev_name(d_name);
	std::string::size_type pos1,pos2;

	pos1 = dev_name.find('/');
	pos2 = dev_name.rfind('/');

	if ((count(dev_name.begin(),dev_name.end(),'/') != 2) ||
		pos1 == 0 || pos2 == (dev_name.size() - 1) || pos2 == pos1 + 1)
	{
		std::cerr << "Wrong syntax for device name" << std::endl;
    return -1;
	}

//
// Check if the device is defined
//

	if (check_device(d_name) != 0)
		return -1;

//
// Convert prop value(s) into a vector
//

	std::string prop_val(p_val);
	std::vector<std::string> prop_val_list;

	list2vect(prop_val,prop_val_list);

//
// Create server in DB
// Dont forget to add the admin device
//

	try
	{
		Tango::Database db;

		Tango::DbData dbd;
		Tango::DbDatum db_s(p_name);

		db_s << prop_val_list;
		dbd.push_back(db_s);

		db.put_device_property(dev_name,dbd);
	}
	catch (Tango::DevFailed &e)
	{
    return -1;
	}

  return 0;
}

//+-------------------------------------------------------------------------
//
// method : 		delete_property
//
// description : 	This function deletes a device property from the DB
//
// argument : in : 	- d_name : The device name
//					- p_name : The property name
//
// The function returns 0 is everything is fine. Otherwise, it returns -1
//
//--------------------------------------------------------------------------

int delete_property(char *d_name,char *p_name)
{
//
// Check dev name syntax
//

	std::string dev_name(d_name);
	std::string::size_type pos1,pos2;

	pos1 = dev_name.find('/');
	pos2 = dev_name.rfind('/');

	if ((count(dev_name.begin(),dev_name.end(),'/') != 2) ||
		pos1 == 0 || pos2 == (dev_name.size() - 1) || pos2 == pos1 + 1)
	{
		std::cerr << "Wrong syntax for device name" << std::endl;
    return -1;
	}

//
// Check if the device is defined
//

	if (check_device(d_name) != 0)
		return -1;

//
// Create server in DB
// Dont forget to add the admin device
//

	try
	{
		Tango::Database db;

		Tango::DbData dbd;
		dbd.push_back(Tango::DbDatum(p_name));

		db.delete_device_property(dev_name,dbd);
	}
	catch (Tango::DevFailed &e)
	{
    return -1;
	}

  return 0;
}

//+-------------------------------------------------------------------------
//
// method : 		list2vect
//
// description : 	This function converts a comma separated
//					device list into a vector of strings with one
//					element for each device
//
// argument : in : 	- dev_list : The device list
//					- dev_names : The device vector
//
//--------------------------------------------------------------------------

void list2vect(std::string &dev_list,std::vector<std::string> &dev_names)
{
	std::string::size_type beg,end;

	bool end_loop = false;
	beg = 0;

	while (end_loop == false)
	{
		end = dev_list.find(',',beg);
		if (end == beg)
		{
			++beg;
			continue;
		}

		if (end == std::string::npos)
		{
			end = dev_list.length();
			end_loop = true;
		}

		std::string one_dev;
		one_dev = dev_list.substr(beg,end - beg);
		dev_names.push_back(one_dev);

		beg = end + 1;
		if (beg == dev_list.size())
			end_loop = true;
	}
}

//+-------------------------------------------------------------------------
//
// method : 		ping_network
//
// description : 	This function checks the network avaibility.
//
// Parameter
//      verbose: Boolean flag set to true if some printing is required
//
// The function returns 0 on success, -1 otherwise.
//--------------------------------------------------------------------------
int ping_network(bool verbose)
{
    char buffer[80]{};
    std::string hostname;

    if (gethostname(buffer,80) == 0)
    {
        hostname = buffer;
        if (verbose == true)
            std::cout << "Host name returned by gethostname function: " << hostname << std::endl;

        struct addrinfo hints{};
        hints.ai_flags     = AI_ADDRCONFIG;
        hints.ai_family    = AF_INET;
        hints.ai_socktype  = SOCK_STREAM;

        struct addrinfo	*info{};
        struct addrinfo *ptr{};
        char tmp_host[512]{};
        int result;

        result = getaddrinfo(buffer, nullptr, &hints, &info);

        if (result == 0)
        {
            ptr = info;
            if (verbose == true)
                std::cout << "getaddrinfo() is a success" << std::endl;
            while (ptr != nullptr)
            {
                if (getnameinfo(ptr->ai_addr,ptr->ai_addrlen,tmp_host,512,0,0,0) != 0)
                {
                    if (verbose == true)
                        std::cout << "getnameinfo() call failed" << std::endl;
                    return -1;
                }
                if (verbose == true)
                    std::cout << "Host name as returned by getnameinfo call: " << tmp_host << std::endl;
                ptr = ptr->ai_next;
            };

            freeaddrinfo(info);
        }
        else
        {
            if (verbose == true)
                std::cout << "getaddrinfo() call failed with returned value = " << result << std::endl;
            return -1;
        }
    }
    else
    {
        std::cerr << "Cant retrieve server host name" << std::endl;
        return -1;
    }

    return 0;
}

//+-------------------------------------------------------------------------
//
// method : 		tac_enabled
//
// description : 	This function check in DB if the TAC is enabled
//
// The function returns 0 if the TAC is disabled. Otherwise, it returns 1
//
//--------------------------------------------------------------------------

int tac_enabled(void)
{
	setenv("SUPER_TANGO","true",1);

	try
	{
		Tango::Database db;

		std::string servicename("AccessControl");
		std::string instname("tango");
		Tango::DbDatum db_datum = db.get_services(servicename,instname);
		std::vector<std::string> service_list;
		db_datum >> service_list;

    return !service_list.empty();
	}
	catch (Tango::DevFailed &e)
	{
    return 0;
	}

  return 1;
}

//+-------------------------------------------------------------------------
//
// method : 		ping_device
//
// description : 	This function checks a device's avaibility
//
// Parameter dev_name: The device name
//
// return:  0 on sucess, -1 otherwise.
//
//--------------------------------------------------------------------------
int ping_device(const std::string &dev_name)
{
    try
    {
        Tango::DeviceProxy dev(dev_name.c_str());
        dev.ping();
    }
    catch (Tango::DevFailed &e)
    {
        return -1;
    }

    return 0;
}

//+-------------------------------------------------------------------------
//
// method : 		unexport_device
//
// description : 	Unexport a device from the database
//
// Parameter dev_name: The device name
//
// return:  0 on success, -1 otherwise.
//
//--------------------------------------------------------------------------
int unexport_device(const char *dev_name)
{
    int ret = check_device(dev_name);
    if (ret != 0)
    {
        if (ret == 1)
        {
            std::cerr << "Device \"" << dev_name << "\" not defined in the database. Cannot unexport.\n";
        }
        return -1;
    }

    Tango::Database db;

    try
    {
        db.unexport_device(dev_name);
    }
    catch (Tango::DevFailed &e)
    {
        print_error("Unexport device failed", e.errors[0]);
        return -1;
    }

    return 0;
}
