| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966 |
- /*
- * 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"
- #define COMMHUB_POLL_TIME (10000) //10 seconds
- //#define COMMHUB_DEBUG
- int listener_socket = -1; //Our listening socket
- struct mailbox_record *mailboxes = NULL; //Our list of user mailboxes
- struct subscriber_record *wiretap_list = NULL; //Our list of clients subscribed for wiretap access
- struct subscriber_record *broadcast_list = NULL; //Our list of all clients not in wiretap mode (to prevent duplicate broadcasts)
- struct subscriber_record *master_list = NULL; //Our list of all clients (for fd/pid tracking)
- int num_clients = 0; //Our active client count
- struct pollfd fds[NUM_SUPPORTED_CLIENTS + 1]; //A buffer to hold the poll() file descriptor table
- 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(progname) //if we have a program name, store it
- {
- strncpy(p->progname, progname, MAX_MODULE_NAME_LENGTH);
- p->progname[MAX_MODULE_NAME_LENGTH - 1] = '\0';
- }
- else //otherwise, set a blank string
- {
- 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;
- p = *list; //iterate through all items
-
- while(p)
- {
- if(p->clientfd == fd) //the client is already on the list
- {
- //update the PID and progname anyway
- update_client_identifiers(p, pid, progname);
- return 0; //and return
- }
-
- q = p; //move our pointers along
- 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(q) //if we're not at the list head
- {
- q->next = p; //insert at the end
- }
- else //if this is the list head (empty list)
- {
- *list = p; //replace the head with our new node.
- }
-
- 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;
- p = *list; //iterate through our list
-
- while(p)
- {
- if(p->clientfd == fd) //if we find our client
- {
- if(q) //and we're not at the head of the list
- {
- q->next = p->next; //snip it out
- }
- else //otherwise
- {
- *list = p->next; //bump the listhead down one
- }
- free(p); //free the deleted node
-
- return 0; //and return
- }
-
- q=p; //advance our pointers
- p=p->next;
- }
-
- return -1; //client not found
- }
- //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;
- p = mailboxes; //traverse the mailboxes list
-
- while(p)
- {
- if( !strncmp(mailbox, p->mailbox_name, MAILBOX_NAME_MAX) ) //if we found a match
- {
- return add_client(fd, pid, progname, &p->clients); //try and add the client
- }
-
- 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;
-
- memset(p, 0, sizeof(struct mailbox_record)); //clear the memory
- strncpy(p->mailbox_name, mailbox, MAILBOX_NAME_MAX); //fill the mailbox name
- p->mailbox_name[MAILBOX_NAME_MAX] = '\0';
- add_client(fd, pid, progname, &p->clients); //add our shiny new subscriber
- p->next = NULL;
-
- if(q) //if we're not adding to an empty list
- {
- q->next = p; //insert at the end
- }
- else //otherwise
- {
- 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;
- p = mailboxes; //traverse the mailbox list
-
- while(p)
- {
- if( !strncmp(mailbox, p->mailbox_name, MAILBOX_NAME_MAX) ) //if we found a match
- {
- retval = del_client(fd, &p->clients); //try to unsubscribe
-
- if(p->clients == NULL) //if we just unsubscribed our last client, delete this list.
- {
- if(q) //if this is not the first list item
- {
- q->next = p->next; //snip it from the middle
- }
- else //otherwise
- {
- mailboxes = p->next; //snip from the beginning (tweaking the listhead)
- }
-
- free(p); //and free it
- }
-
- return retval; //return the result of the removal
- }
-
- q = p; //advance our pointers
- p = p->next;
- }
-
- return -1; //not found!
- }
- //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;
- }
-
- p = mailboxes; //iterate through the mailbox list
-
- while(p)
- {
- if( !strncmp(mailbox, p->mailbox_name, MAILBOX_NAME_MAX) ) //if we found a match
- {
- return p->clients; //return the client list associated
- }
-
- p = p->next; //advance our pointer
- }
-
- return NULL; //not found
- }
- //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;
- p = mailboxes; //then iterate through user mailboxes
-
- while(p)
- {
- del_client(fd, &p->clients); //removing the client when it is present
-
- if(p->clients == NULL) //if we just unsubscribed our last client on this list, delete the list.
- {
- struct mailbox_record *r = p; //save this pointer to delete after advancement
-
- if(q) //if we're deleting from the middle
- {
- q->next = p->next; //snip out
- }
- else //otherwise
- {
- mailboxes = p->next; //advance the listhead
- }
-
- p = p->next; //advance our pointer
- free(r); //free the deleted node
- continue; //continue with the advanced pointer
- }
-
- q = p; //advance our pointers
- 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;
-
- while(p) //iterate through our master list
- {
- if(i >= NUM_SUPPORTED_CLIENTS) //stopping if we have exceeded the space in the fds table.
- break;
- fds[i + 1].fd = p->clientfd; //set the fd for this table slot
- fds[i + 1].events = POLLIN; //specify that we're waiting for input
-
- i++; //count it
- p=p->next; //and advance our pointer
- }
-
- num_clients = i; //remember the number of clients we have so that we can reject new ones if they are
- return i; //in excess of our table size.
- }
- //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;
- 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(num_clients >= NUM_SUPPORTED_CLIENTS) //if we are out of room
- {
- #ifdef COMMHUB_DEBUG
- printf("Cannot add client %d (MAXCLIENTS)\n", retval);
- #endif
- prepare_message(&msg,MAILBOX_ERROR,"MAXCLIENTS",10); //send an error
- DO_SEND_MESSAGE(sendret, retval, &msg);
- close(retval); //close the socket
- return -1; //and return
- }
- //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
-
- if( msg->header.mailbox_name[0] == '>' ) //-------------------------------------------- PID addressed message
- {
- pid = atoi( &msg->header.mailbox_name[1] ); //extract pid
-
- dest = master_list; //look it up in the master list
-
- while(dest)
- {
- if(dest->pid == pid) //if we have a match
- break; //stop looking
-
- dest = dest->next; //advance pointer
- }
-
- if(dest != NULL)
- {
- DO_SEND_MESSAGE(sendret, dest->clientfd, msg); //deliver the message
- 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;
- }
- else if( msg->header.mailbox_name[0] == ':' ) //-------------------------------------------- module addressed message
- {
- dest = master_list; //start looking through the master list
-
- while(dest)
- {
- if(! strncmp(&msg->header.mailbox_name[1], dest->progname, MAX_MODULE_NAME_LENGTH) ) //if we have a match on module name
- {
- DO_SEND_MESSAGE(sendret, dest->clientfd, msg); //deliver the message (but keep looking)
- if(sendret)
- {
- close(dest->clientfd);
- }
- }
-
- dest = dest->next; //advance pointer
- }
-
- return 0;
- }
- else if( !strncmp(msg->header.mailbox_name, MAILBOX_HELLO, MAILBOX_NAME_MAX)) //------------------------- HELLO message
- {
- //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
- }
- else if( !strncmp(msg->header.mailbox_name, MAILBOX_SUBSCRIBE, MAILBOX_NAME_MAX)) //------------------------- SUBSCRIBE message
- {
- //a SUBSCRIBE message subscribes the client to the specified mailbox
- dest = find_client_by_fd(master_list, fd); //look up this client in the master list to obtain progname
-
- if(!dest) //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
- {
- 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 0; //return
- }
- else if( !strncmp(msg->header.mailbox_name, MAILBOX_UNSUBSCRIBE, MAILBOX_NAME_MAX)) //------------------------- UNSUBSCRIBE message
- {
- //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 0; //return
- }
- else if( !strncmp(msg->header.mailbox_name, MAILBOX_BROADCAST, MAILBOX_NAME_MAX)) //------------------------- BROADCAST message
- {
- //a BROADCAST message goes to everybody (except those who already got it through wiretap)
- deliver_message(msg, broadcast_list);
- return 0;
- }
- else if( !strncmp(msg->header.mailbox_name, MAILBOX_WIRETAP, MAILBOX_NAME_MAX)) //------------------------- WIRETAP message
- {
- //a WIRETAP message sets the wiretap mode.
-
- dest = find_client_by_fd(master_list, fd); //look up this client in the master list to obtain progname
-
- if(!dest) //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
- {
- fprintf(stderr,"Request from client %d which appears not to be in our master list!\n", fd);
- dest = &dummy;
- }
- if( !strncmp((char *)msg->payload, "ON", MAX_PAYLOAD_LENGTH) ) //if we are turning wiretap mode ON
- {
- add_client(fd, msg->header.sender, dest->progname, &wiretap_list); //add the client to the wiretap list
- del_client(fd, &broadcast_list); //and remove from the broadcast list
- #ifdef COMMHUB_DEBUG
- printf("Added Client %d to wiretap list\n", fd);
- debug_traverse();
- #endif
- }
- else //if we are turning wiretap mode OFF
- {
- del_client(fd, &wiretap_list); //remove the client from the wiretap list
- add_client(fd, msg->header.sender, dest->progname, &broadcast_list);//and add to the broadcast list
- #ifdef COMMHUB_DEBUG
- printf("Removed Client %d from wiretap list\n", fd);
- debug_traverse();
- #endif
- }
- return 0;
- }
- else if( !strncmp(msg->header.mailbox_name, MAILBOX_PING, MAILBOX_NAME_MAX)) //Even we need to respond to a PING message
- {
- 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(dest) //if it is non-NULL
- {
- deliver_message(msg, dest); //deliver the message to those clients
- }
-
- 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;
- p = mailboxes; //walk the list of user mailboxes
-
- while(p)
- {
- q = p;
- p = p->next; //advance the pointers
-
- qq = NULL;
- pp = q->clients; //walk the just-visited user mailbox
-
- while(pp) //and free all the subscriber records
- {
- qq = pp;
- pp = pp->next;
-
- free(qq);
- }
-
- free(q); //free the node
- }
-
- //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;
- }
- //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;
- //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 )
- {
- //DEBUG
- printf("[%lli] ipc_server: heartbeat\n", get_usec_time());
- //DEBUG
- RESET_WATCHDOG();
-
- if(rebuild) //if we have been flagged to rebuild our client fd list
- {
- 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)
- {
- if(errno == EINTR) //and it was just a signal interruption
- {
- continue; //continue
- }
- else //otherwise complain bitterly
- {
- perror("commserver: Poll failed:");
- break;
- }
- }
- else if(retval == 0) //if poll returns 0, that means a timeout with no events
- {
- continue; //so go back and wait some more...
- }
-
- //if we get a new client
- if(fds[0].revents & POLLIN)
- {
- accept_client(); //accept the connection
- rebuild = 1; //flag a rebuild of the poll list
- continue; //and poll again
- }
- if(fds[0].revents & CLOSE_CONDITION) //if our server socket fails, complain
- {
- fprintf(stderr, "commserver: Server socket failed: revents = %d\n", fds[0].revents);
- break;
- }
-
- for(i=1; i <= num_clients; i++) //for each connected client
- {
- if(fds[i].revents & POLLIN) //check for input events...
- {
- retval = get_message(fds[i].fd, &msg); //if we have one, try and get the message
-
- if(retval == -1) //if that fails
- {
- purge_client(fds[i].fd); //that means the client has disconnected, so we purge them
- close(fds[i].fd); //close the socket
- rebuild = 1; //and flag a rebuild
- }
- else //if it worked
- {
- //DEBUG
- printf("[%lli] %s (fd:%i,pid:%i,n:%i)\n",
- (long long int)msg.header.usec_time,
- msg.header.mailbox_name,
- (int)msg.header.from_fd, (int)msg.header.sender, (int)msg.header.payload_length);
- //DEBUG
- route_message(fds[i].fd, &msg); //we route the message and keep on going
- }
- }
- if(fds[i].revents & CLOSE_CONDITION) //if we have an error condition on this socket
- {
- purge_client(fds[i].fd); //do the purge, close, and rebuild drill
- close(fds[i].fd);
- rebuild = 1;
- }
- }
-
- }
-
- cleanup(); //clean up our resources
-
- return 0;
- }
|