Project

General

Profile

Download (32.5 KB) Statistics
| Branch: | Revision:
62045e98 Hamish Coleman
/*
* wconsd.c - serial port server service for Windows NT
*
83cf653a Hamish Coleman
* Copyright (c) 2008 Hamish Coleman <hamish@zot.org>
* 2003 Benjamin Schweizer <gopher at h07 dot org>
62045e98 Hamish Coleman
* 1998 Stephen Early <Stephen.Early@cl.cam.ac.uk>
*
*
* 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
78d4c0b0 Hamish Coleman
b6970c98 Hamish Coleman
/* Note: winsock2.h MUST be included before windows.h */

#include <winsock2.h>
08ceb73b Hamish Coleman
#include <ws2tcpip.h>
b6970c98 Hamish Coleman
#include <windows.h>
#include <winsvc.h>
#include <stdio.h>
#include <stdlib.h>

83cf653a Hamish Coleman
#define VERSION "0.1.5"
62045e98 Hamish Coleman
78d4c0b0 Hamish Coleman
/* Size of buffers for send and receive */
#define BUFSIZE 1024
62045e98 Hamish Coleman
#define MAXLEN 1024
78d4c0b0 Hamish Coleman
/* Sockets for listening and communicating */
SOCKET ls=INVALID_SOCKET,cs=INVALID_SOCKET;

/* Event objects */
HANDLE stopEvent, connectionCloseEvent, threadTermEvent;
HANDLE readEvent, writeEvent;
WSAEVENT listenSocketEvent;

/* COM port */
DCB dcb;
62045e98 Hamish Coleman
COMMTIMEOUTS timeouts;
61b53eda Hamish Coleman
/* Port default settings are here */
int com_port=1;
DWORD com_speed=9600;
BYTE com_data=8;
BYTE com_parity=NOPARITY;
BYTE com_stop=ONESTOPBIT;
BOOL com_autoclose=TRUE;
78d4c0b0 Hamish Coleman
b9a4e090 Hamish Coleman
int default_tcpport = 23;
61b53eda Hamish Coleman
78d4c0b0 Hamish Coleman
/* Service status: our current status, and handle on service manager */
SERVICE_STATUS wconsd_status;
SERVICE_STATUS_HANDLE wconsd_statusHandle;

25fa1d20 Hamish Coleman
int debug_mode = 0;

ef78cec5 Hamish Coleman
#define MAXCONNECTIONS 8

884971fc Hamish Coleman
int next_connection_id = 0; /* lifetime unique connection id */
aaba0a4c Hamish Coleman
int next_connection_slot = 0; /* next slot to look at for new connection */
ef78cec5 Hamish Coleman
struct connection {
884971fc Hamish Coleman
int active; /* an active entry cannot be reused */
int id; /* connection identifier */
c7c4a6f7 Hamish Coleman
int menuactive; /* dont run the serial pump on a menu */
ef78cec5 Hamish Coleman
HANDLE menuThread;
aaba0a4c Hamish Coleman
int netconnected;
ef78cec5 Hamish Coleman
SOCKET net;
HANDLE netThread;
aaba0a4c Hamish Coleman
int serialconnected;
ef78cec5 Hamish Coleman
HANDLE serial;
HANDLE serialThread;
08ceb73b Hamish Coleman
int option_echo; /* will we echo chars recieved? */
ef78cec5 Hamish Coleman
int net_bytes_rx;
int net_bytes_tx;
};
struct connection connection[MAXCONNECTIONS];

61b53eda Hamish Coleman
/*
e6a4efc5 Hamish Coleman
* output from OutputDebugStringA can be seen using sysinternals debugview
61b53eda Hamish Coleman
* http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx
*/
e6a4efc5 Hamish Coleman
/*
* log a debug message
*/
int dprintf_level = 1;
int dprintf(unsigned char severity, const char *fmt, ...) {
va_list args;
c7c4a6f7 Hamish Coleman
char buf[MAXLEN];
e6a4efc5 Hamish Coleman
int i;

if (severity > dprintf_level)
return 0;

va_start(args,fmt);
c0d1e8fe Hamish Coleman
i=vsnprintf(buf,sizeof(buf),fmt,args);
e6a4efc5 Hamish Coleman
va_end(args);

25fa1d20 Hamish Coleman
if (debug_mode) {
printf("%s",buf);
} else {
OutputDebugStringA(buf);
}
e6a4efc5 Hamish Coleman
return i;
78d4c0b0 Hamish Coleman
}

c7c4a6f7 Hamish Coleman
/*
884971fc Hamish Coleman
* format a string and send it to a net connection
c7c4a6f7 Hamish Coleman
*/
884971fc Hamish Coleman
int netprintf(struct connection *conn, const char *fmt, ...) {
c7c4a6f7 Hamish Coleman
va_list args;
char buf[MAXLEN];
int i;
884971fc Hamish Coleman
int bytes;
c7c4a6f7 Hamish Coleman
va_start(args,fmt);
i=vsnprintf(buf,sizeof(buf),fmt,args);
va_end(args);

884971fc Hamish Coleman
bytes = send(conn->net,buf,(i>MAXLEN)?MAXLEN-1:i,0);

if (bytes==-1) {
08ceb73b Hamish Coleman
dprintf(1,"wconsd[%i]: netprintf: send error %i\n",conn->id,GetLastError());
884971fc Hamish Coleman
} else {
conn->net_bytes_tx += bytes;
}

c7c4a6f7 Hamish Coleman
return i;
}

