/* Application initialization/notification API.
   Rick Smereka, Copyright (C) 2002-2004.

   This program 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.

   This program 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 this program; if not, get a copy via the Internet at
   http://gnu.org/copyleft/gpl.html or write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
   MA 02111-1307 USA

   You can contact the author via email at rsmereka@future-lab.com

   This API notifies other applications that the running program
   is initializing. Notification is done through a lock file.
   This API should be used by any program that depends on other
   applications to be running before starting-up. This API also
   provides a storage location for the application name in case
   this name is required by another API outside of the main
   program module.

   Original Linux version. May/2002, Rick Smereka

   Ported to 32-bit Windows. Jun/2002, Rick Smereka

   Added functions to register and obtain the application name.
   Ported to QNX 4.x. Jul/2002, Rick Smereka

   Ported to Debian Linux. Nov/2002, Rick Smereka

   Fixed bug in function 'appinit_get_name' that ignored whether
   'appinit_apname' was NULL. Changed all logging calls from 
   'log_file_date' to 'logman'. Mar/2004, Rick Smereka */


#include "stdhead.h"
#include "appinit.h"

/* global module data */

char *appinit_apname = (char *)NULL;

/* private function */

static int appinit_get_sname(char *, char *);

int appinit_start(char *sname)
{
   /* Indicate that the application is starting up by creating
      a zero byte lock file. The parameter 'sname' should be a
      unique name to identify the application and is usually
      the 'socloc' service name when dealing with a socket
      server. Function returns 'TRUE' upon success, 'FALSE'
      otherwise. */

   char *nsname, mname[] = "appinit_start";
   
   logman("%s:enter:sname=%s", mname, sname);

   if (sname == (char *)NULL || !strlen(sname))
      {
      logman("%s:exit:null or empty[sname]", mname);
      return(FALSE);
      }

   if ((nsname = (char *)malloc(strlen(sname) + 5)) == (char *)NULL)
      {
      logman("%s:exit:alloc fail[nsname]", mname);
      return(FALSE);
      }

   if (!appinit_get_sname(sname, nsname))
      {
      logman("%s:exit:bad rc[FALSE] from appinit_get_sname", mname);
      free(nsname);
      return(FALSE);
      }

   if (exist(nsname))
      {
      logman("%s:exit:lock file %s already exists", mname, nsname);
      free(nsname);
      return(FALSE);
      }

   if (!zcreate(nsname))
      {
      logman("%s:exit:bad rc[FALSE] from zcreate", mname);
      free(nsname);
      return(FALSE);
      }

   free(nsname);
   logman("%s:normal exit[TRUE]", mname);
   return(TRUE);
}

int appinit_stop(char *sname)
{
   /* Indicate that the application has completed its
      initialization by deleting the zero byte lock file.
      The parameter 'sname' must be the same name used to
      call 'appinit_start'. Function returns 'TRUE' upon 
      success, 'FALSE' otherwise. */

   char *nsname, mname[] = "appinit_stop";
   
   logman("%s:enter:sname=%s", mname, sname);

   if (sname == (char *)NULL || !strlen(sname))
      {
      logman("%s:exit:null or empty[sname]", mname);
      return(FALSE);
      }

   if ((nsname = (char *)malloc(strlen(sname) + 5)) == (char *)NULL)
      {
      logman("%s:exit:alloc fail[nsname]", mname);
      return(FALSE);
      }

   if (!appinit_get_sname(sname, nsname))
      {
      logman("%s:exit:bad rc[FALSE] from appinit_get_sname", mname);
      free(nsname);
      return(FALSE);
      }

   if (!exist(nsname))
      {
      logman("%s:exit:lock file %s not present", mname, nsname);
      free(nsname);
      return(FALSE);
      }

   unlink(nsname);
   free(nsname);
   logman("%s:normal exit[TRUE]", mname);
   return(TRUE);
}

