#ifndef _daemon
#define _daemon

/*

Copyright (C) 2011.  All rights reserved.
This software is the intellectual property of Matthew William Coan.


Author: Matthew William Coan
Date: Sat Dec 22 00:35:37 EST 2012

*/

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include <ctime>

#include <unistd.h>
#include <signal.h>

namespace daemon_tools {

using namespace std;

bool running = true;

void
on_signal(int arg)
{
   running = false;
}

struct log_file_exception { };

extern void daemon_start(int code);

class LogFile {
    string name;
    ofstream fout;
public:
    LogFile(const string & n) 
    : name(n), fout((n + string(".txt")).c_str(), ios::app | ios::out | ios::ate) 
    { if(!fout) throw log_file_exception(); }

    ~LogFile() { fout.close(); }

    void log(const string & message) {
       time_t t = time(NULL);
       fout << "\"" << ctime(&t) << "\",\"" << message << "\"" << endl;
    }
};

class Daemon {
   string name;
   LogFile logfile;

public:
   Daemon(const string & name2) :name(name2), logfile(name2) { }
   ~Daemon() { }

   virtual void main_loop() {
      while(running) {
         system("sleep 1");
      }
   }

   void run() {
#ifdef HAVE_DAEMON
      daemon(0,0);
#endif
#ifndef HAVE_DAEMON
      daemon_start(0);
#endif
      signal(SIGHUP, on_signal);
      signal(SIGTERM, on_signal);
      main_loop(); 
   }
};


#ifndef HAVE_DAEMON
/*
 * Initialize a daemon process.
 */

#include	<stdio.h>
#include	<signal.h>
#include	<sys/param.h>
#include	<sys/stat.h>
#include	<errno.h>
extern int	errno;

#ifdef	SIGTSTP		/* true if BSD system */
#include	<sys/file.h>
#include	<sys/ioctl.h>
#endif

/*
 * Detach a daemon process from login session context.
 */

int
err_sys(const char * message) {
   cerr << "error: " << message << endl;
   exit(1);
}

void
sig_child(int sig) 
{
}

inline
void
daemon_start(int ignsigcld)
/*int	ignsigcld;	
*/
/* nonzero -> handle SIGCLDs so zombies don't clog */
{
	register int	childpid, fd;

	/*
	 * If we were started by init (process 1) from the /etc/inittab file
	 * there's no need to detach.
	 * This test is unreliable due to an unavoidable ambiguity
 	 * if the process is started by some other process and orphaned
	 * (i.e., if the parent process terminates before we are started).
  	 */

	if (getppid() == 1)
  		goto out;

	/*
	 * Ignore the terminal stop signals (BSD).
	 */

#ifdef SIGTTOU
	signal(SIGTTOU, SIG_IGN);
#endif
#ifdef SIGTTIN
	signal(SIGTTIN, SIG_IGN);
#endif
#ifdef SIGTSTP
	signal(SIGTSTP, SIG_IGN);
#endif

	/*
	 * If we were not started in the background, fork and
	 * let the parent exit.  This also guarantees the first child
	 * is not a process group leader.
	 */

	if ( (childpid = fork()) < 0)
		err_sys("can't fork first child");
	else if (childpid > 0)
		exit(0);	/* parent */

	/*
	 * First child process.
	 *
	 * Disassociate from controlling terminal and process group.
	 * Ensure the process can't reacquire a new controlling terminal.
	 */

#ifdef	SIGTSTP		/* BSD */

/*
	if (setpgrp(0, getpid()) == -1)
		err_sys("can't change process group");
*/
	if (setpgrp() == -1)
		err_sys("can't change process group");

	if ( (fd = open("/dev/tty", O_RDWR)) >= 0) {
		ioctl(fd, TIOCNOTTY, (char *)NULL); /* lose controlling tty */
		close(fd);
	}

#else	/* System V */

	if (setpgrp() == -1)
		err_sys("can't change process group");

	signal(SIGHUP, SIG_IGN);	/* immune from pgrp leader death */

	if ( (childpid = fork()) < 0)
		err_sys("can't fork second child");
	else if (childpid > 0)
		exit(0);	/* first child */

	/* second child */
#endif

out:
	/*
	 * Close any open files descriptors.
	 */

	for (fd = 0; fd < NOFILE; fd++)
		close(fd);

	errno = 0;		/* probably got set to EBADF from a close */

	/*
	 * Move the current directory to root, to make sure we
	 * aren't on a mounted filesystem.
	 */

	chdir("/");

	/*
	 * Clear any inherited file mode creation mask.
	 */

	umask(0);

	/*
	 * See if the caller isn't interested in the exit status of its
	 * children, and doesn't want to have them become zombies and
	 * clog up the system.
	 * With System V all we need do is ignore the signal.
	 * With BSD, however, we have to catch each signal
	 * and execute the wait3() system call.
	 */

	if (ignsigcld) {
#ifdef	SIGTSTP
		signal(SIGCHLD, sig_child);	/* BSD */
#else
		signal(SIGCLD, SIG_IGN);	/* System V */
#endif
	}
}

#endif /* HAVE_DAEMON */
}


#endif /* _daemon */
