Changeset - f9a49506a1d0
[Not reviewed]
0 4 0
Jan Kaluza - 13 years ago 2012-08-29 10:39:58
hanzz.k@gmail.com
Starting/stopping instances from web-interface
4 files changed with 35 insertions and 1 deletions:
0 comments (0 inline, 0 general)
spectrum_manager/src/main.cpp
Show inline comments
 
@@ -92,100 +92,100 @@ int main(int argc, char **argv)
 
		;
 
	try
 
	{
 
		boost::program_options::positional_options_description p;
 
		p.add("command", -1);
 
		boost::program_options::store(boost::program_options::command_line_parser(argc, argv).
 
          options(desc).positional(p).run(), vm);
 
		boost::program_options::notify(vm);
 
 
		if(vm.count("help"))
 
		{
 
			std::cout << desc << "\n";
 
			return 1;
 
		}
 
	}
 
	catch (std::runtime_error& e)
 
	{
 
		std::cout << desc << "\n";
 
		return 2;
 
	}
 
	catch (...)
 
	{
 
		std::cout << desc << "\n";
 
		return 3;
 
	}
 
 
	if (!config.load(config_file)) {
 
		std::cerr << "Can't load configuration file.\n";
 
		return 4;
 
	}
 
 
	if (command.empty()) {
 
		std::cout << desc << "\n";
 
		return 1;
 
	}
 
 
	if (command[0] == "start") {
 
		start_instances(&config);
 
	}
 
	else if (command[0] == "stop") {
 
		stop_instances(&config);
 
	}
 
	else if (command[0] == "status") {
 
		return show_status(&config);
 
	}
 
	else if (command[0] == "list") {
 
		std::vector<std::string> list = show_list(&config);
 
	}
 
	else if (command[0] == "server") {
 
		Server server(&config);
 
		server.start(8080, "test", "test");
 
		while (1) { sleep(10); }
 
	}
 
	else {
 
		if (command.size() < 2) {
 
			std::cout << desc << "\n";
 
			return 11;
 
		}
 
		Swift::SimpleEventLoop eventLoop;
 
		Swift::BoostNetworkFactories networkFactories(&eventLoop);
 
 
		std::string jid = command[0];
 
		command.erase(command.begin());
 
		std::string cmd = boost::algorithm::join(command, " ");
 
 
		if (cmd == "start") {
 
			start_instances(&config, jid);
 
			return 0;
 
		}
 
		else if (cmd == "stop") {
 
			stop_instances(&config, jid);
 
			return 0;
 
		}
 
 
		ask_local_server(&config, networkFactories, jid, cmd);
 
// 		std::string message = command;
 
// 		m = &message;
 
 
// 		ask_local_server(&config, networkFactories, message);
 
 
		eventLoop.runUntilEvents();
 
 
 
		struct timeval td_start,td_end;
 
		float elapsed = 0; 
 
		gettimeofday(&td_start, NULL);
 
	
 
		time_t started = time(NULL);
 
		while(get_response().empty()) {
 
			eventLoop.runUntilEvents();
 
		}
 
		if (!get_response().empty()) {
 
			gettimeofday(&td_end, NULL);
 
			elapsed = 1000000.0 * (td_end.tv_sec -td_start.tv_sec); \
 
			elapsed += (td_end.tv_usec - td_start.tv_usec); \
 
			elapsed = elapsed / 1000 / 1000; \
 
			std::cout << "Response received after " << (elapsed) << " seconds\n";
 
// 			std::cout << "Response received after " << (elapsed) << " seconds\n";
 
		}
 
	}
 
}
spectrum_manager/src/methods.cpp
Show inline comments
 
@@ -83,266 +83,272 @@ unsigned long exec_(std::string path, std::string config, std::string jid) {
 
		else {
 
			exit(execl(path.c_str(), path.c_str(), "-j", jid.c_str(), config.c_str(), NULL));
 
		}
 
	} else if ( pid < 0 ) {
 
		// fork failed
 
	}
 
	else {
 
		waitpid(pid, 0, 0);
 
	}
 

	
 
	return (unsigned long) pid;
 
}
 

	
 
int getPort(const std::string &portfile) {
 
	path p(portfile);
 
	if (!exists(p) || is_directory(p)) {
 
		return 0;
 
	}
 

	
 
	std::ifstream f(p.string().c_str(), std::ios_base::in);
 
	std::string port;
 
	f >> port;
 

	
 
	if (port.empty())
 
		return 0;
 

	
 
	return boost::lexical_cast<int>(port);
 
}
 

	
 
