/* * 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; }