62045e98 Hamish Coleman
/* open the com port */
aaba0a4c Hamish Coleman
DWORD open_com_port(struct connection *conn, DWORD *specificError) {
62045e98 Hamish Coleman
/* Open the COM port */
char portstr[12];
aaba0a4c Hamish Coleman
if (conn->serialconnected) {
dprintf(1,"wcons[%i]: open_com_port: serialconnected\n",conn->id);
}

62045e98 Hamish Coleman
sprintf(portstr, "\\\\.\\COM%d", com_port);
aaba0a4c Hamish Coleman
conn->serial = CreateFile(portstr,
62045e98 Hamish Coleman
GENERIC_READ | GENERIC_WRITE,
0, // Exclusive access
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
aaba0a4c Hamish Coleman
if (conn->serial == INVALID_HANDLE_VALUE) {
62045e98 Hamish Coleman
*specificError=GetLastError();
return 13;
}

aaba0a4c Hamish Coleman
if (!GetCommState(conn->serial, &dcb)) {
62045e98 Hamish Coleman
*specificError=GetLastError();
return 14;
}

// Fill in the device control block
dcb.BaudRate=com_speed;
dcb.ByteSize=com_data;
dcb.Parity=com_parity; // NOPARITY, ODDPARITY, EVENPARITY
dcb.StopBits=com_stop; // ONESTOPBIT, ONE5STOPBITS, TWOSTOPBITS
dcb.fBinary=TRUE;
dcb.fOutxCtsFlow=FALSE;
dcb.fOutxDsrFlow=FALSE;
dcb.fDtrControl=DTR_CONTROL_ENABLE; // Always on
dcb.fDsrSensitivity=FALSE;
dcb.fTXContinueOnXoff=FALSE;
dcb.fOutX=FALSE;
dcb.fInX=FALSE;
dcb.fErrorChar=FALSE;
dcb.fNull=FALSE;
dcb.fRtsControl=RTS_CONTROL_ENABLE; // Always on
dcb.fAbortOnError=FALSE;

aaba0a4c Hamish Coleman
if (!SetCommState(conn->serial, &dcb)) {
62045e98 Hamish Coleman
*specificError=GetLastError();
return 15;
}

timeouts.ReadIntervalTimeout=20;
timeouts.ReadTotalTimeoutMultiplier=0;
timeouts.ReadTotalTimeoutConstant=50;
timeouts.WriteTotalTimeoutMultiplier=0;
timeouts.WriteTotalTimeoutConstant=0;
aaba0a4c Hamish Coleman
if (!SetCommTimeouts(conn->serial, &timeouts)) {
62045e98 Hamish Coleman
*specificError=GetLastError();
return 16;
}
aaba0a4c Hamish Coleman
conn->serialconnected=1;
62045e98 Hamish Coleman
return 0;
}

/* close the com port */
aaba0a4c Hamish Coleman
void close_com_port(struct connection *conn) {
CloseHandle(conn->serial);
conn->serialconnected=0;
62045e98 Hamish Coleman
}


78d4c0b0 Hamish Coleman
/* Initialise wconsd: open a listening socket and the COM port, and
* create lots of event objects. */
DWORD wconsd_init(DWORD argc, LPSTR *argv, DWORD *specificError)
{
struct sockaddr_in sin;
WORD wVersionRequested;
WSADATA wsaData;
int err;

/* Start up sockets */
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
*specificError=err;
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
return 1;
}
/* Confirm that the WinSock DLL supports 2.2.*/
/* Note that if the DLL supports versions greater */
/* than 2.2 in addition to 2.2, it will still return */
/* 2.2 in wVersion since that is the version we */
/* requested. */
if ( LOBYTE( wsaData.wVersion ) != 2 ||
HIBYTE( wsaData.wVersion ) != 2 ) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
WSACleanup( );
return 2;
}

/* The WinSock DLL is acceptable. Proceed. */

// Create the event object used to signal service shutdown
stopEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
if (stopEvent==NULL) {
*specificError=GetLastError();
return 3;
}
// Create the event object used to signal connection close
connectionCloseEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
if (connectionCloseEvent==NULL) {
*specificError=GetLastError();
return 4;
}
threadTermEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
if (threadTermEvent==NULL) {
*specificError=GetLastError();
return 5;
}
// Event objects for overlapped IO
readEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
if (readEvent==NULL) {
*specificError=GetLastError();
return 6;
}
writeEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
if (writeEvent==NULL) {
*specificError=GetLastError();
return 7;
}
// Create the event object for socket operations
listenSocketEvent = WSACreateEvent();
if (listenSocketEvent==WSA_INVALID_EVENT) {
*specificError=WSAGetLastError();
return 8;
}

/* Create a socket to listen for connections. */
memset(&sin,0,sizeof(sin));
sin.sin_family=AF_INET;
62727af2 Hamish Coleman
sin.sin_port=htons(default_tcpport);
78d4c0b0 Hamish Coleman
ls=socket(AF_INET,SOCK_STREAM,0);
if (ls==INVALID_SOCKET) {
*specificError=WSAGetLastError();
return 9;
}
ef78cec5 Hamish Coleman
#ifndef MS_WINDOWS
{
int one=1;
setsockopt(ls,SOL_SOCKET,SO_REUSEADDR,(void*)&one,sizeof(one));
}
#endif
78d4c0b0 Hamish Coleman
if (bind(ls,(struct sockaddr *)&sin,sizeof(sin))==SOCKET_ERROR) {
*specificError=WSAGetLastError();
ef78cec5 Hamish Coleman
dprintf(1,"wconsd: wconsd_init: failed to bind socket\n");
78d4c0b0 Hamish Coleman
return 10;
}
if (listen(ls,1)==SOCKET_ERROR) {
*specificError=WSAGetLastError();
return 11;
}

/* Mark the socket as non-blocking */
if (WSAEventSelect(ls,listenSocketEvent,FD_ACCEPT)==SOCKET_ERROR) {
*specificError=WSAGetLastError();
return 12;
}
return 0;
}

