
/*
 * master.cc
 * Copyright (C) 2002 by John Heidemann <johnh@isi.edu>
 * $Id: master.cc,v 1.9 2002/04/05 21:36:56 johnh Exp $
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 * 
 * 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.
 *
 */

/*
 * master.cc:
 * Implements manager functionality.
 * Read the config file, fork the routers, etc.
 */

#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>  // for sscanf
#include <unistd.h>  // for read et al

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


#include "master.hh"
#include "main.hh"
#include "msg.hh"
#include "router.hh"


void
MasterRouter::assign(int fd, int pid, int udp_port)
{
	fd_ = fd;
	pid_ = pid;
	udp_port_ = udp_port;
}


void
Master::add_edge(int a, int b)
{
	// xxx: doesn't check for duplicates
	edges_.push_back(Edge(a, b));
	edges_.push_back(Edge(b, a));
}

MasterRouter *
Master::find_or_make_router(int id)
{
	MasterRouter *router;
	map<int, MasterRouter*>::iterator router_it = routers_.find(id);
	if (router_it == routers_.end()) {
		// make a new one
		// cerr << "Master::find_or_make_router: making router " << id << endl;
		router = new MasterRouter(id);
		assert(router != NULL);
		routers_[id] = router;
	} else {
		// return the old one
		router = router_it->second;
	}
	return router;
}

void
Master::add_internal_route(int id, Route *rte)
{
	MasterRouter *router = find_or_make_router(id);
	assert(router);
	router->add_internal_route(rte);
}

void
Master::add_external_route(int peer, Route *rte)
{
	MasterRouter *router = find_or_make_router(peer);
	assert(router);
	router->add_external_route(rte);
	any_external_routes_ = true;
}


/*
 * Handle all parsing of the input file.
 */
void
Master::input_model(istream& instr)
{
	const int LINELEN = 256;
	char line[LINELEN];
	// set up a simple state table about where we are in the input file
	enum modes { STAGE, NUMNODES, LOSSRATE, EDGES, INTADDRS, EXTADDRS, DONE };
	modes mode = STAGE;
	int a, b, c, d, mask, peer, external, med;
	int internal_addr_count = 1;

	while (mode != DONE) {
		instr.getline(line, LINELEN);
		if (line[0] == '#')
			continue;
		if (line[0] == '\n' || line[0] == 0) // skip blank lines
			continue;
		// cout << line << endl;
		switch (mode) {
		case STAGE:
                        // sigh, more sscanf hackery (see comment below)
                        assert(1==sscanf(line, "stage%d", &a));
                        set_stage(a);
                        mode = NUMNODES;
                        break;
		case NUMNODES:
			num_nodes(atoi(line));
			mode = LOSSRATE;
			break;
		case LOSSRATE:
			loss_rate(atoi(line));
			mode = EDGES;
			break;
		case EDGES:
			/*
			 * xxx: mixing sscanf and C++ is yucky
			 * (it's not idomatic C++).
			 * Better would be to use C++ string
			 * parsing code.
			 */
			assert (2==sscanf(line, "%d %d", &a, &b));
			if (a == 0 && b == 0) {
				mode = INTADDRS;
				break;
			};
			add_edge(a, b);
			break;
		case INTADDRS:
			assert (5==sscanf(line, "%d.%d.%d.%d %d", &a, &b, &c, &d, &mask));
			if (a == 0 && b == 0 && c == 0 && d == 0) {
				mode = EXTADDRS;
				break;
			};
			add_internal_route(internal_addr_count,
					   new Route(new AddrPrefix(a,b,c,d,mask), internal_addr_count, 0));
			internal_addr_count++;
			break;
		case EXTADDRS:
			assert (8==sscanf(line, "%d %d %d.%d.%d.%d %d %d", &peer, &external, &a, &b, &c, &d, &mask, &med));
			if (peer == 0 && a == 0 && b == 0 && c == 0 && d == 0) {
				mode = DONE;
				break;
			};
			add_external_route(peer,
					   new Route(new AddrPrefix(a,b,c,d,mask),
						     external,
						     peer,
						     0,
						     med));
			break;
		case DONE:
			die("contentful lines in config file after all sections parsed.\n");
			break;
		};
	};
}

void
Master::create_master_tcp()
{
	// cerr << "Master::create_master_tcp\n";

	assert(-1 != (master_socket_ = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)));

	// get address
	struct sockaddr_in sa;
	memset(&sa, 0, sizeof(sa));
	sa.sin_family = AF_INET;
	struct hostent *he = gethostbyname("localhost");
	assert(he != NULL);
	memcpy(&sa.sin_addr, he->h_addr, he->h_length);
	master_port_ = 0;
	sa.sin_port = htons(master_port_);

	// allow addr reuse to ease debugging
	// (not needed with dynamic assignment)
	// int opt = 1;
	// assert (-1 != setsockopt(master_socket_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)));
	assert(-1 != (bind(master_socket_, (struct sockaddr *)&sa, sizeof(sa))));
	// read back our port number
	socklen_t sa_len = sizeof(sa);
	assert(-1 != getsockname(master_socket_,  (struct sockaddr *) &sa, &sa_len));
	master_port_ = ntohs(sa.sin_port);
	assert(master_port_ != 0);

	assert(-1 != listen(master_socket_, num_nodes_));
}

