| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545 |
- /*
- * 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 <https://www.gnu.org/licenses/>.
- *
- */
- #include <sys/socket.h>
- #include <sys/un.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <poll.h>
- #include <errno.h>
- #include <signal.h>
- #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;
- }
|