/***********************************************************************
 * 
 *  Copyright (C) 2005-2006 Novell, Inc. All Rights Reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; version 2.1
 *  of the License.
 *
 *  This library 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
 *  Library Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, Novell, Inc.
 * 
 *  To contact Novell about this file by physical or electronic mail, 
 *  you may find current contact information at www.novell.com.
 * 
 ***********************************************************************/

using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Diagnostics;
using sscs.communication;
using sscs.constants;
using sscs.common;

class SecretStoreClientService
{
    private static Communication server = null;
    private static  Thread listeningThread = null;

    public static void Main(string[] args)
    {
        CSSSLogger.ExecutionTrace(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        try
        {
            /* If getting a lock fails, just exit.
             */
            if(!AcquireLock())
            {
                Console.WriteLine("Another instance of micasad is already running");
                Mono.Unix.Native.Syscall.exit(-1);
            }

            RegisterSignals();
			Mono.Unix.Native.Syscall.umask( Mono.Unix.Native.FilePermissions.S_IRGRP | 
											Mono.Unix.Native.FilePermissions.S_IWGRP | 
											Mono.Unix.Native.FilePermissions.S_IROTH | 
											Mono.Unix.Native.FilePermissions.S_IWOTH);
            CSSSLogger.DbgLog("Client Side SecretStore Service has started.");

            server = CommunicationFactory.CreateCommunicationEndPoint();

            listeningThread = new Thread(new ThreadStart(StartServer));
            listeningThread.Start();
            listeningThread.Join();
        }
        catch(Exception e)
        {
            Terminate();
        }        
    }

    /* The thread which listens and spawns threads on every accept
     * starts its execution from this method.
     */
    private static void StartServer()
    {
        try
        {
            CSSSLogger.ExecutionTrace(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
            server.StartCommunicationEndPoint();
        }
        catch(ThreadAbortException exp)
        {
            CSSSLogger.DbgLog("Listening thread of miCASAd is going down.");
            CSSSLogger.ExpLog(exp.ToString());
        }
        catch(Exception exp)
        {
            CSSSLogger.ExpLog(exp.ToString());
        }
        CSSSLogger.DbgLog("Listening thread of miCASAd is going down.");
    }

    /* This ensures that there is only one instance of
     * SSCS at any point.
     */
    private static bool AcquireLock()
    {
        CSSSLogger.ExecutionTrace(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        int platform = (int)Environment.OSVersion.Platform;
        if( (platform == 128) || (platform == 4) )
        {
            if(File.Exists(ConstStrings.SSCS_LINUX_PIDFILE))
            {
                if(CheckIfMiCASAdIsRunning())
                {
                    CSSSLogger.DbgLog("Acquiring lock failed. Terminating miCASAd.");
                    return false;
                }
                else
                {
                    File.Delete(ConstStrings.SSCS_LINUX_PIDFILE);
                    CreatePidFile();
                    return true;
                }
            }
            else
            {
                 CreatePidFile();
                 return true;
            }
        }
        else
            return false;
    }
    private static void RegisterSignals()
    {
        CSSSLogger.ExecutionTrace(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        if(( (int)Environment.OSVersion.Platform) == 128)
        {
            //SIGTERM
            Mono.Unix.Native.Stdlib.signal(Mono.Unix.Native.Signum.SIGTERM, new Mono.Unix.Native.SignalHandler(Terminate));
            //SIGINT
            Mono.Unix.Native.Stdlib.signal(Mono.Unix.Native.Signum.SIGINT, new Mono.Unix.Native.SignalHandler(Terminate));
            //SIGHUP
            Mono.Unix.Native.Stdlib.signal(Mono.Unix.Native.Signum.SIGHUP, new Mono.Unix.Native.SignalHandler(Terminate));
        }

    }
    private static void Terminate(int sigNum)
    {
        CSSSLogger.ExecutionTrace(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        Terminate();
    }
    private static void Terminate()
    {
        CSSSLogger.ExecutionTrace(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        CSSSLogger.DbgLog("Client Side SecretStore Service is now exiting.");

        if( listeningThread != null )
        {
             listeningThread.Abort("Aborting listening thread");
        }
        int platform = (int)Environment.OSVersion.Platform;
        if( (platform == 128) || (platform == 4) ) 
        {
            if( File.Exists(ConstStrings.SSCS_LINUX_PIDFILE) )
            {
                File.Delete(ConstStrings.SSCS_LINUX_PIDFILE);
            }
            Mono.Unix.Native.Syscall.exit(0);
        }
    }
    private static void CreatePidFile()
    {
        int pid = Mono.Unix.Native.Syscall.getpid();
        string pidStr = String.Format("{0}",pid);

        FileInfo fInfo = new FileInfo(ConstStrings.SSCS_LINUX_PIDFILE);
        FileStream fs = fInfo.Open(System.IO.FileMode.OpenOrCreate, FileAccess.ReadWrite);
        StreamWriter w = new StreamWriter(fs);
        w.Write(pidStr);
        w.Flush();
        fs.Close();
    }
    private static bool CheckIfMiCASAdIsRunning()
    {
        try
        {
            StreamReader sr = new StreamReader(ConstStrings.SSCS_LINUX_PIDFILE);
            string line = sr.ReadLine();
            if( line == null )
            {
                sr.Close();
                return false;
            }

            string procPath = "/proc/"+ line + "/cmdline";

            /* If the file procPath itself does not exist,
             * then another instance is surely not running.
             */
            if( !File.Exists(procPath) )
            {
                return false;
            }
            
            /* There is a possibility that the pid stored in 
             * the pidfile has been reassigned to another process.
             * So, if procPath exists, check if the process is
             * micasad.exe. 
             */

            StreamReader procReader = new StreamReader(procPath);
            string cmdline = procReader.ReadLine();

/*
            string assemblyName = (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType).Assembly.FullName + ".exe\0";

*/
            string assemblyName = "micasad.exe\0";

            if(cmdline.EndsWith(assemblyName))
            {
                return true;
            }
            else
            {
                return false;
            }
            
        }
        catch(Exception e)
        {
            return false;
        }

    }
}