asgard/
asgard/.settings/
asgard/area/
asgard/data/clans/
asgard/data/clans/history/
asgard/data/rosters/
asgard/src/notice/
/*
 * Move notice server, multithreaded.
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>

#define BLOCK_SIZE	2048
#define min(A, B) 	((A) < (B) ? (A) : (B))

#define GREEN		""
#define YELLOW		""
#define WHITE		""
#define RESET		""

const char *notice_message =
	"\r\n\r\n"
	YELLOW"**********************************************\r\n"
	YELLOW"* "WHITE"Asgard has moved to another address.       "YELLOW"*\r\n"
	YELLOW"* "WHITE"Please update your mud client settings to: "YELLOW"*\r\n"
	YELLOW"*                                            "YELLOW"*\r\n"
	YELLOW"*       "WHITE"Hostname: "GREEN"an.dyndns-server.com       "YELLOW"*\r\n"
	YELLOW"*       "WHITE"Port:     "GREEN"14242                      "YELLOW"*\r\n"
	YELLOW"*                                            "YELLOW"*\r\n"
	YELLOW"**********************************************\r\n"RESET
	"\r\n\r\n";

int main(int argc, char **argv)
{
	int message_len = strlen(notice_message);
	int port, srv_fd, cli_fd, option;
	struct sockaddr_in srv_addr, cli_addr;
	pid_t child_pid = 0;

	if (argc < 2 || (port = atoi(argv[1])) < 1024)
	{
		fprintf(stderr, "Please provide port greater than 1024.\n");
		return EXIT_FAILURE;
	}
	
	printf("Running notice on port %d.\n", port);

	// Set up a socket.
	if ((srv_fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
	{
		perror("Call to socket() failed");
		return EXIT_FAILURE;
	}
	
	// Set reuse address so we can come up ASAP on reboot.
	option = 1;
	if (setsockopt(srv_fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&option, sizeof(option)) < 0)
	{
		perror("Call to setsockopt() failed");
		close(srv_fd);
		return EXIT_FAILURE;
	}
	
	// Set up address structure for server.
	memset(&srv_addr, 0, sizeof(srv_addr));
	srv_addr.sin_family = AF_INET;
	srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	srv_addr.sin_port = htons(port);
	
	// Bind to port.
	if (bind(srv_fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr)) < 0)
	{
		perror("Call to bind() failed");
		close(srv_fd);
		return EXIT_FAILURE;
	}
	
	// Start listening for connections.
	if (listen(srv_fd,SOMAXCONN) < 0)
	{
		perror("Call to listen() failed");
		close(srv_fd);
		return EXIT_FAILURE;
	}
	
	// Start looping, accepting connections.
	while (1)
	{
		int cli_addr_len = sizeof(cli_addr), status;
		if ((cli_fd = accept(srv_fd, (struct sockaddr *)&cli_addr, &cli_addr_len)) < 0)
		{
			perror("Call to accept() failed");
			close(srv_fd);
			return EXIT_FAILURE;
		}
		printf("Accepted connection from %s.\n", inet_ntoa(cli_addr.sin_addr));

		// Kill previous child - should be finished by then.
		if (child_pid != 0 && waitpid(child_pid, &status, 0) < 0)
		{
			perror("Call to wait() failed");
			close(cli_fd);
			close(srv_fd);
			return EXIT_FAILURE;
		}

		if ((child_pid = fork()) == 0)
		{
			// Child process, write to socket and exit.
			int written = 0;
			while (written < message_len)
			{
				int this_round = 0;
				if ((this_round = write(cli_fd, notice_message+written, min(BLOCK_SIZE, message_len-written))) < 0)
				{
					perror("Call to write() failed");
					close(cli_fd);
					_exit(1);
				}
				written += this_round;
			}
			shutdown(cli_fd, SHUT_WR);
			_exit(0);
		}
		else if (child_pid < 0)
		{
			perror("Call to fork() failed");
			close(cli_fd);
			close(srv_fd);
			return EXIT_FAILURE;
		}
	}
	
	// Close socket.
	close(srv_fd);
	return EXIT_SUCCESS;
}