
// Project: webCDwriter - Network CD Writing
// File: CDWserver.cpp
// Author: Jörg Haeger, 17.03.1999
//	Joerg.Haeger@Uni-Bielefeld.de

#include <assert.h>
#include <sys/types.h>
#include <grp.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <unistd.h>

#include "Config.h"
#include "Log.h"
#include "Server.h"

Config config;
Log log;

void *
runServer(void *arg)
{
	int clientSock = *(int *)arg;
	Server *server = new Server(clientSock);
	server->run();
	delete server;
	log.putv("terminated");
	return NULL;
}

void
sigBusHandler(int n)
{
	log.put("sigBusHandler: preparing for termination");
	_exit(0);
}

void
sigPipeHandler(int n)
{
	log.put("sigPipeHandler: broken pipe");
}

void
sigSegvHandler(int n)
{
	log.put("sigSegvHandler: preparing for termination");
	_exit(0);
}

void
sigTermHandler(int n)
{
	log.put("sigTermHandler: preparing for termination");
	Server::terminate();
	exit(0);
}

int
main(int argc, char **argv)
{
	if (argc != 2 || strcmp(argv[1], "stdout") != 0)
		// go to the background
		if (fork() > 0)
			exit(0);

	// try to create a pid file
	const char *pidFileName = getenv("pidFile");
	if (pidFileName == NULL)
		pidFileName = "/var/run/CDWserver.pid";
	FILE *pidFile = fopen(pidFileName, "w");
	if (pidFile != NULL)
	{
		fprintf(pidFile, "%d\n", getpid());
		fclose(pidFile);
	}

	config.setPermissions();

	// try to change the group of this process
	initgroups(config.getUser(), config.getGid());
	setgid(config.getGid());

	// try to change the user of this process
	setuid(config.getUid());

	// the files created by CDWserver should be unreadable
	// to normal users
	umask(S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH);

	// fix problem with mkisofs
	unsetenv("HOME");

	// start logging
	log.start(argc == 2 && strcmp(argv[1], "stdout") == 0);

	Server::initStatic();
	signal(SIGBUS, sigBusHandler);
	signal(SIGPIPE, sigPipeHandler);
	signal(SIGSEGV, sigSegvHandler);
	signal(SIGTERM, sigTermHandler);
	int sock = socket(PF_INET, SOCK_STREAM, 0);
	assert(sock >= 0);

	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	addr.sin_port = htons(config.getPortNo());
	int i;
	for (i = 0; i < 60; i++)
	{
		if (bind(sock, (struct sockaddr *)&addr, sizeof addr) == 0)
			break;
		sleep(5);
	}
	if (i == 60)
	{
		log.put("Error: bind(sock, ...");
		return 1;
	}

	assert(listen(sock, 5) == 0);

	for (i = 1; ; i++)
	{
		log.putv("waiting for request...");

		struct sockaddr_in clientAddr;
		unsigned int clientAddrLength = sizeof(clientAddr);
		int clientSock = accept(sock,
			(struct sockaddr *)&clientAddr, &clientAddrLength);
		assert(clientSock >= 0);

		log.putv("Connection %d accepted\n", i);

		pthread_t thread;
		int *copy = new int;
		*copy = clientSock;
		if (pthread_create(&thread, NULL, &runServer, copy) != 0)
			close(clientSock);
	}

	return 0;
}
