/*
 * @(#)CDWserver.cpp
 *
 * This file is part of webCDwriter - Network CD Writing.
 *
 * Copyright (C) 1999-2003 Jörg P. M. Haeger
 *
 * webCDwriter 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.
 *
 * webCDwriter 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 webCDwriter; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Jörg Haeger, 17.03.1999
 * eMail: Joerg.Haeger@Uni-Bielefeld.de
 * http://wwwhomes.uni-bielefeld.de/jhaeger/
 */

#include <sys/types.h>
#include <grp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include "BufferedReader.h"
#include "Config.h"
#include "HTTPServer.h"
#include "Log.h"
#include "Server.h"
#include "ServerSocket.h"
#include "Socket.h"
#include "Thread.h"
#include "Version.h"

Config config;
Log log;

class CDWserver: public Thread {
	Socket *clientSocket;
public:
	CDWserver(Socket *clientSocket) {
		this->clientSocket = clientSocket;
	}

	~CDWserver() {
		delete clientSocket;
	}

	void run() {
		try {
			BufferedReader &reader = clientSocket->getInputStream();
			if (reader.ready(10000)) {
				HTTPServer server(*clientSocket);
				server.run();
			}
			else {
				Server server(clientSocket->getOutputStream());
				server.run();
			}
		}
		catch (Exception *e) {
			log.put(1, S.e + "Exception: " + e->getMessage());
			delete e;
		}
		catch (const char *str) {
			log.put(1, S.e + "Catched: " + str);
		}

		log.debug("threads", "terminated");
	}
};

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

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

void
sigHupHandler(int n) {
	log.put(1, "sigHupHandler: reopening the log file");
	log.restart();
}

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

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

pid_t pid = -1;

void
sigTermHandler(int sig) {
	if (getpid() == pid) {
		log.put(1, "sigTermHandler: preparing for termination");
		Server::terminate();
		signal(sig, SIG_DFL);
		raise(sig);
	}
	else {
		log.put(1, "sigTermHandler: not main thread");
		_exit(0);
	}
}

int
main(int argc, char **args) {
	int logToStdOut = 0, foreground = 0;
	for (int i = 1; i < argc; i++)
		if (strcmp(args[i], "--fg") == 0)
			foreground = 1;
		else
		if (strcmp(args[i], "--stdout") == 0)
			logToStdOut = 1;
		else {
			printf("%s %s\n", Version.product, Version.version);
			printf("%s %s\n", Version.copyright, Version.author);
			printf("Usage: %s [options]\n", args[0]);
			printf("\t--fg\t\tdo not go to the background\n");
			printf("\t--stdout\twrite the log to stdout\n");
			printf("\n");
			return 0;
		}

	if (!foreground)
		// go to the background
		if (fork() > 0)
			exit(0);

	// try to create a pid file
	pid = getpid();
	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();
	config.prepareBinaries();

	ServerSocket serverSocket(config.getPortNo());

	// try to change the group of this process
	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(logToStdOut);
	time_t t;
	time(&t);
	log.put(1, S.e + "*** "
		+ Version.product + " "
		+ Version.version + ", "
		+ ctime(&t));
	log.put(1, S.e + "uid = " + (int)getuid()
		+ ", euid = " + (int)geteuid()
		+ ", gid = " + (int)getgid()
		+ ", egid = " + (int)getegid());

	Server::initStatic();
	signal(SIGABRT, sigAbrtHandler);
	signal(SIGBUS, sigBusHandler);
	signal(SIGHUP, sigHupHandler);
	signal(SIGPIPE, sigPipeHandler);
	signal(SIGSEGV, sigSegvHandler);
	signal(SIGTERM, sigTermHandler);

	for (int i = 1; ; i++) {
		log.putv("waiting for request...");
		Socket *clientSocket = serverSocket.accept();
		log.putv("Connection %d accepted", i);

		(new CDWserver(clientSocket))->start();
	}

	return 0;
}
