/* A high level client api module for the socket locate service.
   Applies to TCP socket IPC method only.
   Rick Smereka, Copyright (C) 2000-2005.

   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 is used by both 'socloc' server and client applications.

   Original Windows 32bit version under CodeWarrior V4. Feb/2000,
   Rick Smereka

   Changed function 'sloc_log_status' to accept the returned log
   file name in addition to the code. Mar/2000, Rick Smereka

   Corrected 'sloc_client_connect' to obtain the first config
   entry using the function 'sl_config_get_first'. Modified function
   'sloc_failover' to save the current 'spos' count before entering
   the 'sloc_config_delete' loop. Jun/2000, Rick Smereka
   
   Added include of header 'ip.h' after moving the function 'isip'.
   Jul/2000, Rick Smereka

   Changed name of module global 'sockClientAddr' to
   'slsockClientAddr' to avoid duplicate module global names.
   Jul/2001, Rick Smereka

   Ported to Debian Linux. Nov/2002, Rick Smereka

   Corrected bug in TCP socket connect to the server. The TCP
   socket 'connect' function was being called for each request
   sent to the server.  This caused the socket descriptor pool
   to become exhausted under some Unix's when a large number
   of transactions were being processed. This behavior has been 
   changed to connect to the server only upon initialization or 
   previous send/receive socket error. This means that the socket
   stays connected even when not in use. It is up to the
   developer to call 'sloc_end' when the socket is no longer
   required.

   The 'socloc' server no longer uses this API. A new API called 
   'slocsrv.c' has been created specifically for the server.

   This API will properly talk only to socloc server's that are using 
   the new 'ipcsrv' (GPL package version 1.33 or higher).

   Added functions 'sloc_trans_num', 'sloc_connect_num' and
   'sloc_term_api'. May/2003, Rick Smereka

   Changed all logging calls from 'log_file_date' to 'logman'.
   Mar/2004, Rick Smereka

   Fixed bug in 'sloc_ll_find' that caused the system logger
   not to record the server reply in the log file. Reply needed
   double quotes around it. Removed this change as this fix
   did not help. Jan/2005, Rick Smereka */

#include "stdhead.h"          // standard include
#include "flsocket.h"         // standard socket defines
#include "ipcomm.h"           // low level socket send/receive
#include "sloc.h"             // defines for this module
#include "socloc.h"           // global socket locator defines
#include "slconfig.h"         // socket locate config management
#include "sconnect.h"         // connect string parse/build API
#include "ip.h"               // IP library routines

// global (to module) data

#ifdef OS_WIN32
SOCKET slclientSocket;        // client socket
SOCKADDR_IN slsockClientAddr; // client address structure
LPHOSTENT lpHostEnt;          // host info structure
#endif

#ifdef OS_UNIX
int slclientSocket;
struct sockaddr_in slsockClientAddr;
struct hostent *lpHostEnt;
#endif

char sloc_hostname[128];      // host name of 'socloc' server
int sloc_port = 0;            // port 'socloc' is using

// private functions

static int sloc_connect(char *, int);
static int sloc_sr_code(int, char *);
static int sloc_lsr_code(int, char *);
static int sloc_sr_char(int, char *, char *);
static int sloc_lsr_char(int, char *, char *);
static int sloc_sr_int(int, char *, int *);
static int sloc_lsr_int(int, char *, int *);
static int sloc_sr_long(int, char *, long *);
static int sloc_lsr_long(int, char *, long *);
static int sloc_failover(void);
static int sloc_reload_config_list(void);
static int sloc_client_connect(void);
static int sloc_ll_client_connect(void);

int sloc_initialize(void)
{
   /* Initialize the client for communication with the
      socket locate server. An attempt to get the server status
      is made. Nodeid (host name), port number and optionally the
      IP address are expected in the configuration file
      'SL_CONFIG_FILE_NAME'. The format of this file is discussed
      in 'slconfig.c'. Function returns 'SL_OK' if a successful
      connection was made, a socket locate code otherwise. */

   char mname[] = "sloc_initialize";
   char hname[SL_MAXCOMMAND];
   int ret, port;

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

   if ((ret = sl_config_read(SL_CONFIG_FILE_NAME)) != SL_OK)
      {
      logman("%s:bad rc from 'sl_config_read',rc[%d]", mname, ret);
      return(ret);
      }

   // establish a connection to the first responding 'socloc' server

   if ((ret = sloc_client_connect()) != SL_OK)
      {
      logman("%s:bad rc[%d] from sloc_client_connect", mname, ret);
      return(ret);
      }

   if ((ret = sloc_status()) != SL_OK)
      {
      logman("%s:bad rc from 'status',rc[%d]", mname, ret);
      return(ret);
      }

   logman("%s:normal exit,server responding is '%s' on "
                 "port %d", mname, sloc_hostname, sloc_port);
   return(SL_OK);
}

void sloc_end(void)
{
   /* Stop communication with a 'socloc' server. The client socket
      is closed and the 'socloc' server global details are wiped. */

   char mname[] = "sloc_end";

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

   // if there are no 'socloc' server details, assume no connection

   if (sloc_is_init() != SL_OK)
      return;

#ifdef OS_WIN32
   (void)closesocket(slclientSocket);
#else
   close(slclientSocket);
#endif

   sloc_port = 0;
   sloc_hostname[0] = EOS;
   logman("%s:normal exit", mname);
}

int sloc_log_off(void)
{
   /* Turn 'socloc' server logging off.
      Function returns 'SL_OK' upon success, an error code
      otherwise. */

   char mname[] = "sloc_log_off";
   int ret;

   logman("%s:enter,host=%s,port=%d", mname, sloc_hostname,
                 sloc_port);
   ret = sloc_sr_code(SL_SEND_LOG_OFF, (char *)NULL);
   logman("%s:normal exit,rc[%d]", mname, ret);
   return(ret);
}

int sloc_log_on(char *lname)
{
   /* Turn 'socloc; server logging on. Log file name 'lname'
      is optional. Function returns 'SL_OK'
      upon success, a 'socloc' code otherwise. */

   char mname[] = "sloc_log_on";
   int ret;

   logman("%s:enter", mname);
   ret = sloc_sr_code(SL_SEND_LOG_ON, lname);
   logman("%s:normal exit,rc[%d]", mname, ret);
   return(ret);
}