int isRunning(const std::string &pidfile) {
 
	path p(pidfile);
 
	if (!exists(p) || is_directory(p)) {
 
		return 0;
 
	}
 

	
 
	std::ifstream f(p.string().c_str(), std::ios_base::in);
 
	std::string pid;
 
	f >> pid;
 

	
 
	if (pid.empty())
 
		return 0;
 

	
 
	if (kill(boost::lexical_cast<int>(pid), 0) != 0)
 
		return 0;
 

	
 
	return boost::lexical_cast<int>(pid);
 
}
 

	
 
void start_instances(ManagerConfig *config, const std::string &_jid) {
 
	path p(CONFIG_STRING(config, "service.config_directory"));
 

	
 
	try {
 
		if (!exists(p)) {
 
			std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
 
			exit(6);
 
		}
 

	
 
		if (!is_directory(p)) {
 
			std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
 
			exit(7);
 
		}
 

	
 
		std::string spectrum2_binary = searchForBinary("spectrum2");
 
		if (spectrum2_binary.empty()) {
 
			std::cerr << "spectrum2 binary not found in PATH\n";
 
			exit(8);
 
		}
 

	
 
		directory_iterator end_itr;
 
		for (directory_iterator itr(p); itr != end_itr; ++itr) {
 
			if (is_regular(itr->path()) && extension(itr->path()) == ".cfg") {
 
				Config cfg;
 
				if (cfg.load(itr->path().string()) == false) {
 
					std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
 
					continue;
 
				}
 

	
 
				
 
				std::vector<std::string> vhosts;
 
				if (CONFIG_HAS_KEY(&cfg, "vhosts.vhost"))
 
					vhosts = CONFIG_VECTOR(&cfg, "vhosts.vhost");
 
				vhosts.push_back(CONFIG_STRING(&cfg, "service.jid"));
 

	
 
				BOOST_FOREACH(std::string &vhost, vhosts) {
 
					Config vhostCfg;
 
					if (vhostCfg.load(itr->path().string(), vhost) == false) {
 
						std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
 
						continue;
 
					}
 

	
 
					if (!_jid.empty() && _jid != vhost) {
 
						continue;
 
					}
 

	
 
					int pid = isRunning(CONFIG_STRING(&vhostCfg, "service.pidfile"));
 
					if (pid == 0) {
 
						response = "Starting " + itr->path().string() + ": OK\n";
 
						std::cout << "Starting " << itr->path() << ": OK\n";
 
						exec_(spectrum2_binary, itr->path().string(), vhost);
 
					}
 
					else {
 
						response = "Starting " + itr->path().string() + ": Already started (PID=" + boost::lexical_cast<std::string>(pid) + ")\n";
 
						std::cout << "Starting " << itr->path() << ": Already started (PID=" << pid << ")\n";
 
					}
 
				}
 
			}
 
		}
 
	}
 
	catch (const filesystem_error& ex) {
 
		std::cerr << "boost filesystem error\n";
 
		exit(5);
 
	}
 
}
 

	
 
void stop_instances(ManagerConfig *config, const std::string &_jid) {
 
	path p(CONFIG_STRING(config, "service.config_directory"));
 

	
 
	try {
 
		if (!exists(p)) {
 
			std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
 
			exit(6);
 
		}
 

	
 
		if (!is_directory(p)) {
 
			std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
 
			exit(7);
 
		}
 

	
 
		directory_iterator end_itr;
 
		for (directory_iterator itr(p); itr != end_itr; ++itr) {
 
			if (is_regular(itr->path()) && extension(itr->path()) == ".cfg") {
 
				Config cfg;
 
				if (cfg.load(itr->path().string()) == false) {
 
					std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
 
				}
 

	
 
				std::vector<std::string> vhosts;
 
				if (CONFIG_HAS_KEY(&cfg, "vhosts.vhost"))
 
					vhosts = CONFIG_VECTOR(&cfg, "vhosts.vhost");
 
				vhosts.push_back(CONFIG_STRING(&cfg, "service.jid"));
 

	
 
				BOOST_FOREACH(std::string &vhost, vhosts) {
 
					Config vhostCfg;
 
					if (vhostCfg.load(itr->path().string(), vhost) == false) {
 
						std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
 
						continue;
 
					}
 

	
 
					if (!_jid.empty() && _jid != vhost) {
 
						continue;
 
					}
 

	
 
					int pid = isRunning(CONFIG_STRING(&vhostCfg, "service.pidfile"));
 
					if (pid) {
 
						response ="Stopping " + itr->path().string() + ": ";
 
						std::cout << "Stopping " << itr->path() << ": ";
 
						kill(pid, SIGTERM);
 

	
 
						sleep(1);
 
						int count = 20;
 
						while (kill(pid, 0) == 0 && count != 0) {
 
							std::cout << ".";
 
							sleep(1);
 
							count--;
 
						}
 
						if (count == 0) {
 
							response += "ERROR (timeout)\n";
 
							std::cout << " ERROR (timeout)\n";
 
						}
 
						else {
 
							response += "OK\n";
 
							std::cout << " OK\n";
 
						}
 
					}
 
					else {
 
						response = "Stopping " + itr->path().string() + ": Not running\n";
 
						std::cout << "Stopping " << itr->path() << ": Not running\n";
 
					}
 
				}
 
			}
 
		}
 
	}
 
	catch (const filesystem_error& ex) {
 
		std::cerr << "boost filesystem error\n";
 
		exit(5);
 
	}
 
}
 

	
 
