/* [<][>][^][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_trans_num
- s_connect_num
- do_reply_code
- do_reply_char
- do_reply_int
- do_reply_long
- term_app
- usage
/* dumsocks - A dumb server. This TCP or QNX message passing connection
oriented iterative server is used for testing.
Rick Smereka, Copyright (C) 2000-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
Like the name says...dumb. This server will accept only a
generic status request, a termination request and log on/off
requests. This server is coded to use 'socloc'. It is a fatal
error if there are no 'socloc' servers running. Syntax:
dumsocks [-l log_file] port
Where 'port' is the TCP port to listen and '-l log_file'
is the optional name of the server log file. If no log file
is given, the log file name will be 'DUM_LOG_FILE' in the
current directory. Note that the switch character ('-')
is based on the platform (see include file 'stdhead.h').
Original Windows 32bit version under CodeWarrior V4. Feb/2000,
Rick Smereka
Re-compiled after re-structuring the definition of the common
send and repy codes. Added '-q' comand line option to suppress
console/screen output. Program syntax now:
dumsocks [-q] [-l log_file] port
Apr/2000, Rick Smereka
Modified the function 'srv_initialize' to obtain the IP address
and pass this to the 'sloc_add' socloc function. Jul/2000,
Rick Smereka
Corrected multiple definitions of 'localHost'. Jul/2001,
Rick Smereka
Modified for use with both sockets and QNX message passing IPC.
Ported to QNX V4.23a. Since the QNX message passing variant does
not require a port number, this parameter is absent. QNX message
passing syntax is:
dumsocks [-q] [-l log_file]
Oct/2001, Rick Smereka
Coded to use the 'appinit' API. May/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' to perform all shutdown tasks.
Added support for the send codes 'DM_SEND_TRANS_NUM' and
'DM_SEND_CONNECT_NUM'. Jun/2003, Rick Smereka
Changed all logging calls to use 'logman' API. Rewrote function
'term_app'. 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" /* generic socket definitions */
#include "appinit.h" /* application init API */
#include "ipcsrv.h" /* IPC server routines */
#include "dumsocks.h" /* dumb socket server defines */
#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.09.01-2006.02.23" /* version ID */
#define DM_TERM_PASSWD "rdsWin32" /* terminate server password */
#define APNAME "dumsocks"
// default log file definition
#define DUM_LOG_FILE "dumsocks.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? */
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 server transaction count
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_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(DM_SERVICE_NAME))
{
printf("%s:error creating 'appinit' lock file. Program abort\n",
APNAME);
return(0);
}
/* check for 'socloc' server (TCP) initializing */
#ifdef IPC_TCP
if (!appinit_waitfor(SL_SERVICE_NAME, 5))
{
printf("%s:error waiting for 'socloc' to initialize. "
"Program abort\n", APNAME);
(void)appinit_stop(DM_SERVICE_NAME);
return(0);
}
#endif
#ifdef IPC_TCP
if (argc == 1)
{
(void)appinit_stop(DM_SERVICE_NAME);
usage();
return(0);
}
#endif
if (!parse_comline(argc, argv))
{
(void)appinit_stop(DM_SERVICE_NAME);
return(0);
}
/* read IPC config file and validate other 'socloc' servers
(if any) */
if (!srv_initialize())
{
printf("%s:program abort\n", APNAME);
(void)appinit_stop(DM_SERVICE_NAME);
term_app();
return(0);
}
// set console output
logman_console(console_output);
/* initialize logging */
if (logman_start(d_log_file, APNAME))
{
printf("%s:error staring server logging. Program abort\n",
APNAME);
(void)appinit_stop(DM_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_nf("By Rick Smereka, Copyright (c) 2000-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_nf("This is free software, and you are welcome to redistribute it");
logman_nf("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(DM_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. */
char mname[] = "parse_comline";
int parms = 1, done = FALSE;
/* set default log file */
strcpy(d_log_file, DUM_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",
mname);
parms++;
if (parms >= c_count)
{
printf("%s:log switch with no file name, "
"program abort\n", mname);
return(FALSE);
}
strcpy(d_log_file, c_parm[parms]);
parms++;
break;
case 'Q':
console_output = FALSE;
parms++;
break;
default:
printf("%s:unknown switch[%s], program abort\n",
mname, 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", mname);
return(FALSE);
}
if (server_port <= 0)
{
printf("%s:server port is out of range, "
"program abort\n", mname);
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", mname);
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\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(DM_SERVICE_NAME))
#endif
{
printf("%s:ipc_init initialization failure\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);
return(FALSE);
}
/* register with 'socloc' */
if ((ret = sloc_add(DM_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 (!is_log_active())
ret = DM_LOG_ALREADY_OFF;
else
{
logman_end();
ret = DM_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 global log file name. Of course, check for logging
already on. */
char mname[] = "s_log_on";
int ret;
logman("%s:enter", mname);
if (is_log_active())
ret = DM_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(DM_INTERNAL_ERROR);
return;
}
}
else
strcpy(d_log_file, DUM_LOG_FILE);
if (logman_start(d_log_file, APNAME))
{
(void)do_reply_code(DM_LOG_ERROR);
return;
}
ret = DM_OK;
}
(void)do_reply_code(ret);
logman("%s:normal exit rc[%d]", mname, ret);
}
void s_log_status(void)
{
/* Get the status of the log including the flag value and the
log name. */
char mname[] = "s_log_status", *reply;
int ret;
logman("%s:enter", mname);
ret = logman_is_active();
if ((reply = (char *)malloc(strlen(d_log_file) + 3)) == (char *)NULL)
{
logman("%s:alloc fail[reply]", mname);
(void)do_reply_char(DM_MEMORY_FAIL, "");
return;
}
sprintf(reply, "%d %s", ret, d_log_file);
(void)do_reply_char(DM_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(DM_MAXCOMMAND)) == (char *)NULL)
{
(void)do_reply_code(DM_MEMORY_FAIL);
logman("%s:alloc fail[messbuf]", mname);
return(FALSE);
}
memset(messbuf, 0, DM_MAXCOMMAND);
// get the message from the client
#ifdef IPC_TCP
if ((nbytes = ipc_recv_data(messbuf)) == 0)
#else
if ((nbytes = ipc_recv_data(messbuf, DM_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(DM_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(DM_INTERNAL_ERROR);
logman("%s:error getting first command word", mname);
return(FALSE);
}
if (!qatoi(tmp, &mestype))
{
(void)do_reply_code(DM_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 DM_SEND_STATUS:
(void)do_reply_code(DM_OK);
break;
case DM_SEND_TERM:
if (s_terminate(slm))
ret = TRUE;
break;
case DM_SEND_LOG_OFF:
s_log_off();
break;
case DM_SEND_LOG_ON:
s_log_on(slm);
break;
case DM_SEND_LOG_STATUS:
s_log_status();
break;
case DM_SEND_SERVICE_NAME:
(void)do_reply_char(DM_OK, DM_SERVICE_NAME);
break;
case DM_SEND_VERSION:
(void)do_reply_char(DM_OK, VERSION);
break;
case DM_SEND_TRANS_NUM:
s_trans_num();
break;
case DM_SEND_CONNECT_NUM:
s_connect_num();
break;
default:
logman("%s:received unknown code=%d", mname, mestype);
(void)do_reply_code(DM_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:
SL_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(DM_INVALID_FUNCTION);
logman("%s:no password", mname);
return(FALSE);
}
if (!command_word(comm, wrd, 2))
{
(void)do_reply_code(DM_INTERNAL_ERROR);
logman("%s:error getting password", mname);
return(FALSE);
}
if (!strcmp(wrd, DM_TERM_PASSWD))
{
(void)do_reply_code(DM_OK);
return(TRUE);
}
logman("%s:illegal terminate request", mname);
(void)do_reply_code(DM_ACCESS_DENIED);
return(FALSE);
}
void s_trans_num(void)
{
/* Get the current transaction count. Syntax:
DM_SEND_TRANS_NUM */
char mname[] = "s_trans_num";
long count;
logman("%s:enter", mname);
#ifdef IPC_TCP
count = ipc_trans_num();
#else
count = server_trans_num;
#endif
(void)do_reply_long(DM_OK, count);
logman("%s:normal exit", mname);
}
void s_connect_num(void)
{
/* Get the current connection count. Syntax:
DM_SEND_CONNECT_NUM */
char mname[] = "s_connect_num";
int count;
logman("%s:enter", mname);
#ifdef IPC_TCP
count = ipc_connect_num();
(void)do_reply_int(DM_OK, count);
#else
do_reply_int(DM_NOT_IMPLEMENTED, 0);
#endif
logman("%s:normal exit", mname);
}
int do_reply_code(int reply_code)
{
/* Send a reply to a request which returns the reply code. Function
returns 'SL_OK' upon success, an error code otherwise. */
char reply_char[50];
memset(reply_char, 0, 50);
sprintf(reply_char, "%d", reply_code);
logman("do_reply_code:reply=%s,l=%d", reply_char,
strlen(reply_char));
if (ipc_send_data(reply_char) == 0)
return(DM_VC_ERROR);
return(DM_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 'SL_OK' upon success,
an error code otherwise. */
char *reply;
if ((reply = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
return(DM_MEMORY_FAIL);
memset(reply, 0, DM_MAXCOMMAND);
if (parm != (char *)NULL && strlen(parm))
sprintf(reply, "%d '%s'", reply_code, parm);
else
sprintf(reply, "%d", reply_code);
logman("do_reply_char:reply=%s,l=%d", reply, strlen(reply));
if (ipc_send_data(reply) == 0)
{
free(reply);
return(DM_VC_ERROR);
}
free(reply);
return(DM_OK);
}
int do_reply_int(int reply_code, int ival)
{
/* Send a reply to a request which returns the an integer as
well as the reply code. Function returns 'SL_OK' upon
success, an error code otherwise. */
char reply_char[100];
memset(reply_char, 0, 100);
sprintf(reply_char, "%d %d", reply_code, ival);
logman("do_reply_int:reply=%s,l=%d", reply_char, strlen(reply_char));
if (ipc_send_data(reply_char) == 0)
return(DM_VC_ERROR);
return(DM_OK);
}
int do_reply_long(int reply_code, long lval)
{
/* Send a reply to a request which returns the a long integer as
well as the reply code. Function returns 'SL_OK' upon
success, an error code otherwise. */
char reply_char[100];
memset(reply_char, 0, 100);
sprintf(reply_char, "%d %ld", reply_code, lval);
logman("do_reply_long:reply=%s,l=%d", reply_char, strlen(reply_char));
if (ipc_send_data(reply_char) == 0)
return(DM_VC_ERROR);
return(DM_OK);
}
void term_app(void)
{
// Shutdown anything left open.
logman("term_app:server down");
if (logman_is_active())
logman_end();
#ifdef IPC_TCP
(void)sloc_delete(server_port);
sloc_term_api();
#ifdef OS_WIN32
WSACleanup();
#endif
#else
(void)ipc_close();
#endif
(void)appinit_remove_name();
}
#ifdef IPC_TCP
void usage(void)
{
/* Display program usage. */
printf("usage:%s [%cq] [%cl log_file] port\n", DM_SERVICE_NAME,
SWITCH_CHAR, SWITCH_CHAR);
}
#endif