/*
 * Copyright (C) 2000 Lennert Buytenhek
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <signal.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <asm/param.h>
#include "libbridge.h"
#include "brctl.h"

char *help_message =
"addbr\t\t\t\t\tadd bridge\n"
"addif\t\t\t<device>\tadd interface to bridge\n"
"bridge\t\t\t<bridge>\tselect bridge to work in\n"
"delbr\t\t\t\t\tdelete bridge\n"
"delif\t\t\t<device>\tdelete interface from bridge\n"
"setageing\t\t<time>\t\tset ageing time\n"
"setbridgeprio\t\t<prio>\t\tset bridge priority\n"
"setfd\t\t\t<time>\t\tset bridge forward delay\n"
"setgcint\t\t<time>\t\tset garbage collection interval\n"
"sethello\t\t<time>\t\tset hello time\n"
"setmaxage\t\t<time>\t\tset max message age\n"
"setpathcost\t\t<port> <cost>\tset path cost\n"
"setportprio\t\t<port> <prio>\tset port priority\n"
"show\t\t\t\t\tshow a list of bridges\n"
"showmacs\t\t\t\tshow a list of mac addrs\n"
"showstp\t\t\t\t\tshow bridge stp info\n"
"stp\t\t\t<state>\t\tturn stp on/off\n"
"quit\t\t\t\t\texit this session\n"
"\n";

void help()
{
	fprintf(stderr, help_message);
}

struct bridge *br = NULL;

int forkaway()
{
	int f;

	f = fork();
	if (f < 0) {
		perror("fork");
		exit(-1);
	}

	return f;
}

void runchild(int sock)
{
	char hostname[128];

	if (forkaway())
		return;

	/* Hack. */
	close(0); dup(sock);
	close(1); dup(sock);
	close(2); dup(sock);

	br_init();
	gethostname(hostname, 128);

	printf("\n\n\n\n");
	printf("brctld\t\tCopyright (C) 2000 Lennert Buytenhek <buytenh@gnu.org>\n");
	printf("======================================================================");
	printf("\n\n\n\n");

	while (1) {
		char arg0[128];
		char arg1[128];
		char cmd[128];
		struct command *cmdptr;
		char line[1024];
		int numcmd;

		printf("<%s> ", hostname);
		fflush(stdout);
		line[1023] = 0;
		fgets(line, 1023, stdin);
		while (strlen(line) > 0 &&
		       (line[strlen(line)-1] == '\r' ||
			line[strlen(line)-1] == '\n'))
			line[strlen(line)-1] = 0;

		numcmd = sscanf(line, "%s %s %s", cmd, arg0, arg1);

		if (!strcmp(cmd, "help")) {
			help();
			continue;
		} else if (!strcmp(cmd, "bridge")) {
			if (numcmd != 2) {
				fprintf(stderr, "invalid number of arguments\n");
				continue;
			}

			br = br_find_bridge(arg0);
			if (br != NULL)
				printf("now using bridge %s\n\n", arg0);
			else
				printf("can't find bridge %s\n\n", arg0);
			continue;
		} else if (!strcmp(cmd, "quit")) {
			break;
		}

		if ((cmdptr = br_command_lookup(cmd)) == NULL) {
			printf("unknown command '%s'\n\n", line);
			continue;
		}

		if (cmdptr->needs_bridge_argument && br == NULL) {
			printf("this command needs a bridge\n\n");
			continue;
		}

		br_refresh();
		cmdptr->func(br, arg0, arg1);
		printf("\n");
	}

	shutdown(sock, 2);
	exit(0);
}

void sigchild(int sig)
{
	int status;

	wait3(&status, WNOHANG, NULL);
}

int main(int argc, char *argv[])
{
	struct sockaddr_in addr;
	int sock;
	int x;

	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("socket");
		return 1;
	}

	x = 1;
	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x)) < 0) {
		perror("setsockopt");
		return 1;
	}

	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	addr.sin_port = htons(31338);
	if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
		perror("bind");
		return 1;
	}

	if (listen(sock, 1) < 0) {
		perror("listen");
		return 1;
	}

	if (forkaway())
		return 0;

	setsid();
	signal(SIGCHLD, sigchild);

	while (1) {
		int len;
		int newsock;

		len = sizeof(addr);
		if ((newsock = accept(sock, (struct sockaddr *)&addr, &len)) < 0) {
			perror("accept");
			return 1;
		}

		runchild(newsock);
	}

	return 0;
}