DWORD WINAPI wconsd_net_to_com(LPVOID lpParam)
{
aaba0a4c Hamish Coleman
struct connection * conn = (struct connection*)lpParam;
c0d1e8fe Hamish Coleman
unsigned char buf[BUFSIZE];
78d4c0b0 Hamish Coleman
DWORD size,wsize;
unsigned long zero=0;
fd_set s;
aaba0a4c Hamish Coleman
struct timeval tv;
78d4c0b0 Hamish Coleman
OVERLAPPED o={0};

aaba0a4c Hamish Coleman
dprintf(1,"wconsd[%i]: start wconsd_net_to_com\n",conn->id);
884971fc Hamish Coleman
78d4c0b0 Hamish Coleman
o.hEvent = writeEvent;
aaba0a4c Hamish Coleman
while (conn->netconnected) {
78d4c0b0 Hamish Coleman
/* There's a bug in some versions of Windows which leads
* to recv() returning -1 and indicating error WSAEWOULDBLOCK,
* even on a blocking socket. This select() is here to work
* around that bug. */
aaba0a4c Hamish Coleman
/*
* These sockets are non-blocking, so I am unsure that the
* above statement is still true
*/
FD_SET(conn->net,&s);
tv.tv_sec = 5;
tv.tv_usec = 0;
select(0,&s,NULL,NULL,&tv);
size=recv(conn->net,(void*)&buf,BUFSIZE,0);
78d4c0b0 Hamish Coleman
if (size==0) {
SetEvent(connectionCloseEvent);
aaba0a4c Hamish Coleman
closesocket(conn->net);
conn->net=INVALID_SOCKET;
conn->netconnected=0;
dprintf(1,"wconsd[%i]: wconsd_net_to_com size==0\n",conn->id);
continue;
78d4c0b0 Hamish Coleman
}
if (size==SOCKET_ERROR) {
/* General paranoia about blocking sockets */
aaba0a4c Hamish Coleman
ioctlsocket(conn->net,FIONBIO,&zero);
continue;
78d4c0b0 Hamish Coleman
}
aaba0a4c Hamish Coleman
conn->net_bytes_rx+=size;

/* FIXME - find CR NUL and remove the NUL */
/* FIXME - process and remove telnet options at this point */

if (conn->menuactive) {
/* TODO - if we are in menu mode, hook into the menu here */
dprintf(1,"wconsd[%i]: unexpected menuactive\n",conn->id);
continue;
}

if (!conn->serialconnected) {
dprintf(1,"wconsd[%i]: data to send, but serial closed\n",conn->id);
continue;
}

if (!WriteFile(conn->serial,buf,size,&wsize,&o)) {
if (GetLastError()==ERROR_IO_PENDING) {
// Wait for it...
if (!GetOverlappedResult(conn->serial,&o,&wsize,TRUE)) {
dprintf(1,"wconsd: Error %d (overlapped) writing to COM port\n",GetLastError());
78d4c0b0 Hamish Coleman
}
aaba0a4c Hamish Coleman
} else {
dprintf(1,"wconsd: Error %d writing to COM port\n",GetLastError());
78d4c0b0 Hamish Coleman
}
}
aaba0a4c Hamish Coleman
if (wsize!=size) {
dprintf(1,"wconsd: Eeek! WriteFile: wrote %d of %d\n",wsize,size);
}
78d4c0b0 Hamish Coleman
}
aaba0a4c Hamish Coleman
dprintf(1,"wconsd[%i]: finish wconsd_net_to_com\n",conn->id);
78d4c0b0 Hamish Coleman
return 0;
}

DWORD WINAPI wconsd_com_to_net(LPVOID lpParam)
{
aaba0a4c Hamish Coleman
struct connection * conn = (struct connection*)lpParam;
c0d1e8fe Hamish Coleman
unsigned char buf[BUFSIZE];
78d4c0b0 Hamish Coleman
DWORD size;
OVERLAPPED o={0};

o.hEvent=readEvent;

aaba0a4c Hamish Coleman
dprintf(1,"wconsd[%i]: start wconsd_com_to_net\n",conn->id);
884971fc Hamish Coleman
aaba0a4c Hamish Coleman
while (conn->serialconnected && conn->netconnected) {
if (!ReadFile(conn->serial,buf,BUFSIZE,&size,&o)) {
78d4c0b0 Hamish Coleman
if (GetLastError()==ERROR_IO_PENDING) {
// Wait for overlapped operation to complete
aaba0a4c Hamish Coleman
if (!GetOverlappedResult(conn->serial,&o,&size,TRUE)) {
9b2f3609 Hamish Coleman
dprintf(1,"wconsd: Error %d (overlapped) reading from COM port\n",GetLastError());
78d4c0b0 Hamish Coleman
}
} else {
aaba0a4c Hamish Coleman
dprintf(1,"wconsd[%i]: Error %d reading from COM port\n",conn->id,GetLastError());
78d4c0b0 Hamish Coleman
SetEvent(connectionCloseEvent);
aaba0a4c Hamish Coleman
conn->serialconnected=0;
continue;
78d4c0b0 Hamish Coleman
}
}
aaba0a4c Hamish Coleman
if (!conn->netconnected) {
dprintf(1,"wconsd[%i]: data to send, but net closed\n",conn->id);
continue;
}
78d4c0b0 Hamish Coleman
if (size>0) {
aaba0a4c Hamish Coleman
send(conn->net,(void*)&buf,size,0);
conn->net_bytes_tx+=size;
78d4c0b0 Hamish Coleman
}
}
aaba0a4c Hamish Coleman
dprintf(1,"wconsd[%i]: finish wconsd_com_to_net\n",conn->id);
78d4c0b0 Hamish Coleman
return 0;
62045e98 Hamish Coleman
}

884971fc Hamish Coleman
void send_help(struct connection *conn) {
aaba0a4c Hamish Coleman
netprintf(conn,
"\r\n"
"NOTE: these commands will change in the next version\r\n\n"
"available commands:\r\n\n"
" port, speed, data, parity, stop\r\n"
" help, status, copyright\r\n"
" open, close, autoclose\r\n"
" show_conn_table\r\n"
" quit\r\n");
53110989 Hamish Coleman
}

c7c4a6f7 Hamish Coleman
void show_status(struct connection* conn) {
/* print the status to the net connection */

884971fc Hamish Coleman
netprintf(conn, "status:\r\n\n"
c7c4a6f7 Hamish Coleman
" port=%d speed=%d data=%d parity=%d stop=%d\r\n\n",
com_port, com_speed, com_data, com_parity, com_stop);

aaba0a4c Hamish Coleman
if(conn->serialconnected) {
884971fc Hamish Coleman
netprintf(conn, " state=open autoclose=%d\r\n\n", com_autoclose);
c7c4a6f7 Hamish Coleman
} else {
884971fc Hamish Coleman
netprintf(conn, " state=closed autoclose=%d\r\n\n", com_autoclose);
c7c4a6f7 Hamish Coleman
}
}

/*
* I thought that I would need this a lot, but it turns out that there
* was a lot of duplicated code
*/
884971fc Hamish Coleman
int check_atoi(char *p,int old_value,struct connection *conn,char *error) {
c7c4a6f7 Hamish Coleman
if (!p) {
884971fc Hamish Coleman
netprintf(conn,error);
c7c4a6f7 Hamish Coleman
return old_value;
}

return atoi(p);
53110989 Hamish Coleman
}

