commserver.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967
  1. /*
  2. * Copyright (c) 2019 Clementine Computing LLC.
  3. *
  4. * This file is part of PopuFare.
  5. *
  6. * PopuFare is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * PopuFare is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with PopuFare. If not, see <https://www.gnu.org/licenses/>.
  18. *
  19. */
  20. #include <sys/socket.h>
  21. #include <sys/un.h>
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <unistd.h>
  25. #include <fcntl.h>
  26. #include <poll.h>
  27. #include <errno.h>
  28. #include <signal.h>
  29. #include "../common/common_defs.h"
  30. #include "commhub.h"
  31. #define COMMHUB_POLL_TIME (10000) //10 seconds
  32. //#define COMMHUB_DEBUG
  33. int listener_socket = -1; //Our listening socket
  34. struct mailbox_record *mailboxes = NULL; //Our list of user mailboxes
  35. struct subscriber_record *wiretap_list = NULL; //Our list of clients subscribed for wiretap access
  36. struct subscriber_record *broadcast_list = NULL; //Our list of all clients not in wiretap mode (to prevent duplicate broadcasts)
  37. struct subscriber_record *master_list = NULL; //Our list of all clients (for fd/pid tracking)
  38. int num_clients = 0; //Our active client count
  39. struct pollfd fds[NUM_SUPPORTED_CLIENTS + 1]; //A buffer to hold the poll() file descriptor table
  40. int set_blocking_mode(int fd, int blocking)
  41. {
  42. int retval;
  43. int newflags;
  44. newflags = fcntl(fd, F_GETFL);
  45. if(newflags >= 0)
  46. {
  47. if(blocking)
  48. {
  49. newflags &= ~O_NONBLOCK;
  50. }
  51. else
  52. {
  53. newflags |= O_NONBLOCK;
  54. }
  55. retval = fcntl(fd, F_SETFL, newflags);
  56. if(retval)
  57. {
  58. printf("Cannot set O_NONBLOCK on socket %d to %s!\n", fd, blocking?"ON":"OFF");
  59. return -1;
  60. }
  61. }
  62. else
  63. {
  64. printf("Cannot get flags on socket %d! [%d / %X]\n", fd, newflags, newflags);
  65. return -1;
  66. }
  67. return 0;
  68. }
  69. #ifdef COMMHUB_O_NONBLOCK
  70. #define DO_SEND_MESSAGE(ret, fd, msg) {set_blocking_mode((fd),0); ret = send_message((fd), (msg)); set_blocking_mode((fd),1);}
  71. #else
  72. #define DO_SEND_MESSAGE(ret, fd, msg) {ret = send_message((fd), (msg));}
  73. #endif
  74. void debug_traverse_single_list(struct subscriber_record *p)
  75. {
  76. while(p)
  77. {
  78. printf(" [FD: %d PID: %d Prog: %s]", p->clientfd, p->pid, p->progname);
  79. p = p->next;
  80. }
  81. printf("\n");
  82. }
  83. //This function traverses the global state data structures and prints out a visual indication of state
  84. int debug_traverse()
  85. {
  86. struct mailbox_record *p;
  87. printf("-------------------------------------------------\n");
  88. printf("ALL:");
  89. debug_traverse_single_list(master_list); //iterate through the master list
  90. printf("WIRETAP:");
  91. debug_traverse_single_list(wiretap_list); //iterate through the wiretap list
  92. printf("BROADCAST:");
  93. debug_traverse_single_list(broadcast_list); //do the same for the broadcast list
  94. printf("---User Lists---\n");
  95. p = mailboxes; //then iterate through each generic (user) mailbox
  96. while(p)
  97. { //display the mailbox name
  98. printf("%s:",p->mailbox_name);
  99. debug_traverse_single_list(p->clients);
  100. p = p->next;
  101. }
  102. printf("\n");
  103. return 0;
  104. }
  105. void update_client_identifiers(struct subscriber_record *p, pid_t pid, char *progname)
  106. {
  107. if(!p)
  108. return;
  109. p->pid = pid;
  110. if(progname) //if we have a program name, store it
  111. {
  112. strncpy(p->progname, progname, MAX_MODULE_NAME_LENGTH);
  113. p->progname[MAX_MODULE_NAME_LENGTH - 1] = '\0';
  114. }
  115. else //otherwise, set a blank string
  116. {
  117. memset(p->progname, '\0', MAX_MODULE_NAME_LENGTH);
  118. }
  119. }
  120. //This function adds a subscriber to a subscriber list (usually a mailbox or a 'special' list)
  121. //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)
  122. int add_client(int fd, pid_t pid, char *progname, struct subscriber_record **list)
  123. {
  124. struct subscriber_record *p, *q;
  125. q = NULL;
  126. p = *list; //iterate through all items
  127. while(p)
  128. {
  129. if(p->clientfd == fd) //the client is already on the list
  130. {
  131. //update the PID and progname anyway
  132. update_client_identifiers(p, pid, progname);
  133. return 0; //and return
  134. }
  135. q = p; //move our pointers along
  136. p = p->next;
  137. }
  138. //allocate a new subscriber_record structure
  139. p = (struct subscriber_record *) malloc(sizeof(struct subscriber_record));
  140. if(p == NULL)
  141. return -1;
  142. //clear it and populate it
  143. memset(p,0,sizeof(struct subscriber_record));
  144. p->clientfd = fd;
  145. p->next = NULL;
  146. update_client_identifiers(p, pid, progname); //set our pid and progname
  147. if(q) //if we're not at the list head
  148. {
  149. q->next = p; //insert at the end
  150. }
  151. else //if this is the list head (empty list)
  152. {
  153. *list = p; //replace the head with our new node.
  154. }
  155. return 0;
  156. }
  157. //This function deletes a client from a subscriber list (again we pass a pointer to the listhead).
  158. int del_client(int fd, struct subscriber_record **list)
  159. {
  160. struct subscriber_record *p, *q;
  161. q = NULL;
  162. p = *list; //iterate through our list
  163. while(p)
  164. {
  165. if(p->clientfd == fd) //if we find our client
  166. {
  167. if(q) //and we're not at the head of the list
  168. {
  169. q->next = p->next; //snip it out
  170. }
  171. else //otherwise
  172. {
  173. *list = p->next; //bump the listhead down one
  174. }
  175. free(p); //free the deleted node
  176. return 0; //and return
  177. }
  178. q=p; //advance our pointers
  179. p=p->next;
  180. }
  181. return -1; //client not found
  182. }
  183. //This function subscribes a client to a user mailbox. It is passed the client data (fd and pid)
  184. //as well as a mailbox name to search for.
  185. int subscribe_client(int fd, pid_t pid, char *progname, char *mailbox)
  186. {
  187. struct mailbox_record *p, *q;
  188. if(mailbox == NULL)
  189. {
  190. return -1;
  191. }
  192. q = NULL;
  193. p = mailboxes; //traverse the mailboxes list
  194. while(p)
  195. {
  196. if( !strncmp(mailbox, p->mailbox_name, MAILBOX_NAME_MAX) ) //if we found a match
  197. {
  198. return add_client(fd, pid, progname, &p->clients); //try and add the client
  199. }
  200. q = p; //advance our pointers
  201. p = p->next;
  202. }
  203. //if we have not found a mailbox, create one
  204. p = (struct mailbox_record *) malloc(sizeof(struct mailbox_record));
  205. if(p == NULL)
  206. return -1;
  207. memset(p, 0, sizeof(struct mailbox_record)); //clear the memory
  208. strncpy(p->mailbox_name, mailbox, MAILBOX_NAME_MAX); //fill the mailbox name
  209. p->mailbox_name[MAILBOX_NAME_MAX] = '\0';
  210. add_client(fd, pid, progname, &p->clients); //add our shiny new subscriber
  211. p->next = NULL;
  212. if(q) //if we're not adding to an empty list
  213. {
  214. q->next = p; //insert at the end
  215. }
  216. else //otherwise
  217. {
  218. mailboxes = p; //replace the listhead
  219. }
  220. return 0;
  221. }
  222. //This function unsubscriber a client from a user mailbox
  223. int unsubscribe_client(int fd, char *mailbox)
  224. {
  225. struct mailbox_record *p, *q;
  226. int retval;
  227. if(mailbox == NULL)
  228. {
  229. return -1;
  230. }
  231. q = NULL;
  232. p = mailboxes; //traverse the mailbox list
  233. while(p)
  234. {
  235. if( !strncmp(mailbox, p->mailbox_name, MAILBOX_NAME_MAX) ) //if we found a match
  236. {
  237. retval = del_client(fd, &p->clients); //try to unsubscribe
  238. if(p->clients == NULL) //if we just unsubscribed our last client, delete this list.
  239. {
  240. if(q) //if this is not the first list item
  241. {
  242. q->next = p->next; //snip it from the middle
  243. }
  244. else //otherwise
  245. {
  246. mailboxes = p->next; //snip from the beginning (tweaking the listhead)
  247. }
  248. free(p); //and free it
  249. }
  250. return retval; //return the result of the removal
  251. }
  252. q = p; //advance our pointers
  253. p = p->next;
  254. }
  255. return -1; //not found!
  256. }
  257. //This function scans the mailboxes to find the named mailbox, and if it is found
  258. //returns the client list associated with that mailbox. This is used in delivery.
  259. struct subscriber_record *find_mailbox(char *mailbox)
  260. {
  261. struct mailbox_record *p;
  262. if(mailbox == NULL)
  263. {
  264. return NULL;
  265. }
  266. p = mailboxes; //iterate through the mailbox list
  267. while(p)
  268. {
  269. if( !strncmp(mailbox, p->mailbox_name, MAILBOX_NAME_MAX) ) //if we found a match
  270. {
  271. return p->clients; //return the client list associated
  272. }
  273. p = p->next; //advance our pointer
  274. }
  275. return NULL; //not found
  276. }
  277. //This function walks all global state structures to purge all reference to a (disconnected) client
  278. int purge_client(int fd)
  279. {
  280. struct mailbox_record *p, *q;
  281. //remove the client from all system lists
  282. del_client(fd, &broadcast_list);
  283. del_client(fd, &wiretap_list);
  284. del_client(fd, &master_list);
  285. q = NULL;
  286. p = mailboxes; //then iterate through user mailboxes
  287. while(p)
  288. {
  289. del_client(fd, &p->clients); //removing the client when it is present
  290. if(p->clients == NULL) //if we just unsubscribed our last client on this list, delete the list.
  291. {
  292. struct mailbox_record *r = p; //save this pointer to delete after advancement
  293. if(q) //if we're deleting from the middle
  294. {
  295. q->next = p->next; //snip out
  296. }
  297. else //otherwise
  298. {
  299. mailboxes = p->next; //advance the listhead
  300. }
  301. p = p->next; //advance our pointer
  302. free(r); //free the deleted node
  303. continue; //continue with the advanced pointer
  304. }
  305. q = p; //advance our pointers
  306. p = p->next;
  307. }
  308. #ifdef COMMHUB_DEBUG
  309. printf("Purged client %d\n", fd);
  310. debug_traverse();
  311. #endif
  312. return 0;
  313. }
  314. int create_listener(char *pathname)
  315. {
  316. int fd;
  317. struct sockaddr_un addr;
  318. int len;
  319. int retval;
  320. //create a UNIX domain socket of type SOCK_SEQPACKET
  321. fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
  322. if(fd < 0)
  323. return -1;
  324. //prepare our bind address structure
  325. addr.sun_family = AF_UNIX;
  326. strncpy(addr.sun_path, pathname, sizeof(addr.sun_path) - 1);
  327. addr.sun_path[sizeof(addr.sun_path) - 1]='\0';
  328. //unlink any existing socket or file that lives there
  329. unlink(pathname);
  330. //calculate the address length
  331. len = strlen(pathname) + sizeof(addr.sun_family);
  332. //and perform the bind
  333. retval = bind(fd,(struct sockaddr *)&addr,len);
  334. if(retval)
  335. {
  336. close(fd);
  337. return -2;
  338. }
  339. //set the socket in listening mode...
  340. retval=listen(fd, 5);
  341. if(retval)
  342. {
  343. close(fd);
  344. return -3;
  345. }
  346. return fd;
  347. }
  348. //This function walks the master client list and rebuilds the file descriptor set used by poll()
  349. //to wait for input.
  350. int rebuild_client_fds()
  351. {
  352. struct subscriber_record *p = master_list;
  353. int i = 0;
  354. while(p) //iterate through our master list
  355. {
  356. if(i >= NUM_SUPPORTED_CLIENTS) //stopping if we have exceeded the space in the fds table.
  357. break;
  358. fds[i + 1].fd = p->clientfd; //set the fd for this table slot
  359. fds[i + 1].events = POLLIN; //specify that we're waiting for input
  360. i++; //count it
  361. p=p->next; //and advance our pointer
  362. }
  363. num_clients = i; //remember the number of clients we have so that we can reject new ones if they are
  364. return i; //in excess of our table size.
  365. }
  366. //This function accepts a new client on the listener_socket and either rejects it with an error (if our table is full) or
  367. //accepts it and places it in the master (and broadcast) lists.
  368. int accept_client()
  369. {
  370. int retval;
  371. int sendret = 0;
  372. struct message_record msg;
  373. //try to accept the new client
  374. retval = accept(listener_socket, NULL, NULL);
  375. //if we got a valid file descriptor
  376. if(retval >= 0)
  377. {
  378. if(num_clients >= NUM_SUPPORTED_CLIENTS) //if we are out of room
  379. {
  380. #ifdef COMMHUB_DEBUG
  381. printf("Cannot add client %d (MAXCLIENTS)\n", retval);
  382. #endif
  383. prepare_message(&msg,MAILBOX_ERROR,"MAXCLIENTS",10); //send an error
  384. DO_SEND_MESSAGE(sendret, retval, &msg);
  385. if (sendret < 0) { }
  386. close(retval); //close the socket
  387. return -1; //and return
  388. }
  389. //otherwise add it to our default lists (master, broadcast) with a bogus pid
  390. //which the client will (hopefully) correct with a HELLO message
  391. add_client(retval, -1, "", &master_list);
  392. add_client(retval, -1, "", &broadcast_list);
  393. #ifdef COMMHUB_DEBUG
  394. printf("Added Client %d\n", retval);
  395. debug_traverse();
  396. #endif
  397. }
  398. else
  399. {
  400. if(errno != EINTR)
  401. perror("Accept client failed: ");
  402. }
  403. return 0;
  404. }
  405. //This helper function finds a client record by file descriptor
  406. struct subscriber_record *find_client_by_fd(struct subscriber_record *list, int fd)
  407. {
  408. struct subscriber_record *p = list;
  409. while(p)
  410. {
  411. if(p->clientfd == fd)
  412. break;
  413. p = p->next;
  414. }
  415. return p;
  416. }
  417. //This helper function delivers a message to every subscriber on the provided list.
  418. int deliver_message(struct message_record *msg, struct subscriber_record *dest)
  419. {
  420. struct subscriber_record *p = dest;
  421. int sendret;
  422. while(p)
  423. {
  424. DO_SEND_MESSAGE(sendret, p->clientfd, msg);
  425. if(sendret)
  426. {
  427. close(p->clientfd);
  428. }
  429. p = p->next;
  430. }
  431. return 0;
  432. }
  433. //This function is where the 'magic' happens. It picks out special messages (after first forwarding everything to the wiretap list)
  434. //Messages addressed to:
  435. // HELLO adds the client to the master and broadcast lists and updates its pid
  436. // >pid gets delivered to the client with the specified pid.
  437. // :modulename gets delivered to all clients registered with the supplied module name
  438. // SUBSCRIBE causes the sending client to be added to the named mailbox
  439. // UNSUBSCRIBE causes the sending client to be removed from the named mailbox
  440. // BROADCAST causes the message to be passed to all listeners
  441. // WIRETAP sets the wiretap state (on if payload = "ON" off otherwise)
  442. // PING replies with PONG
  443. //
  444. //All other messages are passed to their user mailbox and on to any subscribing clients. Messages with no receiver are dropped silently.
  445. //
  446. int route_message(int fd, struct message_record *msg)
  447. {
  448. struct subscriber_record dummy = {0};
  449. struct subscriber_record *dest;
  450. int pid;
  451. int sendret;
  452. //Implement wiretap for debug
  453. if(wiretap_list)
  454. {
  455. deliver_message(msg, wiretap_list);
  456. }
  457. //Handle special mailboxes
  458. if( msg->header.mailbox_name[0] == '>' ) //-------------------------------------------- PID addressed message
  459. {
  460. pid = atoi( &msg->header.mailbox_name[1] ); //extract pid
  461. dest = master_list; //look it up in the master list
  462. while(dest)
  463. {
  464. if(dest->pid == pid) //if we have a match
  465. break; //stop looking
  466. dest = dest->next; //advance pointer
  467. }
  468. if(dest != NULL)
  469. {
  470. DO_SEND_MESSAGE(sendret, dest->clientfd, msg); //deliver the message
  471. if(sendret)
  472. {
  473. close(dest->clientfd);
  474. }
  475. }
  476. else
  477. {
  478. if(pid == getpid())
  479. {
  480. //If this is a unicast PING to US...
  481. if( !strncmp((char *)msg->payload, MAILBOX_PING, MAILBOX_NAME_MAX) )
  482. {
  483. struct message_record outgoing;
  484. prepare_message(&outgoing, MAILBOX_PONG, msg->payload, msg->header.payload_length);
  485. dest = find_mailbox(MAILBOX_PONG);
  486. if(dest)
  487. {
  488. deliver_message(&outgoing, dest);
  489. }
  490. deliver_message(&outgoing, wiretap_list);
  491. }
  492. }
  493. }
  494. return 0;
  495. }
  496. else if( msg->header.mailbox_name[0] == ':' ) //-------------------------------------------- module addressed message
  497. {
  498. dest = master_list; //start looking through the master list
  499. while(dest)
  500. {
  501. if(! strncmp(&msg->header.mailbox_name[1], dest->progname, MAX_MODULE_NAME_LENGTH) ) //if we have a match on module name
  502. {
  503. DO_SEND_MESSAGE(sendret, dest->clientfd, msg); //deliver the message (but keep looking)
  504. if(sendret)
  505. {
  506. close(dest->clientfd);
  507. }
  508. }
  509. dest = dest->next; //advance pointer
  510. }
  511. return 0;
  512. }
  513. else if( !strncmp(msg->header.mailbox_name, MAILBOX_HELLO, MAILBOX_NAME_MAX)) //------------------------- HELLO message
  514. {
  515. //a HELLO message sets the client pid, adds it to master and broadcast, and removes it from wiretap
  516. //the payload should contain the module name
  517. add_client(fd, msg->header.sender, (char *)msg->payload, &master_list);
  518. add_client(fd, msg->header.sender, (char *)msg->payload, &broadcast_list);
  519. del_client(fd, &wiretap_list);
  520. #ifdef COMMHUB_DEBUG
  521. printf("Associated client %d with PID %d (progname = %s)\n", fd, msg->header.sender, msg->payload);
  522. debug_traverse();
  523. #endif
  524. return 0; //return
  525. }
  526. else if( !strncmp(msg->header.mailbox_name, MAILBOX_SUBSCRIBE, MAILBOX_NAME_MAX)) //------------------------- SUBSCRIBE message
  527. {
  528. //a SUBSCRIBE message subscribes the client to the specified mailbox
  529. dest = find_client_by_fd(master_list, fd); //look up this client in the master list to obtain progname
  530. 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
  531. {
  532. fprintf(stderr,"Request from client %d which appears not to be in our master list!\n", fd);
  533. dest = &dummy;
  534. }
  535. subscribe_client(fd, msg->header.sender, dest->progname, (char *)msg->payload);
  536. #ifdef COMMHUB_DEBUG
  537. printf("Added Client %d to %s\n", fd, msg->payload);
  538. debug_traverse();
  539. #endif
  540. return 0; //return
  541. }
  542. else if( !strncmp(msg->header.mailbox_name, MAILBOX_UNSUBSCRIBE, MAILBOX_NAME_MAX)) //------------------------- UNSUBSCRIBE message
  543. {
  544. //an UNSUBSCRIBE message removes the client from the specified mailbox
  545. unsubscribe_client(fd, (char *)msg->payload);
  546. #ifdef COMMHUB_DEBUG
  547. printf("Removed Client %d from %s\n", fd, msg->payload);
  548. debug_traverse();
  549. #endif
  550. return 0; //return
  551. }
  552. else if( !strncmp(msg->header.mailbox_name, MAILBOX_BROADCAST, MAILBOX_NAME_MAX)) //------------------------- BROADCAST message
  553. {
  554. //a BROADCAST message goes to everybody (except those who already got it through wiretap)
  555. deliver_message(msg, broadcast_list);
  556. return 0;
  557. }
  558. else if( !strncmp(msg->header.mailbox_name, MAILBOX_WIRETAP, MAILBOX_NAME_MAX)) //------------------------- WIRETAP message
  559. {
  560. //a WIRETAP message sets the wiretap mode.
  561. dest = find_client_by_fd(master_list, fd); //look up this client in the master list to obtain progname
  562. 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
  563. {
  564. fprintf(stderr,"Request from client %d which appears not to be in our master list!\n", fd);
  565. dest = &dummy;
  566. }
  567. if( !strncmp((char *)msg->payload, "ON", MAX_PAYLOAD_LENGTH) ) //if we are turning wiretap mode ON
  568. {
  569. add_client(fd, msg->header.sender, dest->progname, &wiretap_list); //add the client to the wiretap list
  570. del_client(fd, &broadcast_list); //and remove from the broadcast list
  571. #ifdef COMMHUB_DEBUG
  572. printf("Added Client %d to wiretap list\n", fd);
  573. debug_traverse();
  574. #endif
  575. }
  576. else //if we are turning wiretap mode OFF
  577. {
  578. del_client(fd, &wiretap_list); //remove the client from the wiretap list
  579. add_client(fd, msg->header.sender, dest->progname, &broadcast_list);//and add to the broadcast list
  580. #ifdef COMMHUB_DEBUG
  581. printf("Removed Client %d from wiretap list\n", fd);
  582. debug_traverse();
  583. #endif
  584. }
  585. return 0;
  586. }
  587. else if( !strncmp(msg->header.mailbox_name, MAILBOX_PING, MAILBOX_NAME_MAX)) //Even we need to respond to a PING message
  588. {
  589. struct message_record outgoing;
  590. prepare_message(&outgoing, MAILBOX_PONG, msg->payload, msg->header.payload_length);
  591. dest = find_mailbox(MAILBOX_PONG);
  592. if(dest)
  593. {
  594. deliver_message(&outgoing, dest);
  595. }
  596. deliver_message(&outgoing, wiretap_list);
  597. }
  598. //-------------------------------------------------------------------------------Normal delivery to user mailboxes...
  599. dest = find_mailbox(msg->header.mailbox_name); //look up the mailbox client list
  600. if(dest) //if it is non-NULL
  601. {
  602. deliver_message(msg, dest); //deliver the message to those clients
  603. }
  604. return 0;
  605. }
  606. //this function frees our data structures and closes our file handles politely before exit
  607. int cleanup()
  608. {
  609. struct mailbox_record *p, *q;
  610. struct subscriber_record *pp, *qq;
  611. q = NULL;
  612. p = mailboxes; //walk the list of user mailboxes
  613. while(p)
  614. {
  615. q = p;
  616. p = p->next; //advance the pointers
  617. qq = NULL;
  618. pp = q->clients; //walk the just-visited user mailbox
  619. while(pp) //and free all the subscriber records
  620. {
  621. qq = pp;
  622. pp = pp->next;
  623. free(qq);
  624. }
  625. free(q); //free the node
  626. }
  627. //walk the wiretap list freeing all nodes
  628. qq = NULL;
  629. pp = wiretap_list;
  630. while(pp)
  631. {
  632. qq = pp;
  633. pp = pp->next;
  634. free(qq);
  635. }
  636. //walk the broadcast list freeing all nodes
  637. qq = NULL;
  638. pp = broadcast_list;
  639. while(pp)
  640. {
  641. qq = pp;
  642. pp = pp->next;
  643. free(qq);
  644. }
  645. //walk the master list freeing all nodes and closing the file descriptors
  646. qq = NULL;
  647. pp = master_list;
  648. while(pp)
  649. {
  650. qq = pp;
  651. pp = pp->next;
  652. close(qq->clientfd);
  653. free(qq);
  654. }
  655. //NULL our all of our pointers
  656. mailboxes = NULL;
  657. broadcast_list = NULL;
  658. wiretap_list = NULL;
  659. master_list = NULL;
  660. close(listener_socket);
  661. num_clients = 0;
  662. listener_socket = -1;
  663. return 0;
  664. }
  665. //which poll revents flags signal that we need to close and clean up a socket...
  666. #define CLOSE_CONDITION (POLLERR | POLLNVAL | POLLHUP)
  667. int main(int argc, char **argv)
  668. {
  669. int i;
  670. int retval;
  671. int rebuild;
  672. struct message_record msg;
  673. //Install our signal handlers and watchdog
  674. configure_signal_handlers(argv[0]);
  675. //create our listening socket
  676. listener_socket = create_listener(COMMHUB_ADDRESS);
  677. //return an error if we can't
  678. if(listener_socket < 0)
  679. {
  680. printf("Can't create listener socket: %d\n", listener_socket);
  681. return -1;
  682. }
  683. //set our listener socket up in the poll data structure
  684. fds[0].fd = listener_socket;
  685. fds[0].events = POLLIN;
  686. //do the same for our (blank) client list
  687. rebuild_client_fds();
  688. rebuild = 0;
  689. //while we have not yet received a signal telling us to stop
  690. while( exit_request_status == EXIT_REQUEST_NONE )
  691. {
  692. //DEBUG
  693. printf("[%lli] ipc_server: heartbeat\n", get_usec_time());
  694. //DEBUG
  695. RESET_WATCHDOG();
  696. if(rebuild) //if we have been flagged to rebuild our client fd list
  697. {
  698. rebuild_client_fds(); //do so, and clear the flag
  699. rebuild = 0;
  700. }
  701. //poll for any I/O events that we need to service
  702. retval = poll(fds, num_clients + 1, COMMHUB_POLL_TIME);
  703. //if poll returned an error
  704. if(retval < 0)
  705. {
  706. if(errno == EINTR) //and it was just a signal interruption
  707. {
  708. continue; //continue
  709. }
  710. else //otherwise complain bitterly
  711. {
  712. perror("commserver: Poll failed:");
  713. break;
  714. }
  715. }
  716. else if(retval == 0) //if poll returns 0, that means a timeout with no events
  717. {
  718. continue; //so go back and wait some more...
  719. }
  720. //if we get a new client
  721. if(fds[0].revents & POLLIN)
  722. {
  723. accept_client(); //accept the connection
  724. rebuild = 1; //flag a rebuild of the poll list
  725. continue; //and poll again
  726. }
  727. if(fds[0].revents & CLOSE_CONDITION) //if our server socket fails, complain
  728. {
  729. fprintf(stderr, "commserver: Server socket failed: revents = %d\n", fds[0].revents);
  730. break;
  731. }
  732. for(i=1; i <= num_clients; i++) //for each connected client
  733. {
  734. if(fds[i].revents & POLLIN) //check for input events...
  735. {
  736. retval = get_message(fds[i].fd, &msg); //if we have one, try and get the message
  737. if(retval == -1) //if that fails
  738. {
  739. purge_client(fds[i].fd); //that means the client has disconnected, so we purge them
  740. close(fds[i].fd); //close the socket
  741. rebuild = 1; //and flag a rebuild
  742. }
  743. else //if it worked
  744. {
  745. //DEBUG
  746. printf("[%lli] %s (fd:%i,pid:%i,n:%i)\n",
  747. (long long int)msg.header.usec_time,
  748. msg.header.mailbox_name,
  749. (int)msg.header.from_fd, (int)msg.header.sender, (int)msg.header.payload_length);
  750. //DEBUG
  751. route_message(fds[i].fd, &msg); //we route the message and keep on going
  752. }
  753. }
  754. if(fds[i].revents & CLOSE_CONDITION) //if we have an error condition on this socket
  755. {
  756. purge_client(fds[i].fd); //do the purge, close, and rebuild drill
  757. close(fds[i].fd);
  758. rebuild = 1;
  759. }
  760. }
  761. }
  762. cleanup(); //clean up our resources
  763. return 0;
  764. }