/*
* Copyright (c) 2019 Clementine Computing LLC.
*
* This file is part of PopuFare.
*
* PopuFare is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PopuFare 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with PopuFare. If not, see .
*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "../common/common_defs.h"
#include "commhub.h"
// 10 seconds
//
#define COMMHUB_POLL_TIME (10000)
//#define COMMHUB_DEBUG
// Our listening socket
//
int listener_socket = -1;
// Our list of user mailboxes
//
struct mailbox_record *mailboxes = NULL;
// Our list of clients subscribed for wiretap access
//
struct subscriber_record *wiretap_list = NULL;
// Our list of all clients not in wiretap mode (to prevent duplicate broadcasts)
//
struct subscriber_record *broadcast_list = NULL;
// Our list of all clients (for fd/pid tracking)
//
struct subscriber_record *master_list = NULL;
// Our active client count
//
int num_clients = 0;
// A buffer to hold the poll() file descriptor table
//
struct pollfd fds[NUM_SUPPORTED_CLIENTS + 1];
//state_info_t state_info = {0};
int set_blocking_mode(int fd, int blocking) {
int retval;
int newflags;
newflags = fcntl(fd, F_GETFL);
if (newflags >= 0) {
if(blocking) {
newflags &= ~O_NONBLOCK;
}
else {
newflags |= O_NONBLOCK;
}
retval = fcntl(fd, F_SETFL, newflags);
if(retval) {
printf("Cannot set O_NONBLOCK on socket %d to %s!\n", fd, blocking?"ON":"OFF");
return -1;
}
}
else {
printf("Cannot get flags on socket %d! [%d / %X]\n", fd, newflags, newflags);
return -1;
}
return 0;
}
#ifdef COMMHUB_O_NONBLOCK
#define DO_SEND_MESSAGE(ret, fd, msg) {set_blocking_mode((fd),0); ret = send_message((fd), (msg)); set_blocking_mode((fd),1);}
#else
#define DO_SEND_MESSAGE(ret, fd, msg) {ret = send_message((fd), (msg));}
#endif
void debug_traverse_single_list(struct subscriber_record *p) {
while(p) {
printf(" [FD: %d PID: %d Prog: %s]", p->clientfd, p->pid, p->progname);
p = p->next;
}
printf("\n");
}
// This function traverses the global state data structures and prints out a visual indication of state
//
int debug_traverse() {
struct mailbox_record *p;
printf("-------------------------------------------------\n");
printf("ALL:");
debug_traverse_single_list(master_list); //iterate through the master list
printf("WIRETAP:");
debug_traverse_single_list(wiretap_list); //iterate through the wiretap list
printf("BROADCAST:");
debug_traverse_single_list(broadcast_list); //do the same for the broadcast list
printf("---User Lists---\n");
p = mailboxes; //then iterate through each generic (user) mailbox
while (p) {
// display the mailbox name
//
printf("%s:",p->mailbox_name);
debug_traverse_single_list(p->clients);
p = p->next;
}
printf("\n");
return 0;
}
void update_client_identifiers(struct subscriber_record *p, pid_t pid, char *progname) {
if(!p) {
return;
}
p->pid = pid;
// if we have a program name, store it
//
if(progname) {
strncpy(p->progname, progname, MAX_MODULE_NAME_LENGTH);
p->progname[MAX_MODULE_NAME_LENGTH - 1] = '\0';
}
// otherwise, set a blank string
else {
memset(p->progname, '\0', MAX_MODULE_NAME_LENGTH);
}
}
// This function adds a subscriber to a subscriber list (usually a mailbox or a 'special' list)
// It is passed a file descriptor, optional PID, and a pointer to the listhead (so that we can insert at the head if we want)
//
int add_client(int fd, pid_t pid, char *progname, struct subscriber_record **list) {
struct subscriber_record *p, *q;
q = NULL;
// iterate through all items
//
p = *list;
while(p) {
// the client is already on the list
//
if(p->clientfd == fd) {
// update the PID and progname anyway
//
update_client_identifiers(p, pid, progname);
return 0;
}
// move our pointers along
//
q = p;
p = p->next;
}
// allocate a new subscriber_record structure
//
p = (struct subscriber_record *) malloc(sizeof(struct subscriber_record));
if(p == NULL) {
return -1;
}
// clear it and populate it
memset(p,0,sizeof(struct subscriber_record));
p->clientfd = fd;
p->next = NULL;
update_client_identifiers(p, pid, progname); //set our pid and progname
// if we're not at the list head
//
if(q) {
// insert at the end
//
q->next = p;
}
// if this is the list head (empty list)
//
else {
// replace the head with our new node.
//
*list = p;
}
return 0;
}
// This function deletes a client from a subscriber list (again we pass a pointer to the listhead).
//
int del_client(int fd, struct subscriber_record **list) {
struct subscriber_record *p, *q;
q = NULL;
// iterate through our list
//
p = *list;
while(p) {
// if we find our client
//
if (p->clientfd == fd) {
// and we're not at the head of the list
//
if(q) {
// snip it out
//
q->next = p->next;
}
// otherwise
//
else {
// bump the listhead down one
//
*list = p->next;
}
// free the deleted node
//
free(p);
// and return
//
return 0;
}
// advance our pointers
//
q=p;
p=p->next;
}
// client not found
//
return -1;
}
// This function subscribes a client to a user mailbox. It is passed the client data (fd and pid)
// as well as a mailbox name to search for.
//
int subscribe_client(int fd, pid_t pid, char *progname, char *mailbox) {
struct mailbox_record *p, *q;
if(mailbox == NULL) {
return -1;
}
q = NULL;
// traverse the mailboxes list
//
p = mailboxes;
while(p) {
// if we found a match
//
if( !strncmp(mailbox, p->mailbox_name, MAILBOX_NAME_MAX) ) {
// try and add the client
//
return add_client(fd, pid, progname, &p->clients);
}
q = p; //advance our pointers
p = p->next;
}
//if we have not found a mailbox, create one
//
p = (struct mailbox_record *) malloc(sizeof(struct mailbox_record));
if(p == NULL) {
return -1;
}
// clear the memory
//
memset(p, 0, sizeof(struct mailbox_record));
// fill the mailbox name
//
strncpy(p->mailbox_name, mailbox, MAILBOX_NAME_MAX);
p->mailbox_name[MAILBOX_NAME_MAX] = '\0';
// add our shiny new subscriber
//
add_client(fd, pid, progname, &p->clients);
p->next = NULL;
// if we're not adding to an empty list
//
if(q) {
q->next = p; //insert at the end
}
// otherwise
//
else {
mailboxes = p; //replace the listhead
}
return 0;
}
//This function unsubscriber a client from a user mailbox
//
int unsubscribe_client(int fd, char *mailbox) {
struct mailbox_record *p, *q;
int retval;
if(mailbox == NULL) {
return -1;
}
q = NULL;
// traverse the mailbox list
//
p = mailboxes;
while(p) {
// if we found a match
//
if( !strncmp(mailbox, p->mailbox_name, MAILBOX_NAME_MAX) ) {
// try to unsubscribe
//
retval = del_client(fd, &p->clients);
// if we just unsubscribed our last client, delete this list.
//
if(p->clients == NULL) {
// if this is not the first list item
//
if(q) {
//snip it from the middle
//
q->next = p->next;
}
else {
// snip from the beginning (tweaking the listhead)
//
mailboxes = p->next;
}
//and free it
//
free(p);
}
// return the result of the removal
//
return retval;
}
// advance our pointers
//
q = p;
p = p->next;
}
// not found!
//
return -1;
}
// This function scans the mailboxes to find the named mailbox, and if it is found
// returns the client list associated with that mailbox. This is used in delivery.
//
struct subscriber_record *find_mailbox(char *mailbox) {
struct mailbox_record *p;
if(mailbox == NULL) {
return NULL;
}
// iterate through the mailbox list
//
p = mailboxes;
while(p) {
// if we found a match
//
if( !strncmp(mailbox, p->mailbox_name, MAILBOX_NAME_MAX) ) {
// return the client list associated
//
return p->clients;
}
// advance our pointer
//
p = p->next;
}
// not found
//
return NULL;
}
// This function walks all global state structures to purge all reference to a (disconnected) client
//
int purge_client(int fd) {
struct mailbox_record *p, *q;
// remove the client from all system lists
//
del_client(fd, &broadcast_list);
del_client(fd, &wiretap_list);
del_client(fd, &master_list);
q = NULL;
// then iterate through user mailboxes
//
p = mailboxes;
while(p) {
// removing the client when it is present
//
del_client(fd, &p->clients);
// if we just unsubscribed our last client on this list, delete the list.
//
if(p->clients == NULL) {
// save this pointer to delete after advancement
//
struct mailbox_record *r = p;
// if we're deleting from the middle
//
if(q) {
// snip out
//
q->next = p->next;
}
else {
// advance the listhead
//
mailboxes = p->next;
}
// advance our pointer
//
p = p->next;
// free the deleted node
//
free(r);
// continue with the advanced pointer
//
continue;
}
// advance our pointers
//
q = p;
p = p->next;
}
#ifdef COMMHUB_DEBUG
printf("Purged client %d\n", fd);
debug_traverse();
#endif
return 0;
}
int create_listener(char *pathname) {
int fd;
struct sockaddr_un addr;
int len;
int retval;
// create a UNIX domain socket of type SOCK_SEQPACKET
//
fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if(fd < 0) {
return -1;
}
// prepare our bind address structure
//
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, pathname, sizeof(addr.sun_path) - 1);
addr.sun_path[sizeof(addr.sun_path) - 1]='\0';
// unlink any existing socket or file that lives there
//
unlink(pathname);
// calculate the address length
//
len = strlen(pathname) + sizeof(addr.sun_family);
// and perform the bind
//
retval = bind(fd,(struct sockaddr *)&addr,len);
if(retval) {
close(fd);
return -2;
}
// set the socket in listening mode...
//
retval=listen(fd, 5);
if(retval) {
close(fd);
return -3;
}
return fd;
}
// This function walks the master client list and rebuilds the file descriptor set used by poll()
// to wait for input.
//
int rebuild_client_fds() {
struct subscriber_record *p = master_list;
int i = 0;
// iterate through our master list
//
while(p) {
// stopping if we have exceeded the space in the fds table.
//
if(i >= NUM_SUPPORTED_CLIENTS) {
break;
}
// set the fd for this table slot
//
fds[i + 1].fd = p->clientfd;
// specify that we're waiting for input
//
fds[i + 1].events = POLLIN;
// count it
//
i++;
// and advance our pointer
//
p=p->next;
}
// remember the number of clients we have so that we can reject new ones if they are
//
num_clients = i;
// in excess of our table size.
//
return i;
}
// This function accepts a new client on the listener_socket and either rejects it with an error (if our table is full) or
// accepts it and places it in the master (and broadcast) lists.
//
int accept_client() {
int retval;
int sendret = 0;
struct message_record msg;
// try to accept the new client
//
retval = accept(listener_socket, NULL, NULL);
// if we got a valid file descriptor
//
if(retval >= 0) {
// if we are out of room
//
if(num_clients >= NUM_SUPPORTED_CLIENTS) {
#ifdef COMMHUB_DEBUG
printf("Cannot add client %d (MAXCLIENTS)\n", retval);
#endif
// send an error
//
prepare_message(&msg,MAILBOX_ERROR,"MAXCLIENTS",10);
DO_SEND_MESSAGE(sendret, retval, &msg);
if (sendret < 0) { }
// close the socket
//
close(retval);
// and return
//
return -1;
}
// otherwise add it to our default lists (master, broadcast) with a bogus pid
// which the client will (hopefully) correct with a HELLO message
//
add_client(retval, -1, "", &master_list);
add_client(retval, -1, "", &broadcast_list);
#ifdef COMMHUB_DEBUG
printf("Added Client %d\n", retval);
debug_traverse();
#endif
}
else {
if(errno != EINTR) {
perror("Accept client failed: ");
}
}
return 0;
}
// This helper function finds a client record by file descriptor
//
struct subscriber_record *find_client_by_fd(struct subscriber_record *list, int fd) {
struct subscriber_record *p = list;
while(p) {
if (p->clientfd == fd) {
break;
}
p = p->next;
}
return p;
}
// This helper function delivers a message to every subscriber on the provided list.
//
int deliver_message(struct message_record *msg, struct subscriber_record *dest) {
struct subscriber_record *p = dest;
int sendret;
while(p) {
DO_SEND_MESSAGE(sendret, p->clientfd, msg);
if (sendret) {
close(p->clientfd);
}
p = p->next;
}
return 0;
}
//This function is where the 'magic' happens. It picks out special messages (after first forwarding everything to the wiretap list)
//Messages addressed to:
// HELLO adds the client to the master and broadcast lists and updates its pid
// >pid gets delivered to the client with the specified pid.
// :modulename gets delivered to all clients registered with the supplied module name
// SUBSCRIBE causes the sending client to be added to the named mailbox
// UNSUBSCRIBE causes the sending client to be removed from the named mailbox
// BROADCAST causes the message to be passed to all listeners
// WIRETAP sets the wiretap state (on if payload = "ON" off otherwise)
// PING replies with PONG
//
//All other messages are passed to their user mailbox and on to any subscribing clients. Messages with no receiver are dropped silently.
//
int route_message(int fd, struct message_record *msg) {
struct subscriber_record dummy = {0};
struct subscriber_record *dest;
int pid;
int sendret;
// Implement wiretap for debug
//
if(wiretap_list) {
deliver_message(msg, wiretap_list);
}
// Handle special mailboxes
//
//-------------------------------------------- PID addressed message
//
if( msg->header.mailbox_name[0] == '>' ) {
// extract pid
//
pid = atoi( &msg->header.mailbox_name[1] );
// look it up in the master list
//
dest = master_list;
while (dest) {
// if we have a match
// stop looking
//
if(dest->pid == pid) {
break;
}
// advance pointer
//
dest = dest->next;
}
if(dest != NULL) {
// deliver the message
//
DO_SEND_MESSAGE(sendret, dest->clientfd, msg);
if (sendret) {
close(dest->clientfd);
}
}
else {
if (pid == getpid()) {
// If this is a unicast PING to US...
//
if( !strncmp((char *)msg->payload, MAILBOX_PING, MAILBOX_NAME_MAX) ) {
struct message_record outgoing;
prepare_message(&outgoing, MAILBOX_PONG, msg->payload, msg->header.payload_length);
dest = find_mailbox(MAILBOX_PONG);
if (dest) {
deliver_message(&outgoing, dest);
}
deliver_message(&outgoing, wiretap_list);
}
}
}
return 0;
}
//-------------------------------------------- module addressed message
//
else if( msg->header.mailbox_name[0] == ':' ) {
// start looking through the master list
//
dest = master_list;
while(dest) {
//if we have a match on module name
//
if(! strncmp(&msg->header.mailbox_name[1], dest->progname, MAX_MODULE_NAME_LENGTH) ) {
// deliver the message (but keep looking)
//
DO_SEND_MESSAGE(sendret, dest->clientfd, msg);
if (sendret) {
close(dest->clientfd);
}
}
dest = dest->next; //advance pointer
}
return 0;
}
// ------------------------- HELLO message
//
else if( !strncmp(msg->header.mailbox_name, MAILBOX_HELLO, MAILBOX_NAME_MAX)) {
// a HELLO message sets the client pid, adds it to master and broadcast, and removes it from wiretap
// the payload should contain the module name
//
add_client(fd, msg->header.sender, (char *)msg->payload, &master_list);
add_client(fd, msg->header.sender, (char *)msg->payload, &broadcast_list);
del_client(fd, &wiretap_list);
#ifdef COMMHUB_DEBUG
printf("Associated client %d with PID %d (progname = %s)\n", fd, msg->header.sender, msg->payload);
debug_traverse();
#endif
return 0; //return
}
// ------------------------- SUBSCRIBE message
//
else if( !strncmp(msg->header.mailbox_name, MAILBOX_SUBSCRIBE, MAILBOX_NAME_MAX)) {
// a SUBSCRIBE message subscribes the client to the specified mailbox
//
// look up this client in the master list to obtain progname
//
dest = find_client_by_fd(master_list, fd);
// if this fd is not found (this should NEVER happen, but still...) avoid a null ponter by using a dummy record of all 0's
if(!dest) {
fprintf(stderr,"Request from client %d which appears not to be in our master list!\n", fd);
dest = &dummy;
}
subscribe_client(fd, msg->header.sender, dest->progname, (char *)msg->payload);
#ifdef COMMHUB_DEBUG
printf("Added Client %d to %s\n", fd, msg->payload);
debug_traverse();
#endif
// return
return 0;
}
// ------------------------- UNSUBSCRIBE message
//
else if( !strncmp(msg->header.mailbox_name, MAILBOX_UNSUBSCRIBE, MAILBOX_NAME_MAX)) {
//an UNSUBSCRIBE message removes the client from the specified mailbox
//
unsubscribe_client(fd, (char *)msg->payload);
#ifdef COMMHUB_DEBUG
printf("Removed Client %d from %s\n", fd, msg->payload);
debug_traverse();
#endif
// return
return 0;
}
// ------------------------- BROADCAST message
//
else if( !strncmp(msg->header.mailbox_name, MAILBOX_BROADCAST, MAILBOX_NAME_MAX)) {
// a BROADCAST message goes to everybody (except those who already got it through wiretap)
//
deliver_message(msg, broadcast_list);
return 0;
}
// ------------------------- WIRETAP message
//
else if( !strncmp(msg->header.mailbox_name, MAILBOX_WIRETAP, MAILBOX_NAME_MAX)) {
// a WIRETAP message sets the wiretap mode.
//
// look up this client in the master list to obtain progname
//
dest = find_client_by_fd(master_list, fd);
// if this fd is not found (this should NEVER happen, but still...) avoid a null ponter by using a dummy record of all 0's
//
if (!dest) {
fprintf(stderr,"Request from client %d which appears not to be in our master list!\n", fd);
dest = &dummy;
}
// if we are turning wiretap mode ON
//
if( !strncmp((char *)msg->payload, "ON", MAX_PAYLOAD_LENGTH) ) {
// add the client to the wiretap list
//
add_client(fd, msg->header.sender, dest->progname, &wiretap_list);
// and remove from the broadcast list
//
del_client(fd, &broadcast_list);
#ifdef COMMHUB_DEBUG
printf("Added Client %d to wiretap list\n", fd);
debug_traverse();
#endif
}
// if we are turning wiretap mode OFF
//
else {
// remove the client from the wiretap list
//
del_client(fd, &wiretap_list);
// and add to the broadcast list
//
add_client(fd, msg->header.sender, dest->progname, &broadcast_list);
#ifdef COMMHUB_DEBUG
printf("Removed Client %d from wiretap list\n", fd);
debug_traverse();
#endif
}
return 0;
}
// Even we need to respond to a PING message
//
else if ( !strncmp(msg->header.mailbox_name, MAILBOX_PING, MAILBOX_NAME_MAX)) {
struct message_record outgoing;
prepare_message(&outgoing, MAILBOX_PONG, msg->payload, msg->header.payload_length);
dest = find_mailbox(MAILBOX_PONG);
if (dest) {
deliver_message(&outgoing, dest);
}
deliver_message(&outgoing, wiretap_list);
}
//-------------------------------------------------------------------------------Normal delivery to user mailboxes...
//
dest = find_mailbox(msg->header.mailbox_name); //look up the mailbox client list
// if it is non-NULL
//
if (dest) {
// deliver the message to those clients
//
deliver_message(msg, dest);
}
return 0;
}
// this function frees our data structures and closes our file handles politely before exit
//
int cleanup() {
struct mailbox_record *p, *q;
struct subscriber_record *pp, *qq;
q = NULL;
// walk the list of user mailboxes
//
p = mailboxes;
while(p) {
q = p;
// advance the pointers
//
p = p->next;
qq = NULL;
// walk the just-visited user mailbox
//
pp = q->clients;
// and free all the subscriber records
//
while (pp) {
qq = pp;
pp = pp->next;
free(qq);
}
// free the node
//
free(q);
}
// walk the wiretap list freeing all nodes
//
qq = NULL;
pp = wiretap_list;
while(pp) {
qq = pp;
pp = pp->next;
free(qq);
}
// walk the broadcast list freeing all nodes
//
qq = NULL;
pp = broadcast_list;
while(pp) {
qq = pp;
pp = pp->next;
free(qq);
}
// walk the master list freeing all nodes and closing the file descriptors
qq = NULL;
pp = master_list;
while(pp) {
qq = pp;
pp = pp->next;
close(qq->clientfd);
free(qq);
}
// NULL our all of our pointers
mailboxes = NULL;
broadcast_list = NULL;
wiretap_list = NULL;
master_list = NULL;
close(listener_socket);
num_clients = 0;
listener_socket = -1;
return 0;
}
/*
// DRIVER_STATE_FILE
// GPS_STATE_FILE
// STOP_STATE_FILE
//
int get_driver_state(driver_status *_driver_stat) {
FILE *fp;
int input_idx=0,
n=0,
ch;
char buffer[LINE_BUFFER_SIZE];
// 0 - logged_in_driver
// 1 - driver_name
// 2 - equip_num
//
int _read_state = 0;
if (access(DRIVER_STATE_FILE, R_OK)!=0) {
return -1;
}
memset(buffer, 0, sizeof(char)*LINE_BUFFER_SIZE);
n = DRIVER_NAME_LEN;
if (LINE_BUFFER_SIZE < n) {
n = LINE_BUFFER_SIZE;
}
fp = fopen(DRIVER_STATE_FILE, "r");
while ( (ch = fgetc(fp)) != EOF ) {
if ((ch == '\n') || (ch == EOF)) {
if (_read_state == 0) {
_driver_stat->logged_in_driver = atoi(buffer);
_read_state++;
}
else if (_read_state == 1) {
n = input_idx+1;
if (n > DRIVER_NAME_LEN) {
n = DRIVER_NAME_LEN;
}
buffer[n-1] = '\0';
memcpy(_driver_stat->driver_name, buffer, n);
_read_state++;
}
else if (_read_state == 2) {
_driver_stat->equip_num = atoi(buffer);
_read_state++;
}
memset(buffer, 0, sizeof(char)*LINE_BUFFER_SIZE);
input_idx=0;
continue;
}
buffer[input_idx] = ch;
input_idx++;
if (input_idx >= LINE_BUFFER_SIZE) {
input_idx = LINE_BUFFER_SIZE-1;
buffer[LINE_BUFFER_SIZE-1] = 0;
}
}
fclose(fp);
if (_read_state < 3) {
return -2;
}
return 0;
}
int get_stop_state(stop_status *_stop_stat) {
FILE *fp;
int input_idx=0,
n=0,
ch;
char buffer[LINE_BUFFER_SIZE];
// 0 - paddle
// 1 - route
// 2 - trip
// 3 - stop
// 4 - lat
// 5 - lon
// 6 - stopname
//
int _read_state = 0;
if (access(STOP_STATE_FILE, R_OK)!=0) {
return -1;
}
memset(buffer, 0, sizeof(char)*LINE_BUFFER_SIZE);
fp = fopen(DRIVER_STATE_FILE, "r");
while ( (ch = fgetc(fp)) != EOF ) {
if ((ch == '\n') || (ch == EOF)) {
if (_read_state == 0) {
_stop_stat->paddle= atoi(buffer);
_read_state++;
}
else if (_read_state == 1) {
_stop_stat->route = atoi(buffer);
_read_state++;
}
else if (_read_state == 2) {
_stop_stat->trip = atoi(buffer);
_read_state++;
}
else if (_read_state == 3) {
_stop_stat->stop= atoi(buffer);
_read_state++;
}
else if (_read_state == 4) {
_stop_stat->lat = atof(buffer);
_read_state++;
}
else if (_read_state == 5) {
_stop_stat->lon = atof(buffer);
_read_state++;
}
else if (_read_state == 6) {
n = input_idx+1;
if (n > STOP_NAME_LEN) {
n = DRIVER_NAME_LEN;
}
buffer[n-1] = '\0';
memcpy(_stop_stat->stopname, buffer, n);
_read_state++;
}
memset(buffer, 0, sizeof(char)*LINE_BUFFER_SIZE);
input_idx=0;
continue;
}
buffer[input_idx] = ch;
input_idx++;
if (input_idx >= LINE_BUFFER_SIZE) {
input_idx = LINE_BUFFER_SIZE-1;
buffer[LINE_BUFFER_SIZE-1] = 0;
}
}
fclose(fp);
if (_read_state < 6) {
return -2;
}
return 0;
}
int get_gps_state(gps_status *_gps_stat) {
FILE *fp;
int input_idx=0,
ch;
char buffer[LINE_BUFFER_SIZE];
// 0 - lat
// 1 - lon
// 2 - heading
// 3 - velocity
// 4 - num_sats
// 5 - gps_good
// 6 - stamp
// 7 - gpstime
//
int _read_state = 0;
if (access(GPS_STATE_FILE, R_OK)!=0) {
return -1;
}
memset(buffer, 0, sizeof(char)*LINE_BUFFER_SIZE);
fp = fopen(DRIVER_STATE_FILE, "r");
while ( (ch = fgetc(fp)) != EOF ) {
if ((ch == '\n') || (ch == EOF)) {
if (_read_state == 0) {
_gps_stat->lat = atof(buffer);
_read_state++;
}
else if (_read_state == 1) {
_gps_stat->lon = atof(buffer);
_read_state++;
}
else if (_read_state == 2) {
_gps_stat->heading = atof(buffer);
_read_state++;
}
else if (_read_state == 3) {
_gps_stat->velocity = atof(buffer);
_read_state++;
}
else if (_read_state == 4) {
_gps_stat->num_sats = atoi(buffer);
_read_state++;
}
else if (_read_state == 5) {
_gps_stat->gps_good = atoi(buffer);
_read_state++;
}
else if (_read_state == 6) {
_gps_stat->stamp = (time_t)atoi(buffer);
_read_state++;
}
else if (_read_state == 7) {
_gps_stat->gpstime = (time_t)atoi(buffer);
_read_state++;
}
memset(buffer, 0, sizeof(char)*LINE_BUFFER_SIZE);
input_idx=0;
continue;
}
buffer[input_idx] = ch;
input_idx++;
if (input_idx >= LINE_BUFFER_SIZE) {
input_idx = LINE_BUFFER_SIZE-1;
buffer[LINE_BUFFER_SIZE-1] = 0;
}
}
fclose(fp);
if (_read_state < 7) {
return -2;
}
return 0;
}
*/
// which poll revents flags signal that we need to close and clean up a socket...
//
#define CLOSE_CONDITION (POLLERR | POLLNVAL | POLLHUP)
int main(int argc, char **argv) {
int i;
int retval;
int rebuild;
struct message_record msg;
#ifdef DEBUG_PRINT
long long int _usec_now, _usec_prv, _usec_del;
_usec_del = 60000000;
_usec_now = get_usec_time();
_usec_prv = _usec_now;
#endif
// Before anything load driver, stop and
// avls state into IPC server (from disk).
// Messages will override these values but in case
// the IPC server goes away while the others persist,
// this will allow the various state variables to retain
// their values across restarts.
//
//get_driver_state(&driver_stat);
//get_stop_state(&stop_stat);
//get_gps_state(&gps_stat);
//init_state_info();
// Install our signal handlers and watchdog
//
configure_signal_handlers(argv[0]);
// create our listening socket
//
listener_socket = create_listener(COMMHUB_ADDRESS);
// return an error if we can't
//
if (listener_socket < 0) {
printf("Can't create listener socket: %d\n", listener_socket);
return -1;
}
// set our listener socket up in the poll data structure
//
fds[0].fd = listener_socket;
fds[0].events = POLLIN;
// do the same for our (blank) client list
//
rebuild_client_fds();
rebuild = 0;
// while we have not yet received a signal telling us to stop
//
while( exit_request_status == EXIT_REQUEST_NONE ) {
#ifdef DEBUG_PRINT
_usec_now = get_usec_time();
if ((_usec_now - _usec_prv) > _usec_del) {
printf("[%lli] ipc_server: heartbeat\n", get_usec_time());
_usec_prv = _usec_now;
}
#endif
RESET_WATCHDOG();
// if we have been flagged to rebuild our client fd list
//
if(rebuild) {
rebuild_client_fds(); //do so, and clear the flag
rebuild = 0;
}
// poll for any I/O events that we need to service
//
retval = poll(fds, num_clients + 1, COMMHUB_POLL_TIME);
// if poll returned an error
if(retval < 0) {
// and it was just a signal interruption
//
if(errno == EINTR) {
continue;
}
// otherwise complain bitterly
//
else {
perror("commserver: Poll failed:");
break;
}
}
// if poll returns 0, that means a timeout with no events
//
else if(retval == 0) {
// so go back and wait some more...
//
continue;
}
//if we get a new client
//
if (fds[0].revents & POLLIN) {
// accept the connection
//
accept_client();
// flag a rebuild of the poll list
//
rebuild = 1;
// and poll again
//
continue;
}
// if our server socket fails, complain
//
if (fds[0].revents & CLOSE_CONDITION) {
fprintf(stderr, "commserver: Server socket failed: revents = %d\n", fds[0].revents);
break;
}
// for each connected client
//
for (i=1; i <= num_clients; i++) {
// check for input events...
//
if (fds[i].revents & POLLIN) {
// if we have one, try and get the message
//
retval = get_message(fds[i].fd, &msg);
// if that fails
//
if (retval == -1) {
// that means the client has disconnected, so we purge them
//
purge_client(fds[i].fd);
// close the socket
//
close(fds[i].fd);
// and flag a rebuild
//
rebuild = 1;
}
// if it worked
//
else {
// we route the message and keep on going
//
route_message(fds[i].fd, &msg);
}
}
// if we have an error condition on this socket
//
if(fds[i].revents & CLOSE_CONDITION) {
// do the purge, close, and rebuild drill
//
purge_client(fds[i].fd);
close(fds[i].fd);
rebuild = 1;
}
}
}
// clean up our resources
//
cleanup();
return 0;
}