int show_status(ManagerConfig *config) {
 
	int ret = 0;
 
	path p(CONFIG_STRING(config, "service.config_directory"));
 

	
 
	try {
 
		if (!exists(p)) {
 
			std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
 
			exit(6);
 
		}
 

	
 
		if (!is_directory(p)) {
 
			std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n";
 
			exit(7);
 
		}
 

	
 
		directory_iterator end_itr;
 
		for (directory_iterator itr(p); itr != end_itr; ++itr) {
 
			if (is_regular(itr->path()) && extension(itr->path()) == ".cfg") {
 
				Config cfg;
 
				if (cfg.load(itr->path().string()) == false) {
 
					std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
 
				}
 

	
 
				std::vector<std::string> vhosts;
 
				if (CONFIG_HAS_KEY(&cfg, "vhosts.vhost"))
 
					vhosts = CONFIG_VECTOR(&cfg, "vhosts.vhost");
 
				vhosts.push_back(CONFIG_STRING(&cfg, "service.jid"));
 

	
 
				BOOST_FOREACH(std::string &vhost, vhosts) {
 
					Config vhostCfg;
 
					if (vhostCfg.load(itr->path().string(), vhost) == false) {
 
						std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
 
						continue;
 
					}
 

	
 
					int pid = isRunning(CONFIG_STRING(&vhostCfg, "service.pidfile"));
 
					if (pid) {
 
						std::cout << itr->path() << ": " << vhost << " Running\n";
 
					}
 
					else {
 
						ret = 3;
 
						std::cout << itr->path() << ": " << vhost << " Stopped\n";
 
					}
 
				}
 
			}
 
		}
 
	}
 
	catch (const filesystem_error& ex) {
 
		std::cerr << "boost filesystem error\n";
 
		exit(5);
 
	}
 
	return ret;
 
}
 

	
 
