/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- main
- parse_comline
- srv_initialize
- s_log_off
- s_log_on
- s_log_status
- process_request
- do_comm
- s_terminate
- s_get_datime
- s_trans_num
- s_connect_num
- do_reply_code
- do_reply_char
- do_reply_int
- do_reply_long
- term_app
- usage
/* timesync - A date/time synchronization server for TCP sockets
and QNX message passing IPC methods. Rick Smereka, Copyright (C) 2001-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
This program does not attempt to implement any of the Internet
date/time protocols. If absolute atomic time is required, this
server should be run on a machine that runs a atomic time client.
TCP program syntax:
timesync [-q] [-l log_file] port
QNX message passing syntax:
timesync [-q] [-l log_file]
Where the 'q' option will supress console output (quiet) and the 'l'
(el) option specifies the server log file.
The TCP IPC method version of this server requires at least one running
'socloc' server.
Original QNX version, Nov/2001, Rick Smereka
Ported to Red Hat Linux and Windows 32bit.
Dec/2001, Rick Smereka
Coded to use the 'appinit' API. May/2002, Rick Smereka
Ported to Debian Linux. Jan/2003, Rick Smereka
Modified to use the updated 'socloc' API ('sloc.c').
Modified to use the updated server IPC API ('ipcsrv.c').
Added function 'term_app' and modified exit points to use
this function. Added support for the send codes
'TS_SEND_TRANS_NUM' and 'TS_SEND_CONNECT_NUM'.
Jun/2003, Rick Smereka
Changed all logging calls to use the 'logman' API.
Added the command line parameter '-s' to have this
server wait for the system logging server to become
available if it is also initializing. TCP program syntax:
timesync [-q] [-s] [-l log_file] port
QNX message passing syntax:
timesync [-q] [-s] [-l log_file]
Dec/2004, Rick Smereka
Re-compile after changing the 'socloc' API.
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" /* IPC generic definitions */
#include "ipcsrv.h" /* IPC server routines */
#include "timesync.h" /* timesync defines */
#include "appinit.h" /* application initialization API */
#ifdef IPC_TCP
#include "socloc.h" /* socket locate defines */
#include "ip.h" /* IP library routines */
#include "sloc.h" /* 'socloc' client API */
#endif
#define VERSION "1.07.01-2006.02.23" /* version ID */
#define TS_TERM_PASSWD "rdsWin32" /* terminate server password */
#define APNAME "timesync"
/* default private log file */
#define TS_LOG_FILE "timesync.log"
/* WinSock specific global data structure */
#ifdef OS_WIN32
WSADATA wsaData; /* struct used by 'WSAStartup()' */
#endif
/* global data */
int console_output = TRUE; /* log messages to screen? */
int waitfor_syslog = FALSE; /* wait for system logger upon init? */
char d_log_file[256]; /* path/name of log file */
#ifdef IPC_TCP
int server_port = 0; /* TCP port that server is using */
char localHost[256]; /* host name of this machine */
#else
// QNX transaction counter
long server_trans_num = 0L;
#endif
/* function prototypes */
int main(int, char **);
int parse_comline(int, char **);
int srv_initialize(void);
int process_request(void);
int do_comm(int, char *);
int s_terminate(char *);
void s_log_off(void);
void s_log_on(char *);
void s_log_status(void);
void s_get_datime(void);
void s_trans_num(void);
void s_connect_num(void);
int do_reply_code(int);
int do_reply_char(int, char *);
int do_reply_int(int, int);
int do_reply_long(int, long);
void term_app(void);
#ifdef IPC_TCP
void usage();
#endif
int main(int argc, char **argv)
{
int isterm;
/* indicate that we are initializing */
if (!appinit_start(TS_SERVICE_NAME))
{
printf("%s:error creating 'appinit' lock file. Program abort\n", APNAME);
return(0);
}
/* check for 'socloc' server initializing */
#ifdef IPC_TCP
if (!appinit_waitfor(SL_SERVICE_NAME, 5))
{
printf("%s:error waiting for 'socloc' to initialize. "
"Program abort\n", APNAME);
return(0);
}
#endif
#ifdef IPC_TCP
if (argc == 1)
{
(void)appinit_stop(TS_SERVICE_NAME);
usage();
return(0);
}
#endif
if (!parse_comline(argc, argv))
{
(void)appinit_stop(TS_SERVICE_NAME);
return(0);
}
// wait for system logger to init if requested
if (waitfor_syslog)
if (!appinit_waitfor(SYS_LOG_SERVICE_NAME, 1))
{
printf("%s:error waiting for 'sys_log' to initialize. "
"Program abort\n", APNAME);
(void)appinit_stop(TS_SERVICE_NAME);
return(0);
}
/* set logger console output flag */
logman_console(console_output);
/* start server on IPC level, connect to a 'socloc' server and
register this server */
if (!srv_initialize())
{
(void)appinit_stop(TS_SERVICE_NAME);
term_app();
return(0);
}
/* initialize logging */
if (logman_start(d_log_file, APNAME))
{
printf("%s:error initializing logging. Program abort\n", APNAME);
(void)appinit_stop(TS_SERVICE_NAME);
return(0);
}
/* build logo string based on platform */
#ifndef OS_UNIX
/* non-Unix */
logman("%s Server for %s Version %s", APNAME, PLATFORM_STRING, VERSION);
#else
/* Unix */
logman("%s Server for %s Version %s", APNAME, SUB_PLATFORM_STRING, VERSION);
#endif
logman("By Rick Smereka, Copyright (c) 2001-2006");
#ifdef IPC_TCP
logman("%s:server on \"%s\" servicing TCP port %d (decimal)",
APNAME, localHost, server_port);
#endif
logman("%s:server started", APNAME);
logman("%s comes with ABSOLUTELY NO WARRANTY", APNAME);
logman("This is free software, and you are welcome to redistribute it");
logman("under certain conditions; see \"gpl.txt\" for information.");
/* turn off log as this is used only for debugging */
logman_end();
/* indicate that the server has completed initialization */
(void)appinit_stop(TS_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 file */
strcpy(d_log_file, TS_LOG_FILE);
#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",
APNAME);
parms++;
if (parms >= c_count)
{
printf("%s:log switch with no file name, "
"program abort\n", APNAME);
return(FALSE);
}
strcpy(d_log_file, c_parm[parms]);
parms++;
break;
case 'Q':
console_output = FALSE;
parms++;
break;
case 'S':
waitfor_syslog = TRUE;
parms++;
break;
default:
printf("%s:unknown switch[%s], program abort\n",
APNAME, 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", APNAME);
return(FALSE);
}
if (server_port <= 0)
{
printf("%s:server port is out of range, "
"program abort\n", APNAME);
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", APNAME);
return(FALSE);
}
#endif
return(TRUE);
}
int srv_initialize(void)
{
/* Initialize the server. Function returns 'TRUE' upon
success, 'FALSE' otherwise. */
char mname[] = "srv_initialize";
char mes[128];
int ret;
/* startup WinSock (if Windoze) */
#ifdef OS_WIN32
if (WSAStartup(WINSOCK_VERSION, &wsaData))
{
printf("%s:unable to start WinSock, program abort\n", mname);
return(FALSE);
}
#endif
/* initialize the server on the IPC level, note that
the hostname 'localhost' is returned to us (TCP only) */
#ifdef IPC_TCP
if (!ipc_init(localHost, server_port))
#else
if (!ipc_init(TS_SERVICE_NAME))
#endif
{
printf("%s:'ipc_init initialization failure, program abort\n", mname);
return(FALSE);
}
/* initialize 'socloc' client API */
#ifdef IPC_TCP
if ((ret = sloc_initialize()) != SL_OK)
{
sl_code_string(ret, mes);
printf("%s:bad rc[%s] from 'sloc_initialize'\n", mname, mes);
return(FALSE);
}
/* get IP address of this machine */
if (!ip_host2ip(localHost, mes))
{
printf("%s:unable to obtain IP address of this machine\n", mname);
printf("%s:program abort\n", mname);
return(FALSE);
}
/* register with 'socloc' */
if ((ret = sloc_add(TS_SERVICE_NAME, localHost, server_port,
mes)) != SL_OK)
{
sl_code_string(ret, mes);
printf("%s:bad rc[%s] from 'sloc_add'\n", mname, mes);
return(FALSE);
}
#endif
return(TRUE);
}
void s_log_off(void)
{
/* Turn all loggging off. Of course, check for logging
already off. */
char mname[] = "s_log_off";
int ret;
logman("%s:enter", mname);
if (!logman_is_active())
ret = TS_LOG_ALREADY_OFF;
else
{
logman_end();
ret = TS_OK;
}
(void)do_reply_code(ret);
logman("%s:normal exit rc[%d]", mname, ret);
}
void s_log_on(char *comm)
{
/* Turn server logging on using either the supplied log file name
or the previously loaded log file name. Of course, check for logging
already on. */
char mname[] = "s_log_on";
int ret;
logman("%s:enter", mname);
if (logman_is_active())
ret = TS_LOG_ALREADY_ON;
else
{
if (command_words(comm) > 1)
{
if (!command_word(comm, d_log_file, 2))
{
logman("%s:error getting log file name", mname);
(void)do_reply_code(TS_INTERNAL_ERROR);
return;
}
}
if (logman_start(d_log_file, APNAME))
{
(void)do_reply_code(TS_LOG_ERROR);
return;
}
ret = TS_OK;
}
(void)do_reply_code(ret);
logman("%s:normal exit rc[%d]", mname, ret);
}
void s_log_status(void)
{
/* Get the current log status (on or off). */
char *reply, *wrd, mname[] = "s_log_status";
int full_len;
logman("%s:enter", mname);
full_len = strlen(d_log_file) + 5;
if ((reply = (char *)malloc(full_len)) == (char *)NULL)
{
(void)do_reply_char(TS_MEMORY_FAIL, (char *)NULL);
logman("%s:alloc fail[reply]", mname);
return;
}
if ((wrd = initqstring(d_log_file)) == (char *)NULL)
{
free(reply);
(void)do_reply_char(TS_MEMORY_FAIL, (char *)NULL);
logman("%s:alloc fail[wrd]", mname);
return;
}
if (logman_is_active())
sprintf(reply, "1 %s", wrd);
else
sprintf(reply, "0 %s", wrd);
free(wrd);
(void)do_reply_char(TS_OK, reply);
free(reply);
logman("%s:normal exit rc[0]", mname);
}
int process_request(void)
{
/* Process a single request. Function returns 'FALSE' if
termination signal was not encountered, 'TRUE'
otherwise. */
char mname[] = "process_request";
char *messbuf, tmp[128];
int ret, nbytes, nwords, mestype;
logman("%s:enter", mname);
if ((messbuf = (char *)malloc(TS_MAXCOMMAND)) == (char *)NULL)
{
(void)do_reply_code(TS_MEMORY_FAIL);
logman("%s:alloc fail[messbuf]", mname);
return(FALSE);
}
memset(messbuf, 0, TS_MAXCOMMAND);
// get the message from the client
#ifdef IPC_TCP
if ((nbytes = ipc_recv_data(messbuf)) == 0)
#else
if ((nbytes = ipc_recv_data(messbuf, TS_MAXCOMMAND)) == 0)
#endif
{
free(messbuf);
logman("%s:connection closed by client", mname);
#ifdef IPC_TCP
return(-1);
#else
return(FALSE);
#endif
}
messbuf[nbytes] = EOS;
logman("%s:recv %s,l=%d", mname, messbuf, strlen(messbuf));
nwords = command_words(messbuf);
// no command words, empty string
if (!nwords)
{
(void)do_reply_code(TS_INVALID_FUNCTION);
free(messbuf);
logman("%s:empty command", mname);
return(FALSE);
}
// first command word must be a number
if (!command_word(messbuf, tmp, 1))
{
free(messbuf);
(void)do_reply_code(TS_INTERNAL_ERROR);
logman("%s:error getting first command word", mname);
return(FALSE);
}
if (!qatoi(tmp, &mestype))
{
(void)do_reply_code(TS_INVALID_FUNCTION);
logman("%s:command signature not a number", mname);
free(messbuf);
return(FALSE);
}
/* pass entire string including command number */
ret = do_comm(mestype, messbuf);
free(messbuf);
logman("%s:normal exit,rc=%d", mname, ret);
return(ret);
}
int do_comm(int mestype, char *slm)
{
/* High level server command dispatcher.
Function returns 'TRUE' if a terminate
request was encountered, 'FALSE'
otherwise. */
char mname[] = "do_comm";
int ret;
logman("%s:enter", mname);
ret = FALSE;
switch(mestype)
{
case TS_SEND_STATUS:
(void)do_reply_code(TS_OK);
break;
case TS_SEND_GET_DATIME:
s_get_datime();
break;
case TS_SEND_TERM:
if (s_terminate(slm))
ret = TRUE;
break;
case TS_SEND_LOG_OFF:
s_log_off();
break;
case TS_SEND_LOG_ON:
s_log_on(slm);
break;
case TS_SEND_LOG_STATUS:
s_log_status();
break;
case TS_SEND_SERVICE_NAME:
(void)do_reply_char(TS_OK, TS_SERVICE_NAME);
break;
case TS_SEND_VERSION:
(void)do_reply_char(TS_OK, VERSION);
break;
case TS_SEND_TRANS_NUM:
s_trans_num();
break;
case TS_SEND_CONNECT_NUM:
s_connect_num();
break;
default:
logman("%s:received unknown code=%d", mname, mestype);
(void)do_reply_code(TS_INVALID_FUNCTION);
break;
};
logman("%s:exit[%d]", mname, ret);
return(ret);
}
int s_terminate(char *comm)
{
/* Terminate the server. Check the supplied password first.
Syntax:
TS_SEND_TERM password
Function returns 'TRUE' if correct password was supplied,
'FALSE' otherwise. */
char mname[] = "s_terminate";
char wrd[256];
int ret;
logman("%s:enter", mname);
if (command_words(comm) < 2)
{
(void)do_reply_code(TS_INVALID_FUNCTION);
logman("%s:no password", mname);
return(FALSE);
}
if (!command_word(comm, wrd, 2))
{
(void)do_reply_code(TS_INTERNAL_ERROR);
logman("%s:error getting password", mname);
return(FALSE);
}
if (!strcmp(wrd, TS_TERM_PASSWD))
{
(void)do_reply_code(TS_OK);
return(TRUE);
}
logman("%s:illegal terminate request", mname);
(void)do_reply_code(TS_ACCESS_DENIED);
return(FALSE);
}
void s_get_datime(void)
{
/* Obtain, format the current date and time and send it back
to the client. Note that the year and month are properly
adjusted. */
time_t tod;
struct tm *tmbuf;
char mname[] = "s_get_datime", buf[15];
logman("%s:enter", mname);
tod = time(NULL);
tmbuf = localtime(&tod);
sprintf(buf, "%d%02d%02d%02d%02d%02d", tmbuf->tm_year + 1900,
tmbuf->tm_mon + 1, tmbuf->tm_mday, tmbuf->tm_hour,
tmbuf->tm_min, tmbuf->tm_sec);
(void)do_reply_char(TS_OK, buf);
logman("%s:normal exit", mname);
}
void s_trans_num(void)
{
/* Get the current transaction count. Syntax:
TS_SEND_TRANS_NUM */
long count;
#ifdef IPC_TCP
count = ipc_trans_num();
#else
count = server_trans_num;
#endif
(void)do_reply_long(TS_OK, count);
}
void s_connect_num(void)
{
/* Get the current connection count. Syntax:
TS_SEND_CONNECT_NUM */
#ifdef IPC_TCP
int count;
count = ipc_connect_num();
(void)do_reply_int(TS_OK, count);
#else
(void)do_reply_int(TS_NOT_IMPLEMENTED, 0);
#endif
}
int do_reply_code(int reply_code)
{
/* Send a reply to a request which returns the reply code. Function
returns 'TS_OK' upon success, an error code otherwise. */
char reply_char[50], mname[] = "do_reply_code";
memset(reply_char, 0, 50);
sprintf(reply_char, "%d", reply_code);
logman("%s:reply=%s,l=%d", mname, reply_char, strlen(reply_char));
if (ipc_send_data(reply_char) == 0)
{
logman("%s:bad rc[0] from ipc_send_data", mname);
return(TS_VC_ERROR);
}
return(TS_OK);
}
int do_reply_char(int reply_code, char *parm)
{
/* Send a reply to a request which returns the reply code and
possibly a string. Function returns 'TS_OK' upon success,
an error code otherwise. */
char *reply, mname[] = "do_reply_char";
if ((reply = (char *)malloc(TS_MAXCOMMAND)) == (char *)NULL)
return(TS_MEMORY_FAIL);
memset(reply, 0, TS_MAXCOMMAND);
if (parm != (char *)NULL && strlen(parm))
sprintf(reply, "%d %s", reply_code, parm);
else
sprintf(reply, "%d", reply_code);
logman("%s:reply=%s,l=%d", mname, reply, strlen(reply));
if (ipc_send_data(reply) == 0)
{
free(reply);
logman("%s:bad rc[0] from ipc_send_data", mname);
return(TS_VC_ERROR);
}
free(reply);
return(TS_OK);
}
int do_reply_int(int reply_code, int parm)
{
/* Send a reply to a request which returns the reply code and
an integer. Function returns 'TS_OK' upon success,
an error code otherwise. */
char reply[50], mname[] = "do_reply_int";
memset(reply, 0, 50);
sprintf(reply, "%d %d", reply_code, parm);
logman("%s:reply=%s,l=%d", mname, reply, strlen(reply));
if (ipc_send_data(reply) == 0)
{
logman("%s:bad rc[0] from ipc_send_data", mname);
return(TS_VC_ERROR);
}
return(TS_OK);
}
int do_reply_long(int reply_code, long parm)
{
/* Send a reply to a request which returns the reply code and
a long integer. Function returns 'TS_OK' upon success,
an error code otherwise. */
char reply[50], mname[] = "do_reply_long";
memset(reply, 0, 50);
sprintf(reply, "%d %ld", reply_code, parm);
logman("%s:reply=%s,l=%d", mname, reply, strlen(reply));
if (ipc_send_data(reply) == 0)
{
logman("%s:bad rc[0] from ipc_send_data", mname);
return(TS_VC_ERROR);
}
return(TS_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
logman("term_app:server down");
if (logman_is_active())
logman_end();
}
#ifdef IPC_TCP
void usage(void)
{
/* Display program usage. */
printf("usage:%s [%cq] [%cs] [%cl log_file] port\n", APNAME, SWITCH_CHAR,
SWITCH_CHAR, SWITCH_CHAR);
}
#endif