int sloc_log_status(int *lflag, char *lfname)
{
   /* Get staus of 'socloc' server logging. Function returns
      the log status (0=off,1=on) in 'lflag' and the current
      log file name in 'lfname' upon success. Function returns a 'socloc'
      code. */

   char mname[] = "sloc_log_status";
   char reply[128], flag_char[25];
   int ret, flag;

   logman("%s:enter,host=%s,port=%d", mname, sloc_hostname,
                 sloc_port);

   if (lflag == (int *)NULL)
      {
      logman("%s:null[lflag]", mname);
      return(SL_INVALID_PARAMETER);
      }

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

   lfname[0] = EOS;
   *lflag = 0;

   if ((ret = sloc_sr_char(SL_SEND_LOG_STATUS, (char *)NULL, reply))
       != SL_OK)
      {
      logman("%s:bad rc[%d] from sloc_sr_char", mname, ret);
      return(ret);
      }

   if (!command_word(reply, flag_char, 1))
      {
      logman("%s:error getting flag_char", mname);
      return(SL_INTERNAL_ERROR);
      }

   if (!qatoi(flag_char, &flag))
      {
      logman("%s:status flag is non-numeric", mname);
      return(SL_INTERNAL_ERROR);
      }

   if (flag != 0 && flag != 1)
      {
      logman("%s:status flag has incorrect value", mname);
      return(SL_INTERNAL_ERROR);
      }

   *lflag = flag;

   if (!command_word(reply, lfname, 2))
      {
      logman("%s:error getting lfname", mname);
      return(SL_INTERNAL_ERROR);
      }

   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

int sloc_add(char *sname, char *hname, int port, char *ip_ad)
{
   /* Add a socket server entry to 'socloc'. The IP address ('ip_ad')
      may be null but if it is not null, it must be a valid
      IP address. Function returns 'SL_OK' upon success,
      a 'socloc' code otherwise. */

   char mname[] = "sloc_add";
   char *parm, *tsname, *thname;
   int len, ret;

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

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

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

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

   /* make sure IP address (if present) is valid */

   if (ip_ad != (char *)NULL)
      if (!isip(ip_ad))
         {
         logman("%s:invalid ip address", mname);
         return(SL_BAD_IP);
         }

   /* calc total length of parameter
      length of service name + host name + port + ip_ad + 5 */

   len = strlen(sname) + strlen(hname) + 30;

   if ((parm = (char *)malloc(len)) == (char *)NULL)
      {
      logman("%s:alloc fail[parm]", mname);
      return(SL_MEMORY_FAIL);
      }

   /* load each string into tmp strings, expanding
      string with quotes if spaces are present */

   if ((tsname = (char *)malloc(len)) == (char *)NULL)
      {
      logman("%s:alloc fail[tsname]", mname);
      free(parm);
      return(SL_MEMORY_FAIL);
      }

   if (words(sname) > 1)
      sprintf(tsname, "'%s'", sname);
   else
      strcpy(tsname, sname);

   if ((thname = (char *)malloc(len)) == (char *)NULL)
      {
      logman("%s:alloc fail[thname]", mname);
      free(parm);
      free(tsname);
      return(SL_MEMORY_FAIL);
      }

   if (words(hname) > 1)
      sprintf(thname, "'%s'", hname);
   else
      strcpy(thname, hname);

   /* build final parameter string, we send a one as the
      first parameter to indicate the originating client */

   if (ip_ad != (char *)NULL)
      sprintf(parm, "1 %s %s %d %s", tsname, thname, port, ip_ad);
   else
      sprintf(parm, "1 %s %s %d", tsname, thname, port);

   free(tsname);
   free(thname);
   logman("%s:parm=%s", mname, parm);
   ret = sloc_sr_code(SL_SEND_ADD, parm);
   logman("%s:normal exit[%d]", mname, ret);
   free(parm);
   return(ret);
}

int sloc_config_add(char *hname, int port, char *ip_ad)
{
   /* Add a config entry to 'socloc'. The IP address ('ip_ad')
      may be null. Function returns 'SL_OK' upon success,
      a 'socloc' code otherwise. */

   char mname[] = "sloc_config_add";
   char *parm, *thname;
   int len, ret;

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

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

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

   /* make sure IP address (if present) is valid */

   if (ip_ad != (char *)NULL)
      if (!isip(ip_ad))
         {
         logman("%s:invalid ip address", mname);
         return(SL_BAD_IP);
         }

   /* calc total length of parameter
      length of host name + port + ip_ad + 5 */

   len = strlen(hname) + 30;

   if ((parm = (char *)malloc(len)) == (char *)NULL)
      {
      logman("%s:alloc fail[parm]", mname);
      return(SL_MEMORY_FAIL);
      }

   /* load host name into tmp strings, expanding
      string with quotes if spaces are present */

   if ((thname = (char *)malloc(len)) == (char *)NULL)
      {
      logman("%s:alloc fail[thname]", mname);
      free(parm);
      return(SL_MEMORY_FAIL);
      }

   if (words(hname) > 1)
      sprintf(thname, "'%s'", hname);
   else
      strcpy(thname, hname);

   /* build final parameter string, we send a one as the
      first parameter to indicate the originating client */

   if (ip_ad != (char *)NULL)
      sprintf(parm, "1 %s %d %s", thname, port, ip_ad);
   else
      sprintf(parm, "1 %s %d", thname, port);

   free(thname);
   ret = sloc_sr_code(SL_SEND_CONFIG_ADD, parm);
   logman("%s:exit[%d]:parm=%s", mname, ret, parm);
   free(parm);
   return(ret);
}

int sloc_config_delete(int port)
{
   /* Delete a config 'socloc' entry. Function returns 'SL_OK'
      upon success, a 'socloc' code otherwise. */

   char mname[] = "sloc_config_delete";
   char parm[25];
   int ret;

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

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

   sprintf(parm,"1 %d", port);
   ret = sloc_sr_code(SL_SEND_CONFIG_DELETE, parm);
   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

int sloc_status(void)
{
   /* Get 'socloc' server status.
      Function returns 'SL_OK' upon success, an error code
      otherwise. */

   char mname[] = "sloc_status";
   int ret;

   logman("%s:enter,host=%s,port=%d", mname, sloc_hostname,
                 sloc_port);
   ret = sloc_sr_code(SL_SEND_STATUS, (char *)NULL);
   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

int sloc_service_name(char *sname)
{
   /* Get the service name of a 'socloc' server.
      Upon success, the service name will be loaded into
      'sname' which must already be allocated to sufficient
      size. Function returns a 'socloc' code. */

   char mname[] = "sloc_service_name";
   int ret;

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

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

   if ((ret = sloc_sr_char(SL_SEND_SERVICE_NAME, (char *)NULL, sname))
       != SL_OK)
      {
      logman("%s:rc[%d] from sloc_sr_char", mname, ret);
      return(ret);
      }

   return(SL_OK);
}

int sloc_version(char *version)
{
   /* Get the version of a 'socloc' server.
      Upon success, the version string will be loaded into
      'version' which must already be allocated to sufficient
      size. Function returns a 'socloc' code. */

   char mname[] = "sloc_version";
   int ret;

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

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

   if ((ret = sloc_sr_char(SL_SEND_VERSION, (char *)NULL, version))
       != SL_OK)
      {
      logman("%s:rc[%d] from sloc_sr_char", mname, ret);
      return(ret);
      }

   return(SL_OK);
}

int sloc_delete(int port)
{
   /* Delete a socket server entry. Function returns 'SL_OK'
      upon success, a 'socloc' code otherwise. */

   char mname[] = "sloc_delete";
   char parm[25];
   int ret;

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

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

   /* indicate a client send with 'orig_flag' set to one */

   sprintf(parm,"1 %d", port);
   ret = sloc_sr_code(SL_SEND_DELETE, parm);
   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

int sloc_find(char *sname, char *hname, int *port, char *ip_ad)
{
   /* Find a 'socloc' entry. Only the service name is used.
      The IP address ('ip_ad') may be null. Upon success, details
      are loaded into 'hname', 'port' and optionally 'ip_ad'.
      Function returns a 'socloc' code. */

   char mname[] = "sloc_find";
   int ret;

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

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

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

   if (port == (int *)NULL)
      {
      logman("%s:null[port]", mname);
      return(SL_INVALID_PARAMETER);
      }

   hname[0] = EOS;
   *port = 0;

   if (ip_ad != (char *)NULL)
      ip_ad[0] = EOS;

   ret = sloc_ll_find(sname, hname, port, ip_ad);
   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

int sloc_config_find(char *hname, int *port, char *ip_ad)
{
   /* Find a 'socloc' config entry by one or more details.
      At least one detail must be present.Upon success,
      missing details are loaded into 'hname', 'port'
      and optionally 'ip_ad'. Function returns a 'socloc' code. */

   char mname[] = "sloc_config_find";
   char *buildstr, *reply, mes[128];
   int ret, ishname, isport, isip;

   logman("%s:enter", mname);
   ishname = isport = isip = TRUE;

   if (hname == (char *)NULL || !strlen(hname))
      ishname = FALSE;

   if (port == (int *)NULL || *port <= 0)
      isport = FALSE;

   if (ip_ad == (char *)NULL || !strlen(ip_ad))
      isip = FALSE;

   if (!ishname && !isport && !isip)
      {
      logman("%s:no parameters", mname);
      return(SL_INVALID_PARAMETER);
      }

   /* use 'sconnect_build' to form the connect string */

   if ((buildstr = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
      {
      logman("%s:alloc fail[buildstr]", mname);
      return(SL_MEMORY_FAIL);
      }

   if ((ret = sconnect_build((char *)NULL, hname, port, ip_ad, buildstr))
        != SP_OK)
      {
      free(buildstr);
      sp_code_string(ret, mes);
      logman("%s:bad rc[%s] from sconnect_build", mname, mes);
      return(SL_INVALID_PARAMETER);
      }

   if ((reply = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
      {
      logman("%s:alloc fail[reply]", mname);
      free(buildstr);
      return(SL_MEMORY_FAIL);
      }

   logman("%s:parm=%s,l=%d", mname, buildstr, strlen(buildstr));
   ret = sloc_sr_char(SL_SEND_CONFIG_FIND, buildstr, reply);
   free(buildstr);

   if (ret != SL_OK)
      {
      logman("%s:exit bad rc[%d]", mname, ret);
      free(reply);
      return(ret);
      }

   logman("%s:server reply=%s,l=%d", mname, reply, strlen(reply));

   /* take apart the return string and load only the missing pieces */

   if (command_words(reply) < 2)
      {
      logman("%s:incorrect number of words returned", mname);
      free(reply);
      return(SL_INTERNAL_ERROR);
      }

   if ((buildstr = (char *)malloc(strlen(reply) + 1)) == (char *)NULL)
      {
      logman("%s:alloc fail[buildstr]", mname);
      free(reply);
      return(SL_MEMORY_FAIL);
      }

   if (!ishname && hname != (char *)NULL)
      if (!command_word(reply, hname, 1))
         {
         logman("%s:error getting host name", mname);
         free(reply);
         free(buildstr);
         return(SL_INTERNAL_ERROR);
         }

   if (!isport && port != (int *)NULL)
      {
      if (!command_word(reply, buildstr, 2))
         {
         logman("%s:error getting port char", mname);
         free(reply);
         free(buildstr);
         return(SL_INTERNAL_ERROR);
         }

      if (!qatoi(buildstr, port))
         {
         logman("%s:server returned port that is not a number", mname);
         free(reply);
         free(buildstr);
         return(SL_INTERNAL_ERROR);
         }
      }

   if (command_words(reply) > 2)
      if (!isip && ip_ad != (char *)NULL)
         if (!command_word(reply, ip_ad, 3))
            {
            logman("%s:error getting ip address", mname);
            free(reply);
            free(buildstr);
            return(SL_INTERNAL_ERROR);
            }

   free(reply);
   free(buildstr);
   logman("%s:normal exit[0]", mname);
   return(SL_OK);
}

int sloc_find_list(char *sname, char *list_out)
{
   /* Find one or more 'socloc' entries by service name. All entries
      matching the service name will be loaded into 'list_out'.
      The list is delimited by 'SL_LIST_DELIM'. Function returns
      a 'socloc' code. */

   char mname[] = "sloc_find_list";
   int ret;

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

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

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

   ret = sloc_ll_find_list(sname, (char *)NULL, (int *)NULL, (char *)NULL,
                           list_out);
   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

int sloc_ll_find(char *sname, char *hname, int *port, char *ip_ad)
{
   /* Find a 'socloc' entry. All parameters are optional. At least
      one parameter must be specified. The lookup will use all
      supplied parameters to perform the lookup. If you just want
      to find by service name, do not call this function, use
      'sloc_find' instead. Upon success, the missing items will
      be loaded. Function returns 'SL_OK' upon success, a
      'socloc' code otherwise. */

   char mname[] = "sloc_ll_find";
   char *buildstr, *parm, *wrd, mes[128];
   int ret;

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

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

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

   if (port == (int *)NULL)
      {
      logman("%s:null[port]", mname);
      return(SL_INVALID_PARAMETER);
      }

   if (!strlen(sname) && !strlen(hname) && *port <= 0 &&
       (ip_ad == (char *)NULL || !strlen(ip_ad)))
      {
      logman("%s:at least one parameter must be supplied", mname);
      return(SL_INVALID_PARAMETER);
      }

   /* use 'sconnect_build' to form the connect string */

   if ((buildstr = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
      {
      logman("%s:alloc fail[buildstr]", mname);
      return(SL_MEMORY_FAIL);
      }

   if ((ret = sconnect_build(sname, hname, port, ip_ad, buildstr))
        != SP_OK)
      {
      free(buildstr);
      sp_code_string(ret, mes);
      logman("%s:bad rc[%s] from sconnect_build", mname, mes);
      return(SL_INVALID_PARAMETER);
      }

   if ((wrd = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
      {
      logman("%s:alloc fail[wrd]", mname);
      free(buildstr);
      return(SL_MEMORY_FAIL);
      }

   logman("%s:buildstr=%s,l=%d", mname, buildstr, strlen(buildstr));
   ret = sloc_sr_char(SL_SEND_FIND, buildstr, wrd);
   free(buildstr);

   if (ret != SL_OK)
      {
      logman("%s:exit bad rc[%d]", mname, ret);
      free(wrd);
      return(ret);
      }

   logman("%s:server reply=%s,l=%d", mname, wrd, strlen(wrd));

   /* take apart the return string and load only the missing pieces */

   if (command_words(wrd) < 2)
      {
      logman("%s:incorrect number of words returned", mname);
      logman("%s:wrd=%s,l=%d", mname, wrd, strlen(wrd));
      free(wrd);
      return(SL_INTERNAL_ERROR);
      }

   if ((parm = (char *)malloc(strlen(wrd) + 1)) == (char *)NULL)
      {
      logman("%s:alloc fail[parm]", mname);
      free(wrd);
      return(SL_MEMORY_FAIL);
      }

   if (!strlen(sname))
      if (!command_word(wrd, sname, 1))
         {
         logman("%s:error getting service name", mname);
         free(wrd);
         free(parm);
         return(SL_INTERNAL_ERROR);
         }

   if (!strlen(hname))
      if (!command_word(wrd, hname, 2))
         {
         logman("%s:error getting host name", mname);
         free(wrd);
         free(parm);
         return(SL_INTERNAL_ERROR);
         }

   if (*port == 0)
      {
      if (!command_word(wrd, parm, 3))
         {
         logman("%s:error getting port char", mname);
         free(wrd);
         free(parm);
         return(SL_INTERNAL_ERROR);
         }

      if (!qatoi(parm, port))
         {
         logman("%s:server returned port that is not a number", mname);
         free(wrd);
         free(parm);
         return(SL_INTERNAL_ERROR);
         }
      }

   if (command_words(wrd) > 3)
      if (ip_ad != (char *)NULL && !strlen(ip_ad))
         if (!command_word(wrd, ip_ad, 4))
            {
            logman("%s:error getting ip address", mname);
            free(wrd);
            free(parm);
            return(SL_INTERNAL_ERROR);
            }

   free(wrd);
   free(parm);
   logman("%s:normal exit[0]", mname);
   return(SL_OK);
}

int sloc_ll_find_list(char *sname, char *hname, int *port, char *ip_ad,
                      char *list_out)
{
   /* Find one or more 'socloc' entries. All parameters are optional
      except 'list_out' which is the output list. The output list is
      delimited by 'SL_LIST_DELIM' The lookup will use all supplied
      parameters to perform the lookup. If you just want to find a list
      of socket servers by service name, do not call this function,
      use 'sloc_find_list' instead. Function returns
      'SL_OK' upon success, a 'socloc' code otherwise. */

   char mname[] = "sloc_ll_find_list";
   char *parm, mes[128];
   int ret;

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

   if (sname == (char *)NULL && hname == (char *)NULL && port == (int *)NULL
       && ip_ad == (char *)NULL)
      {
      logman("%s:all null parameters", mname);
      return(SL_INVALID_PARAMETER);
      }

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

   /* use 'sconnect_build' to form the connect string */

   if ((parm = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
      {
      logman("%s:alloc fail[parm]", mname);
      return(SL_MEMORY_FAIL);
      }

   if ((ret = sconnect_build(sname, hname, port, ip_ad, parm))
        != SP_OK)
      {
      free(parm);
      sp_code_string(ret, mes);
      logman("%s:bad rc[%s] from sconnect_build", mname, mes);
      return(SL_INVALID_PARAMETER);
      }

   logman("%s:parm=%s,l=%d", mname, parm, strlen(parm));
   ret = sloc_sr_char(SL_SEND_FIND_LIST, parm, list_out);
   logman("%s:reply list_out=%s,%d", mname, list_out,
                 strlen(list_out));
   free(parm);
   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

int sloc_get_list(char *list_out)
{
   /* Get the 'socloc' list. The delimited list is
      returned in 'list_out' which must already be allocated
      by the caller to sufficient size. Function returns
      'SL_OK' upon success, a 'socloc' code otherwise. */

   char mname[] = "sloc_get_list";
   int ret;

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

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

   ret = sloc_sr_char(SL_SEND_GET_LIST, (char *)NULL, list_out);
   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

int sloc_put_list(char *list_in)
{
   /* Put/write a 'socloc' list to a server. The delimited
      list (delimited by 'SL_LIST_DELIM') is expected in
      'list_in'. Function returns 'SL_OK' upon success,
      a 'socloc' code otherwise. */

   char mname[] = "sloc_put_list";
   int ret;

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

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

   ret = sloc_sr_code(SL_SEND_PUT_LIST, list_in);
   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

int sloc_config_update(void)
{
   /* Get the current config list from the first active
      'socloc' server and use this list to update the
      config list in memory and write a new config file.
      Function returns 'SL_OK' upon success, a 'socloc'
      code otherwise. */

   char mname[]= "sloc_config_update";
   char *thelist;
   int ret;

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

   if ((thelist = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
      {
      logman("%s:alloc fail[thelist]", mname);
      return(SL_MEMORY_FAIL);
      }

   /* get the config list from the first active server */

   if ((ret = sloc_config_get_list(thelist)) != SL_OK)
      {
      logman("%s:bad rc[%d] from sloc_config_get_list", mname, ret);
      free(thelist);
      return(ret);
      }

   /* create a new list in memory */

   if ((ret = sl_config_put_list(thelist)) != SL_OK)
      {
      logman("%s:bad rc[%d] from sl_config_put_list", mname, ret);
      free(thelist);
      return(ret);
      }

   free(thelist);

   /* write this new list to the config file */

   if ((ret = sl_config_write_file()) != SL_OK)
      {
      logman("%s:bad rc[%d] from sl_config_write_file", mname, ret);
      return(ret);
      }

   logman("%s:normal exit[%d]", mname, ret);
   return(SL_OK);
}

int sloc_config_get_list(char *list_out)
{
   /* Get the 'socloc' config list. The delimited list is
      returned in 'list_out' which must already be allocated
      by the caller to sufficient size. Function returns
      'SL_OK' upon success, a 'socloc' code otherwise. */

   char mname[] = "sloc_config_get_list";
   int ret;

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

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

   ret = sloc_sr_char(SL_SEND_CONFIG_GET_LIST, (char *)NULL, list_out);
   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

int sloc_config_put_list(char *list_in)
{
   /* Put/write a 'socloc' config list to a server. The delimited
      list (delimited by 'SL_LIST_DELIM') is expected in
      'list_in'. Function returns 'SL_OK' upon success,
      a 'socloc' code otherwise. */

   char mname[] = "sloc_config_put_list";
   int ret;

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

   if (list_in == (char *)NULL || !strlen(list_in))
      {
      logman("%s:null[list_in]", mname);
      return(SL_INVALID_PARAMETER);
      }

   ret = sloc_sr_code(SL_SEND_CONFIG_PUT_LIST, list_in);
   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

int sloc_dump_debug(void)
{
   /* Command server to display and log debug information.
      Function returns 'SL_OK' upon success, an error code
      otherwise. */

   char mname[] = "sloc_dump_debug";
   int ret;

   logman("%s:enter,host=%s,port=%d", mname, sloc_hostname,
                 sloc_port);
   ret = sloc_sr_code(SL_SEND_DUMP_DEBUG, (char *)NULL);
   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

int sloc_config_dump_debug(void)
{
   /* Command server to display and log config debug information.
      Function returns 'SL_OK' upon success, an error code
      otherwise. */

   char mname[] = "sloc_config_dump_debug";
   int ret;

   logman("%s:enter,host=%s,port=%d", mname, sloc_hostname,
                 sloc_port);
   ret = sloc_sr_code(SL_SEND_CONFIG_DUMP_DEBUG, (char *)NULL);
   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

int sloc_terminate(char *passwd)
{
   /* Terminate a 'socloc' server. Function returns 'SL_OK'
      upon success, a 'socloc' code otherwise. */

   char mname[] = "sloc_terminate";
   int ret;

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

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

   ret = sloc_sr_code(SL_SEND_TERM, passwd);
   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

void sloc_get_active_socloc(char *hname, int *port)
{
   /* Get and return the active 'socloc' server host name
      and port number. 'hname' must be allocated by the
      caller to sufficient size. */

   char mname[] = "sloc_get_active_socloc";

   if (hname == (char *)NULL || port == (int *)NULL)
      {
      logman("%s:exit:null[hname or port]", mname);
      return;
      }

   hname[0] = EOS;
   *port = 0;

   if (!strlen(sloc_hostname) || sloc_port == 0)
      {
      logman("%s:no current socloc server", mname);
      return;
      }

   strcpy(hname, sloc_hostname);
   *port = sloc_port;
   logman("%s:normal exit", mname);
}

int sloc_set_active_socloc(char *hname, int port)
{
   /* Set the current active 'socloc' server. Connection is
      tested and service name is verified. Function returns
      a 'socloc' code. */

   char mname[] = "sloc_set_active_socloc";
   int ret;

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

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

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

   if ((ret = sloc_connect(hname, port)) != SL_OK)
      {
      logman("%s:bad rc[%d] from sloc_connect", mname, ret);
      return(ret);
      }

   logman("%s:normal exit[0]", mname);
   return(SL_OK);
}

int sloc_is_init(void)
{
   /* Determine whether the 'socloc' API has been initialized.
      The API is considered initialized when there is an active
      host name in 'sloc_hostname' and a positive port number in
      'sloc_port'. Function returns 'SL_OK' if the API has been
      initialized, 'SL_NO_INIT' otherwise. */

   char mname[] = "sloc_is_init";
   int ret;

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

   if (!sloc_port || sloc_hostname == (char *)NULL || !strlen(sloc_hostname))
      ret = SL_NO_INIT;
   else
      ret = SL_OK;

   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

int sloc_trans_num(long *transout)
{
   /* Get the 'socloc' server transaction count.
      Function returns 'SL_OK' upon success, a 'socloc' 
      code otherwise. */

   char mname[] = "sloc_trans_num";
   int ret;

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

   if (transout == (long *)NULL)
      {
      logman("%s:null[transout]", mname);
      return(SL_INVALID_PARAMETER);
      }

   ret = sloc_sr_long(SL_SEND_TRANS_NUM, (char *)NULL, transout);
   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

int sloc_connect_num(int *connectout)
{
   /* Get the 'socloc' server client connection count.
      Function returns 'SL_OK' upon success, a 'socloc' 
      code otherwise. */

   char mname[] = "sloc_connect_num";
   int ret;

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

   if (connectout == (int *)NULL)
      {
      logman("%s:null[connectout]", mname);
      return(SL_INVALID_PARAMETER);
      }

   ret = sloc_sr_int(SL_SEND_CONNECT_NUM, (char *)NULL, connectout);
   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

void sloc_term_api(void)
{
   /* Shutdown the API. Close the socket in use, wipe the globals
      indicating the server host name and port and delete the
      local 'socloc' config link list. */

   char mname[] = "sloc_term_api";

   logman("%s:enter", mname);
   sloc_end();
   sl_config_delete_all();
   logman("%s:normal exit", mname);
}
   
/* private functions */

static int sloc_connect(char *hname, int port)
{
   /* Attempt to connect to any 'socloc' server. Host name is
      expected in 'hname' and TCP/IP port number is expected
      in 'port'. Upon connection, the service name will be
      verified. Upon success, the 'socloc' API
      host name and port number will be set. Function returns a
      'socloc' code. */

   char mname[] = "sloc_connect";
   char thname[128], sname[128];
   int tport, ret;

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

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

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

   /* save current connection values and terminate any existing connection */

   strcpy(thname, sloc_hostname);
   tport = sloc_port;
   sloc_end();

   /* load new values */

   strcpy(sloc_hostname, hname);
   sloc_port = port;

   // attempt a connection

   if ((ret = sloc_ll_client_connect()) != SL_OK)
      {
      // put host name and port back and re-connect to original server

      sloc_end();
      strcpy(sloc_hostname, thname);
      sloc_port = tport;
      (void)sloc_ll_client_connect();
      logman("%s:bad rc[%d] from sloc_ll_client_connect", mname, ret);
      return(ret);
      }

   // get and verify the service name

   if ((ret = sloc_sr_char(SL_SEND_SERVICE_NAME, (char *)NULL, sname))
       != SL_OK)
      {
      sloc_end();
      strcpy(sloc_hostname, thname);
      sloc_port = tport;
      (void)sloc_ll_client_connect();
      logman("%s:bad rc[%d] from sloc_sr_char", mname, ret);
      return(ret);
      }

   /* verify that this is a 'socloc' server */

   if (strcmp(sname, SL_SERVICE_NAME))
      {
      sloc_end();
      strcpy(sloc_hostname, thname);
      sloc_port = tport;
      (void)sloc_ll_client_connect();
      logman("%s:not a socloc server", mname);
      return(SL_NOT_A_SOCLOC_SERVER);
      }

   logman("%s:normal exit[0]", mname);
   return(SL_OK);
}

static int sloc_sr_code(int typ, char *parm)
{
   /* Send and receive a message to the 'socloc' server that
      returns only a code. Function will failover to another
      'socloc' server (if one available) upon a socket
      communication error. Function returns a 'socloc' code. */

   char mname[] = "sloc_sr_code";
   int ret, done = FALSE;

   // make sure there is an active connection

   if ((ret = sloc_is_init()) != SL_OK)
      {
      logman("%s:no active connection", mname);
      return(ret);
      }

   /* loop while there is a socket error and other 'socloc'
      servers are available */

   while(!done)
      {
      /* attempt to send command and receive reply */

      ret = sloc_lsr_code(typ, parm);
      logman("%s:sloc_lsr_code rc[%d]", mname, ret);

      /* if a socket communication error, failover */

      if (ret == SL_VC_ERROR)
         {
         logman("%s:server not responding,will failover", mname);

         /* if failover failed, exit */

         if ((ret = sloc_failover()) != SL_OK)
            {
            logman("%s:bad rc[%d] from sloc_failover", mname, ret);
            done = TRUE;
            }
         }
      else
         done = TRUE;
      }

   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

static int sloc_lsr_code(int typ, char *parm)
{
   /* Send and recveive a message to the 'socloc' server that returns
      only a return code. Its ok if 'parm' is NULL or empty.
      Function returns a 'socloc' code. */

   char mname[] = "sloc_sr_code", *mess;
   int len, full_len, ret;

   /* if init has not been called, exit error */

   if ((ret = sloc_is_init()) != SL_OK)
      {
      logman("%s:no socloc server details", mname);
      return(ret);
      }

   // estimate length and alloc send/receive buffer

   len = (parm == (char *)NULL) ? 0 : strlen(parm);
   full_len = len + 5;

   if ((mess = (char *)malloc(full_len)) == (char *)NULL)
      {
      logman("%s:alloc fail[mess]", mname);
      return(SL_MEMORY_FAIL);
      }

   memset(mess, 0, full_len);

   // format send string

   if (parm == (char *)NULL || !len)
      sprintf(mess, "%d", typ);
   else
      sprintf(mess, "%d %s", typ, parm);

   // send message

   if (!ipc_send(slclientSocket, mess))
      {
      logman("%s:bad rc[FALSE] from ipc_send", mname);
      free(mess);
      return(SL_VC_ERROR);
      }

   memset(mess, 0, full_len);

   // receive return code

   if (!ipc_recv(slclientSocket, mess))
      {
      logman("%s:bad rc[FALSE] from ipc_recv", mname);
      free(mess);
      return(SL_VC_ERROR);
      }

   // make sure its a number

   if (!qatoi(mess, &ret))
      {
      logman("%s:bad rc[FALSE] from qatoi[wrd 1]", mname);
      free(mess);
      return(SL_INTERNAL_ERROR);
      }

   free(mess);
   return(ret);
}

static int sloc_sr_char(int typ, char *parm, char *char_out)
{
   /* Send and receive a message to the 'socloc' server that
      returns a char string in addition to the reply code. 
      Function will failover to another
      'socloc' server (if one available) upon a socket
      communication error. Function returns a 'socloc' code. */

   char mname[] = "sloc_sr_char";
   int ret, done = FALSE;

   // make sure there is an active connection

   if ((ret = sloc_is_init()) != SL_OK)
      {
      logman("%s:no active connection", mname);
      return(ret);
      }

   /* loop while there is a socket error and other 'socloc'
      servers are available */

   while(!done)
      {
      /* attempt to send command and receive reply */

      ret = sloc_lsr_char(typ, parm, char_out);
      logman("%s:sloc_lsr_char rc[%d]", mname, ret);

      /* if a socket communication error, failover */

      if (ret == SL_VC_ERROR)
         {
         logman("%s:server not responding,will failover", mname);

         /* if failover failed, exit */

         if ((ret = sloc_failover()) != SL_OK)
            {
            logman("%s:bad rc[%d] from sloc_failover", mname, ret);
            done = TRUE;
            }
         }
      else
         done = TRUE;
      }

   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

static int sloc_lsr_char(int typ, char *parm, char *char_out)
{
   /* Send and recveive a message to the 'socloc' server that returns a
      string as well as a return code. 'char_out' must already
      be allocated to sufficient size for the receiving string.
      Its ok if 'parm' is NULL or empty. Function returns a 'socloc' code. */

   char mname[] = "sloc_sr_char", *mess, tmp[50];
   int len, full_len, nwords, pos, ret;

   /* if init has not been called, exit error */

   if ((ret = sloc_is_init()) != SL_OK)
      {
      logman("%s:no socloc server details", mname);
      return(ret);
      }

   char_out[0] = EOS;

   // estimate length and alloc send/receive buffer

   len = (parm == (char *)NULL) ? 0 : strlen(parm);
   full_len = len + 5;

   if ((mess = (char *)malloc(full_len)) == (char *)NULL)
      {
      logman("%s:alloc fail[mess]", mname);
      return(SL_MEMORY_FAIL);
      }

   memset(mess, 0, full_len);

   // format send string

   if (parm == (char *)NULL || !len)
      sprintf(mess, "%d", typ);
   else
      sprintf(mess, "%d %s", typ, parm);

   // send message

   if (!ipc_send(slclientSocket, mess))
      {
      logman("%s:bad rc[FALSE] from ipc_send", mname);
      free(mess);
      return(SL_VC_ERROR);
      }

   free(mess);

   // re-allocate 'mess' for receive buffer (max size)

   if ((mess = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
      {
      logman("%s:alloc fail[mess]", mname);
      return(SL_MEMORY_FAIL);
      }

   memset(mess, 0, SL_MAXCOMMAND);

   // receive reply

   if (!ipc_recv(slclientSocket, mess))
      {
      logman("%s:bad rc[FALSE] from ipc_recv", mname);
      free(mess);
      return(SL_VC_ERROR);
      }

   // s/b two words in reply

   nwords = command_words(mess);

   // s/b reply code in word one

   if (!command_word(mess, tmp, 1))
      {
      logman("%s:bad rc[FALSE] from command_word[1]", mname);
      free(mess);
      return(SL_INTERNAL_ERROR);
      }

   // make sure its a number

   if (!qatoi(tmp, &ret))
      {
      logman("%s:bad rc[FALSE] from qatoi[wrd 1]", mname);
      free(mess);
      return(SL_INTERNAL_ERROR);
      }

   // if reply is 'ok', load 'char_out'

   if (ret == SL_OK)
      {
      if (nwords < 2)
         {
         logman("%s:expecting at least two words in reply", mname);
         free(mess);
         return(SL_INTERNAL_ERROR);
         }

      // 'char_value' is from the end of the first word

      if ((pos = command_indxword(mess, 2)) == -1)
         {
         logman("%s:bad rc[-1] from command_indxword[2]", mname);
         free(mess);
         return(SL_INTERNAL_ERROR);
         }

      /* if second command word starts with quote backup one */

      if (mess[pos - 1] == '\'' || mess[pos - 1] == '"')
         pos -= 1;

      len = strlen(mess) - pos;
      strncpy(char_out, &mess[pos], len);
      char_out[len] = EOS;
      }

   free(mess);
   return(ret);
}

static int sloc_sr_int(int typ, char *parm, int *intout)
{
   /* Send and receive a message to the 'socloc' server that
      returns an integer in addition to the reply code. 
      Function will failover to another
      'socloc' server (if one available) upon a socket
      communication error. Function returns a 'socloc' code. */

   char mname[] = "sloc_sr_int";
   int ret, done = FALSE;

   // make sure there is an active connection

   if ((ret = sloc_is_init()) != SL_OK)
      {
      logman("%s:no active connection", mname);
      return(ret);
      }

   /* loop while there is a socket error and other 'socloc'
      servers are available */

   while(!done)
      {
      /* attempt to send command and receive reply */

      ret = sloc_lsr_int(typ, parm, intout);
      logman("%s:sloc_lsr_int rc[%d]", mname, ret);

      /* if a socket communication error, failover */

      if (ret == SL_VC_ERROR)
         {
         logman("%s:server not responding,will failover", mname);

         /* if failover failed, exit */

         if ((ret = sloc_failover()) != SL_OK)
            {
            logman("%s:bad rc[%d] from sloc_failover", mname, ret);
            done = TRUE;
            }
         }
      else
         done = TRUE;
      }

   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

static int sloc_lsr_int(int typ, char *parm, int *intout)
{
   /* Send and recveive a message to the 'socloc' server that returns an
      integer as well as a return code. Its ok if 'parm' is NULL or empty. 
      Function returns a 'socloc' code. */

   char mname[] = "sloc_lsr_int", *mess, tmp[50];
   int len, full_len, nwords, pos, ret;

   /* if init has not been called, exit error */

   if ((ret = sloc_is_init()) != SL_OK)
      {
      logman("%s:no socloc server details", mname);
      return(ret);
      }

   *intout = 0;

   // estimate length and alloc send/receive buffer

   len = (parm == (char *)NULL) ? 0 : strlen(parm);
   full_len = len + 5;

   if ((mess = (char *)malloc(full_len)) == (char *)NULL)
      {
      logman("%s:alloc fail[mess]", mname);
      return(SL_MEMORY_FAIL);
      }

   memset(mess, 0, full_len);

   // format send string

   if (parm == (char *)NULL || !len)
      sprintf(mess, "%d", typ);
   else
      sprintf(mess, "%d %s", typ, parm);

   // send message

   if (!ipc_send(slclientSocket, mess))
      {
      logman("%s:bad rc[FALSE] from ipc_send", mname);
      free(mess);
      return(SL_VC_ERROR);
      }

   free(mess);

   // re-allocate 'mess' for receive buffer (max size)

   if ((mess = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
      {
      logman("%s:alloc fail[mess]", mname);
      return(SL_MEMORY_FAIL);
      }

   memset(mess, 0, SL_MAXCOMMAND);

   // receive reply

   if (!ipc_recv(slclientSocket, mess))
      {
      logman("%s:bad rc[FALSE] from ipc_recv", mname);
      free(mess);
      return(SL_VC_ERROR);
      }

   // s/b two words in reply

   nwords = words(mess);

   // s/b reply code in word one

   if (!word(mess, tmp, 1))
      {
      logman("%s:bad rc[FALSE] from word[1]", mname);
      free(mess);
      return(SL_INTERNAL_ERROR);
      }

   // make sure its a number

   if (!qatoi(tmp, &ret))
      {
      logman("%s:bad rc[FALSE] from qatoi[wrd 1]", mname);
      free(mess);
      return(SL_INTERNAL_ERROR);
      }

   // if reply is 'ok', load 'intout'

   if (ret == SL_OK)
      {
      if (nwords < 2)
         {
         logman("%s:expecting at least two words in reply", mname);
         free(mess);
         return(SL_INTERNAL_ERROR);
         }

      if (!word(mess, tmp, 2))
         {
         logman("%s:bad rc[FALSE] from word[2]", mname);
         free(mess);
         return(SL_INTERNAL_ERROR);
         }

      if (!qatoi(tmp, intout))
         {
         logman("%s:bad rc[FALSE] from qatoi[wrd 2]", mname);
         free(mess);
         return(SL_INTERNAL_ERROR);
         }
      }

   free(mess);
   return(ret);
}

static int sloc_sr_long(int typ, char *parm, long *longout)
{
   /* Send and receive a message to the 'socloc' server that
      returns a long integer in addition to the reply code. 
      Function will failover to another
      'socloc' server (if one available) upon a socket
      communication error. Function returns a 'socloc' code. */

   char mname[] = "sloc_sr_long";
   int ret, done = FALSE;

   // make sure there is an active connection

   if ((ret = sloc_is_init()) != SL_OK)
      {
      logman("%s:no active connection", mname);
      return(ret);
      }

   /* loop while there is a socket error and other 'socloc'
      servers are available */

   while(!done)
      {
      /* attempt to send command and receive reply */

      ret = sloc_lsr_long(typ, parm, longout);
      logman("%s:sloc_lsr_long rc[%d]", mname, ret);

      /* if a socket communication error, failover */

      if (ret == SL_VC_ERROR)
         {
         logman("%s:server not responding,will failover", mname);

         /* if failover failed, exit */

         if ((ret = sloc_failover()) != SL_OK)
            {
            logman("%s:bad rc[%d] from sloc_failover", mname, ret);
            done = TRUE;
            }
         }
      else
         done = TRUE;
      }

   logman("%s:normal exit[%d]", mname, ret);
   return(ret);
}

static int sloc_lsr_long(int typ, char *parm, long *longout)
{
   /* Send and recveive a message to the 'socloc' server that returns a
      long integer as well as a return code. Its ok if 'parm' is NULL or empty. 
      Function returns a 'socloc' code. */

   char mname[] = "sloc_lsr_long", *mess, tmp[50];
   int len, full_len, nwords, pos, ret;

   /* if init has not been called, exit error */

   if ((ret = sloc_is_init()) != SL_OK)
      {
      logman("%s:no socloc server details", mname);
      return(ret);
      }

   *longout = 0L;

   // estimate length and alloc send/receive buffer

   len = (parm == (char *)NULL) ? 0 : strlen(parm);
   full_len = len + 5;

   if ((mess = (char *)malloc(full_len)) == (char *)NULL)
      {
      logman("%s:alloc fail[mess]", mname);
      return(SL_MEMORY_FAIL);
      }

   memset(mess, 0, full_len);

   // format send string

   if (parm == (char *)NULL || !len)
      sprintf(mess, "%d", typ);
   else
      sprintf(mess, "%d %s", typ, parm);

   // send message

   if (!ipc_send(slclientSocket, mess))
      {
      logman("%s:bad rc[FALSE] from ipc_send", mname);
      free(mess);
      return(SL_VC_ERROR);
      }

   free(mess);

   // re-allocate 'mess' for receive buffer (max size)

   if ((mess = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
      {
      logman("%s:alloc fail[mess]", mname);
      return(SL_MEMORY_FAIL);
      }

   memset(mess, 0, SL_MAXCOMMAND);

   // receive reply

   if (!ipc_recv(slclientSocket, mess))
      {
      logman("%s:bad rc[FALSE] from ipc_recv", mname);
      free(mess);
      return(SL_VC_ERROR);
      }

   // s/b two words in reply

   nwords = words(mess);

   // s/b reply code in word one

   if (!word(mess, tmp, 1))
      {
      logman("%s:bad rc[FALSE] from word[1]", mname);
      free(mess);
      return(SL_INTERNAL_ERROR);
      }

   // make sure its a number

   if (!qatoi(tmp, &ret))
      {
      logman("%s:bad rc[FALSE] from qatoi[wrd 1]", mname);
      free(mess);
      return(SL_INTERNAL_ERROR);
      }

   // if reply is 'ok', load 'longout'

   if (ret == SL_OK)
      {
      if (nwords < 2)
         {
         logman("%s:expecting at least two words in reply", mname);
         free(mess);
         return(SL_INTERNAL_ERROR);
         }

      if (!word(mess, tmp, 2))
         {
         logman("%s:bad rc[FALSE] from word[2]", mname);
         free(mess);
         return(SL_INTERNAL_ERROR);
         }

      if (!qatol(tmp, longout))
         {
         logman("%s:bad rc[FALSE] from qatol[wrd 2]", mname);
         free(mess);
         return(SL_INTERNAL_ERROR);
         }
      }

   free(mess);
   return(ret);
}

static int sloc_failover(void)
{
   /* Failover to another 'socloc' server (if one is available).
      Function returns a 'socloc' code. */

   char mname[] = "sloc_failover";
   int ret;

   logman("%s:enter:port=%d", mname, sloc_port);

   // delete the current entry from the local config link list

   if ((ret = sl_config_delete(sloc_port)) != SL_OK)
      {
      logman("%s:bad rc[%d] from sl_config_delete", mname, ret);
      return(ret);
      }

   // attempt to connect to the first available 'socloc' server

   if ((ret = sloc_client_connect()) != SL_OK)
      {
      logman("%s:bad rc[%d] from sloc_client_connect", mname, ret);
      return(ret);
      }

   logman("%s:normal exit[0]:port=%d", mname, sloc_port);
   return(SL_OK);
}

static int sloc_reload_config_list(void)
{
   /* Get a fresh copy of the socloc config list
      from the current 'socloc' server and place into
      memory. Function returns a 'socloc' code. */

   char mname[] = "sloc_reload_config_list";
   char *thelist;
   int ret;

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

   if ((thelist = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
      {
      logman("%s:alloc fail[thelist]", mname);
      return(SL_MEMORY_FAIL);
      }

   // get the config list from the current 'socloc' server

   if ((ret = sloc_config_get_list(thelist)) != SL_OK)
      {
      logman("%s:bad rc[%d] from sloc_config_get_list", mname, ret);
      free(thelist);
      return(ret);
      }

   // create a new list in memory

   if ((ret = sl_config_put_list(thelist)) != SL_OK)
      {
      logman("%s:bad rc[%d] from sl_config_put_list", mname, ret);
      free(thelist);
      return(ret);
      }

   free(thelist);
   logman("%s:normal exit[0]", mname);
   return(SL_OK);
}

static int sloc_client_connect(void)
{
   /* Attempt to connect to a 'socloc' server. The host name
      and port number should have already been initialized by
      calling 'sloc_initialize'. If the initialization routine
      has not been called, it is an error. If the current 'socloc'
      server has been found to be not responding, another
      'socloc' server will attempt to be found. Function
      returns 'SL_OK' if a connection was made to any
      'socloc' server, a 'socloc' code otherwise. */

   char mname[] = "sloc_client_connect";
   int ret;

   logman("%s:enter,sloc_hostname=%s,sloc_port=%d", mname,
                 sloc_hostname, sloc_port);

   // if there is an active connection, close it

   if ((ret = sloc_is_init()) == SL_OK)
      {
      logman("%s:active connection found,closing it", mname, ret);
      sloc_end();
      }

   /* loop which attempts to connect to currently listed
      'socloc' server, if this fails, get next active and
      try */

   while(1)
      {
      // get top entry in config link list

      if ((ret = sl_config_get_first(sloc_hostname, &sloc_port,
           (char *)NULL)) != SL_OK)
         {
         logman("%s:bad rc[%d] from sl_config_get_first", mname, ret);
         return(ret);
         }

      // open the socket and attempt a connection

      if ((ret = sloc_ll_client_connect()) == SL_OK)
         {
         /* if connection was successful, get a new copy
            of the config link list from the server and
            place it into memory */

         (void)sloc_reload_config_list();
         logman("%s:exit[0],connected to server %s", mname, 
                       sloc_hostname);
         return(ret);
         }

      logman("%s:server %s not responding,rc=%d", mname,
                    sloc_hostname, ret);

      // delete the current entry

      if ((ret = sl_config_delete(sloc_port)) != SL_OK)
         {
         logman("%s:bad rc[%d] from sl_config_delete", mname, ret);
         return(ret);
         }

      /* wipe out current host name and port */

      sloc_hostname[0] = EOS;
      sloc_port = 0;
      }

   /* we should never get here, but just in case */

   logman("%s:unanticipated wacky exit SL_NO_SUCH_SOCLOC", mname);
   return(SL_NO_SUCH_SOCLOC);
}

static int sloc_ll_client_connect(void)
{
   /* Connect to the 'socloc' server. An attempt to open the TCP
      socket is made. The host name 'sloc_hostname' and the
      TCP port 'sloc_port' are assumed to be already loaded
      via 'sloc_initialize'. Function returns 'SL_OK' if a
      successful connection was made, a 'socloc' code otherwise.
      Private function. */

   int ret;

   // make sure host name and port are loaded

   if ((ret = sloc_is_init()) != SL_OK)
      return(ret);

   // resolve server host name

   lpHostEnt = gethostbyname(sloc_hostname);

   if (!lpHostEnt)
      return(SL_VC_ERROR);

   // create the socket

#ifdef OS_WIN32
   slclientSocket = socket(PF_INET, SOCK_STREAM, DEFAULT_PROTOCOL);
#endif

#ifdef OS_UNIX
   slclientSocket = socket(AF_INET, SOCK_STREAM, DEFAULT_PROTOCOL);
#endif

   if (slclientSocket == INVALID_SOCKET)
      return(SL_VC_ERROR);

   // load client address data

   memset(&slsockClientAddr, 0, sizeof(slsockClientAddr));
   slsockClientAddr.sin_family = AF_INET;
   slsockClientAddr.sin_port = htons(sloc_port);

#ifdef OS_WIN32
   slsockClientAddr.sin_addr = *((LPIN_ADDR)*lpHostEnt->h_addr_list);
#endif

#ifdef OS_UNIX
   slsockClientAddr.sin_addr.s_addr = ((struct in_addr *)(lpHostEnt->h_addr))->s_addr;
#endif

   // connect to server

#ifdef OS_WIN32
   if (connect(slclientSocket, (LPSOCKADDR)&slsockClientAddr,
               sizeof(slsockClientAddr)))
#endif

#ifdef OS_UNIX
   if (connect(slclientSocket, (SA *)&slsockClientAddr,
               sizeof(slsockClientAddr)) == SOCKET_ERROR)
#endif

      return(SL_VC_ERROR);

   return(SL_OK);
}