int appinit_waitfor(char *sname, int min_timeout)
{
   /* Check for a lock file using the name 'sname'. Wait
      up to 'min_timeout' minutes for the lock file to be
      removed. Function returns 'TRUE' if the lock file
      no longer exists, 'FALSE' otherwise. */

   time_t nowtime, target_time;
   char *nsname, mname[] = "appinit_waitfor";

   logman("%s:enter:sname=%s", mname, sname);

   if (sname == (char *)NULL || !strlen(sname))
      {
      logman("%s:exit:null or empty[sname]", mname);
      return(FALSE);
      }

   if (min_timeout <= 0)
      {
      logman("%s:exit:out of range[min_timeout]", mname);
      return(FALSE);
      }

   if ((nsname = (char *)malloc(strlen(sname) + 5)) == (char *)NULL)
      {
      logman("%s:exit:alloc fail[nsname]", mname);
      return(FALSE);
      }

   if (!appinit_get_sname(sname, nsname))
      {
      logman("%s:exit:bad rc[FALSE] from appinit_get_sname", mname);
      free(nsname);
      return(FALSE);
      }

   nowtime = time(NULL);
   target_time = nowtime + (min_timeout * 60);

   do
      {
      if (!exist(nsname))
         {
         logman("%s:normal exit[TRUE],lock file %s is not present",
                       mname, nsname);
         free(nsname);
         return(TRUE);
         }
      
#ifdef OS_UNIX
      /* use 'sleep' under Unix systems to block process */

      sleep(2);
#endif

      nowtime = time(NULL);
      }
   while(nowtime < target_time);

   free(nsname);
   logman("%s:exit[FALSE]:time expired", mname);
   return(FALSE);
}

int appinit_register_name(char *apname)
{
   /* Load the passed 'apname' into the module global 
      'appinit_apname'. Function returns 'TRUE' upon
      success, 'FALSE' otherwise. */

   char mname[] = "appinit_register_name";
   int len, ret = FALSE;

   logman("%s:enter", mname);
   
   if (apname == (char *)NULL || !strlen(apname))
      {
      logman("%s:exit[FALSE]:null or empty[apname]", mname);
      return(ret);
      }

   len = strlen(apname);

   /* if name already allocated, de-allocate it */

   if (appinit_apname != (char *)NULL)
      if (!appinit_remove_name())
         {
         logman("%s:exit[FALSE]:bad rc from appinit_remove_name",
                       mname);
         return(ret);
         }
 
   if ((appinit_apname = (char *)malloc(len + 1)) == (char *)NULL)
      {
      logman("%s:exit[FALSE]:alloc fail[appinit_apname]", mname);
      return(ret);
      }

   strcpy(appinit_apname, apname);
   ret = TRUE;
   logman("%s:normal exit[TRUE]", mname);
   return(ret);
}

int appinit_get_name(char *apname)
{
   /* Obtain the application name which must already be defined
      by calling the function 'appinit_register_name'. Function
      returns 'TRUE' upon success with the application name
      loaded into 'apname' (which must already be allocated to
      sufficient size), 'FALSE' otherwise. */

   char mname[] = "appinit_get_name";
   int ret = FALSE;

   logman("%s:enter", mname);

   if (apname == (char *)NULL)
      {
      logman("%s:exit[FALSE]:null[apname]", mname);
      return(ret);
      }

   apname[0] = EOS;

   if (appinit_apname == (char *)NULL || !strlen(appinit_apname))
      return(FALSE);

   strcpy(apname, appinit_apname);
   logman("%s:normal exit[TRUE]", mname);
   ret = TRUE;
   return(ret);
}

int appinit_remove_name(void)
{
   /* De-allocate the current application name ('appinit_apname').
      Function returns 'TRUE' upon success, 'FALSE' otherwise. */

   char mname[] = "appinit_remove_name";
   int ret = FALSE;

   logman("%s:enter", mname);

   if (appinit_apname == (char *)NULL || !strlen(appinit_apname))
      {
      logman("%s:exit[FALSE]:no application name present", mname);
      return(ret);
      }

   free(appinit_apname);
   appinit_apname = (char *)NULL;
   logman("%s:normal exit[TRUE]", mname);
   ret = TRUE;
   return(ret);
}

static int appinit_get_sname(char *oldname, char *newname)
{
   /* Translate an application name. The lock file extension
      will be appended to the name. In addition, any QNX 4.x
      nameloc name (starting with '/') will have its leading
      slash removed. Function will load the translated name
      into 'newname' which must already be allocated by the
      caller to sufficient size. Function returns 'TRUE'
      upon success, 'FALSE' otherwise. */

   char mname[] = "appinit_get_sname";

   logman("%s:enter:oldname=%s", mname, oldname);

   if (oldname == (char *)NULL || !strlen(oldname))
      {
      logman("%s:exit:null or empty[oldname]", mname);
      return(FALSE);
      }

   if (newname == (char *)NULL)
      {
      logman("%s:exit:null[newname]", mname);
      return(FALSE);
      }

   newname[0] = EOS;

#ifdef IPC_QNX
   if (oldname[0] == '/')
      sprintf(newname, "%s.lck", &oldname[1]);
   else
      sprintf(newname, "%s.lck", oldname);
#else
   sprintf(newname, "%s.lck", oldname);
#endif

   logman("%s:normal exit[TRUE]:newname=%s", mname, newname);
   return(TRUE);
}