c7c4a6f7 Hamish Coleman
int process_menu_line(struct connection*conn, char *line) {
d51f658a Hamish Coleman
char *command;
char *parameter1;

c7c4a6f7 Hamish Coleman
/*
* FIXME - non re-entrant code
*
4450bbca Hamish Coleman
* my windows build environment does not have strtok_r
c7c4a6f7 Hamish Coleman
* and thus I am running these two strtok as close as possible.
* I am definitely not going to re-invent the wheel with my own
* code.
*/
d51f658a Hamish Coleman
command = strtok(line," ");
parameter1 = strtok(NULL," ");

if (!strcmp(command, "help") || !strcmp(command, "?")) {
// help
884971fc Hamish Coleman
send_help(conn);
d51f658a Hamish Coleman
} else if (!strcmp(command, "status")) {
// status
c7c4a6f7 Hamish Coleman
show_status(conn);
d51f658a Hamish Coleman
} else if (!strcmp(command, "copyright")) { // copyright
884971fc Hamish Coleman
netprintf(conn,
c7c4a6f7 Hamish Coleman
" Copyright (c) 2008 by Hamish Coleman <hamish@zot.org>\r\n"
" 2003 by Benjamin Schweizer <gopher at h07 dot org>\r\n"
" 1998 by Stephen Early <Stephen.Early@cl.cam.ac.uk>\r\n"
"\r\n"
"\r\n"
" This program is free software; you can redistribute it and/or modify\r\n"
" it under the terms of the GNU General Public License as published by\r\n"
" the Free Software Foundation; either version 2 of the License, or\r\n"
" (at your option) any later version.\r\n"
"\r\n"
" This program is distributed in the hope that it will be useful,\r\n"
" but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n"
" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r\n"
" GNU General Public License for more details.\r\n"
"\r\n"
" You should have received a copy of the GNU General Public License\r\n"
" along with this program; if not, write to the Free Software\r\n"
" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r\n"
"\n");
d51f658a Hamish Coleman
} else if (!strcmp(command, "port")) { // port
884971fc Hamish Coleman
int new = check_atoi(parameter1,com_port,conn,"must specify a port\r\n");
c7c4a6f7 Hamish Coleman
if (new >= 1 && new <= 16) {
com_port=new;
d51f658a Hamish Coleman
}
} else if (!strcmp(command, "speed")) { // speed
884971fc Hamish Coleman
com_speed = check_atoi(parameter1,com_port,conn,"must specify a speed\r\n");
d51f658a Hamish Coleman
} else if (!strcmp(command, "data")) { // data
if (!strcmp(parameter1, "5")) {
com_data=5;
} else if (!strcmp(parameter1, "6")) {
com_data=6;
} else if (!strcmp(parameter1, "7")) {
com_data=7;
} else if (!strcmp(parameter1, "8")) {
com_data=8;
}
c7c4a6f7 Hamish Coleman
show_status(conn);
d51f658a Hamish Coleman
} else if (!strcmp(command, "parity")) { // parity
if (!strcmp(parameter1, "no") || !strcmp(parameter1, "0")) {
com_parity=NOPARITY;
} else if (!strcmp(parameter1, "even") || !strcmp(parameter1, "2")) {
com_parity=EVENPARITY;
} else if (!strcmp(parameter1, "odd") || !strcmp(parameter1, "1")) {
com_parity=ODDPARITY;
} else if (!strcmp(parameter1, "mark")) {
com_parity=MARKPARITY;
} else if (!strcmp(parameter1, "space")) {
com_parity=SPACEPARITY;
}
c7c4a6f7 Hamish Coleman
show_status(conn);
d51f658a Hamish Coleman
} else if (!strcmp(command, "stop")) {
if (!strcmp(parameter1, "one") || !strcmp(parameter1, "1")) {
com_stop=ONESTOPBIT;
} else if (!strcmp(parameter1, "one5") || !strcmp(parameter1, "1.5")) {
com_stop=ONE5STOPBITS;
} else if (!strcmp(parameter1, "two") || !strcmp(parameter1, "2")) {
com_stop=TWOSTOPBITS;
}
c7c4a6f7 Hamish Coleman
show_status(conn);
d51f658a Hamish Coleman
} else if (!strcmp(command, "open")) { // open
4450bbca Hamish Coleman
DWORD errcode;
884971fc Hamish Coleman
int new = check_atoi(parameter1,com_port,conn,"open default port\r\n");
c7c4a6f7 Hamish Coleman
if (new >= 1 && new <= 16) {
com_port=new;
d51f658a Hamish Coleman
}
aaba0a4c Hamish Coleman
if (conn->serialconnected) {
c7c4a6f7 Hamish Coleman
/* port ist still open */
884971fc Hamish Coleman
netprintf(conn,"\r\n\n");
c7c4a6f7 Hamish Coleman
/* signal to quit the menu */
conn->menuactive=0;
/* and return still running */
return 1;
}

aaba0a4c Hamish Coleman
if (!open_com_port(conn,&errcode)) {
884971fc Hamish Coleman
netprintf(conn,"\r\n\n");
b0f1897b Hamish Coleman
// signal to quit the menu
c7c4a6f7 Hamish Coleman
conn->menuactive=0;
/* and return still running */
return 1;
} else {
884971fc Hamish Coleman
netprintf(conn,"error: cannot open port\r\n\n");
d51f658a Hamish Coleman
}
} else if (!strcmp(command, "close")) { // close
aaba0a4c Hamish Coleman
close_com_port(conn);
884971fc Hamish Coleman
netprintf(conn,"info: actual com port closed\r\n\n");
d51f658a Hamish Coleman
} else if (!strcmp(command, "autoclose")) { // autoclose
if (!strcmp(parameter1, "true") || !strcmp(parameter1, "1") || !strcmp(parameter1, "yes")) {
com_autoclose=TRUE;
} else if (!strcmp(parameter1, "false") || !strcmp(parameter1, "0") || !strcmp(parameter1, "no")) {
com_autoclose=FALSE;
}
c7c4a6f7 Hamish Coleman
show_status(conn);
b0f1897b Hamish Coleman
} else if (!strcmp(command, "quit")) {
// quit the connection
SetEvent(connectionCloseEvent);
c7c4a6f7 Hamish Coleman
conn->menuactive=0;
aaba0a4c Hamish Coleman
closesocket(conn->net);
conn->net=INVALID_SOCKET;
conn->netconnected=0;
c7c4a6f7 Hamish Coleman
/* and return not running */
return 0;
884971fc Hamish Coleman
} else if (!strcmp(command, "show_conn_table")) {
int i;
netprintf(conn,
aaba0a4c Hamish Coleman
"slot A id M mThr N net netTh S serial serialTh E netrx nettx\r\n");
08ceb73b Hamish Coleman
netprintf(conn,
aaba0a4c Hamish Coleman
"---- - -- - ---- - ---- ----- - ------ -------- - ----- -----\r\n");
884971fc Hamish Coleman
for (i=0;i<MAXCONNECTIONS;i++) {
netprintf(conn,
aaba0a4c Hamish Coleman
"%-4i %i %2i %i %4i %i %4i %5i %i %6i %8i %i %5i %5i\r\n",
884971fc Hamish Coleman
i, connection[i].active, connection[i].id,
connection[i].menuactive, connection[i].menuThread,
aaba0a4c Hamish Coleman
connection[i].netconnected, connection[i].net, connection[i].netThread,
connection[i].serialconnected, connection[i].serial, connection[i].serialThread,
08ceb73b Hamish Coleman
connection[i].option_echo,
884971fc Hamish Coleman
connection[i].net_bytes_rx, connection[i].net_bytes_tx
);
}
c7c4a6f7 Hamish Coleman
} else {
/* other, unknown commands */
884971fc Hamish Coleman
netprintf(conn,"debug: line='%s', command='%s'\r\n\n",line,command);
d51f658a Hamish Coleman
}
c7c4a6f7 Hamish Coleman
/* return still running */
return 1;
53110989 Hamish Coleman
}

