<?php

// Logowanie
define('DLOG_TO_CONSOLE', 1);
define('DLOG_NOTICE', 2);
define('DLOG_WARNING', 4);
define('DLOG_ERROR', 8);
define('DLOG_CRITICAL', 16);

/**
 * Daemon
 *
 * Wymagania:
 * Unix
 * PHP 4 >= 4.3.0 or PHP 5
 * PHP z:
 * --enable-sigchild
 * --enable-pcntl
 */
class Daemon
{

	private $userID = 99;

	public $x=0;

	private $groupID = 99;

	/**
	 * Zakonczyc prace gdy nie da sie ustawic tozsamosci demona ?
	 */
	private $requireSetIdentity = false;

	/**
	 * sciezka do pida
	 *
	 */
	private $pidFileLocation = '/home/users/turystyka/public_html/test/daemon.pid';

	/**
	 * sciezka home'a.. tylko po co?
	 *
	 */
	private $homePath = '/home/users/turystyka/public_html/Server/Daemon';



	/**
	 * id procesu
	 *
	 */
	protected $pid = 0;

	/**
	 * czy proces jest dzieckiem
	 *
	 */
	private $isChildren = false;

	/**
	 * czy dzialamy czy nie :)
	 *
	 */
	private $isRunning = false;

	private $children = array();

	private $maxProcesses = 10;


	/**
	 * Konstruktor
	 */
	public function __construct($homePath, $pidFileLocation) {
		error_reporting(0);
		set_time_limit(0);
		ob_implicit_flush();

		register_shutdown_function(array(&$this, 'releaseDaemon'));
	}

	public function GetUserID() {
		return $this->userID;
	}

	public function SetUserID($userID) {
		$this->userID = $userID;
	}

	public function GetGroupID() {
		return $this->groupID;
	}

	public function SetGroupID($groupID) {
		$this->groupID = $groupID;
	}

	public function GetRequireSetIdentity() {
		return $this->requireSetIdentity;
	}

	public function SetRequireSetIdentity($requireSetIdentity) {
		$this->requireSetIdentity = $requireSetIdentity;
	}

	public function GetPidFileLocation() {
		return $this->pidFileLocation;
	}

	public function SetPidFileLocation($pidFileLocation) {
		$this->pidFileLocation = $pidFileLocation;
	}

	public function GetHomePath() {
		return $this->homePath;
	}

	public function SetHomePath($homePath) {
		$this->homePath = $homePath;
	}

	public function GetPid() {
		return $this->pid;
	}

	public function SetPid($pid) {
		$this->pid = $pid;
		$fp = fopen($this->GetHomePath().'/'.$this->pid.'.pid', 'w');
		fwrite($fp, serialize($this));
		fclose($fp);
	}

	public function GetIsChildren() {
		return $this->isChildren;
	}

	public function SetIsChildren($isChildren) {
		$this->isChildren = $isChildren;
	}

	public function GetIsRunning() {
		return $this->isRunning;
	}

	public function SetIsRunning($isRunning) {
		$this->isRunning = $isRunning;
	}

	public function GetChildren() {
		return $this->children;
	}

	public function AddChild($pid) {
		$this->children[$pid] = true;
	}

	public function RemChild($pid) {
		if(isset($this->children[$pid])) {
			unset($this->childern[$pid]);
		}
	}

	public function StartChild($child) {

		$pid = Daemon::InitChild($chils, $this->GetPid());
		$this->AddChild($pid);
		MFLog::DbLog('Child process '.$pid.' started');
		if(!is_dir($filename)) {
			mkdir($this->GetHomePath().'/'.$this->GetPid(), 0777);
		}
		$fp = fopen($this->GetHomePath().'/'.$this->GetPid().'/'.$pid.'.pid', 'w');
		fwrite($fp, serialize($child));
		fclose($fp);
		$pidReturn = pcntl_waitpid($pid, $status);
		if(pcntl_wifexited($status)) {
			MFLog::DbLog('Child process '.$pid.' finished with status: '.$status);
		} else {
			MFLog::DbLog('child process '.$pid.' died');
		}

		return true;

	}

	public static function InitChild($child, $masterPid) {
		$pid = pcntl_fork();
		if($pid == 0) {
			exit($child->Run($masterPid));
		} else {
			return $pid;
		}
	}

	public function KillAll() {
		foreach($this->GetChildren() as $pid => $data) {
			MFLog::DbLog('Killing: '.$pid);
			posix_kill($pid, 0);
			if(is_file($this->GetHomePath().'/'.$this->GetPid().'/'.$pid.'.pid')) {
				unlink($this->GetHomePath().'/'.$this->GetPid().'/'.$pid.'.pid');
			}
		}
	}


	/**
	 * Inicjowanie komponentow systemu
	 *
	 */
	public function Init() {

		//include('class/DB.class.php');
		//include('class/Registry.class.php');


		//include('ErrorHandler.php');
		$db = new DBProd();
		Registry::Remove('db');
		Registry::Set('db', $db);

	}

	/**
	 * Start daemona
	 *
	 */
	public function Start() {
		$this->LogMessage('Starting daemon');

		if (!$this->Daemonize())
		{
			$this->LogMessage('Could not start daemon', DLOG_ERROR);

			return false;
		}


		$this->LogMessage('Running...');

		$this->IsRunning = true;


		while ($this->IsRunning)
		{
			$this->DoTask();
		}

		return true;
	}

