syndilights/open-lighting-architecture/ola-0.8.4/olad/OlaServer.cpp

435 lines
12 KiB
C++

/*
* 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 Library 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.
*
* OlaServer.cpp
* OlaServer is the main OLA Server class
* Copyright (C) 2005-2008 Simon Newton
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <map>
#include <vector>
#include "common/protocol/Ola.pb.h"
#include "common/rpc/StreamRpcChannel.h"
#include "ola/BaseTypes.h"
#include "ola/ExportMap.h"
#include "ola/Logging.h"
#include "ola/network/InterfacePicker.h"
#include "ola/rdm/UID.h"
#include "olad/Client.h"
#include "olad/DeviceManager.h"
#include "olad/InternalRDMController.h"
#include "olad/OlaServer.h"
#include "olad/OlaServerServiceImpl.h"
#include "olad/Plugin.h"
#include "olad/PluginAdaptor.h"
#include "olad/PluginManager.h"
#include "olad/Port.h"
#include "olad/PortManager.h"
#include "olad/Preferences.h"
#include "olad/Universe.h"
#include "olad/UniverseStore.h"
#ifdef HAVE_LIBMICROHTTPD
#include "olad/OlaHttpServer.h"
#endif
namespace ola {
using ola::rpc::StreamRpcChannel;
using std::pair;
const char OlaServer::UNIVERSE_PREFERENCES[] = "universe";
const char OlaServer::K_CLIENT_VAR[] = "clients-connected";
const char OlaServer::K_UID_VAR[] = "server-uid";
const unsigned int OlaServer::K_HOUSEKEEPING_TIMEOUT_MS = 1000;
/*
* Create a new OlaServer
* @param factory the factory to use to create OlaService objects
* @param m_plugin_loader the loader to use for the plugins
* @param socket the socket to listen on for new connections
*/
OlaServer::OlaServer(OlaServerServiceImplFactory *factory,
const vector<PluginLoader*> &plugin_loaders,
PreferencesFactory *preferences_factory,
ola::network::SelectServer *select_server,
ola_server_options *ola_options,
ola::network::AcceptingSocket *socket,
ExportMap *export_map)
: m_service_factory(factory),
m_plugin_loaders(plugin_loaders),
m_ss(select_server),
m_accepting_socket(socket),
m_device_manager(NULL),
m_plugin_manager(NULL),
m_plugin_adaptor(NULL),
m_preferences_factory(preferences_factory),
m_universe_preferences(NULL),
m_universe_store(NULL),
m_export_map(export_map),
m_port_manager(NULL),
m_reload_plugins(false),
m_init_run(false),
m_free_export_map(false),
m_housekeeping_timeout(ola::network::INVALID_TIMEOUT),
m_httpd(NULL),
m_options(*ola_options),
m_rdm_controller(NULL) {
if (!m_export_map) {
m_export_map = new ExportMap();
m_free_export_map = true;
}
if (!m_options.http_port)
m_options.http_port = DEFAULT_HTTP_PORT;
m_export_map->GetIntegerVar(K_CLIENT_VAR);
}
/*
* Shutdown the server
*/
OlaServer::~OlaServer() {
#ifdef HAVE_LIBMICROHTTPD
if (m_httpd) {
m_httpd->Stop();
delete m_httpd;
m_httpd = NULL;
}
#endif
if (m_housekeeping_timeout != ola::network::INVALID_TIMEOUT)
m_ss->RemoveTimeout(m_housekeeping_timeout);
StopPlugins();
// this will remove any input ports and fail any outstanding rdm requests
if (m_rdm_controller)
delete m_rdm_controller;
map<int, OlaServerServiceImpl*>::iterator iter;
for (iter = m_sd_to_service.begin(); iter != m_sd_to_service.end(); ++iter) {
CleanupConnection(iter->second);
// TODO(simon): close the socket here
/*Socket *socket = ;
m_ss->RemoveSocket(socket);
socket->Close();
*/
}
if (m_accepting_socket
&& m_accepting_socket->ReadDescriptor() != Socket::INVALID_SOCKET)
m_ss->RemoveSocket(m_accepting_socket);
if (m_universe_store) {
m_universe_store->DeleteAll();
delete m_universe_store;
}
if (m_universe_preferences) {
m_universe_preferences->Save();
}
delete m_port_manager;
delete m_plugin_adaptor;
delete m_device_manager;
delete m_plugin_manager;
if (m_free_export_map)
delete m_export_map;
}
/*
* Initialise the server
* * @return true on success, false on failure
*/
bool OlaServer::Init() {
if (m_init_run)
return false;
if (!m_service_factory || !m_ss)
return false;
// TODO(simon): run without preferences & PluginLoader
if (!m_plugin_loaders.size() || !m_preferences_factory)
return false;
if (m_accepting_socket) {
if (!m_accepting_socket->Listen())
return false;
m_accepting_socket->SetOnData(
ola::NewClosure(this, &OlaServer::AcceptNewConnection,
m_accepting_socket));
m_ss->AddSocket(m_accepting_socket);
}
signal(SIGPIPE, SIG_IGN);
// fetch the interface info
ola::rdm::UID default_uid(OPEN_LIGHTING_ESTA_CODE, 0);
ola::network::Interface interface;
ola::network::InterfacePicker *picker =
ola::network::InterfacePicker::NewPicker();
if (!picker->ChooseInterface(&interface, "")) {
OLA_WARN << "No network interface found";
} else {
// default to using the ip as a id
default_uid = ola::rdm::UID(OPEN_LIGHTING_ESTA_CODE,
interface.ip_address.s_addr);
}
delete picker;
m_export_map->GetStringVar(K_UID_VAR)->Set(default_uid.ToString());
OLA_INFO << "Server UID is " << default_uid;
m_universe_preferences = m_preferences_factory->NewPreference(
UNIVERSE_PREFERENCES);
m_universe_preferences->Load();
m_universe_store = new UniverseStore(m_universe_preferences, m_export_map);
m_port_manager = new PortManager(m_universe_store);
m_rdm_controller = new InternalRDMController(default_uid,
m_port_manager,
m_export_map);
// setup the objects
m_device_manager = new DeviceManager(m_preferences_factory, m_port_manager);
m_plugin_adaptor = new PluginAdaptor(m_device_manager,
m_ss,
m_preferences_factory);
m_plugin_manager = new PluginManager(m_plugin_loaders, m_plugin_adaptor);
if (!m_universe_store || !m_device_manager || !m_plugin_adaptor ||
!m_port_manager || !m_plugin_manager) {
delete m_plugin_adaptor;
delete m_device_manager;
delete m_port_manager;
delete m_universe_store;
delete m_plugin_manager;
return false;
}
m_plugin_manager->LoadAll();
#ifdef HAVE_LIBMICROHTTPD
if (!StartHttpServer(interface))
OLA_WARN << "Failed to start the HTTP server.";
#endif
m_housekeeping_timeout = m_ss->RegisterRepeatingTimeout(
K_HOUSEKEEPING_TIMEOUT_MS,
ola::NewClosure(this, &OlaServer::RunHousekeeping));
m_ss->RunInLoop(ola::NewClosure(this, &OlaServer::CheckForReload));
m_init_run = true;
return true;
}
/*
* Reload all plugins, this can be called from a separate thread or in an
* interrupt handler.
*/
void OlaServer::ReloadPlugins() {
m_reload_plugins = true;
}
/*
* Add a new ConnectedSocket to this Server.
* @param accepting_socket the AcceptingSocket with the new connection pending.
*/
void OlaServer::AcceptNewConnection(
ola::network::AcceptingSocket *accepting_socket) {
ola::network::ConnectedSocket *socket = accepting_socket->Accept();
if (socket)
NewConnection(socket);
}
/*
* Add a new ConnectedSocket to this Server.
* @param socket the new ConnectedSocket
*/
bool OlaServer::NewConnection(ola::network::ConnectedSocket *socket) {
if (!socket)
return false;
StreamRpcChannel *channel = new StreamRpcChannel(NULL, socket, m_export_map);
socket->SetOnClose(NewSingleClosure(this, &OlaServer::SocketClosed, socket));
OlaClientService_Stub *stub = new OlaClientService_Stub(channel);
Client *client = new Client(stub);
OlaServerServiceImpl *service = m_service_factory->New(m_universe_store,
m_device_manager,
m_plugin_manager,
client,
m_export_map,
m_port_manager,
m_rdm_controller,
m_ss->WakeUpTime());
channel->SetService(service);
map<int, OlaServerServiceImpl*>::const_iterator iter;
iter = m_sd_to_service.find(socket->ReadDescriptor());
if (iter != m_sd_to_service.end())
OLA_INFO << "New socket but the client already exists!";
pair<int, OlaServerServiceImpl*> pair(socket->ReadDescriptor(), service);
m_sd_to_service.insert(pair);
// This hands off ownership to the select server
m_ss->AddSocket(socket, true);
(*m_export_map->GetIntegerVar(K_CLIENT_VAR))++;
return 0;
}
/*
* Called when a socket is closed
*/
void OlaServer::SocketClosed(ola::network::ConnectedSocket *socket) {
map<int, OlaServerServiceImpl*>::iterator iter;
iter = m_sd_to_service.find(socket->ReadDescriptor());
if (iter == m_sd_to_service.end())
OLA_INFO << "A socket was closed but we didn't find the client";
(*m_export_map->GetIntegerVar(K_CLIENT_VAR))--;
CleanupConnection(iter->second);
m_sd_to_service.erase(iter);
}
/*
* Run the garbage collector
*/
bool OlaServer::RunHousekeeping() {
OLA_DEBUG << "Garbage collecting";
m_universe_store->GarbageCollectUniverses();
m_rdm_controller->CheckTimeouts(*m_ss->WakeUpTime());
return true;
}
/*
* Called once per loop iteration
*/
void OlaServer::CheckForReload() {
if (m_reload_plugins) {
m_reload_plugins = false;
OLA_INFO << "Reloading plugins";
StopPlugins();
m_plugin_manager->LoadAll();
}
}
/*
* Setup the HTTP server if required.
* @param interface the primary interface that the server is using.
*/
#ifdef HAVE_LIBMICROHTTPD
bool OlaServer::StartHttpServer(const ola::network::Interface &interface) {
if (!m_options.http_enable)
return true;
// create a pipe socket for the http server to communicate with the main
// server on.
ola::network::PipeSocket *socket = new ola::network::PipeSocket();
if (!socket->Init()) {
delete socket;
return false;
}
// ownership of the socket is transferred here.
m_httpd = new OlaHttpServer(m_export_map,
socket->OppositeEnd(),
this,
m_options.http_port,
m_options.http_enable_quit,
m_options.http_data_dir,
interface);
if (m_httpd->Init()) {
m_httpd->Start();
// register the pipe socket as a client
NewConnection(socket);
return true;
} else {
socket->Close();
delete socket;
delete m_httpd;
m_httpd = NULL;
return false;
}
}
#endif
/*
* Stop and unload all the plugins
*/
void OlaServer::StopPlugins() {
if (m_plugin_manager)
m_plugin_manager->UnloadAll();
if (m_device_manager) {
if (m_device_manager->DeviceCount()) {
OLA_WARN << "Some devices failed to unload, we're probably leaking "
<< "memory now";
}
m_device_manager->UnregisterAllDevices();
}
}
/*
* Cleanup everything related to a client connection
*/
void OlaServer::CleanupConnection(OlaServerServiceImpl *service) {
Client *client = service->GetClient();
vector<Universe*> universe_list;
m_universe_store->GetList(&universe_list);
vector<Universe*>::iterator uni_iter;
// O(universes * clients). Clean this up sometime.
for (uni_iter = universe_list.begin();
uni_iter != universe_list.end();
++uni_iter) {
(*uni_iter)->RemoveSourceClient(client);
(*uni_iter)->RemoveSinkClient(client);
}
delete client->Stub()->channel();
delete client->Stub();
delete client;
delete service;
}
} // ola