08ceb73b Hamish Coleman
int process_telnet_option(struct connection*conn, unsigned char *buf) {

switch (buf[1]) {
case 0xf0: /* suboption end */
case 241: /* NOP */
case 242: /* Data Mark */
dprintf(1,"wconsd[%i]: option IAC %i\n",conn->id,buf[1]);
return 1;
case 243: /* Break */
/* TODO */
dprintf(1,"wconsd[%i]: break not supported\n",conn->id);
return 1;
case 244: /* suspend */
case 245: /* abort output */
dprintf(1,"wconsd[%i]: option IAC %i\n",conn->id,buf[1]);
return 1;
case 246: /* are you there */
dprintf(1,"wconsd[%i]: option IAC AYT\n",conn->id);
netprintf(conn,"yes\r\n");
return 1;
case 247: /* erase character */
case 248: /* erase line */
case 249: /* go ahead */
dprintf(1,"wconsd[%i]: option IAC %i\n",conn->id,buf[1]);
return 1;
case 0xfa: /* suboption begin */
if (buf[3]==0) {
/*
* I dont expect us to get any IS statements ...
* and if we do, I'm going to need to rewrite the
* way chars are absorbed
*/
dprintf(1,"wconsd[%i]: option IAC SB %i IS\n",conn->id,buf[2]);
return 3;
} else if (buf[3]>1) {
dprintf(1,"wconsd[%i]: option IAC SB %i error\n",conn->id,buf[2]);
return 3;
}
dprintf(1,"wconsd[%i]: option IAC SB %i SEND\n",conn->id,buf[2]);
switch (buf[2]) {
case 5: /* STATUS */
netprintf(conn,"%s%c%s%s%s",
"\xff\xfa\x05",
0,
"\xfb\x05",
conn->option_echo?"\xfb\x01":"",
"\xff\xf0");
}
return 5;
case 0xfb: /* WILL */
dprintf(1,"wconsd[%i]: option IAC WILL %i\n",conn->id,buf[2]);
return 2;
case 0xfc: /* WONT */
dprintf(1,"wconsd[%i]: option IAC WONT %i\n",conn->id,buf[2]);
return 2;
case 0xfd: /* DO */
switch (buf[2]) {
case 0x01: /* ECHO */
dprintf(1,"wconsd[%i]: DO ECHO\n",conn->id);
conn->option_echo=1;
break;
case 0x03: /* suppress go ahead */
dprintf(1,"wconsd[%i]: DO suppress go ahead\n",conn->id);
break;
}
return 2;
case 0xfe: /* DONT */
dprintf(1,"wconsd[%i]: option IAC DONT %i\n",conn->id,buf[2]);
return 2;
case 0xff: /* send ff */
/* TODO - work out how to support this */
dprintf(1,"wconsd[%i]: does not support quoted 0xff\n",conn->id);
return 1;
default:
dprintf(1,"wconsd[%i]: option IAC %i %i\n",conn->id,buf[1],buf[2]);
}

/* not normally reached */
return 2;
}