static void handleDataRead(boost::shared_ptr<Swift::Connection> m_conn, boost::shared_ptr<Swift::SafeByteArray> data) {
 
	_data += std::string(data->begin(), data->end());
 

	
 
	// Parse data while there are some
 
	while (_data.size() != 0) {
 
		// expected_size of wrapper message
 
		unsigned int expected_size;
 

	
 
		// if data is >= 4, we have whole header and we can
 
		// read expected_size.
 
		if (_data.size() >= 4) {
 
			expected_size = *((unsigned int*) &_data[0]);
 
			expected_size = ntohl(expected_size);
 
			// If we don't have whole wrapper message, wait for next
 
			// handleDataRead call.
 
			if (_data.size() - 4 < expected_size)
 
				return;
 
		}
 
		else {
 
			return;
 
		}
 

	
 
		// Parse wrapper message and erase it from buffer.
 
		pbnetwork::WrapperMessage wrapper;
 
		if (wrapper.ParseFromArray(&_data[4], expected_size) == false) {
 
			std::cout << "PARSING ERROR " << expected_size << "\n";
 
			_data.erase(_data.begin(), _data.begin() + 4 + expected_size);
 
			continue;
 
		}
 
		_data.erase(_data.begin(), _data.begin() + 4 + expected_size);
spectrum_manager/src/server.cpp
Show inline comments
 
@@ -213,153 +213,179 @@ void Server::authorize(struct mg_connection *conn, const struct mg_request_info
 

	
 
	// Fetch user name and password.
 
	get_qsvar(request_info, "user", user, sizeof(user));
 
	get_qsvar(request_info, "password", password, sizeof(password));
 

	
 
	if (check_password(user, password) && (session = new_session(user)) != NULL) {
 
		std::cout << "User authorized\n";
 
		// Authentication success:
 
		//   1. create new session
 
		//   2. set session ID token in the cookie
 
		//   3. remove original_url from the cookie - not needed anymore
 
		//   4. redirect client back to the original URL
 
		//
 
		// The most secure way is to stay HTTPS all the time. However, just to
 
		// show the technique, we redirect to HTTP after the successful
 
		// authentication. The danger of doing this is that session cookie can
 
		// be stolen and an attacker may impersonate the user.
 
		// Secure application must use HTTPS all the time.
 
		mg_printf(conn, "HTTP/1.1 302 Found\r\n"
 
			"Set-Cookie: session=%s; max-age=3600; http-only\r\n"  // Session ID
 
			"Set-Cookie: user=%s\r\n"  // Set user, needed by Javascript code
 
			"Set-Cookie: original_url=/; max-age=0\r\n"  // Delete original_url
 
			"Location: /\r\n\r\n",
 
			session->session_id, session->user);
 
	} else {
 
		// Authentication failure, redirect to login.
 
		redirect_to(conn, request_info, "/login");
 
	}
 
}
 

	
 
bool Server::is_authorized(const struct mg_connection *conn, const struct mg_request_info *request_info) {
 
	Server::session *session;
 
	char valid_id[33];
 
	bool authorized = false;
 

	
 
	// Always authorize accesses to login page and to authorize URI
 
	if (!strcmp(request_info->uri, "/login") ||
 
		!strcmp(request_info->uri, "/authorize")) {
 
		return true;
 
	}
 

	
 
// 	pthread_rwlock_rdlock(&rwlock);
 
	if ((session = get_session(conn)) != NULL) {
 
		generate_session_id(valid_id, session->random, session->user);
 
		if (strcmp(valid_id, session->session_id) == 0) {
 
		session->expire = time(0) + SESSION_TTL;
 
		authorized = true;
 
		}
 
	}
 
// 	pthread_rwlock_unlock(&rwlock);
 

	
 
	return authorized;
 
}
 

	
 
void Server::redirect_to(struct mg_connection *conn, const struct mg_request_info *request_info, const char *where) {
 
	mg_printf(conn, "HTTP/1.1 302 Found\r\n"
 
		"Set-Cookie: original_url=%s\r\n"
 
		"Location: %s\r\n\r\n",
 
		request_info->uri, where);
 
}
 

	
 
void Server::print_html(struct mg_connection *conn, const struct mg_request_info *request_info, const std::string &html) {
 
	mg_printf(conn,
 
			"HTTP/1.1 200 OK\r\n"
 
			"Content-Type: text/html\r\n"
 
			"Content-Length: %d\r\n"        // Always set Content-Length
 
			"\r\n"
 
			"%s",
 
			(int) html.size(), html.c_str());
 
}
 

	
 
void Server::serve_login(struct mg_connection *conn, const struct mg_request_info *request_info) {
 
	std::string html= "\
 
<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\
 
  \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"> \
 
<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" dir=\"ltr\"> \
 
  <head>\
 
    <title>Spectrum 2 web interface</title>\
 
    <meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\"/>\
 
  </head>\
 
  <body>\
 
    <center>\
 
      <h2>Spectrum 2 web interface login</h2>\
 
      <br/>\
 
      <form action=\"/authorize\">\
 
        Username: <input type=\"text\" name=\"user\"></input><br/>\
 
        Password: <input type=\"text\" name=\"password\"></input><br/>\
 
        <input type=\"submit\" value=\"Login\"></input>\
 
      </form>\
 
    </center>\
 
  </body>\
 
</html>";
 

	
 
	print_html(conn, request_info, html);
 
}
 

	
 
void Server::serve_start(struct mg_connection *conn, const struct mg_request_info *request_info) {
 
	std::string html= get_header() ;
 
	char jid[255];
 
	get_qsvar(request_info, "jid", jid, sizeof(jid));
 

	
 
	start_instances(m_config, jid);
 
	html += "<b>" + get_response() + "</b>";
 
	html += "</body></html>";
 
	print_html(conn, request_info, html);
 
}
 

	
 
void Server::serve_stop(struct mg_connection *conn, const struct mg_request_info *request_info) {
 
	std::string html= get_header();
 
	char jid[255];
 
	get_qsvar(request_info, "jid", jid, sizeof(jid));
 

	
 
	stop_instances(m_config, jid);
 
	html += "<b>" + get_response() + "</b>";
 
	html += "</body></html>";
 
	print_html(conn, request_info, html);
 
}
 

	
 
void Server::serve_root(struct mg_connection *conn, const struct mg_request_info *request_info) {
 
	std::vector<std::string> list = show_list(m_config, false);
 
	std::string html= get_header() + "<h2>List of instances</h2><table><tr><th>JID<th>Status</th><th>Command</th></tr>";
 

	
 
	BOOST_FOREACH(std::string &instance, list) {
 
		html += "<tr>";
 
		html += "<td>" + instance + "</td>";
 
		Swift::SimpleEventLoop eventLoop;
 
		Swift::BoostNetworkFactories networkFactories(&eventLoop);
 

	
 
		ask_local_server(m_config, networkFactories, instance, "status");
 
		eventLoop.runUntilEvents();
 
		while(get_response().empty()) {
 
			eventLoop.runUntilEvents();
 
		}
 
		html += "<td>" + get_response() + "</td>";
 
		if (get_response().find("Running") == 0) {
 
			html += "<td><a href=\"/stop?jid=" + instance + "\">Stop</a></td>";
 
		}
 
		else {
 
			html += "<td><a href=\"/start?jid=" + instance + "\">Start</a></td>";
 
		}
 
		html += "</tr>";
 
	}
 

	
 
	html += "</table></body></html>";
 
	print_html(conn, request_info, html);
 
}
 

	
 
void *Server::event_handler(enum mg_event event, struct mg_connection *conn) {
 
	const struct mg_request_info *request_info = mg_get_request_info(conn);
 
	void *processed = (void *) 0x1;
 

	
 
	if (event == MG_NEW_REQUEST) {
 
		if (!is_authorized(conn, request_info)) {
 
			redirect_to(conn, request_info, "/login");
 
		} else if (strcmp(request_info->uri, "/authorize") == 0) {
 
			authorize(conn, request_info);
 
		} else if (strcmp(request_info->uri, "/login") == 0) {
 
			serve_login(conn, request_info);
 
		} else if (strcmp(request_info->uri, "/") == 0) {
 
			serve_root(conn, request_info);
 
		} else if (strcmp(request_info->uri, "/start") == 0) {
 
			serve_start(conn, request_info);
 
		} else if (strcmp(request_info->uri, "/stop") == 0) {
 
			serve_stop(conn, request_info);
 
		} else {
 
			// No suitable handler found, mark as not processed. Mongoose will
 
			// try to serve the request.
 
			processed = NULL;
 
		}
 
	} else {
 
		processed = NULL;
 
	}
 

	
 
	return processed;
 
}
 

	
 

	
 

	
 

	
spectrum_manager/src/server.h
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 
 */
 

	
 
#pragma once
 

	
 
#include <boost/program_options.hpp>
 
#include <boost/foreach.hpp>
 
#include <boost/format.hpp>
 
#include <boost/algorithm/string.hpp>
 
#include <boost/assign.hpp>
 
#include <boost/bind.hpp>
 
#include <boost/signal.hpp>
 

	
 
#include "mongoose.h"
 
#include "managerconfig.h"
 

	
 
class Server {
 
	public:
 
		struct session {
 
			char session_id[33];      // Session ID, must be unique
 
			char random[20];          // Random data used for extra user validation
 
			char user[255];  // Authenticated user
 
			time_t expire;            // Expiration timestamp, UTC
 
		};
 

	
 
		/// Constructor.
 
		Server(ManagerConfig *config);
 

	
 
		/// Destructor
 
		virtual ~Server();
 

	
 
		bool start(int port, const std::string &user, const std::string &password);
 

	
 
		void *event_handler(enum mg_event event, struct mg_connection *conn);
 

	
 
	private:
 
		void serve_login(struct mg_connection *conn, const struct mg_request_info *request_info);
 
		void serve_root(struct mg_connection *conn, const struct mg_request_info *request_info);
 
		void serve_start(struct mg_connection *conn, const struct mg_request_info *request_info);
 
		void serve_stop(struct mg_connection *conn, const struct mg_request_info *request_info);
 
		void print_html(struct mg_connection *conn, const struct mg_request_info *request_info, const std::string &html);
 

	
 
	private:
 
		bool check_password(const char *user, const char *password);
 
		session *new_session(const char *user);
 
		session *get_session(const struct mg_connection *conn);
 

	
 
		void authorize(struct mg_connection *conn, const struct mg_request_info *request_info);
 

	
 
		bool is_authorized(const struct mg_connection *conn, const struct mg_request_info *request_info);
 

	
 
		void redirect_to(struct mg_connection *conn, const struct mg_request_info *request_info, const char *where);
 

	
 
	private:
 
		struct mg_context *ctx;
 
		std::map<std::string, session *> sessions;
 
		std::string m_user;
 
		std::string m_password;
 
		ManagerConfig *m_config;
 
};
0 comments (0 inline, 0 general)