void
Master::create_router_processes()
{
	// cerr << "Master::create_router_processes\n";
	for (int i = 0; i < num_nodes_; i++) {
		pid_t pid = fork();
		assert(pid != -1);
		if (pid == 0) {
			cerr << "router " << i << " " << getpid() << endl;
			close(master_socket_);
			// child: become a router
			RealRouter((int) master_port_);
			exit(0);
		} else {
			// parent, keep going
		};

	};
}

void
Master::output_stage_1()
{
	ofstream outstr("s1.out");
	assert(outstr.good());
	for (RouterMap::iterator router_it = routers_.begin();
	     router_it != routers_.end();
	     router_it++) {
		outstr << router_it->second->id() << " "
		     << router_it->second->pid() << " "
		     << *(router_it->second->internal_route()->ap()) << " "
		     << router_it->second->port() << endl;
	};
}

void
Master::loop()
{
	// cerr << "Master::loop\n";

	/*
	 * First, establish communication with all routers.
	 *
	 * This is not robust to router failure/absence,
	 * but that's OK for a class project.
	 */
	int routers_left = num_nodes_;
	while (routers_left > 0) {
		struct sockaddr_in other_end_addr;
		socklen_t addrlen = sizeof(other_end_addr);

		int fd = accept(master_socket_, (struct sockaddr*)&other_end_addr, &addrlen);
		assert(fd != -1);

		router_fds_.push_back(fd);
		routers_left--;
	};
	assert((int)router_fds_.size() == num_nodes_);  // double check

	/*
	 * Everyone's present, now interrogate them.
	 *
	 * Ideally we should do this asynchronously,
	 * but don't bother.
	 */
	RouterMap::iterator next_router_it = routers_.begin();
	for (list<int>::iterator router_fd_it = router_fds_.begin();
	     router_fd_it != router_fds_.end();
	     router_fd_it++) {
		RouterReport report;
		int read_len = read(*router_fd_it,
				    &report,
				    sizeof(report));
		// a robust implementation would allow for partial reads
		assert(read_len == sizeof(report));

		// pick a router and assign it
		assert(next_router_it != routers_.end());
		MasterRouter *router = next_router_it->second;
		next_router_it++;
		router->assign(*router_fd_it,
			      report.pid_,
			      report.udp_port_);
	};
	if (next_router_it != routers_.end()) {
		cerr << "    routers_.size() " << routers_.size() << endl;
		cerr << "    router_fds_.size() " << router_fds_.size() << endl;
		assert(next_router_it == routers_.end());  // got them all
	};

        // sanity check
        if (any_external_routes_)
                assert(stage_ >= 4);
        assert(stage_ <= 4);


	/*
	 * Talked to everyone, so now tell them about their neighbors.
	 *
	 * ggg: An error would be to expiclity tell them 
	 * anything other than their neighbor's ports.
	 */
	for (RouterMap::iterator router_it = routers_.begin();
	     router_it != routers_.end(); 
	     router_it++) {
		MasterRouter *router = router_it->second;
		RouterConfig config;
		config.stage_ = stage_; 
		config.id_ = router->id();
		config.address_ = router->internal_route()->addr();
		config.prefix_ = router->internal_route()->prefix();
		config.num_nodes_ = num_nodes_;
		config.loss_rate_ = loss_rate_;
		config.num_neighbors_ = 0;
		// build the list of neighbors ports (sigh, O(n^2))
		for (list<Edge>::iterator edge_it = edges_.begin();
		     edge_it != edges_.end();
		     edge_it++) {
			if (edge_it->a() == router->id()) {
				// find the other port
				MasterRouter *other_router = routers_[edge_it->b()];
				int i = config.num_neighbors_++;
				config.neighbor_ids_[i] = other_router->id();					
				config.neighbor_ports_[i] = other_router->port();					
				assert(config.num_neighbors_ < RouterConfig::MAX_NEIGHBORS);
			};
		};
		// build the list of external routes, if any
		config.num_externals_ = 0;
		for (list<Route*>::iterator rte_it = router->external_routes().begin();
		     rte_it != router->external_routes().end();
		     rte_it++) {
			assert(config.num_externals_ < RouterConfig::MAX_EXTERNALS);
			int i = config.num_externals_++;
			config.external_ids_[i] = (*rte_it)->origin_id();
			config.external_meds_[i] = (*rte_it)->med();
			config.external_addrs_[i] = (*rte_it)->addr();
			config.external_prefixes_[i] = (*rte_it)->prefix();
		};
		// send it off
		int out_len = write(router->fd(), &config, sizeof(config));
		assert (out_len == sizeof(config));
	};
        if (stage_ == 1) {
                output_stage_1();
                return;
        }

	/*
	 * xxx: could hang round for termination,
	 * but we don't.
	 * (We'll need to do more for Project B.)
	 */
}


Master::Master(istream &instr) :
		num_nodes_(0), loss_rate_(0), any_external_routes_(false)
{
	input_model(instr);

	create_master_tcp();
	create_router_processes();

	loop();
}
