Revision aaba0a4c
Added by Hamish Coleman over 16 years ago
- ID aaba0a4ca5e7366ffb12a2bea569b9c0c48429a1
wconsd.c | ||
---|---|---|
WSAEVENT listenSocketEvent;
|
||
|
||
/* COM port */
|
||
HANDLE hCom;
|
||
DCB dcb;
|
||
COMMTIMEOUTS timeouts;
|
||
|
||
... | ... | |
BYTE com_parity=NOPARITY;
|
||
BYTE com_stop=ONESTOPBIT;
|
||
BOOL com_autoclose=TRUE;
|
||
BOOL com_state=FALSE; // FALSE=closed,TRUE=open
|
||
|
||
int default_tcpport = 23;
|
||
|
||
... | ... | |
#define MAXCONNECTIONS 8
|
||
|
||
int next_connection_id = 0; /* lifetime unique connection id */
|
||
int next_connection_slot = 0; /* next slot to look at for new connection */
|
||
struct connection {
|
||
int active; /* an active entry cannot be reused */
|
||
int id; /* connection identifier */
|
||
int menuactive; /* dont run the serial pump on a menu */
|
||
HANDLE menuThread;
|
||
int netconnected;
|
||
SOCKET net;
|
||
HANDLE netThread;
|
||
int serialconnected;
|
||
HANDLE serial;
|
||
HANDLE serialThread;
|
||
int option_echo; /* will we echo chars recieved? */
|
||
... | ... | |
}
|
||
|
||
/* open the com port */
|
||
DWORD open_com_port(DWORD *specificError) {
|
||
DWORD open_com_port(struct connection *conn, DWORD *specificError) {
|
||
/* Open the COM port */
|
||
char portstr[12];
|
||
|
||
if (conn->serialconnected) {
|
||
dprintf(1,"wcons[%i]: open_com_port: serialconnected\n",conn->id);
|
||
}
|
||
|
||
sprintf(portstr, "\\\\.\\COM%d", com_port);
|
||
hCom = CreateFile(portstr,
|
||
conn->serial = CreateFile(portstr,
|
||
GENERIC_READ | GENERIC_WRITE,
|
||
0, // Exclusive access
|
||
NULL,
|
||
OPEN_EXISTING,
|
||
FILE_FLAG_OVERLAPPED,
|
||
NULL);
|
||
if (hCom == INVALID_HANDLE_VALUE) {
|
||
if (conn->serial == INVALID_HANDLE_VALUE) {
|
||
*specificError=GetLastError();
|
||
return 13;
|
||
}
|
||
|
||
if (!GetCommState(hCom, &dcb)) {
|
||
if (!GetCommState(conn->serial, &dcb)) {
|
||
*specificError=GetLastError();
|
||
return 14;
|
||
}
|
||
... | ... | |
dcb.fRtsControl=RTS_CONTROL_ENABLE; // Always on
|
||
dcb.fAbortOnError=FALSE;
|
||
|
||
if (!SetCommState(hCom, &dcb)) {
|
||
if (!SetCommState(conn->serial, &dcb)) {
|
||
*specificError=GetLastError();
|
||
return 15;
|
||
}
|
||
... | ... | |
timeouts.ReadTotalTimeoutConstant=50;
|
||
timeouts.WriteTotalTimeoutMultiplier=0;
|
||
timeouts.WriteTotalTimeoutConstant=0;
|
||
if (!SetCommTimeouts(hCom, &timeouts)) {
|
||
if (!SetCommTimeouts(conn->serial, &timeouts)) {
|
||
*specificError=GetLastError();
|
||
return 16;
|
||
}
|
||
com_state=TRUE;
|
||
conn->serialconnected=1;
|
||
return 0;
|
||
}
|
||
|
||
/* close the com port */
|
||
void close_com_port() {
|
||
CloseHandle(hCom);
|
||
com_state=FALSE;
|
||
void close_com_port(struct connection *conn) {
|
||
CloseHandle(conn->serial);
|
||
conn->serialconnected=0;
|
||
}
|
||
|
||
|
||
... | ... | |
|
||
DWORD WINAPI wconsd_net_to_com(LPVOID lpParam)
|
||
{
|
||
struct connection * conn = (struct connection*)lpParam;
|
||
unsigned char buf[BUFSIZE];
|
||
DWORD size,wsize;
|
||
unsigned long zero=0;
|
||
fd_set s;
|
||
struct timeval tv;
|
||
OVERLAPPED o={0};
|
||
|
||
dprintf(1,"wconsd: start wconsd_net_to_com\n");
|
||
dprintf(1,"wconsd[%i]: start wconsd_net_to_com\n",conn->id);
|
||
|
||
o.hEvent = writeEvent;
|
||
while (WaitForSingleObject(threadTermEvent,0)!=WAIT_OBJECT_0) {
|
||
while (conn->netconnected) {
|
||
/* 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. */
|
||
FD_SET(cs,&s);
|
||
select(0,&s,NULL,NULL,NULL);
|
||
size=recv(cs,(void*)&buf,BUFSIZE,0);
|
||
/*
|
||
* 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);
|
||
if (size==0) {
|
||
SetEvent(connectionCloseEvent);
|
||
return 0;
|
||
closesocket(conn->net);
|
||
conn->net=INVALID_SOCKET;
|
||
conn->netconnected=0;
|
||
dprintf(1,"wconsd[%i]: wconsd_net_to_com size==0\n",conn->id);
|
||
continue;
|
||
}
|
||
if (size==SOCKET_ERROR) {
|
||
/* General paranoia about blocking sockets */
|
||
ioctlsocket(cs,FIONBIO,&zero);
|
||
ioctlsocket(conn->net,FIONBIO,&zero);
|
||
continue;
|
||
}
|
||
if (size!=SOCKET_ERROR) {
|
||
if (!WriteFile(hCom,buf,size,&wsize,&o)) {
|
||
if (GetLastError()==ERROR_IO_PENDING) {
|
||
// Wait for it...
|
||
if (!GetOverlappedResult(hCom,&o,&wsize,TRUE)) {
|
||
dprintf(1,"wconsd: Error %d (overlapped) writing to COM port\n",GetLastError());
|
||
}
|
||
} else {
|
||
dprintf(1,"wconsd: Error %d writing to COM port\n",GetLastError());
|
||
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());
|
||
}
|
||
} else {
|
||
dprintf(1,"wconsd: Error %d writing to COM port\n",GetLastError());
|
||
}
|
||
if (wsize!=size) {
|
||
dprintf(1,"wconsd: Eeek! WriteFile: wrote %d of %d\n",wsize,size);
|
||
}
|
||
}
|
||
if (wsize!=size) {
|
||
dprintf(1,"wconsd: Eeek! WriteFile: wrote %d of %d\n",wsize,size);
|
||
}
|
||
}
|
||
dprintf(1,"wconsd[%i]: finish wconsd_net_to_com\n",conn->id);
|
||
return 0;
|
||
}
|
||
|
||
DWORD WINAPI wconsd_com_to_net(LPVOID lpParam)
|
||
{
|
||
struct connection * conn = (struct connection*)lpParam;
|
||
unsigned char buf[BUFSIZE];
|
||
DWORD size;
|
||
OVERLAPPED o={0};
|
||
|
||
o.hEvent=readEvent;
|
||
|
||
dprintf(1,"wconsd: start wconsd_com_to_net\n");
|
||
dprintf(1,"wconsd[%i]: start wconsd_com_to_net\n",conn->id);
|
||
|
||
while (WaitForSingleObject(threadTermEvent,0)!=WAIT_OBJECT_0) {
|
||
if (!ReadFile(hCom,buf,BUFSIZE,&size,&o)) {
|
||
while (conn->serialconnected && conn->netconnected) {
|
||
if (!ReadFile(conn->serial,buf,BUFSIZE,&size,&o)) {
|
||
if (GetLastError()==ERROR_IO_PENDING) {
|
||
// Wait for overlapped operation to complete
|
||
if (!GetOverlappedResult(hCom,&o,&size,TRUE)) {
|
||
if (!GetOverlappedResult(conn->serial,&o,&size,TRUE)) {
|
||
dprintf(1,"wconsd: Error %d (overlapped) reading from COM port\n",GetLastError());
|
||
}
|
||
} else {
|
||
dprintf(1,"wconsd: Error %d reading from COM port\n",GetLastError());
|
||
dprintf(1,"wconsd[%i]: Error %d reading from COM port\n",conn->id,GetLastError());
|
||
SetEvent(connectionCloseEvent);
|
||
return 0;
|
||
conn->serialconnected=0;
|
||
continue;
|
||
}
|
||
}
|
||
if (!conn->netconnected) {
|
||
dprintf(1,"wconsd[%i]: data to send, but net closed\n",conn->id);
|
||
continue;
|
||
}
|
||
if (size>0) {
|
||
send(cs,(void*)&buf,size,0);
|
||
send(conn->net,(void*)&buf,size,0);
|
||
conn->net_bytes_tx+=size;
|
||
}
|
||
}
|
||
dprintf(1,"wconsd[%i]: finish wconsd_com_to_net\n",conn->id);
|
||
return 0;
|
||
}
|
||
|
||
void send_help(struct connection *conn) {
|
||
netprintf(conn,"available commands:\r\n\n"
|
||
" port, speed, data, parity, stop\r\n"
|
||
" help, status, copyright\r\n"
|
||
" open, close, autoclose\r\n"
|
||
" quit\r\n");
|
||
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");
|
||
}
|
||
|
||
void show_status(struct connection* conn) {
|
||
... | ... | |
" port=%d speed=%d data=%d parity=%d stop=%d\r\n\n",
|
||
com_port, com_speed, com_data, com_parity, com_stop);
|
||
|
||
if(com_state) {
|
||
if(conn->serialconnected) {
|
||
netprintf(conn, " state=open autoclose=%d\r\n\n", com_autoclose);
|
||
} else {
|
||
netprintf(conn, " state=closed autoclose=%d\r\n\n", com_autoclose);
|
||
... | ... | |
if (new >= 1 && new <= 16) {
|
||
com_port=new;
|
||
}
|
||
if (com_state) {
|
||
if (conn->serialconnected) {
|
||
/* port ist still open */
|
||
netprintf(conn,"\r\n\n");
|
||
/* signal to quit the menu */
|
||
... | ... | |
return 1;
|
||
}
|
||
|
||
if (!open_com_port(&errcode)) {
|
||
if (!open_com_port(conn,&errcode)) {
|
||
netprintf(conn,"\r\n\n");
|
||
// signal to quit the menu
|
||
conn->menuactive=0;
|
||
... | ... | |
netprintf(conn,"error: cannot open port\r\n\n");
|
||
}
|
||
} else if (!strcmp(command, "close")) { // close
|
||
close_com_port();
|
||
close_com_port(conn);
|
||
netprintf(conn,"info: actual com port closed\r\n\n");
|
||
} else if (!strcmp(command, "autoclose")) { // autoclose
|
||
if (!strcmp(parameter1, "true") || !strcmp(parameter1, "1") || !strcmp(parameter1, "yes")) {
|
||
... | ... | |
// quit the connection
|
||
SetEvent(connectionCloseEvent);
|
||
conn->menuactive=0;
|
||
closesocket(conn->net);
|
||
conn->net=INVALID_SOCKET;
|
||
conn->netconnected=0;
|
||
/* and return not running */
|
||
return 0;
|
||
} else if (!strcmp(command, "show_conn_table")) {
|
||
int i;
|
||
netprintf(conn,
|
||
"slot A id M mThr net netTh serial serialTh E netrx nettx\r\n");
|
||
"slot A id M mThr N net netTh S serial serialTh E netrx nettx\r\n");
|
||
netprintf(conn,
|
||
"---- - -- - ---- ---- ----- ------ -------- - ----- -----\r\n");
|
||
"---- - -- - ---- - ---- ----- - ------ -------- - ----- -----\r\n");
|
||
for (i=0;i<MAXCONNECTIONS;i++) {
|
||
netprintf(conn,
|
||
"%-4i %i %2i %i %4i %4i %5i %6i %8i %i %5i %5i\r\n",
|
||
"%-4i %i %2i %i %4i %i %4i %5i %i %6i %8i %i %5i %5i\r\n",
|
||
i, connection[i].active, connection[i].id,
|
||
connection[i].menuactive, connection[i].menuThread,
|
||
connection[i].net, connection[i].netThread,
|
||
connection[i].serial, connection[i].serialThread,
|
||
connection[i].netconnected, connection[i].net, connection[i].netThread,
|
||
connection[i].serialconnected, connection[i].serial, connection[i].serialThread,
|
||
connection[i].option_echo,
|
||
connection[i].net_bytes_rx, connection[i].net_bytes_tx
|
||
);
|
||
... | ... | |
/* IAC WILL status */
|
||
netprintf(conn,"\xff\xfb\x01\xff\xfb\x03\xff\xfb\x05");
|
||
|
||
netprintf(conn,"wconsd " VERSION " a serial port server\r\n\r\n");
|
||
netprintf(conn,"\r\nwconsd serial port server (version %s)\r\n\r\n",VERSION);
|
||
send_help(conn);
|
||
netprintf(conn,"> ");
|
||
|
||
FD_ZERO(&set_read);
|
||
while (conn->menuactive) {
|
||
while (conn->menuactive && conn->netconnected) {
|
||
FD_SET(conn->net,&set_read);
|
||
select(conn->net+1,&set_read,NULL,NULL,NULL);
|
||
size=recv(conn->net,(void*)&buf,BUFSIZE,0);
|
||
... | ... | |
if (size==0) {
|
||
closesocket(conn->net);
|
||
conn->net=INVALID_SOCKET;
|
||
conn->netconnected=0;
|
||
SetEvent(connectionCloseEvent);
|
||
return 0; /* signal running=0 */
|
||
}
|
||
... | ... | |
|
||
DWORD WINAPI thread_new_connection(LPVOID lpParam) {
|
||
struct connection * conn = (struct connection*)lpParam;
|
||
HANDLE netThread=NULL, comThread=NULL;
|
||
int running=1;
|
||
|
||
while(running) {
|
||
while(conn->netconnected) {
|
||
dprintf(1,"wconsd[%i]: top of menu thread running loop\n",conn->id);
|
||
if (conn->menuactive) {
|
||
if (conn->menuactive && conn->netconnected) {
|
||
/* run the menu to ask the user questions */
|
||
running = run_menu(conn);
|
||
} else {
|
||
}
|
||
if (!conn->menuactive && conn->netconnected && conn->serialconnected) {
|
||
/* they must have opened the com port, so start the threads */
|
||
PurgeComm(hCom,PURGE_RXCLEAR|PURGE_RXABORT);
|
||
netThread=CreateThread(NULL,0,wconsd_net_to_com,NULL,0,NULL);
|
||
comThread=CreateThread(NULL,0,wconsd_com_to_net,NULL,0,NULL);
|
||
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);
|
||
|
||
WaitForSingleObject(netThread,INFINITE);
|
||
WaitForSingleObject(comThread,INFINITE);
|
||
WaitForSingleObject(conn->netThread,INFINITE);
|
||
WaitForSingleObject(conn->serialThread,INFINITE);
|
||
|
||
/*
|
||
* since I wait for the threads and there currently
|
||
... | ... | |
|
||
/* TODO print bytecounts */
|
||
/* maybe close file descriptors? */
|
||
if (com_autoclose) {
|
||
close_com_port(conn);
|
||
}
|
||
CloseHandle(conn->netThread);
|
||
CloseHandle(conn->serialThread);
|
||
shutdown(conn->net,SD_BOTH);
|
||
closesocket(conn->net);
|
||
conn->active=0;
|
||
|
||
/* TODO - who closes menuThread ? */
|
||
return 0;
|
||
}
|
||
|
||
... | ... | |
BOOL run=TRUE;
|
||
DWORD o;
|
||
SOCKET as;
|
||
HANDLE netThread=NULL, comThread=NULL;
|
||
unsigned long zero=0;
|
||
|
||
struct sockaddr_in sa;
|
||
int salen;
|
||
char buf[MAXLEN];
|
||
|
||
int i;
|
||
int count;
|
||
|
||
/* clear out any bogus data in the connections table */
|
||
for (i=0;i<MAXCONNECTIONS;i++) {
|
||
... | ... | |
|
||
/* getnameinfo does not appear to be supported in my windows build environment */
|
||
#ifdef GETNAMEINFO
|
||
char buf[MAXLEN];
|
||
if (!getnameinfo((struct sockaddr*)&sa,salen,buf,sizeof(buf),NULL,0,0)) {
|
||
#endif
|
||
dprintf(1,"wconsd: new connection from %08x\n",
|
||
htonl(sa.sin_addr.s_addr));
|
||
#ifdef GETNAMEINFO
|
||
} else {
|
||
#endif
|
||
dprintf(1,"wconsd: new connection from %s\n",
|
||
&buf);
|
||
#ifdef GETNAMEINFO
|
||
}
|
||
#endif
|
||
|
||
/* search for an empty connection slot */
|
||
i=0;
|
||
while(connection[i].active && i<MAXCONNECTIONS) {
|
||
i++;
|
||
i=next_connection_slot%MAXCONNECTIONS;
|
||
count=0;
|
||
while(connection[i].active && count<MAXCONNECTIONS) {
|
||
count++;
|
||
i = (i+1)%MAXCONNECTIONS;
|
||
}
|
||
if (i==MAXCONNECTIONS) {
|
||
dprintf(1,"wconsd: connection table overflow\n");
|
||
... | ... | |
closesocket(as);
|
||
break;
|
||
}
|
||
next_connection_slot = (next_connection_slot+1)%MAXCONNECTIONS;
|
||
connection[i].active=1; /* mark this entry busy */
|
||
connection[i].id = next_connection_id++;
|
||
connection[i].menuactive=1; /* start in the menu */
|
||
connection[i].menuThread=NULL;
|
||
connection[i].netconnected=1;
|
||
connection[i].net=as;
|
||
connection[i].netThread=NULL;
|
||
connection[i].serialconnected=0;
|
||
connection[i].serial=INVALID_HANDLE_VALUE;
|
||
connection[i].serialThread=NULL;
|
||
connection[i].option_echo=0;
|
||
connection[i].net_bytes_rx=0;
|
||
connection[i].net_bytes_tx=0;
|
||
|
||
dprintf(1,"wconsd[%i]: accepted new connection\n",connection[i].id);
|
||
dprintf(1,"wconsd[%i]: accepted new connection slot=%i\n",connection[i].id,i);
|
||
|
||
|
||
/* we successfully accepted the connection */
|
||
... | ... | |
/* Close down the existing connection and let the new one through */
|
||
SetEvent(threadTermEvent);
|
||
shutdown(cs,SD_BOTH);
|
||
WaitForSingleObject(netThread,INFINITE);
|
||
WaitForSingleObject(comThread,INFINITE);
|
||
CloseHandle(netThread);
|
||
CloseHandle(comThread);
|
||
closesocket(cs);
|
||
ResetEvent(connectionCloseEvent);
|
||
ResetEvent(threadTermEvent);
|
||
}
|
||
cs=as;
|
||
ioctlsocket(cs,FIONBIO,&zero);
|
||
/* cs=as; */
|
||
|
||
ioctlsocket(connection[i].net,FIONBIO,&zero);
|
||
|
||
connection[i].menuThread = CreateThread(NULL,0,thread_new_connection,&connection[i],0,NULL);
|
||
|
||
... | ... | |
/* The data connection has been broken */
|
||
dprintf(1,"wconsd: connectionCloseEvent\n");
|
||
SetEvent(threadTermEvent);
|
||
WaitForSingleObject(netThread,INFINITE);
|
||
WaitForSingleObject(comThread,INFINITE);
|
||
CloseHandle(netThread);
|
||
CloseHandle(comThread);
|
||
netThread=NULL;
|
||
comThread=NULL;
|
||
if (cs!=INVALID_SOCKET) {
|
||
shutdown(cs,SD_BOTH);
|
||
closesocket(cs);
|
||
... | ... | |
}
|
||
ResetEvent(connectionCloseEvent);
|
||
ResetEvent(threadTermEvent);
|
||
if (com_autoclose) {
|
||
close_com_port();
|
||
}
|
||
break;
|
||
default:
|
||
run=FALSE; // Stop the service - I want to get off!
|
||
... | ... | |
closesocket(cs);
|
||
}
|
||
|
||
if (netThread || comThread) {
|
||
SetEvent(threadTermEvent);
|
||
WaitForSingleObject(netThread,INFINITE);
|
||
WaitForSingleObject(comThread,INFINITE);
|
||
CloseHandle(netThread);
|
||
CloseHandle(comThread);
|
||
}
|
||
/* TODO - look through the connection table and close everything */
|
||
|
||
CloseHandle(hCom);
|
||
closesocket(ls);
|
||
WSACleanup();
|
||
}
|
Also available in: Unified diff
Migrate threads to new connection model, allowing multiple connections.
This is the first part of migrating the two threads to use the new
connection table. This change also rips out a lot of the event waiting
system. All the unused variables are going, but I have not yet looked
at removing these events. It still remains to cook the net data for
CL NUL and IAC options. refs #19