## root/source/sys_log.c

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

## DEFINITIONS

This source file includes following definitions.

1. main
2. parse_comline
3. srv_initialize
4. process_request
5. s_message
6. s_terminate
7. s_trans_num
8. s_connect_num
9. sys_log_reply
10. output_log
11. output_log_nf
12. open_log
13. open_alt_log
14. term_app
15. usage

    /* sys_log - A system log server. This program will take messages
       sent to it and log the message text in the system log file.

       Program works by receiving messages, acting on them
       and replying. Most messages are instructions to send
       a string to the system log file. There are actually
       two log files, the primary and alternate. When the
       primary log file is locked, data will be written
       to the alternate log file. The primary log file
       is normally left closed just in case. You can,
       however, manually open and close this file.
       Rick Smereka, Copyright (C) 1997-2006.

       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 Feb/97, Rick Smereka

       Changed to receive the node ID (nid) in the send
       message structure and print this and the calling
       process ID (pid) on the output line. Dec/97,
       Rick Smereka

       Resolved a bug that caused the incorrect 'nid' and
       'pid' of the system logger process to be output to
       the log. Added function 'output_log_local' which will
       preload the system logger process 'nid' and 'tid'.
       Jan/98, Rick Smereka

       Ported to 32bit Windows under CodeWarrior. See
       'sys_log.h' for message types and explaination
       of syntax. Server is TCP connection oriented
       iterative. TCP communication is accomplished
       through the library files 'ipcomm.c' (low level
       send/receive) and 'ipcsrv.c' (application
       level). TCP configuration is managed by
       'ipconfig.c'. Dec/98, Rick Smereka

       Ported to HP-UX under GNU C 2.8.1.
       Jan/99, Rick Smereka

       Implemented multi-platform program logo.
       Feb/99, Rick Smereka

       Ported to Red Hat Linux 5.2, Jul/99, Rick Smereka

       Corrected situations in 'process_request' where a
       reply would not be sent back upon certain error
       conditions. Changed to use the 'socloc' interface
       instead of the 'sys_log.ipc' config file. Changed
       to allow command line options consisting of
       the TCP port number that the server will listen to.
       Also added processing of all common socket send codes.
       Program syntax now is:

          sys_log [-l log_file] [-a alternate_log_file] port

       Where 'port' is the TCP port to listen, '-l log_file'
       is the optional name of the server primary log file and
       '-a alternate_log_file' is the optional name of the server
       alternate log file. If no primary log file is given, the
       log file name will be 'SYS_LOG_FNAME' in the current
       directory. If no alternate log file is given, the
       log file name will be 'SYS_LOG_ALT_FNAME' in the current
       directory. Note that the switch character ('-')
       is based on the platform (see include file 'stdhead.h').
       Mar/2000, Rick Smereka

       Re-compiled after re-structuring the definition of the common
       send and repy codes. Added the option '-q' to control message
       output to the console. Program syntax now is:

          sys_log [-q] [-l log_file] [-a alt_log_file] port

       Default value for console output is on, the '-q' parameter
       (quiet) will turn it off. When console output is off, no
       messages will be output to 'stdout' or 'stderr'. All output
       will appear only in the log. Apr/2000, Rick Smereka

       Modified function 'srv_initialize' to obtain the IP address of
       the current machine and pass this to the socloc function
       'sloc_add'. Jul/2000, Rick Smereka

       Modified for use by both sockets and QNX message passing IPC.
       Since the QNX message passing variant does not use a TCP port
       number, its syntax is:

          sys_log [-q] [-l log_file] [-a alt_log_file]

       Oct/2001, Rick Smereka

       Coded to use the 'appinit' API. May/2002, Rick Smereka

       Modified function 'output_log' to obtain the date/time
       string from function 'get_time'. Sep/2002, Rick Smereka

       Ported to Debian Linux. Jan/2003, Rick Smereka

       Changed to in accordance with the modifications in 'ipcsrv.c'.
       Modified for use with the updated socloc client API ('sloc.c').
       May/2003, Rick Smereka

       Added function 'term_app'. Moved all shutdown code to this
       function. Placed call to this function upon termination.
       Re-wrote function 'output_log' to take a variable number of
       parameters and added function 'output_log_nf'.
       Jun/2003, Rick Smereka

       Changed message when unable to receive data from client
       connection in 'process_request'. Dec/2003,
       Rick Smereka

       Re-compile after changing the 'socloc' API. Added function
       's_message' and changed how message text is obtained. Everything
       passed the message command number is now taken as the message
       text. Jan/2005, Rick Smereka

       Re-compile after modifications to low level TCP socket communication
       module (ipcomm.c). Feb/2006, Rick Smereka */

    #include "stdhead.h"
    #include "flsocket.h"
    #include "ipcsrv.h"
    #include "appinit.h"
    #ifdef IPC_TCP
    #include "socloc.h"
    #include "sloc.h"
    #include "slconfig.h"
    #include "sliocode.h"
    #include "ip.h"
    #endif

    #define VERSION "1.16.01-2006.02.23"
    #define APNAME "sys_log"

    #ifdef OS_UNIX
    #define SYS_LOG_FNAME "/home/rick/logs/system.log"
    #define SYS_LOG_ALT_FNAME "/home/rick/logs/altsys.log"
    #else
    #define SYS_LOG_FNAME "C:\\LOGS\\SYSTEM.LOG"
    #define SYS_LOG_ALT_FNAME "C:\\LOGS\\ALTSYS.LOG"
    #endif

    #define SYS_LOG_TERM_PASSWD "rdsWin32"

    int main(int, char **);
    int parse_comline(int, char **);
    int srv_initialize(void);
    int process_request(void);
    int s_message(char *);
    int s_terminate(char *);
    void s_trans_num(void);
    void s_connect_num(void);
    void sys_log_reply(int, char *);
    int output_log(char *,...);
    int output_log_nf(char *);
    int open_alt_log(void);
    int open_log(void);
    void term_app(void);
    #ifdef IPC_TCP
    void usage();
    #endif

    /* global data */

    int islocked;              /* is the primary log file locked */
    int isopen;                /* is the log file already open */
    int console_output = TRUE; /* output to console as well as log? */
    FILE *out;                 /* output file pointer */
    char server_log[128];      /* server log file name */
    char server_alt_log[128];  /* server alternate log file name */
    #ifdef IPC_TCP
    char server_host[128];     /* server host name */
    int server_port = 0;       /* TCP port that server is using */

    /* WinSock specific global data structure */

    #ifdef OS_WIN32
    WSADATA wsaData;           /* struct used by 'WSAStartup()' */
    #endif
    #else
    // QNX transaction counter
    long server_trans_num = 0L;
    #endif

    int main(int argc, char **argv)
    {
       char mess[128];
       int isterm;

       /* indicate that we are initializing */

       if (!appinit_start(SYS_LOG_SERVICE_NAME))
          {
          printf("%s:error creating init lock file. Program abort\n", APNAME);
          return(0);
          }

       /* check for 'socloc' server (TCP) initializing and wait until this is
          complete */

    #ifdef IPC_TCP
       if (!appinit_waitfor(SL_SERVICE_NAME, 5))
          {
          printf("%s:timeout waiting for 'socloc' to complete "
                 "initialization. Program abort\n", APNAME);
          return(0);
          }
    #endif

       /* check for command line parameters */

    #ifdef IPC_TCP
       if (argc == 1)
          {
          (void)appinit_stop(SYS_LOG_SERVICE_NAME);
          usage();
          return(0);
          }
    #endif

       /* parse command line */

       if (!parse_comline(argc, argv))
          {
          (void)appinit_stop(SYS_LOG_SERVICE_NAME);
          return(0);
          }

       // initialize

       islocked = isopen = FALSE;

       // validate primary log file by attempting to open it

       if (open_log() != SYS_LOG_OK)
          {
          printf("%s:unable to open log file %s, program abort\n", APNAME,
                 SYS_LOG_FNAME);
          (void)appinit_stop(SYS_LOG_SERVICE_NAME);
          return(2);
          }

       fclose(out);

         /* build logo string based on platform */

    #ifndef OS_UNIX
       /* non-Unix */

       (void)output_log("%s System Logger for %s Version %s", APNAME,
                        PLATFORM_STRING, VERSION);
    #else
       /* Unix */

       (void)output_log("%s System Logger for %s Version %s", APNAME,
                        SUB_PLATFORM_STRING, VERSION);
    #endif

       (void)output_log_nf("By Rick Smereka, Copyright (c) 1997-2006");
       (void)output_log_nf("sys_log comes with ABSOLUTELY NO WARRANTY");
       (void)output_log_nf("This is free software, and you are welcome to "
                           "redistribute it");
       (void)output_log_nf("under certain conditions; see 'gpl.txt' for "
                           "information.");

       /* read IPC config file and validate other 'socloc' servers
          (if any) */

       if (!srv_initialize())
          {
          (void)appinit_stop(SYS_LOG_SERVICE_NAME);
          term_app();
          return(4);
          }

    #ifdef IPC_TCP
       (void)output_log("%s:server on '%s' servicing TCP port %d (decimal)",
                        APNAME, server_host, server_port);
    #endif
       (void)output_log("%s:primary log file is %s", APNAME, server_log);
       (void)output_log("%s:alternate log file is %s", APNAME, server_alt_log);
       (void)output_log("%s:log server started", APNAME);
       (void)appinit_stop(SYS_LOG_SERVICE_NAME);

    #ifdef IPC_TCP
       ipc_server_wait();
    #else
       // QNX, process each connection (infinite loop)

       while(1)
          {
          isterm = process_request();
          server_trans_num++;

          if (isterm)
             break;
          }
    #endif

       term_app();
       return(0);
    }

    int parse_comline(int c_count, char **c_parm)
    {
       /* Parse the command line for parameters. Function
          returns 'TRUE' if no error was detected, 'FALSE'
          otherwise. */

       int parms = 1, done = FALSE;

       /* set default log files */

       strcpy(server_log, SYS_LOG_FNAME);
       strcpy(server_alt_log, SYS_LOG_ALT_FNAME);

    #ifdef IPC_QNX
       if (c_count == 1)
          return(TRUE);
    #endif

       while(!done)
          {
          if (c_parm[parms][0] == SWITCH_CHAR)
             {
             switch(toupper(c_parm[parms][1]))
                {
                case 'L':
                   if (strlen(c_parm[parms]) > 2)
                      printf("%s:extraneous input with log switch, ignoring\n",
                             SYS_LOG_SERVICE_NAME);

                   parms++;

                   if (parms >= c_count)
                      {
                      printf("%s:log switch with no file name, "
                             "program abort\n", SYS_LOG_SERVICE_NAME);
                      return(FALSE);
                      }

                   strcpy(server_log, c_parm[parms]);
                   parms++;
                   break;

                case 'A':
                   if (strlen(c_parm[parms]) > 2)
                      printf("%s:extraneous input with alt log switch, "
                             "ignoring\n", SYS_LOG_SERVICE_NAME);

                   parms++;

                   if (parms >= c_count)
                      {
                      printf("%s:alt log switch with no file name, "
                             "program abort\n", SYS_LOG_SERVICE_NAME);
                      return(FALSE);
                      }

                   strcpy(server_alt_log, c_parm[parms]);
                   parms++;
                   break;

                case 'Q':
                   console_output = FALSE;
                   parms++;
                   break;

                default:
                   printf("%s:unknown switch[%s], program abort\n",
                          SYS_LOG_SERVICE_NAME, c_parm[parms]);
                   return(FALSE);
                };
             }
    #ifdef IPC_TCP
          else
             {
             if (!qatoi(c_parm[parms], &server_port))
                {
                printf("%s:server port is non-numeric, "
                       "program abort\n", SYS_LOG_SERVICE_NAME);
                return(FALSE);
                }

             if (server_port <= 0)
                {
                printf("%s:server port is out of range, "
                       "program abort\n", SYS_LOG_SERVICE_NAME);
                return(FALSE);
                }

             parms++;
             }
    #endif

          if (parms >= c_count)
             done = TRUE;
          }

    #ifdef IPC_TCP
       if (server_port == 0)
          {
          printf("%s:server port missing, program abort\n",
                 SYS_LOG_SERVICE_NAME);
          return(FALSE);
          }
    #endif

       /* make sure log file names are valid */

       if (open_log() != SYS_LOG_OK)
          {
          printf("%s:parse_comline:primary log file name is invalid, "
                 "program abort\n", SYS_LOG_SERVICE_NAME);
          return(FALSE);
          }

       fclose(out);

       if (open_alt_log() != SYS_LOG_OK)
          {
          printf("%s:parse_comline:alternate log file name is invalid, "
                 "program abort\n", SYS_LOG_SERVICE_NAME);
          return(FALSE);
          }

       fclose(out);
       return(TRUE);
    }

    int srv_initialize(void)
    {
       /* Initialize the server. Function returns 'TRUE' upon
          success, 'FALSE' otherwise. */

       char mes[128], mname[] = "srv_initialize";
       int ret;

       /* startup WinSock (if Windoze) */

    #ifdef OS_WIN32
       if (WSAStartup(WINSOCK_VERSION, &wsaData))
          {
          (void)output_log("%s:%s:unable to start WinSock, program abort",
                           APNAME, mname);
          return(FALSE);
          }
    #endif

       /* initialize the server on the IPC level, note that
          the hostname 'server_host' is returned to us (TCP only) */

    #ifdef IPC_TCP
       if (!ipc_init(server_host, server_port))
    #else
       if (!ipc_init(SYS_LOG_SERVICE_NAME))
    #endif
          {
          (void)output_log("%s:%s:ipc_init initialization failure, "
                           "program abort", APNAME, mname);
          return(FALSE);
          }

       /* initialize 'socloc' client API */

    #ifdef IPC_TCP
       if ((ret = sloc_initialize()) != SL_OK)
          {
          sl_code_string(ret, mes);
          (void)output_log("%s:%s:socloc init failure,rc[%s]", APNAME, mname, mes);
          (void)output_log("%s:%s:program abort", APNAME, mname);
          return(FALSE);
          }

       /* get IP address of this machine */

       if (!ip_host2ip(server_host, mes))
          {
          (void)output_log("%s:%s:unable to obtain IP address "
                           "of this machine", APNAME, mname);
          (void)output_log("%s:%s:program abort", APNAME, mname);
          return(FALSE);
          }

       /* register with 'socloc' */

       if ((ret = sloc_add(SYS_LOG_SERVICE_NAME, server_host, server_port,
           mes)) != SL_OK)
          {
          sl_code_string(ret, mes);
          (void)output_log("%s:%s:socloc add failure,rc[%s]", APNAME, mname, mes);
          (void)output_log("%s:%s:program abort", APNAME, mname);
          return(FALSE);
          }
    #endif

       return(TRUE);
    }

    int process_request(void)
    {
       /* Process a single request. Function returns 'TRUE'
          upon success, 'FALSE' otherwise. */

       char *messbuf, *tmp, mname[] = "process_request";
       int nbytes, nwords, mestype, slreply, ret;

       if ((messbuf = malloc(SYS_LOG_MAXMES)) == NULL)
          {
          (void)output_log("%s:%s:alloc fail[messbuf]", APNAME, mname);
          return(FALSE);
          }

       memset(messbuf, 0, SYS_LOG_MAXMES);

       // get the message from the client

    #ifdef IPC_TCP
       if ((nbytes = ipc_recv_data(messbuf)) == 0)
    #else
       if ((nbytes = ipc_recv_data(messbuf, SYS_LOG_MAXMES)) == 0)
    #endif
          {
          free(messbuf);
          (void)output_log("%s:%s:connection closed by client", APNAME, mname);
    #ifdef IPC_TCP
          return(-1);
    #else
          return(FALSE);
    #endif
          }

       messbuf[nbytes] = EOS;
       nwords = command_words(messbuf);

       // no command words, empty string

       if (!nwords)
          {
          sys_log_reply(SYS_LOG_UNKNOWN, (char *)NULL);
          free(messbuf);
          return(FALSE);
          }

       // first command word must be a number

       if ((tmp = (char *)malloc(SYS_LOG_MAXMES)) == NULL)
          {
          (void)output_log("%s:%s:alloc fail[tmp]", APNAME, mname);
          sys_log_reply(SYS_LOG_ALLOC_FAIL, (char *)NULL);
          free(messbuf);
          return(FALSE);
          }

       if (!command_word(messbuf, tmp, 1))
          {
          (void)output_log("%s:%s:error getting message type", APNAME, mname);
          sys_log_reply(SYS_LOG_PARM_ERROR, (char *)NULL);
          free(tmp);
          free(messbuf);
          return(FALSE);
          }

       if (!qatoi(tmp, &mestype))
          {
          (void)output_log("%s:%s:received an invalid message type",
                           APNAME, mname);
          sys_log_reply(SYS_LOG_UNKNOWN, (char *)NULL);
          free(tmp);
          free(messbuf);
          return(FALSE);
          }

       free(tmp);

       switch(mestype)
          {
          case SYS_LOG_SEND:
             if (nwords < 2)
                {
                (void)output_log("%s:%s:send:no message to log", APNAME, mname);
                sys_log_reply(SYS_LOG_NOMES, (char *)NULL);
                free(messbuf);
                return(FALSE);
                }

             slreply = s_message(messbuf);
             break;

          case SYS_LOG_SEND_STATUS:
             slreply = SYS_LOG_OK;
             break;

          case SYS_LOG_SEND_STOP:
             ret = s_terminate(messbuf);
             free(messbuf);
             return(ret);
             break;

          case SYS_LOG_SEND_LOCK:
             /* refuse to lock if already locked or log
                file has been left open */

             if (islocked || isopen)
                slreply = SYS_LOG_ALREADY_LOCKED;
             else
                {
                islocked = TRUE;
                slreply = SYS_LOG_OK;
                }

             break;

          case SYS_LOG_SEND_UNLOCK:
             if (!islocked)
                slreply = SYS_LOG_ALREADY_UNLOCKED;
             else
                {
                islocked = FALSE;
                slreply = SYS_LOG_OK;
                }

             break;

          case SYS_LOG_SEND_OPEN:
             /* refuse to open if already open or log
                file has been locked */

             if (isopen || islocked)
                slreply = SYS_LOG_ALREADY_OPEN;
             else
                {
                isopen = TRUE;
                slreply = open_log();
                }

             break;

          case SYS_LOG_SEND_CLOSE:
             /* refuse to close if already closed or log
                file has been locked */

             if (!isopen || islocked)
                slreply = SYS_LOG_ALREADY_CLOSED;
             else
                {
                isopen = FALSE;
                fclose(out);
                slreply = SYS_LOG_OK;
                }

             break;

          case SYS_LOG_SEND_DELETE:
             /* refuse to delete if file is open or locked */

             if (isopen)
                slreply = SYS_LOG_ALREADY_OPEN;
             else
                if (islocked)
                   slreply = SYS_LOG_ALREADY_LOCKED;
                else
                   {
                   if ((tmp = (char *)malloc(SYS_LOG_MAXMES)) == NULL)
                      {
                      (void)output_log("%s:%s:delete:alloc fail[tmp]",
                                       APNAME, mname);
                      sys_log_reply(SYS_LOG_ALLOC_FAIL, (char *)NULL);
                      free(messbuf);
                      return(FALSE);
                      }

                   if (!command_word(messbuf, tmp, 2))
                      {
                      (void)output_log("%s:%s:delete:error getting password",
                                       APNAME, mname);
                      sys_log_reply(SYS_LOG_PARM_ERROR, (char *)NULL);
                      free(tmp);
                      free(messbuf);
                      return(FALSE);
                      }

                   if (strcmp(tmp, SYS_LOG_TERM_PASSWD))
                      slreply = SYS_LOG_NO_ACCESS;
                   else
                      {
                      unlink(server_log);
                      (void)output_log("%s:%s:delete:primary log deleted",
                                       APNAME, mname);
                      slreply = SYS_LOG_OK;
                      }

                   free(tmp);
                   }

             break;

          case SYS_LOG_SEND_LOG_OFF:
             /* common socket code turn log off, does not make sense
                and is not implemented */

             slreply = SYS_LOG_NOT_IMPLEMENTED;
             break;

          case SYS_LOG_SEND_LOG_ON:
             /* common socket code turn log on, does not make sense
                and is not implemented */

             slreply = SYS_LOG_NOT_IMPLEMENTED;
             break;

          case SYS_LOG_SEND_LOG_STATUS:
             /* reply with ok and the primary and alternate log file
                names */

             if (words(server_log) > 1 || words(server_alt_log) > 1)
                sprintf(messbuf, "1 '%s,%s'", server_log, server_alt_log);
             else
                sprintf(messbuf, "1 %s,%s", server_log, server_alt_log);

             sys_log_reply(SYS_LOG_OK, messbuf);
             free(messbuf);
             return(FALSE);
             break;

          case SYS_LOG_SEND_SERVICE_NAME:
             sys_log_reply(SYS_LOG_OK, SYS_LOG_SERVICE_NAME);
             free(messbuf);
             return(FALSE);
             break;

          case SYS_LOG_SEND_VERSION:
             sys_log_reply(SYS_LOG_OK, VERSION);
             free(messbuf);
             return(FALSE);
             break;

          case SYS_LOG_SEND_TRANS_NUM:
             s_trans_num();
             free(messbuf);
             return(FALSE);
             break;

          case SYS_LOG_SEND_CONNECT_NUM:
             s_connect_num();
             free(messbuf);
             return(FALSE);
             break;

          default:
             (void)output_log("%s:%s:received unknown message", APNAME, mname);
             slreply = SYS_LOG_UNKNOWN;
             break;
          };

       sys_log_reply(slreply, (char *)NULL);
       free(messbuf);
       return(FALSE);
    }

    int s_message(char *comm)
    {
       // Output a message to the log. Function returns a reply code.

       char mname[] = "s_message", *tmp;
       int ret, pos, len, newlen;

       if ((tmp = (char *)malloc(SYS_LOG_MAXMES)) == (char *)NULL)
          {
          (void)output_log("%s:%s:send:alloc fail[tmp]", APNAME, mname);
          sys_log_reply(SYS_LOG_ALLOC_FAIL, (char *)NULL);
          return(SYS_LOG_ALLOC_FAIL);
          }

       // message starts in second command word

       len = strlen(comm);
       pos = command_indxword(comm, 2);
       newlen = len - pos;

       // copy all of message into buffer

       strncpy(tmp, &comm[pos], newlen);
       tmp[newlen] = EOS;

       // if message ends in a quote (single or double), remove it

       if (tmp[newlen - 1] == '\'' || tmp[newlen - 1] == '\"')
          tmp[newlen - 1] = EOS;

       ret = output_log_nf(tmp);
       free(tmp);
       return(ret);
    }

    int s_terminate(char *comm)
    {
       /* Terminate the server. Check the supplied password first.
          Syntax:

             SL_SEND_TERM password

          Function returns 'TRUE' if correct password was supplied,
          'FALSE' otherwise. */

       char wrd[128], mname[] = "s_terminate";
       int ret;

       if (command_words(comm) < 2)
          {
          sys_log_reply(SYS_LOG_PARM_ERROR, (char *)NULL);
          (void)output_log("%s:%s:no password", APNAME, mname);
          return(FALSE);
          }

       if (!command_word(comm, wrd, 2))
          {
          sys_log_reply(SYS_LOG_PARM_ERROR, (char *)NULL);
          (void)output_log("%s:%s:error getting password", APNAME, mname);
          return(FALSE);
          }

       if (!strcmp(wrd, SYS_LOG_TERM_PASSWD))
          {
          sys_log_reply(SYS_LOG_OK, (char *)NULL);
          return(TRUE);
          }

       (void)output_log("%s:%s:illegal terminate request", APNAME, mname);
       sys_log_reply(SYS_LOG_NO_ACCESS, (char *)NULL);
       return(FALSE);
    }

    void s_trans_num(void)
    {
       /* Get the current transaction count. Syntax:

             SYS_LOG_SEND_TRANS_NUM */

       char mess[128];
       long count;

    #ifdef IPC_TCP
       count = ipc_trans_num();
    #else
       count = server_trans_num;
    #endif

       sprintf(mess, "%ld", count);
       sys_log_reply(SYS_LOG_OK, mess);
    }

    void s_connect_num(void)
    {
       /* Get the current connection count. Syntax:

             SYS_LOG_SEND_CONNECT_NUM */

    #ifdef IPC_TCP
       char mess[128];
       int count;
       count = ipc_connect_num();
       sprintf(mess, "%d", count);
       sys_log_reply(SYS_LOG_OK, mess);
    #else
       sys_log_reply(SYS_LOG_NOT_IMPLEMENTED, (char *)NULL);
    #endif
    }

    void sys_log_reply(int reply_type, char *mess)
    {
       // Send the reply to the client.

       char *reply_char, mname[] = "sys_log_reply";
       int len;

       len = (mess == (char *)NULL) ? 25 : strlen(mess) + 25;

       if ((reply_char = (char *)malloc(len)) == (char *)NULL)
          {
          output_log("%s:%s:alloc fail[reply_char]", APNAME, mname);
          return;
          }

       memset(reply_char, 0, len);

       if (mess != (char *)NULL && strlen(mess))
          sprintf(reply_char, "%d %s", reply_type, mess);
       else
          sprintf(reply_char, "%d", reply_type);

       if (ipc_send_data(reply_char) == 0)
          output_log("%s:%s:error sending data", APNAME, mname);

       free(reply_char);
    }

    int output_log(char *fmt,...)
    {
       /* Output 'mess' to the system log file and to the screen.
          The message will be prefixed with a header containing
          the current date and time. */

       va_list argptr;
       char *mes;
       int ret;

       va_start(argptr, fmt);

       if ((mes = (char *)malloc(SYS_LOG_MAXMES)) == (char *)NULL)
          return(SYS_LOG_ALLOC_FAIL);

       vsprintf(mes, fmt, argptr);
       ret = output_log_nf(mes);
       free(mes);
       return(ret);
    }

    int output_log_nf(char *mes)
    {
       /* Output 'mess' to the system log file and to the screen.
          The message will be prefixed with a header containing
          the current date and time. */

       char tmch[15];
       int ret;

       if (mes == (char *)NULL || !strlen(mes))
          return(SYS_LOG_PARM_ERROR);

       if (!isopen)
          if (islocked)
             {
             if ((out = fopen(server_alt_log, "a")) == NULL)
                return(SYS_LOG_WERR);
             }
          else
             if ((ret = open_log()) != SYS_LOG_OK)
                return(ret);

       get_time(tmch);
       fprintf(out, "[%s]%s\n", tmch, mes);

       if (console_output)
          printf("[%s]%s\n", tmch, mes);

       if (!isopen)
          fclose(out);

       return(SYS_LOG_OK);
    }

    int open_log(void)
    {
       /* Open the primary log file. */

       if ((out = fopen(server_log, "a")) == NULL)
          return(SYS_LOG_WERR);

       return(SYS_LOG_OK);
    }

    int open_alt_log(void)
    {
       /* Open the alternate log file. */

       if ((out = fopen(server_alt_log, "a")) == (FILE *)NULL)
          return(SYS_LOG_WERR);

       return(SYS_LOG_OK);
    }

    void term_app(void)
    {
       // Shutdown anything left open.

    #ifdef IPC_TCP
       (void)sloc_delete(server_port);
       sloc_term_api();
    #ifdef OS_WIN32
       WSACleanup();
    #endif
    #else
       (void)ipc_close();
    #endif

       (void)output_log("%s:term_app:server down", APNAME);
    }

    #ifdef IPC_TCP
    void usage(void)
    {
       /* Display program usage. */

       printf("usage:%s [%cq] [%cl server_log][%ca server_alt_log] port\n",
              APNAME, SWITCH_CHAR, SWITCH_CHAR, SWITCH_CHAR);
    }
    #endif

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