Initial load of driver
r1083 | dcl | 2006-08-25 14:46:51 +1000 (Fri, 25 Aug 2006) | 2 lines
This commit is contained in:
504
site_ansto/hardsup/Monitor/sock.c
Normal file
504
site_ansto/hardsup/Monitor/sock.c
Normal file
@@ -0,0 +1,504 @@
|
||||
#include "sock.h"
|
||||
#include "utility.h"
|
||||
#include "display.h"
|
||||
#include "cntr.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h> /* superset of previous */
|
||||
|
||||
#define LINE_LEN 1024
|
||||
#define MAX_SOCK 50
|
||||
|
||||
/**
|
||||
* Mode of the socket
|
||||
*/
|
||||
typedef enum terminal_mode_t
|
||||
{
|
||||
/** unknown or uninitialised */
|
||||
term_idle = 0,
|
||||
/** telnet socket */
|
||||
term_tty,
|
||||
/** web page GET */
|
||||
term_page,
|
||||
/** web form POST */
|
||||
term_form,
|
||||
/** SOAP request */
|
||||
term_soap
|
||||
} TERM_MODE;
|
||||
|
||||
/**
|
||||
* Terminal Control Structure
|
||||
*
|
||||
* Maintains the state of the connection
|
||||
*/
|
||||
typedef struct terminal_t
|
||||
{
|
||||
/** file descriptor for the socket */
|
||||
int fd;
|
||||
/** mode of the connection */
|
||||
TERM_MODE mode;
|
||||
/** current state of the connection */
|
||||
int state;
|
||||
/** value to match for reports */
|
||||
int match;
|
||||
/** TOD socket connected */
|
||||
struct timeval connect_time;
|
||||
/** address of peer */
|
||||
struct sockaddr_in addr;
|
||||
/** function to handle input ready */
|
||||
void (*input)(int idx);
|
||||
/** input line buffer */
|
||||
char line[LINE_LEN];
|
||||
/** length of text in line */
|
||||
int line_len;
|
||||
/** URL for GET/POST */
|
||||
char url[LINE_LEN];
|
||||
/** value from Content-Length header */
|
||||
int content_length;
|
||||
} TERMINAL, *pTERMINAL;
|
||||
|
||||
/**
|
||||
* Array of terminal control structures
|
||||
*/
|
||||
static TERMINAL fdv[MAX_SOCK];
|
||||
|
||||
/**
|
||||
* This structure parallels the terminal control structure
|
||||
*/
|
||||
static struct pollfd fds[MAX_SOCK];
|
||||
/** Number of active connections */
|
||||
static int num_fds = 0;
|
||||
/** descriptor of the listen socket */
|
||||
static int sock_l;
|
||||
|
||||
/**
|
||||
* Initialise the socket interface
|
||||
*
|
||||
* Opens and binds the listen socket and listens for incomming
|
||||
* connections.
|
||||
*
|
||||
* \param addr the TCP/IP port number on which to listen
|
||||
*/
|
||||
void sock_init(int addr)
|
||||
{
|
||||
dprintf(0, "sock_init\n");
|
||||
int status;
|
||||
long flags;
|
||||
int i;
|
||||
int one = 1;
|
||||
struct sockaddr_in my_addr;
|
||||
memset(fdv, 0, sizeof(fdv));
|
||||
for (i = 0; i < MAX_SOCK; ++i)
|
||||
{
|
||||
fdv[i].fd = -1;
|
||||
}
|
||||
memset(fds, 0, sizeof(fds));
|
||||
status = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (status < 0)
|
||||
{
|
||||
perror("socket");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
sock_l = status;
|
||||
flags = fcntl(sock_l, F_GETFL);
|
||||
flags |= O_NONBLOCK;
|
||||
fcntl(sock_l, F_SETFL, flags);
|
||||
setsockopt(sock_l, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
|
||||
memset(&my_addr, 0, sizeof(my_addr));
|
||||
my_addr.sin_family = AF_INET;
|
||||
my_addr.sin_port = htons(addr);
|
||||
my_addr.sin_addr.s_addr = INADDR_ANY;
|
||||
status = bind(sock_l, (struct sockaddr*) &my_addr, sizeof(my_addr));
|
||||
if (status < 0)
|
||||
{
|
||||
perror("bind");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
status = listen(sock_l, 5);
|
||||
if (status < 0)
|
||||
{
|
||||
perror("listen");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
fds[0].fd = sock_l;
|
||||
fds[0].events = POLLIN | POLLOUT;
|
||||
fdv[0].fd = sock_l;
|
||||
fdv[0].input = sock_accept;
|
||||
++num_fds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for socket activity
|
||||
*
|
||||
* Polls active sockets for activity: connections on the listen socket or
|
||||
* received data on the active sockets.
|
||||
*
|
||||
* \param timeout wait time in milliseconds
|
||||
*/
|
||||
void sock_check(int timeout)
|
||||
{
|
||||
int i;
|
||||
int ready;
|
||||
ready = poll(fds, num_fds, timeout);
|
||||
if (ready < 0)
|
||||
{
|
||||
perror("poll");
|
||||
return;
|
||||
}
|
||||
if (ready == 0)
|
||||
return;
|
||||
dprintf(0, "sock_check, ready=%d\n", ready);
|
||||
for (i = 0; i < MAX_SOCK; ++i)
|
||||
{
|
||||
if (fds[i].revents)
|
||||
{
|
||||
if (fds[i].revents & POLLIN)
|
||||
fdv[i].input(i);
|
||||
if (--ready)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close and remove an active or disconnected socket
|
||||
*
|
||||
* \param n index of socket
|
||||
*/
|
||||
void sock_close(int n)
|
||||
{
|
||||
dprintf(0, "sock_close\n");
|
||||
shutdown(fdv[n].fd, SHUT_RDWR);
|
||||
close(fdv[n].fd);
|
||||
if (n != num_fds)
|
||||
{
|
||||
fdv[n] = fdv[num_fds];
|
||||
fds[n] = fds[num_fds];
|
||||
}
|
||||
fdv[num_fds].fd = -1;
|
||||
--num_fds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a line of input from a socket
|
||||
*
|
||||
* \param n index of socket
|
||||
*/
|
||||
void sock_line(int n)
|
||||
{
|
||||
char *cp = &fdv[n].line[0];
|
||||
char *up;
|
||||
dprintf(0, "%3d: %s", fdv[n].state, fdv[n].line);
|
||||
if (fdv[n].line[fdv[n].line_len - 1] != '\n')
|
||||
dprintf(0, "\n");
|
||||
switch(fdv[n].state)
|
||||
{
|
||||
case 0:
|
||||
/*
|
||||
* we are looking for HTTP GET or POST or a SICS command
|
||||
*/
|
||||
if (toupper(cp[0]) == 'G' &&
|
||||
toupper(cp[1]) == 'E' &&
|
||||
toupper(cp[2]) == 'T' &&
|
||||
isspace(cp[3]) &&
|
||||
strstr(cp, "HTTP/"))
|
||||
{
|
||||
cp+= 4;
|
||||
while (isspace(*cp))
|
||||
++cp;
|
||||
up = &fdv[n].url[0];
|
||||
while (*cp && !isspace(*cp))
|
||||
*up++ = *cp++;
|
||||
*up = '\0';
|
||||
fdv[n].state = 1;
|
||||
}
|
||||
else if (toupper(cp[0]) == 'P' &&
|
||||
toupper(cp[1]) == 'O' &&
|
||||
toupper(cp[2]) == 'S' &&
|
||||
toupper(cp[3]) == 'T' &&
|
||||
isspace(cp[4]) &&
|
||||
strstr(cp, "HTTP/"))
|
||||
{
|
||||
cp+= 4;
|
||||
while (isspace(*cp))
|
||||
++cp;
|
||||
up = &fdv[n].url[0];
|
||||
while (*cp && !isspace(*cp))
|
||||
*up++ = *cp++;
|
||||
*up = '\0';
|
||||
fdv[n].content_length = 0;
|
||||
fdv[n].state = 3;
|
||||
}
|
||||
else if (toupper(cp[0]) == 'S' &&
|
||||
toupper(cp[1]) == 'I' &&
|
||||
toupper(cp[2]) == 'C' &&
|
||||
toupper(cp[3]) == 'S' &&
|
||||
isspace(cp[4]))
|
||||
{
|
||||
BUFFER buf;
|
||||
memcpy(buf.body, fdv[n].line, fdv[n].line_len);
|
||||
buf.length = fdv[n].line_len;
|
||||
buf.body[buf.length] = '\0';
|
||||
process_command(n, &buf);
|
||||
fdv[n].content_length = 0;
|
||||
fdv[n].state = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
BUFFER buf;
|
||||
memcpy(buf.body, fdv[n].line, fdv[n].line_len);
|
||||
buf.length = fdv[n].line_len;
|
||||
buf.body[buf.length] = '\0';
|
||||
process_command(n, &buf);
|
||||
fdv[n].content_length = 0;
|
||||
fdv[n].state = 0;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
/*
|
||||
* We are scanning the header of a GET
|
||||
*/
|
||||
while (*cp == '\r' || *cp == '\n')
|
||||
++cp;
|
||||
if (!*cp)
|
||||
{
|
||||
if (strncasecmp(fdv[n].url, "/form", 5) == 0)
|
||||
put_form(n);
|
||||
else if (strncasecmp(fdv[n].url, "/cmd=", 5) == 0)
|
||||
{
|
||||
if (strncasecmp(fdv[n].url, "/cmd=pause", 10) == 0)
|
||||
cntr_pause(&counter);
|
||||
else if (strncasecmp(fdv[n].url, "/cmd=continue", 10) == 0)
|
||||
cntr_resume(&counter);
|
||||
else if (strncasecmp(fdv[n].url, "/cmd=resume", 10) == 0)
|
||||
cntr_resume(&counter);
|
||||
else if (strncasecmp(fdv[n].url, "/cmd=start", 10) == 0)
|
||||
cntr_start(&counter);
|
||||
else if (strncasecmp(fdv[n].url, "/cmd=stop", 10) == 0)
|
||||
cntr_stop(&counter);
|
||||
put_page_refresh(n);
|
||||
}
|
||||
else
|
||||
put_page(n);
|
||||
fdv[n].state = 0;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
/*
|
||||
* we are scanning the header of a POST
|
||||
*/
|
||||
while (*cp == '\r' || *cp == '\n')
|
||||
++cp;
|
||||
if (!*cp)
|
||||
{
|
||||
if (fdv[n].content_length == 0)
|
||||
fdv[n].state = 5;
|
||||
else
|
||||
fdv[n].state = 4;
|
||||
break;
|
||||
}
|
||||
if (strncasecmp("Content-Length", cp, 14) == 0 &&
|
||||
(cp = strchr(&cp[14], ':')))
|
||||
{
|
||||
fdv[n].content_length = atoi(&cp[1]);
|
||||
dprintf(0, "Content Length = %d\n", fdv[n].content_length);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
/*
|
||||
* we are scanning the body of a POST
|
||||
*/
|
||||
dprintf(0, "Content-Length: %d, Line-Length: %d\n",
|
||||
fdv[n].content_length, fdv[n].line_len);
|
||||
fdv[n].content_length -= fdv[n].line_len;
|
||||
if (fdv[n].content_length <= 0)
|
||||
{
|
||||
BUFFER buf;
|
||||
memcpy(buf.body, fdv[n].line, fdv[n].line_len);
|
||||
buf.length = fdv[n].line_len;
|
||||
process_form(n, &buf);
|
||||
fdv[n].state = 5;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* If we have reached the end of a POST, send the new form
|
||||
*/
|
||||
if (fdv[n].state == 5)
|
||||
{
|
||||
put_form(n);
|
||||
fdv[n].state = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle data received on a socket
|
||||
*
|
||||
* \param n index of socket
|
||||
*/
|
||||
void sock_input(int n)
|
||||
{
|
||||
dprintf(0, "sock_input(%d)\n", n);
|
||||
ssize_t sz;
|
||||
char buffer[1024];
|
||||
sz = recv(fdv[n].fd, &buffer, sizeof(buffer), 0);
|
||||
if (sz == 0)
|
||||
{
|
||||
sock_close(n);
|
||||
return;
|
||||
}
|
||||
if (sz < 0)
|
||||
{
|
||||
if (errno == EAGAIN) /* AKA EWOULDBLOCK */
|
||||
dprintf(0, "EAGAIN:");
|
||||
else if (errno == ESPIPE) /* Illegal seek (on pipe or socket) */
|
||||
dprintf(0, "ESPIPE:");
|
||||
else
|
||||
perror("recv");
|
||||
return;
|
||||
}
|
||||
int i;
|
||||
for (i = 0; i < sz; ++i)
|
||||
{
|
||||
if (fdv[n].line_len < LINE_LEN - 1)
|
||||
fdv[n].line[fdv[n].line_len++] = buffer[i];
|
||||
if (buffer[i] == '\n' ||
|
||||
(fdv[n].state == 4 &&
|
||||
fdv[n].line_len == fdv[n].content_length)) {
|
||||
fdv[n].line[fdv[n].line_len] = '\0';
|
||||
sock_line(n);
|
||||
fdv[n].line_len = 0;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle connection arrived on listen socket
|
||||
*
|
||||
* \param n index of socket
|
||||
*/
|
||||
void sock_accept(int n)
|
||||
{
|
||||
dprintf(0, "sock_accept(%d)\n", n);
|
||||
int sock_n;
|
||||
struct sockaddr_in my_addr;
|
||||
socklen_t my_len = sizeof(my_addr);
|
||||
sock_n = accept(fdv[n].fd, (struct sockaddr*) &my_addr, &my_len);
|
||||
if (sock_n < 0)
|
||||
{
|
||||
perror("accept");
|
||||
return;
|
||||
}
|
||||
if (num_fds < MAX_SOCK)
|
||||
{
|
||||
fdv[num_fds].fd = sock_n;
|
||||
fdv[num_fds].addr = my_addr;
|
||||
fdv[num_fds].input = sock_input;
|
||||
fdv[num_fds].line_len = 0;
|
||||
fdv[num_fds].state = 0;
|
||||
fds[num_fds].fd = sock_n;
|
||||
fds[num_fds].events = POLLIN;
|
||||
++num_fds;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* maximum connections active */
|
||||
/* TODO log error */
|
||||
close(sock_n);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send data to active connection
|
||||
*
|
||||
* The data in the buffer is sent to the socket
|
||||
*
|
||||
* \param n index of socket
|
||||
* \param bp pointer to buffer to send
|
||||
*/
|
||||
void sock_send(int n, BUFFER* bp)
|
||||
{
|
||||
int status;
|
||||
status = send(fdv[n].fd, bp->body, bp->length, MSG_NOSIGNAL);
|
||||
if (status < 0)
|
||||
{
|
||||
if (errno == EAGAIN) /* flooded */
|
||||
;
|
||||
else if (errno == EPIPE)
|
||||
{
|
||||
perror("send EPIPE");
|
||||
sock_close(n);
|
||||
}
|
||||
else
|
||||
{
|
||||
perror("send");
|
||||
sock_close(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The report in the buffer is sent to all active sockets which match
|
||||
*
|
||||
* \param bp pointer to buffer containing report
|
||||
* \param match value to match for this report
|
||||
*/
|
||||
void sock_report(BUFFER* bp, int match)
|
||||
{
|
||||
int i;
|
||||
for (i = 1; i < num_fds; ++i)
|
||||
{
|
||||
if (fdv[i].match == match)
|
||||
sock_send(i, bp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the match parameter for the socket
|
||||
*
|
||||
* \param n index of socket
|
||||
* \param match value to match for this socket
|
||||
*/
|
||||
void sock_set_match(int n, int match)
|
||||
{
|
||||
fdv[n].match = match;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an OK to the socket
|
||||
*
|
||||
* \param n index of socket
|
||||
*/
|
||||
void sock_ok(int n)
|
||||
{
|
||||
BUFFER buffer;
|
||||
strcpy(buffer.body, "OK\r\n");
|
||||
buffer.length = strlen(buffer.body);
|
||||
sock_send(n, &buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an ERR to the socket
|
||||
*
|
||||
* \param n index of socket
|
||||
*/
|
||||
void sock_err(int n)
|
||||
{
|
||||
BUFFER buffer;
|
||||
strcpy(buffer.body, "ERR\r\n");
|
||||
buffer.length = strlen(buffer.body);
|
||||
sock_send(n, &buffer);
|
||||
}
|
||||
Reference in New Issue
Block a user