c7c4a6f7 Hamish Coleman
int run_menu(struct connection * conn) {
08ceb73b Hamish Coleman
unsigned char buf[BUFSIZE+5]; /* ensure there is room for our kludge telnet options */
unsigned char line[MAXLEN];
62045e98 Hamish Coleman
DWORD size, linelen=0;
WORD i;

unsigned long zero=0;
89312c37 Hamish Coleman
fd_set set_read;
53110989 Hamish Coleman
08ceb73b Hamish Coleman
/* IAC WILL ECHO */
/* IAC WILL suppress go ahead */
/* IAC WILL status */
netprintf(conn,"\xff\xfb\x01\xff\xfb\x03\xff\xfb\x05");

aaba0a4c Hamish Coleman
netprintf(conn,"\r\nwconsd serial port server (version %s)\r\n\r\n",VERSION);
884971fc Hamish Coleman
send_help(conn);
netprintf(conn,"> ");
c7c4a6f7 Hamish Coleman
89312c37 Hamish Coleman
FD_ZERO(&set_read);
aaba0a4c Hamish Coleman
while (conn->menuactive && conn->netconnected) {
89312c37 Hamish Coleman
FD_SET(conn->net,&set_read);
select(conn->net+1,&set_read,NULL,NULL,NULL);
c7c4a6f7 Hamish Coleman
size=recv(conn->net,(void*)&buf,BUFSIZE,0);
08ceb73b Hamish Coleman
62045e98 Hamish Coleman
if (size==0) {
c7c4a6f7 Hamish Coleman
closesocket(conn->net);
conn->net=INVALID_SOCKET;
aaba0a4c Hamish Coleman
conn->netconnected=0;
62045e98 Hamish Coleman
SetEvent(connectionCloseEvent);
c7c4a6f7 Hamish Coleman
return 0; /* signal running=0 */
62045e98 Hamish Coleman
}
if (size==SOCKET_ERROR) {
08ceb73b Hamish Coleman
dprintf(1,"wconsd[%i]: socket error\n",conn->id);
62045e98 Hamish Coleman
/* General paranoia about blocking sockets */
c7c4a6f7 Hamish Coleman
ioctlsocket(conn->net,FIONBIO,&zero);
53110989 Hamish Coleman
continue;
62045e98 Hamish Coleman
}
884971fc Hamish Coleman
conn->net_bytes_rx+=size;
62045e98 Hamish Coleman
53110989 Hamish Coleman
for (i = 0; i < size; i++) {
b9a4e090 Hamish Coleman
if (buf[i]==0) {
// strange, I'm not expecting nul bytes !
continue;
} else if (buf[i] == 127 || buf[i]==8) {
// backspace
53110989 Hamish Coleman
if (linelen > 0) {
884971fc Hamish Coleman
netprintf(conn,"\x08 \x08");
53110989 Hamish Coleman
linelen--;
} else {
c7c4a6f7 Hamish Coleman
/* if the linebuf is empty, ring the bell */
884971fc Hamish Coleman
netprintf(conn,"\x07");
53110989 Hamish Coleman
}
continue;
b9a4e090 Hamish Coleman
} else if (buf[i] == 0x0d || buf[i]==0x0a) {
// detected cr or lf

08ceb73b Hamish Coleman
if (i+1<size && (buf[i+1]==0x0a || buf[i+1]==0)) {
i++;
b9a4e090 Hamish Coleman
}

08ceb73b Hamish Coleman
if (conn->option_echo)
/* echo the endofline */
netprintf(conn,"\r\n");
53110989 Hamish Coleman
0bcbd4d3 Hamish Coleman
if (linelen!=0) {
c7c4a6f7 Hamish Coleman
int running;
0bcbd4d3 Hamish Coleman
line[linelen]=0; // ensure string is terminated
c7c4a6f7 Hamish Coleman
running = process_menu_line(conn,(char*)line);
if (!conn->menuactive) {
/* exiting the menu.. */
return running;
0bcbd4d3 Hamish Coleman
}
53110989 Hamish Coleman
}

884971fc Hamish Coleman
netprintf(conn,"> ");
53110989 Hamish Coleman
linelen=0;
b9a4e090 Hamish Coleman
continue;
08ceb73b Hamish Coleman
} else if (buf[i] <0x20) {
/* ignore other ctrl chars */
continue;
b9a4e090 Hamish Coleman
} else if (buf[i]==0xff) {
// telnet option packet
08ceb73b Hamish Coleman
/*
* Note that we avoid accessing outside the buffer
* by ensuring that the buffer is larger than
* the largest recv
*
* this doesnt help us if there is a split buffer from
* the client
*/
i+=process_telnet_option(conn,&buf[i]);
b9a4e090 Hamish Coleman
continue;
53110989 Hamish Coleman
} else {
// other chars
0bcbd4d3 Hamish Coleman
53110989 Hamish Coleman
if (linelen < MAXLEN - 1) {
line[linelen] = buf[i];
linelen++;
08ceb73b Hamish Coleman
if (conn->option_echo) {
netprintf(conn,"%c",buf[i]); /* echo */
}
53110989 Hamish Coleman
} else {
884971fc Hamish Coleman
netprintf(conn,"\x07"); /* linebuf full bell */
62045e98 Hamish Coleman
}
b9a4e090 Hamish Coleman
continue;
62045e98 Hamish Coleman
}
}
}
c7c4a6f7 Hamish Coleman
/* not reached */
62045e98 Hamish Coleman
return 0;
78d4c0b0 Hamish Coleman
}

ef78cec5 Hamish Coleman
DWORD WINAPI thread_new_connection(LPVOID lpParam) {
c7c4a6f7 Hamish Coleman
struct connection * conn = (struct connection*)lpParam;
int running=1;
ef78cec5 Hamish Coleman
aaba0a4c Hamish Coleman
while(conn->netconnected) {
884971fc Hamish Coleman
dprintf(1,"wconsd[%i]: top of menu thread running loop\n",conn->id);
aaba0a4c Hamish Coleman
if (conn->menuactive && conn->netconnected) {
c7c4a6f7 Hamish Coleman
/* run the menu to ask the user questions */
running = run_menu(conn);
aaba0a4c Hamish Coleman
}
if (!conn->menuactive && conn->netconnected && conn->serialconnected) {
c7c4a6f7 Hamish Coleman
/* they must have opened the com port, so start the threads */
aaba0a4c Hamish Coleman
PurgeComm(conn->serial,PURGE_RXCLEAR|PURGE_RXABORT);
conn->netThread=CreateThread(NULL,0,wconsd_net_to_com,conn,0,NULL);
conn->serialThread=CreateThread(NULL,0,wconsd_com_to_net,conn,0,NULL);
c7c4a6f7 Hamish Coleman
aaba0a4c Hamish Coleman
WaitForSingleObject(conn->netThread,INFINITE);
WaitForSingleObject(conn->serialThread,INFINITE);
884971fc Hamish Coleman
/*
* since I wait for the threads and there currently
* is no escape from the threads, we finish here
*/
c7c4a6f7 Hamish Coleman
running = 0;
}
}
ef78cec5 Hamish Coleman
c7c4a6f7 Hamish Coleman
/* cleanup */
884971fc Hamish Coleman
dprintf(1,"wconsd[%i]: connection closing\n",conn->id);

c7c4a6f7 Hamish Coleman
/* TODO print bytecounts */
/* maybe close file descriptors? */
aaba0a4c Hamish Coleman
if (com_autoclose) {
close_com_port(conn);
}
CloseHandle(conn->netThread);
CloseHandle(conn->serialThread);
884971fc Hamish Coleman
shutdown(conn->net,SD_BOTH);
closesocket(conn->net);
conn->active=0;
ef78cec5 Hamish Coleman
aaba0a4c Hamish Coleman
/* TODO - who closes menuThread ? */
ef78cec5 Hamish Coleman
return 0;
}