	/**
	 * Stop daemona
	 *
	 */
	public function Stop()
	{
		$this->LogMessage('Stoping daemon');

		$this->KillAll();

		if(is_file($this->GetHomePath().'/'.$this->pid.'.pid')) {
			unlink($this->GetHomePath().'/'.$this->pid.'.pid');
		}

		$this->IsRunning = false;
		//posix_kill($this->GetPid(), 0);
		//exit;
	}

	/**
	 * Podaj lape :)
	 *
	 */
	function DoTask()
	{

		while(1) {
			if(is_dir($this->GetHomePath().'/todo')) {
				if($handle = opendir($this->GetHomePath().'/todo')) {
					while (false !== ($file = readdir($handle))) {
						if(count($this->GetChildren()) < $this->maxProcesses) {
						$taskFile = fopen($this->GetHomePath().'/todo/'.$file, 'r');
						$taskData = fread($taskFile, filesize($this->GetHomePath().'/todo/'.$file));
						fclose($taskFile);
						unlink($this->GetHomePath().'/todo/'.$file);
						$taskObj = unserialize($taskData);
						$this->InitChild($taskObj, $this->GetPid());
						}
					}
				}
			}
		}

		// hau hau
		//mail('pawy@formix.pl', 'demonizer', 'im running');
//		$fp = fopen('/home/users/turystyka/public_html/test/data'.$this->x.'.txt', 'w');
//		fwrite($fp, '1');
//		fwrite($fp, '23');
//		fclose($fp);
//		sleep(10);
//		//$fp = fopen('/home/users/turystyka/public_html/test/data2.txt', 'w');
//		$this->x++;
//		if($this->x < 5){
//			$this->DoTask();
//		} else {
//			$this->Stop();
//		}
	}

	/**
	 * Logujemy cos
	 *
	 */
	public function LogMessage($msg, $level = DLOG_NOTICE)
	{
		// logowanie
		MFLog::DbLog($msg);

	}

	/**
	 * Nie taki diabel straszny..
	 *
	 */
	private function Daemonize()
	{
	//	ob_end_flush();

		if ($this->IsDaemonRunning())
		{
			// Deamon dziala. Exiting
			return false;
		}

		if (!$this->Fork())
		{
			// niesforkowalo sie. Exiting.
			return false;
		}

		if (!$this->SetIdentity() && $this->requireSetIdentity)
		{
			// nie udalo sie ustawic tozsamosci. Exiting
			return false;
		}

		if (!posix_setsid())
		{
			$this->LogMessage('Could not make the current process a session leader', DLOG_ERROR);

			return false;
		}

		if (!$fp = @fopen($this->pidFileLocation, 'w'))
		{
			$this->LogMessage('Could not write to PID file', DLOG_ERROR);

			return false;
		}
		else
		{
			fputs($fp, $this->pid);
			fclose($fp);
		}

		@chdir($this->homePath);
		umask(0);

		declare(ticks = 1);

		pcntl_signal(SIGCHLD, array(&$this, 'sigHandler'));
		pcntl_signal(SIGTERM, array(&$this, 'sigHandler'));

		return true;
	}

	/**
	 * sprawdzamy czy dzialamy juz
	 *
	 */
	private function IsDaemonRunning()
	{
		$oldPid = false;
		if(is_file($this->pidFileLocation)) {
		$oldPid = @file_get_contents($this->pidFileLocation);

}
		if ($oldPid !== false && posix_kill(trim($oldPid),0))
		{
			$this->LogMessage('Daemon already running with PID: '.$oldPid, (DLOG_TO_CONSOLE | DLOG_ERROR));

			return true;
		}
		else
		{
			return false;
		}
	}

	/**
	 * Forkujemy proces
	 */
	private function Fork()
	{
		$this->LogMessage('Forking...');

		$pid = pcntl_fork();

		if ($pid == -1) // error
		{
			$this->LogMessage('Could not fork', DLOG_ERROR);

			return false;
		}
		else if ($pid) // parent
		{
			$this->LogMessage('Killing parent');

			exit();
		}
		else // children
		{

			$this->IsChildren = true;
			$this->SetPid(posix_getpid());

			return true;
		}
	}

	/**
	 * Ustawiamy tozsamosc diobła i zwracamy wynik
	 *
	 */
	private function SetIdentity()
	{
		if (!posix_setgid($this->groupID) || !posix_setuid($this->userID))
		{
			$this->LogMessage('Could not set identity', DLOG_WARNING);

			return false;
		}
		else
		{
			return true;
		}
	}

	/**
	 * Obsluga sig'ow
	 *
	 */
	public function SigHandler($sigNo)
	{
		switch ($sigNo)
		{
			case SIGTERM:   // Shutdown
				$this->LogMessage('Shutdown signal');
				$this->Stop();
				exit();
				break;

			case SIGCHLD:   // Halt
				$this->LogMessage('Halt signal');
				while (pcntl_waitpid(-1, $status, WNOHANG) > 0);
				break;
		}
	}

	/**
	 * Zwalnia plik z pidem
	 * Cos ala desktruktor
	 *
	 */
	public function ReleaseDaemon()
	{
		if (file_exists($this->pidFileLocation))
//		if ($this->isChildren && file_exists($this->pidFileLocation))
		{
			$this->LogMessage('Releasing daemon');

			unlink($this->pidFileLocation);
		}
	}
}




?>