## root/clib/timesapi.c

/* [<][>][^][v][top][bottom][index][help] */

## DEFINITIONS

This source file includes following definitions.

1. timesync_init
2. timesync_end
3. timesync_get_datime
4. timesync_status
5. timesync_stop
6. timesync_log_off
7. timesync_log_on
8. timesync_log_status
9. timesync_service_name
10. timesync_version
11. timesync_trans_num
12. timesync_connect_num
13. timesync_get_active
14. ts_sr_code
15. ts_sr_char
16. ts_sr_long
17. ts_sr_int
18. ts_failover
19. ts_lsr_code
20. ts_lsr_char
21. ts_lsr_long
22. ts_lsr_int
23. ts_client_connect
24. ts_header

    /* Timesync client API (TCP and QNX message passing IPC methods).
       Rick Smereka, Copyright (C) 2001-2004.

       This API uses the 'socloc' server to obtain the TCP/IP
       details of a 'timesync' server (TCP IPC method only). You must
       have already initialized the 'socloc' API by calling
       'sloc_initialize'. There must be at least one running
       'socloc' server and one running 'timesync' server.
       Automatic failover of the 'socloc' server is provided
       in the 'socloc' API. Automatic failover of the 'timesync'
       server is provided in this API (provided there is another
       server to failover to). Note that this interface does not
       use the IPC library (with the exception of the 'IPCOMM.C'
       low level routines) since the client of this API might
       be a TCP server whose executable will be bound
       to the IPC routines in 'IPCSRV.C'.

       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

       Original QNX version and ported to Red Hat Linux and Windows 32bit.
       Dec/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 'timesync_end' when the socket is no longer
       required.

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

       Added functions 'timesync_trans_num', 'timesync_connect_num' and
       'timesync_end'. Jun/2003, Rick Smereka

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

    #include "stdhead.h"
    #include "flsocket.h"
    #include "timesync.h"

    #ifdef IPC_TCP
    #include "ipcomm.h"
    #include "socloc.h"
    #include "sloc.h"
    #include "sliocode.h"
    #endif

    // global (to module) data

    #ifdef IPC_TCP
    #ifdef OS_WIN32
    SOCKET ts_clientSocket;           // client socket
    SOCKADDR_IN ts_sockClientAddr;    // client address structure
    LPHOSTENT ts_lpHostEnt;           // host info structure
    #endif

    #ifdef OS_UNIX
    int ts_clientSocket;
    struct sockaddr_in ts_sockClientAddr;
    struct hostent *ts_lpHostEnt;
    #endif

    char ts_hostname[128];       // host name of timesync server
    int ts_port = 0;             // port server is using
    int ts_is_connect;           // have we already connected?
    #else
    pid_t ts_pid = -1;           // QNX message passing PID
    #endif

    // private function prototypes

    static int ts_sr_code(int, char *);
    static int ts_sr_char(int, char *, char *);
    static int ts_sr_long(int, char *, long *);
    static int ts_sr_int(int, char *, int *);
    static int ts_lsr_code(int, char *);
    static int ts_lsr_char(int, char *, char *);
    static int ts_lsr_long(int, char *, long *);
    static int ts_lsr_int(int, char *, int *);
    static void ts_header(char *);

    #ifdef IPC_TCP
    static int ts_failover(int);
    static int ts_client_connect(void);
    #endif

    int timesync_init(void)
    {
       /* Initialize the client for communication with the
          timesync server. This function will attempt to find a
          running 'timesync' server by using the 'socloc' API.
          A check is made to make sure that the socloc interface
          has already been initialized. Note that 'socloc' is used
          only in TCP IPC. Function returns a 'timesync' code. */

       char mname[] = "timesync_init";
       char mes[128];
       int ret;

       ts_header(mname);

    #ifdef IPC_TCP
       ts_hostname[0] = EOS;
       ts_port = ts_is_connect = 0;

       /* make sure 'socloc' has already been initialized */

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

       /* locate a 'timesync' server */

       if ((ret = sloc_find(TS_SERVICE_NAME, ts_hostname,
                            &ts_port, (char *)NULL)) != SL_OK)
          /* it is assumed that the failure is 'not found' */
          {
          sl_code_string(ret, mes);
          logman("%s:bad rc[%d,%s] from 'sloc_find'", mname,
                        ret, mes);
          return(TS_NO_SERVER);
          }

       if ((ret = ts_client_connect()) != TS_OK)
          {
          ts_code_string(ret, mes);
          logman("%s:bad rc[%s] from ts_client_connect", mname, mes);
          return(ret);
          }
    #else
       if ((ts_pid = qnx_name_locate(0, TS_SERVICE_NAME, 0, NULL)) == -1)
          {
          logman("%s:bad rc[-1] from qnx_name_locate", mname);
          return(TS_NO_SERVER);
          }
    #endif

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

    void timesync_end(void)
    {
       // API shutdown.

       char mname[] = "timesync_end";

       ts_header(mname);

    #ifdef IPC_TCP
       ts_hostname[0] = EOS;
       ts_port = 0;
       ts_is_connect = FALSE;

       // only close socket upon API shutdown

    #ifdef OS_WIN32
       (void)closesocket(ts_clientSocket);
    #else
       close(ts_clientSocket);
    #endif
    #else
       ts_pid = -1;
    #endif

       logman("%s:normal exit", mname);
    }

    int timesync_get_datime(int *year, int *month, int *day, int *hour,
                            int *minute, int *second)
    {
       /* Obtain the current date and time from the timesync server.
          Upon success, the date and time details will be loaded.
          Function returns a 'timesync' code. */

       char mname[] = "timesync_get_datime", word[5], *reply;
       int ret;

       ts_header(mname);

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

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

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

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

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

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

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

       if ((ret = ts_sr_char(TS_SEND_GET_DATIME, (char *)NULL, reply))
           != TS_OK)
          {
          logman("%s:bad rc[%d] from 'ts_sr_char'", mname, ret);
          free(reply);
          return(ret);
          }

       /* break returned date/time string into component parts */

       /* year */

       strncpy(word, reply, 4);
       word[4] = EOS;

       if (!qatoi(word, year))
          {
          logman("%s:non-numeric[year]", mname);
          free(reply);
          return(TS_INTERNAL_ERROR);
          }

       /* month */

       strncpy(word, &reply[4], 2);
       word[2] = EOS;

       if (!qatoi(word, month))
          {
          logman("%s:non-numeric[month]", mname);
          free(reply);
          return(TS_INTERNAL_ERROR);
          }

       /* day */

       strncpy(word, &reply[6], 2);
       word[2] = EOS;

       if (!qatoi(word, day))
          {
          logman("%s:non-numeric[day]", mname);
          free(reply);
          return(TS_INTERNAL_ERROR);
          }

       /* hour */

       strncpy(word, &reply[8], 2);
       word[2] = EOS;

       if (!qatoi(word, hour))
          {
          logman("%s:non-numeric[hour]", mname);
          free(reply);
          return(TS_INTERNAL_ERROR);
          }

       /* minute */

       strncpy(word, &reply[10], 2);
       word[2] = EOS;

       if (!qatoi(word, minute))
          {
          logman("%s:non-numeric[minute]", mname);
          free(reply);
          return(TS_INTERNAL_ERROR);
          }

       /* second */

       strncpy(word, &reply[12], 2);
       word[2] = EOS;

       if (!qatoi(word, second))
          {
          logman("%s:non-numeric[second]", mname);
          free(reply);
          return(TS_INTERNAL_ERROR);
          }

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

    int timesync_status(void)
    {
       /* Get the current status of the timesync server.
          Function returns 'TS_OK' upon success, another
          'timesync' code otherwise. */

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

       ts_header(mname);
       ret = ts_sr_code(TS_SEND_STATUS, (char *)NULL);
       logman("%s:normal exit,rc[%d]", mname, ret);
       return(ret);
    }

    int timesync_stop(char *passwd)
    {
       /* Attempt to halt the timesync server. Server
          password is expected in 'passwd'. Function returns
          a 'timesync' code. */

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

       ts_header(mname);

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

       ret = ts_sr_code(TS_SEND_TERM, passwd);

       /* if termination was successful, attempt to
          failover to another 'timesync' server (TCP only) */

    #ifdef IPC_TCP
       if (ret == TS_OK)
          ret = ts_failover(FALSE);
    #endif

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

    int timesync_log_off(void)
    {
       /* Implementation of the common socket send code to turn the
          server log off. */

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

       ts_header(mname);
       ret = ts_sr_code(TS_SEND_LOG_OFF, (char *)NULL);
       logman("%s:normal exit,rc[%d]", mname, ret);
       return(ret);
    }

    int timesync_log_on(char *lfname)
    {
       /* Implementation of the common socket send code to turn the
          log on. */

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

       ts_header(mname);
       ret = ts_sr_code(TS_SEND_LOG_ON, lfname);
       logman("%s:normal exit[%d]", mname, ret);
       return(ret);
    }

    int timesync_log_status(int *lflag, char *lfname)
    {
       /* Get status of 'timesync' 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 'timesync' code. */

       char mname[] = "timeysnc_log_status";
       char *reply, tmp[25];
       int ret, flag;

       ts_header(mname);

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

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

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

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

       if ((ret = ts_sr_char(TS_SEND_LOG_STATUS, (char *)NULL, reply))
           != TS_OK)
          {
          logman("%s:bad rc[%d] from 'ts_sr_char'", mname, ret);
          free(reply);
          return(ret);
          }

       if (!command_word(reply, tmp, 1))
          {
          logman("%s:parse error[flag_char]", mname);
          free(reply);
          return(TS_INTERNAL_ERROR);
          }

       if (!qatoi(tmp, &flag))
          {
          logman("%s:status flag not numeric", mname);
          free(reply);
          return(TS_INTERNAL_ERROR);
          }

       if (flag != 0 && flag != 1)
          {
          logman("%s:status flag not zero or one", mname);
          free(reply);
          return(TS_INTERNAL_ERROR);
          }

       *lflag = flag;

       if (!command_word(reply, lfname, 2))
          {
          logman("%s:parse error[lfname]", mname);
          free(reply);
          return(TS_INTERNAL_ERROR);
          }

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

    int timesync_service_name(char *sname)
    {
       /* Get the 'timesync' server service name which should always
          be 'timesync'. Function returns the service name in 'sname'
          upon success which already be allocated to sufficient size.
          Function returns a 'timesync' code. */

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

       ts_header(mname);

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

       sname[0] = EOS;
       ret = ts_sr_char(TS_SEND_SERVICE_NAME, (char *)NULL, sname);
       logman("%s:normal exit,rc[%d]", mname, ret);
       return(ret);
    }

    int timesync_version(char *ver)
    {
       /* Get the 'timesync' server version ID string. Function returns the
          version string in 'ver' upon success which must already be
          allocated to sufficient size. Function returns a 'timesync'
          code. */

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

       ts_header(mname);

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

       ver[0] = EOS;
       ret = ts_sr_char(TS_SEND_VERSION, (char *)NULL, ver);
       logman("%s:normal exit,rc[%d]", mname, ret);
       return(ret);
    }

    int timesync_trans_num(long *trans_num)
    {
       /* Get the 'timesync' server current transaction count.
          Function returns the transaction count in 'trans_num'
          upon success. Function returns a 'timesync' code. */

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

       ts_header(mname);

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

       *trans_num = 0L;
       ret = ts_sr_long(TS_SEND_TRANS_NUM, (char *)NULL, trans_num);
       logman("%s:normal exit,rc[%d]", mname, ret);
       return(ret);
    }

    int timesync_connect_num(int *connect_num)
    {
       /* Get the 'timesync' server current connection count.
          Function returns the connection count in 'connect_num'
          upon success. Function returns a 'timesync' code. */

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

       ts_header(mname);

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

       *connect_num = 0;
       ret = ts_sr_int(TS_SEND_CONNECT_NUM, (char *)NULL, connect_num);
       logman("%s:normal exit,rc[%d]", mname, ret);
       return(ret);
    }

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

       if (hname == (char *)NULL || port == (int *)NULL)
          return;

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

       if (!strlen(ts_hostname) || ts_port == 0)
          return;

       strcpy(hname, ts_hostname);
       *port = ts_port;
    }
    #endif

    // private functions

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

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

       ts_header(mname);

       /* make sure server specs are present */

    #ifdef IPC_TCP
       if (!ts_port || ts_hostname == (char *)NULL ||
           !strlen(ts_hostname))
    #else
       if (ts_pid == -1)
    #endif
          {
          logman("%s:no init or no current timesync server", mname);
          return(TS_NO_SERVER);
          }

    #ifdef IPC_QNX
       ret = ts_lsr_code(typ, parm);
    #else
       /* loop while there is a socket error and other 'timesync'
          servers are available */

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

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

          /* if a socket communication error, failover */

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

             /* if failover failed, exit */

             if ((ret = ts_failover(TRUE)) != TS_OK)
                {
                logman("%s:bad rc[%d] from 'ts_failover'",
                              mname, ret);
                done = TRUE;
                }
             }
          else
             done = TRUE;
          }
    #endif

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

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

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

       ts_header(mname);

       /* make sure server specs are present */

    #ifdef IPC_TCP
       if (!ts_port || ts_hostname == (char *)NULL ||
           !strlen(ts_hostname))
    #else
       if (ts_pid == -1)
    #endif
          {
          logman("%s:no init or no current timesync server", mname);
          return(TS_NO_SERVER);
          }

    #ifdef IPC_QNX
       ret = ts_lsr_char(typ, parm, char_out);
    #else
       /* loop while there is a socket error and other 'timesync'
          servers are available */

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

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

          /* if a socket communication error, failover */

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

             /* if failover failed, exit */

             if ((ret = ts_failover(TRUE)) != TS_OK)
                {
                logman("%s:bad rc[%d] from 'ts_failover'",
                              mname, ret);
                done = TRUE;
                }
             }
          else
             done = TRUE;
          }
    #endif

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

    static int ts_sr_long(int typ, char *parm, long *long_out)
    {
       /* Send and receive a message to the 'timesync' server that
          returns a long integer in addition to the return code. Function
          will failover to another 'timesync' server (if one available)
          upon a socket communication error (TCP only). Function returns
          a 'timesync' code. */

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

       ts_header(mname);

       /* make sure server specs are present */

    #ifdef IPC_TCP
       if (!ts_port || ts_hostname == (char *)NULL ||
           !strlen(ts_hostname))
    #else
       if (ts_pid == -1)
    #endif
          {
          logman("%s:no init or no current timesync server", mname);
          return(TS_NO_SERVER);
          }

    #ifdef IPC_QNX
       ret = ts_lsr_long(typ, parm, long_out);
    #else
       /* loop while there is a socket error and other 'timesync'
          servers are available */

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

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

          /* if a socket communication error, failover */

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

             /* if failover failed, exit */

             if ((ret = ts_failover(TRUE)) != TS_OK)
                {
                logman("%s:bad rc[%d] from 'ts_failover'",
                              mname, ret);
                done = TRUE;
                }
             }
          else
             done = TRUE;
          }
    #endif

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

    static int ts_sr_int(int typ, char *parm, int *int_out)
    {
       /* Send and receive a message to the 'timesync' server that
          returns an integer in addition to the return code. Function
          will failover to another 'timesync' server (if one available)
          upon a socket communication error (TCP only). Function returns
          a 'timesync' code. */

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

       ts_header(mname);

       /* make sure server specs are present */

    #ifdef IPC_TCP
       if (!ts_port || ts_hostname == (char *)NULL ||
           !strlen(ts_hostname))
    #else
       if (ts_pid == -1)
    #endif
          {
          logman("%s:no init or no current timesync server", mname);
          return(TS_NO_SERVER);
          }

    #ifdef IPC_QNX
       ret = ts_lsr_int(typ, parm, int_out);
    #else
       /* loop while there is a socket error and other 'timesync'
          servers are available */

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

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

          /* if a socket communication error, failover */

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

             /* if failover failed, exit */

             if ((ret = ts_failover(TRUE)) != TS_OK)
                {
                logman("%s:bad rc[%d] from 'ts_failover'",
                              mname, ret);
                done = TRUE;
                }
             }
          else
             done = TRUE;
          }
    #endif

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

    #ifdef IPC_TCP
    static int ts_failover(int dflag)
    {
       /* Failover to another 'timesync' server. The flag 'dflag'
          indicates whether we should get 'socloc' to delete the
          old entry. */

       char mname[] = "ts_failover";
       char mes[128];
       int ret;

       ts_header(mname);
       ts_is_connect = FALSE;

       // close current socket

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

       if (dflag)
          if ((ret = sloc_delete(ts_port)) != SL_OK)
             {
             /* if delete fails log a message but continue */

             sl_code_string(ret, mes);
             logman("%s:bad rc[%s] from 'sloc_delete'", mname, mes);
             }

       ts_hostname[0] = EOS;
       ts_port = 0;

       if ((ret = sloc_find(TS_SERVICE_NAME, ts_hostname, &ts_port,
           (char *)NULL)) != SL_OK)
          {
          sl_code_string(ret, mes);
          logman("%s:bad rc[%s] from 'sloc_find'", mname, mes);
          return(TS_NO_SERVER);
          }

       logman("%s:normal exit,rc[0]", mname);
       return(TS_OK);
    }
    #endif

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

       char *mess, *qparm, mname[] = "ts_lsr_code";
       char tmp[256];
       int len, full_len, ret;

    #ifdef IPC_QNX
       int i, sent = FALSE;
    #endif

       ts_header(mname);

       /* make sure server specs are present */

    #ifdef IPC_TCP
       if (!ts_port || ts_hostname == (char *)NULL ||
           !strlen(ts_hostname))
    #else
       if (ts_pid == -1)
    #endif
          {
          logman("%s:timesync server host empty or port zero", mname);
          return(TS_NO_SERVER);
          }

       /* estimate length and alloc send/receive buffer */

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

       /* quote 'parm' if necessary */

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

          logman("%s:qparm=%s,%d", mname, qparm, strlen(qparm));
          }

       if ((mess = (char *)malloc(full_len)) == (char *)NULL)
          {
          if (len)
             free(qparm);

          logman("%s:alloc fail[mess]", mname);
          return(TS_MEMORY_FAIL);
          }

       memset(mess, 0, full_len);

       /* format send string */

       if (!len)
          sprintf(mess, "%d", typ);
       else
          {
          sprintf(mess, "%d %s", typ, qparm);
          free(qparm);
          }

       logman("%s:mess=%s,%d", mname, mess, strlen(mess));

       /* send message */

    #ifdef IPC_QNX
       if ((qparm = (char *)malloc(full_len)) == NULL)
          return(TS_MEMORY_FAIL);

       for(i = 0; i < IPC_SEND_RETRY; i++)
          if (!Send(ts_pid, mess, qparm, strlen(mess) + 1, full_len))
             {
             sent = TRUE;
             break;
             }

       if (!sent)
          {
          free(qparm);
          return(TS_VC_ERROR);
          }

       strcpy(mess, qparm);
       free(qparm);
    #else
       if (!ipc_send(ts_clientSocket, mess))
          {
          free(mess);
          logman("%s:socket error sending message", mname);
          return(TS_VC_ERROR);
          }

       logman("%s:message sent successfully", mname);
       memset(mess, 0, full_len);

       /* receive return code */

       if (!ipc_recv(ts_clientSocket, mess))
          {
          logman("%s:socket error receiving message", mname);
          free(mess);
          return(TS_VC_ERROR);
          }
    #endif

       logman("%s:message received successfully,mess=%s,%d", mname,
                      mess, strlen(mess));

       /* make sure its a number */

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

       free(mess);
       ts_code_string(ret, tmp);
       logman("%s:server rc[%s]", mname, tmp);
       logman("%s:normal exit[%d]", mname, ret);
       return(ret);
    }

    static int ts_lsr_char(int typ, char *parm, char *char_out)
    {
       /* Send and recveive a message to the 'timesync' 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
          'timesync' code. Private function. */

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

    #ifdef IPC_QNX
       int i, sent = FALSE;
    #endif

       /* make sure server specs are present */

    #ifdef IPC_TCP
       if (!ts_port || ts_hostname == (char *)NULL ||
           !strlen(ts_hostname))
    #else
       if (ts_pid == -1)
    #endif
          {
          logman("%s:timesync server host empty or port zero", mname);
          return(TS_NO_SERVER);
          }

       char_out[0] = EOS;

       /* estimate length and alloc send/receive buffer */

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

       /* quote 'parm' if necessary */

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

       if ((mess = (char *)malloc(full_len)) == (char *)NULL)
          {
          if (len)
             free(qparm);

          logman("%s:alloc fail[mess]", mname);
          return(TS_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, qparm);
          free(qparm);
          }

       /* send message */

    #ifdef IPC_QNX
       if ((qparm = (char *)malloc(TS_MAXCOMMAND)) == NULL)
          return(TS_MEMORY_FAIL);

       for(i = 0; i < IPC_SEND_RETRY; i++)
          if (!Send(ts_pid, mess, qparm, strlen(mess) + 1, TS_MAXCOMMAND - 1))
             {
             sent = TRUE;
             break;
             }

       if (!sent)
          {
          free(qparm);
          return(TS_VC_ERROR);
          }

       free(mess);

       if ((mess = initstring(qparm)) == (char *)NULL)
          return(TS_MEMORY_FAIL);

       strcpy(mess, qparm);
       free(qparm);
    #else
       if (!ipc_send(ts_clientSocket, mess))
          {
          free(mess);
          logman("%s:socket error sending message", mname);
          return(TS_VC_ERROR);
          }

       free(mess);

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

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

       memset(mess, 0, TS_MAXCOMMAND);

       /* receive reply */

       if (!ipc_recv(ts_clientSocket, mess))
          {
          free(mess);
          logman("%s:socket error receiving message", mname);
          return(TS_VC_ERROR);
          }
    #endif

       /* s/b two words in reply */

       nwords = command_words(mess);

       /* s/b reply code in word one */

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

       /* make sure its a number */

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

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

       if (ret == TS_OK)
          {
          if (nwords < 2)
             {
             free(mess);
             logman("%s:expecting at least two command words", mname);
             return(TS_INTERNAL_ERROR);
             }

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

          if ((pos = command_indxword(mess, 2)) == -1)
             {
             free(mess);
             logman("%s:bad rc[-1] from command_indxword", mname);
             return(TS_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 ts_lsr_long(int typ, char *parm, long *long_out)
    {
       /* Send and recveive a message to the 'timesync' server that
          returns a long integer as well as a return code.
          Its ok if 'parm' is NULL or empty. Function returns a
          'timesync' code. Private function. */

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

    #ifdef IPC_QNX
       int i, sent = FALSE;
    #endif

       /* make sure server specs are present */

    #ifdef IPC_TCP
       if (!ts_port || ts_hostname == (char *)NULL ||
           !strlen(ts_hostname))
    #else
       if (ts_pid == -1)
    #endif
          {
          logman("%s:no connect to a timesync server", mname);
          return(TS_NO_SERVER);
          }

       *long_out = 0L;

       /* estimate length and alloc send/receive buffer */

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

       /* quote 'parm' if necessary */

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

       if ((mess = (char *)malloc(full_len)) == (char *)NULL)
          {
          logman("%s:alloc fail[mess]", mname);
          return(TS_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, qparm);
          free(qparm);
          }

       /* send message */

    #ifdef IPC_QNX
       if ((qparm = (char *)malloc(TS_MAXCOMMAND)) == NULL)
          return(TS_MEMORY_FAIL);

       for(i = 0; i < IPC_SEND_RETRY; i++)
          if (!Send(ts_pid, mess, qparm, strlen(mess) + 1, TS_MAXCOMMAND - 1))
             {
             sent = TRUE;
             break;
             }

       if (!sent)
          {
          free(qparm);
          return(TS_VC_ERROR);
          }

       free(mess);

       if ((mess = initstring(qparm)) == (char *)NULL)
          return(TS_MEMORY_FAIL);

       strcpy(mess, qparm);
       free(qparm);
    #else
       if (!ipc_send(ts_clientSocket, mess))
          {
          logman("%s:bad rc[FALSE] from ipc_send", mname);
          free(mess);
          return(TS_VC_ERROR);
          }

       free(mess);

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

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

       memset(mess, 0, TS_MAXCOMMAND);

       /* receive reply */

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

       /* s/b two words in reply */

       nwords = words(mess);

       /* s/b reply code in word one */

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

       /* make sure its a number */

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

       /* if reply is 'ok', load 'long_out' */

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

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

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

       free(mess);
       return(ret);
    }

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

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

    #ifdef IPC_QNX
       int i, sent = FALSE;
    #endif

       /* make sure server specs are present */

    #ifdef IPC_TCP
       if (!ts_port || ts_hostname == (char *)NULL ||
           !strlen(ts_hostname))
    #else
       if (ts_pid == -1)
    #endif
          {
          logman("%s:no connect to a timesync server", mname);
          return(TS_NO_SERVER);
          }

       *int_out = 0;

       /* estimate length and alloc send/receive buffer */

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

       /* quote 'parm' if necessary */

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

       if ((mess = (char *)malloc(full_len)) == (char *)NULL)
          {
          logman("%s:alloc fail[mess]", mname);
          return(TS_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, qparm);
          free(qparm);
          }

       /* send message */

    #ifdef IPC_QNX
       if ((qparm = (char *)malloc(TS_MAXCOMMAND)) == NULL)
          return(TS_MEMORY_FAIL);

       for(i = 0; i < IPC_SEND_RETRY; i++)
          if (!Send(ts_pid, mess, qparm, strlen(mess) + 1, TS_MAXCOMMAND - 1))
             {
             sent = TRUE;
             break;
             }

       if (!sent)
          {
          free(qparm);
          return(TS_VC_ERROR);
          }

       free(mess);

       if ((mess = initstring(qparm)) == (char *)NULL)
          return(TS_MEMORY_FAIL);

       strcpy(mess, qparm);
       free(qparm);
    #else
       if (!ipc_send(ts_clientSocket, mess))
          {
          logman("%s:bad rc[FALSE] from ipc_send", mname);
          free(mess);
          return(TS_VC_ERROR);
          }

       free(mess);

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

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

       memset(mess, 0, TS_MAXCOMMAND);

       /* receive reply */

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

       /* s/b two words in reply */

       nwords = words(mess);

       /* s/b reply code in word one */

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

       /* make sure its a number */

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

       /* if reply is 'ok', load 'long_out' */

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

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

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

       free(mess);
       return(ret);
    }

    #ifdef IPC_TCP
    static int ts_client_connect(void)
    {
       /* Connect to the 'timesync' server. An attempt to open the TCP
          socket is made. Function returns a 'timesync' code. Private
          function. */

       char mname[] = "ts_client_connect";

       ts_header(mname);

       if (ts_is_connect)
          {
          logman("%s:already connected", mname);
          return(TS_OK);
          }

       // resolve server host name

       ts_lpHostEnt = gethostbyname(ts_hostname);

       if (!ts_lpHostEnt)
          {
          logman("%s:unable to resolve timesync server host name", mname);
          return(TS_VC_ERROR);
          }

       // create the socket

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

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

       if (ts_clientSocket == INVALID_SOCKET)
          {
          logman("%s:unable to open the timesync server socket", mname);
          return(TS_VC_ERROR);
          }

       // load client address data
       memset(&ts_sockClientAddr, 0, sizeof(ts_sockClientAddr));
       ts_sockClientAddr.sin_family = AF_INET;
       ts_sockClientAddr.sin_port = htons(ts_port);

    #ifdef OS_WIN32
       ts_sockClientAddr.sin_addr = *((LPIN_ADDR)*ts_lpHostEnt->h_addr_list);
    #endif

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

       // connect to server

    #ifdef OS_WIN32
       if (connect(ts_clientSocket, (LPSOCKADDR)&ts_sockClientAddr,
           sizeof(ts_sockClientAddr)))
    #endif

    #ifdef OS_UNIX
       if (connect(ts_clientSocket, (SA *)&ts_sockClientAddr,
           sizeof(ts_sockClientAddr)) == SOCKET_ERROR)
    #endif
          {
          logman("%s:error connecting the socket to the timesync "
                        "server", mname);
          return(TS_VC_ERROR);
          }

       ts_is_connect = TRUE;
       logman("%s:normal exit[%d]", mname, TS_OK);
       return(TS_OK);
    }
    #endif

    static void ts_header(char *mname)
    {
       /* Start of function logging header. */

       if (!is_log_active())
          return;

    #ifdef IPC_TCP
       if (ts_port == 0 || !strlen(ts_hostname))
          return;

       logman("%s:enter:host=%s,port=%d", mname, ts_hostname,
                     ts_port);
    #else
       logman("%s:enter", mname);
    #endif
    }

/* [<][>][^][v][top][bottom][index][help] */