78d4c0b0 Hamish Coleman
static void wconsd_main(void)
{
HANDLE wait_array[3];
BOOL run=TRUE;
DWORD o;
SOCKET as;
c0d1e8fe Hamish Coleman
unsigned long zero=0;
78d4c0b0 Hamish Coleman
ef78cec5 Hamish Coleman
struct sockaddr_in sa;
int salen;

int i;
aaba0a4c Hamish Coleman
int count;
ef78cec5 Hamish Coleman
/* clear out any bogus data in the connections table */
for (i=0;i<MAXCONNECTIONS;i++) {
884971fc Hamish Coleman
connection[i].active = 0;
ef78cec5 Hamish Coleman
}

78d4c0b0 Hamish Coleman
/* Main loop: wait for a connection, service it, repeat
* until signalled that the service is terminating */
wait_array[0]=stopEvent;
wait_array[1]=listenSocketEvent;
wait_array[2]=connectionCloseEvent;

while (run) {
bd5605dd Hamish Coleman
dprintf(1,"wconsd: top of wconsd_main run loop\n");

78d4c0b0 Hamish Coleman
o=WaitForMultipleObjects(3,wait_array,FALSE,INFINITE);

switch (o-WAIT_OBJECT_0) {
ef78cec5 Hamish Coleman
case 0: /* stopEvent */
78d4c0b0 Hamish Coleman
run=FALSE;
ResetEvent(stopEvent);
break;
ef78cec5 Hamish Coleman
case 1: /* listenSocketEvent */
/* There is an incoming connection */
78d4c0b0 Hamish Coleman
WSAResetEvent(listenSocketEvent);
ef78cec5 Hamish Coleman
salen = sizeof(sa);
as=accept(ls,(struct sockaddr*)&sa,&salen);

if (as==INVALID_SOCKET) {
break;
}
53110989 Hamish Coleman
4450bbca Hamish Coleman
/* getnameinfo does not appear to be supported in my windows build environment */
#ifdef GETNAMEINFO
aaba0a4c Hamish Coleman
char buf[MAXLEN];
08ceb73b Hamish Coleman
if (!getnameinfo((struct sockaddr*)&sa,salen,buf,sizeof(buf),NULL,0,0)) {
aaba0a4c Hamish Coleman
#endif
bd5605dd Hamish Coleman
dprintf(1,"wconsd: new connection from %08x\n",
884971fc Hamish Coleman
htonl(sa.sin_addr.s_addr));
aaba0a4c Hamish Coleman
#ifdef GETNAMEINFO
bd5605dd Hamish Coleman
} else {
dprintf(1,"wconsd: new connection from %s\n",
&buf);
}
4450bbca Hamish Coleman
#endif
53110989 Hamish Coleman
ef78cec5 Hamish Coleman
/* search for an empty connection slot */
aaba0a4c Hamish Coleman
i=next_connection_slot%MAXCONNECTIONS;
count=0;
while(connection[i].active && count<MAXCONNECTIONS) {
count++;
i = (i+1)%MAXCONNECTIONS;
ef78cec5 Hamish Coleman
}
if (i==MAXCONNECTIONS) {
dprintf(1,"wconsd: connection table overflow\n");
/* FIXME - properly reject the incoming connection */
/* for now, just close the socket */
closesocket(as);
break;
78d4c0b0 Hamish Coleman
}
aaba0a4c Hamish Coleman
next_connection_slot = (next_connection_slot+1)%MAXCONNECTIONS;
884971fc Hamish Coleman
connection[i].active=1; /* mark this entry busy */
connection[i].id = next_connection_id++;
c7c4a6f7 Hamish Coleman
connection[i].menuactive=1; /* start in the menu */
ef78cec5 Hamish Coleman
connection[i].menuThread=NULL;
aaba0a4c Hamish Coleman
connection[i].netconnected=1;
ef78cec5 Hamish Coleman
connection[i].net=as;
connection[i].netThread=NULL;
aaba0a4c Hamish Coleman
connection[i].serialconnected=0;
ef78cec5 Hamish Coleman
connection[i].serial=INVALID_HANDLE_VALUE;
connection[i].serialThread=NULL;
08ceb73b Hamish Coleman
connection[i].option_echo=0;
ef78cec5 Hamish Coleman
connection[i].net_bytes_rx=0;
connection[i].net_bytes_tx=0;

aaba0a4c Hamish Coleman
dprintf(1,"wconsd[%i]: accepted new connection slot=%i\n",connection[i].id,i);
ef78cec5 Hamish Coleman

/* we successfully accepted the connection */

if (cs!=INVALID_SOCKET) {
/* There is an existing connected socket */
/* Close down the existing connection and let the new one through */
SetEvent(threadTermEvent);
shutdown(cs,SD_BOTH);
closesocket(cs);
ResetEvent(connectionCloseEvent);
ResetEvent(threadTermEvent);
}
aaba0a4c Hamish Coleman
/* cs=as; */

ioctlsocket(connection[i].net,FIONBIO,&zero);
ef78cec5 Hamish Coleman
connection[i].menuThread = CreateThread(NULL,0,thread_new_connection,&connection[i],0,NULL);

78d4c0b0 Hamish Coleman
break;
ef78cec5 Hamish Coleman
case 2: /* connectionCloseEvent*/
/* The data connection has been broken */
884971fc Hamish Coleman
dprintf(1,"wconsd: connectionCloseEvent\n");
78d4c0b0 Hamish Coleman
SetEvent(threadTermEvent);
if (cs!=INVALID_SOCKET) {
shutdown(cs,SD_BOTH);
closesocket(cs);
cs=INVALID_SOCKET;
}
ResetEvent(connectionCloseEvent);
62045e98 Hamish Coleman
ResetEvent(threadTermEvent);
78d4c0b0 Hamish Coleman
break;
default:
run=FALSE; // Stop the service - I want to get off!
break;
}
}

if (cs!=INVALID_SOCKET) {
shutdown(cs,SD_BOTH);
closesocket(cs);
}

aaba0a4c Hamish Coleman
/* TODO - look through the connection table and close everything */
78d4c0b0 Hamish Coleman
closesocket(ls);
WSACleanup();
}

VOID WINAPI MyServiceCtrlHandler(DWORD opcode)
{
DWORD status;

switch(opcode) {
case SERVICE_CONTROL_STOP:
wconsd_status.dwWin32ExitCode = 0;
wconsd_status.dwCurrentState = SERVICE_STOP_PENDING;
wconsd_status.dwCheckPoint = 0;
wconsd_status.dwWaitHint = 0;

if (!SetServiceStatus(wconsd_statusHandle, &wconsd_status)) {
status = GetLastError();
9b2f3609 Hamish Coleman
dprintf(1,"wconsd: SetServiceStatus error %ld\n",status);
78d4c0b0 Hamish Coleman
}

SetEvent(stopEvent);
break;

case SERVICE_CONTROL_INTERROGATE:
// fall through to send current status
break;

default:
9b2f3609 Hamish Coleman
dprintf(1,"wconsd: unrecognised opcode %ld\n",opcode);
78d4c0b0 Hamish Coleman
break;
}

// Send current status
if (!SetServiceStatus(wconsd_statusHandle, &wconsd_status)) {
status = GetLastError();
9b2f3609 Hamish Coleman
dprintf(1,"wconsd: SetServiceStatus error %ld\n",status);
78d4c0b0 Hamish Coleman
}
return;
}

VOID WINAPI ServiceStart(DWORD argc, LPSTR *argv)
{
DWORD status;
DWORD specificError;

wconsd_status.dwServiceType = SERVICE_WIN32;
wconsd_status.dwCurrentState = SERVICE_START_PENDING;
wconsd_status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
wconsd_status.dwWin32ExitCode = 0;
wconsd_status.dwServiceSpecificExitCode = 0;
wconsd_status.dwCheckPoint = 0;
wconsd_status.dwWaitHint = 0;
25fa1d20 Hamish Coleman
wconsd_statusHandle = RegisterServiceCtrlHandler(TEXT("wconsd"),MyServiceCtrlHandler);
78d4c0b0 Hamish Coleman
if (wconsd_statusHandle == (SERVICE_STATUS_HANDLE)0) {
9b2f3609 Hamish Coleman
dprintf(1,"wconsd: RegisterServiceCtrlHandler failed %d\n", GetLastError());
78d4c0b0 Hamish Coleman
return;
}

status = wconsd_init(argc, argv, &specificError);

if (status != NO_ERROR) {
wconsd_status.dwCurrentState = SERVICE_STOPPED;
wconsd_status.dwCheckPoint = 0;
wconsd_status.dwWaitHint = 0;
wconsd_status.dwWin32ExitCode = status;
wconsd_status.dwServiceSpecificExitCode = specificError;

SetServiceStatus(wconsd_statusHandle, &wconsd_status);
return;
}

/* Initialisation complete - report running status */
wconsd_status.dwCurrentState = SERVICE_RUNNING;
wconsd_status.dwCheckPoint = 0;
wconsd_status.dwWaitHint = 0;

if (!SetServiceStatus(wconsd_statusHandle, &wconsd_status)) {
status = GetLastError();
9b2f3609 Hamish Coleman
dprintf(1,"wconsd: SetServiceStatus error %ld\n",status);
78d4c0b0 Hamish Coleman
}

wconsd_main();

wconsd_status.dwCurrentState = SERVICE_STOPPED;
wconsd_status.dwCheckPoint = 0;
wconsd_status.dwWaitHint = 0;
wconsd_status.dwWin32ExitCode = 0;
wconsd_status.dwServiceSpecificExitCode = 0;

SetServiceStatus(wconsd_statusHandle, &wconsd_status);

return;
}

static void RegisterService(LPSTR path)
{
SC_HANDLE schSCManager, schService;

schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

schService = CreateService(
schSCManager,
TEXT("wconsd"),
ff03d660 Hamish Coleman
TEXT("wconsd - a serial port server"),
78d4c0b0 Hamish Coleman
SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START,
SERVICE_ERROR_NORMAL,
path,
NULL, NULL, NULL, NULL, NULL);

if (schService == NULL) {
printf("CreateService failed\n");
} else {
printf("Created service 'wconsd', binary path %s\n",path);
printf("You should now start the service using the service manager.\n");
}
CloseServiceHandle(schService);
}

static void RemoveService(void)
{
SC_HANDLE schSCManager, schService;

schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (schSCManager == NULL) {
printf("Couldn't open service manager\n");
return;
}

schService = OpenService(schSCManager, TEXT("wconsd"), DELETE);

if (schService == NULL) {
printf("Couldn't open wconsd service\n");
return;
}

if (!DeleteService(schService)) {
printf("Couldn't delete wconsd service\n");
return;
}

printf("Deleted service 'wconsd'\n");

CloseServiceHandle(schService);
}

static void usage(void)
{
printf("Usage: wconsd [-i pathname | -r | -d]\n");
printf(" -i pathname install service 'wconsd'; pathname\n");
printf(" must be the full path to the binary\n");
printf(" -r remove service 'wconsd'\n");
printf(" -d run wconsd in debug mode (in the foreground)\n");
}

int main(int argc, char **argv)
{
DWORD err;
62727af2 Hamish Coleman
int console_application=0;
78d4c0b0 Hamish Coleman
SERVICE_TABLE_ENTRY DispatchTable[] =
{
{ "wconsd", ServiceStart },
{ NULL, NULL }
62045e98 Hamish Coleman
};
78d4c0b0 Hamish Coleman
62727af2 Hamish Coleman
// debug info for when I test this as a service
dprintf(1,"wconsd: started with argc==%i\n",argc);

62045e98 Hamish Coleman
if (argc==1 || argc==0) {
62727af2 Hamish Coleman
// assume that our messages are going to the debug log
53110989 Hamish Coleman
debug_mode=0;
62727af2 Hamish Coleman
// start by trying to run as a service
if (StartServiceCtrlDispatcher(DispatchTable)==0) {
err = GetLastError();
dprintf(1,"wconsd: StartServiceCtrlDispatcher error = %d\n", err);

if (err != ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
// any other error, assume fatal
return 1;
}
78d4c0b0 Hamish Coleman
}
62727af2 Hamish Coleman
// fall through and try running as a command-line application
console_application=1;
78d4c0b0 Hamish Coleman
}

53110989 Hamish Coleman
// We are running in debug mode (or any other command-line mode)
debug_mode=1;

62727af2 Hamish Coleman
dprintf(1,"wconsd: Serial Console server\n");

c2484981 Hamish Coleman
if (argc>1) {
62727af2 Hamish Coleman
if (strcmp(argv[1],"-i")==0) {
// request service installation
if (argc!=3) {
usage();
return 1;
}
RegisterService(argv[2]);
return 0;
} else if (strcmp(argv[1],"-r")==0) {
// request service removal
RemoveService();
return 0;
780d7607 Hamish Coleman
} else if (strcmp(argv[1],"-p")==0) {
console_application=1;
default_tcpport = atoi(argv[2]);
62727af2 Hamish Coleman
} else if (strcmp(argv[1],"-d")==0) {
console_application=1;
} else {
78d4c0b0 Hamish Coleman
usage();
return 1;
}
62727af2 Hamish Coleman
}

884971fc Hamish Coleman
dprintf(1,"wconsd: listen on port %i\n",default_tcpport);
c2484981 Hamish Coleman
62727af2 Hamish Coleman
// if we have decided to run as a console app..
if (console_application) {
78d4c0b0 Hamish Coleman
int r;
4450bbca Hamish Coleman
dprintf(1,"wconsd: Console Application Mode (version %s)\n",VERSION);
78d4c0b0 Hamish Coleman
r=wconsd_init(argc,argv,&err);
if (r!=0) {
4450bbca Hamish Coleman
dprintf(1,"wconsd: wconsd_init failed, return code %d [%l]\n",r, err);
78d4c0b0 Hamish Coleman
return 1;
}
wconsd_main();
62727af2 Hamish Coleman
}

return 0;
78d4c0b0 Hamish Coleman
}