commserver.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966
  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;
  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. close(retval); //close the socket
  386. return -1; //and return
  387. }
  388. //otherwise add it to our default lists (master, broadcast) with a bogus pid
  389. //which the client will (hopefully) correct with a HELLO message
  390. add_client(retval, -1, "", &master_list);
  391. add_client(retval, -1, "", &broadcast_list);
  392. #ifdef COMMHUB_DEBUG
  393. printf("Added Client %d\n", retval);
  394. debug_traverse();
  395. #endif
  396. }
  397. else
  398. {
  399. if(errno != EINTR)
  400. perror("Accept client failed: ");
  401. }
  402. return 0;
  403. }
  404. //This helper function finds a client record by file descriptor
  405. struct subscriber_record *find_client_by_fd(struct subscriber_record *list, int fd)
  406. {
  407. struct subscriber_record *p = list;
  408. while(p)
  409. {
  410. if(p->clientfd == fd)
  411. break;
  412. p = p->next;
  413. }
  414. return p;
  415. }
  416. //This helper function delivers a message to every subscriber on the provided list.
  417. int deliver_message(struct message_record *msg, struct subscriber_record *dest)
  418. {
  419. struct subscriber_record *p = dest;
  420. int sendret;
  421. while(p)
  422. {
  423. DO_SEND_MESSAGE(sendret, p->clientfd, msg);
  424. if(sendret)
  425. {
  426. close(p->clientfd);
  427. }
  428. p = p->next;
  429. }
  430. return 0;
  431. }
  432. //This function is where the 'magic' happens. It picks out special messages (after first forwarding everything to the wiretap list)
  433. //Messages addressed to:
  434. // HELLO adds the client to the master and broadcast lists and updates its pid
  435. // >pid gets delivered to the client with the specified pid.
  436. // :modulename gets delivered to all clients registered with the supplied module name
  437. // SUBSCRIBE causes the sending client to be added to the named mailbox
  438. // UNSUBSCRIBE causes the sending client to be removed from the named mailbox
  439. // BROADCAST causes the message to be passed to all listeners
  440. // WIRETAP sets the wiretap state (on if payload = "ON" off otherwise)
  441. // PING replies with PONG
  442. //
  443. //All other messages are passed to their user mailbox and on to any subscribing clients. Messages with no receiver are dropped silently.
  444. //
  445. int route_message(int fd, struct message_record *msg)
  446. {
  447. struct subscriber_record dummy = {0};
  448. struct subscriber_record *dest;
  449. int pid;
  450. int sendret;
  451. //Implement wiretap for debug
  452. if(wiretap_list)
  453. {
  454. deliver_message(msg, wiretap_list);
  455. }
  456. //Handle special mailboxes
  457. if( msg->header.mailbox_name[0] == '>' ) //-------------------------------------------- PID addressed message
  458. {
  459. pid = atoi( &msg->header.mailbox_name[1] ); //extract pid
  460. dest = master_list; //look it up in the master list
  461. while(dest)
  462. {
  463. if(dest->pid == pid) //if we have a match
  464. break; //stop looking
  465. dest = dest->next; //advance pointer
  466. }
  467. if(dest != NULL)
  468. {
  469. DO_SEND_MESSAGE(sendret, dest->clientfd, msg); //deliver the message
  470. if(sendret)
  471. {
  472. close(dest->clientfd);
  473. }
  474. }
  475. else
  476. {
  477. if(pid == getpid())
  478. {
  479. //If this is a unicast PING to US...
  480. if( !strncmp((char *)msg->payload, MAILBOX_PING, MAILBOX_NAME_MAX) )
  481. {
  482. struct message_record outgoing;
  483. prepare_message(&outgoing, MAILBOX_PONG, msg->payload, msg->header.payload_length);
  484. dest = find_mailbox(MAILBOX_PONG);
  485. if(dest)
  486. {
  487. deliver_message(&outgoing, dest);
  488. }
  489. deliver_message(&outgoing, wiretap_list);
  490. }
  491. }
  492. }
  493. return 0;
  494. }
  495. else if( msg->header.mailbox_name[0] == ':' ) //-------------------------------------------- module addressed message
  496. {
  497. dest = master_list; //start looking through the master list
  498. while(dest)
  499. {
  500. if(! strncmp(&msg->header.mailbox_name[1], dest->progname, MAX_MODULE_NAME_LENGTH) ) //if we have a match on module name
  501. {
  502. DO_SEND_MESSAGE(sendret, dest->clientfd, msg); //deliver the message (but keep looking)
  503. if(sendret)
  504. {
  505. close(dest->clientfd);
  506. }
  507. }
  508. dest = dest->next; //advance pointer
  509. }
  510. return 0;
  511. }
  512. else if( !strncmp(msg->header.mailbox_name, MAILBOX_HELLO, MAILBOX_NAME_MAX)) //------------------------- HELLO message
  513. {
  514. //a HELLO message sets the client pid, adds it to master and broadcast, and removes it from wiretap
  515. //the payload should contain the module name
  516. add_client(fd, msg->header.sender, (char *)msg->payload, &master_list);
  517. add_client(fd, msg->header.sender, (char *)msg->payload, &broadcast_list);
  518. del_client(fd, &wiretap_list);
  519. #ifdef COMMHUB_DEBUG
  520. printf("Associated client %d with PID %d (progname = %s)\n", fd, msg->header.sender, msg->payload);
  521. debug_traverse();
  522. #endif
  523. return 0; //return
  524. }
  525. else if( !strncmp(msg->header.mailbox_name, MAILBOX_SUBSCRIBE, MAILBOX_NAME_MAX)) //------------------------- SUBSCRIBE message
  526. {
  527. //a SUBSCRIBE message subscribes the client to the specified mailbox
  528. dest = find_client_by_fd(master_list, fd); //look up this client in the master list to obtain progname
  529. 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
  530. {
  531. fprintf(stderr,"Request from client %d which appears not to be in our master list!\n", fd);
  532. dest = &dummy;
  533. }
  534. subscribe_client(fd, msg->header.sender, dest->progname, (char *)msg->payload);
  535. #ifdef COMMHUB_DEBUG
  536. printf("Added Client %d to %s\n", fd, msg->payload);
  537. debug_traverse();
  538. #endif
  539. return 0; //return
  540. }
  541. else if( !strncmp(msg->header.mailbox_name, MAILBOX_UNSUBSCRIBE, MAILBOX_NAME_MAX)) //------------------------- UNSUBSCRIBE message
  542. {
  543. //an UNSUBSCRIBE message removes the client from the specified mailbox
  544. unsubscribe_client(fd, (char *)msg->payload);
  545. #ifdef COMMHUB_DEBUG
  546. printf("Removed Client %d from %s\n", fd, msg->payload);
  547. debug_traverse();
  548. #endif
  549. return 0; //return
  550. }
  551. else if( !strncmp(msg->header.mailbox_name, MAILBOX_BROADCAST, MAILBOX_NAME_MAX)) //------------------------- BROADCAST message
  552. {
  553. //a BROADCAST message goes to everybody (except those who already got it through wiretap)
  554. deliver_message(msg, broadcast_list);
  555. return 0;
  556. }
  557. else if( !strncmp(msg->header.mailbox_name, MAILBOX_WIRETAP, MAILBOX_NAME_MAX)) //------------------------- WIRETAP message
  558. {
  559. //a WIRETAP message sets the wiretap mode.
  560. dest = find_client_by_fd(master_list, fd); //look up this client in the master list to obtain progname
  561. 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
  562. {
  563. fprintf(stderr,"Request from client %d which appears not to be in our master list!\n", fd);
  564. dest = &dummy;
  565. }
  566. if( !strncmp((char *)msg->payload, "ON", MAX_PAYLOAD_LENGTH) ) //if we are turning wiretap mode ON
  567. {
  568. add_client(fd, msg->header.sender, dest->progname, &wiretap_list); //add the client to the wiretap list
  569. del_client(fd, &broadcast_list); //and remove from the broadcast list
  570. #ifdef COMMHUB_DEBUG
  571. printf("Added Client %d to wiretap list\n", fd);
  572. debug_traverse();
  573. #endif
  574. }
  575. else //if we are turning wiretap mode OFF
  576. {
  577. del_client(fd, &wiretap_list); //remove the client from the wiretap list
  578. add_client(fd, msg->header.sender, dest->progname, &broadcast_list);//and add to the broadcast list
  579. #ifdef COMMHUB_DEBUG
  580. printf("Removed Client %d from wiretap list\n", fd);
  581. debug_traverse();
  582. #endif
  583. }
  584. return 0;
  585. }
  586. else if( !strncmp(msg->header.mailbox_name, MAILBOX_PING, MAILBOX_NAME_MAX)) //Even we need to respond to a PING message
  587. {
  588. struct message_record outgoing;
  589. prepare_message(&outgoing, MAILBOX_PONG, msg->payload, msg->header.payload_length);
  590. dest = find_mailbox(MAILBOX_PONG);
  591. if(dest)
  592. {
  593. deliver_message(&outgoing, dest);
  594. }
  595. deliver_message(&outgoing, wiretap_list);
  596. }
  597. //-------------------------------------------------------------------------------Normal delivery to user mailboxes...
  598. dest = find_mailbox(msg->header.mailbox_name); //look up the mailbox client list
  599. if(dest) //if it is non-NULL
  600. {
  601. deliver_message(msg, dest); //deliver the message to those clients
  602. }
  603. return 0;
  604. }
  605. //this function frees our data structures and closes our file handles politely before exit
  606. int cleanup()
  607. {
  608. struct mailbox_record *p, *q;
  609. struct subscriber_record *pp, *qq;
  610. q = NULL;
  611. p = mailboxes; //walk the list of user mailboxes
  612. while(p)
  613. {
  614. q = p;
  615. p = p->next; //advance the pointers
  616. qq = NULL;
  617. pp = q->clients; //walk the just-visited user mailbox
  618. while(pp) //and free all the subscriber records
  619. {
  620. qq = pp;
  621. pp = pp->next;
  622. free(qq);
  623. }
  624. free(q); //free the node
  625. }
  626. //walk the wiretap list freeing all nodes
  627. qq = NULL;
  628. pp = wiretap_list;
  629. while(pp)
  630. {
  631. qq = pp;
  632. pp = pp->next;
  633. free(qq);
  634. }
  635. //walk the broadcast list freeing all nodes
  636. qq = NULL;
  637. pp = broadcast_list;
  638. while(pp)
  639. {
  640. qq = pp;
  641. pp = pp->next;
  642. free(qq);
  643. }
  644. //walk the master list freeing all nodes and closing the file descriptors
  645. qq = NULL;
  646. pp = master_list;
  647. while(pp)
  648. {
  649. qq = pp;
  650. pp = pp->next;
  651. close(qq->clientfd);
  652. free(qq);
  653. }
  654. //NULL our all of our pointers
  655. mailboxes = NULL;
  656. broadcast_list = NULL;
  657. wiretap_list = NULL;
  658. master_list = NULL;
  659. close(listener_socket);
  660. num_clients = 0;
  661. listener_socket = -1;
  662. return 0;
  663. }
  664. //which poll revents flags signal that we need to close and clean up a socket...
  665. #define CLOSE_CONDITION (POLLERR | POLLNVAL | POLLHUP)
  666. int main(int argc, char **argv)
  667. {
  668. int i;
  669. int retval;
  670. int rebuild;
  671. struct message_record msg;
  672. //Install our signal handlers and watchdog
  673. configure_signal_handlers(argv[0]);
  674. //create our listening socket
  675. listener_socket = create_listener(COMMHUB_ADDRESS);
  676. //return an error if we can't
  677. if(listener_socket < 0)
  678. {
  679. printf("Can't create listener socket: %d\n", listener_socket);
  680. return -1;
  681. }
  682. //set our listener socket up in the poll data structure
  683. fds[0].fd = listener_socket;
  684. fds[0].events = POLLIN;
  685. //do the same for our (blank) client list
  686. rebuild_client_fds();
  687. rebuild = 0;
  688. //while we have not yet received a signal telling us to stop
  689. while( exit_request_status == EXIT_REQUEST_NONE )
  690. {
  691. //DEBUG
  692. printf("[%lli] ipc_server: heartbeat\n", get_usec_time());
  693. //DEBUG
  694. RESET_WATCHDOG();
  695. if(rebuild) //if we have been flagged to rebuild our client fd list
  696. {
  697. rebuild_client_fds(); //do so, and clear the flag
  698. rebuild = 0;
  699. }
  700. //poll for any I/O events that we need to service
  701. retval = poll(fds, num_clients + 1, COMMHUB_POLL_TIME);
  702. //if poll returned an error
  703. if(retval < 0)
  704. {
  705. if(errno == EINTR) //and it was just a signal interruption
  706. {
  707. continue; //continue
  708. }
  709. else //otherwise complain bitterly
  710. {
  711. perror("commserver: Poll failed:");
  712. break;
  713. }
  714. }
  715. else if(retval == 0) //if poll returns 0, that means a timeout with no events
  716. {
  717. continue; //so go back and wait some more...
  718. }
  719. //if we get a new client
  720. if(fds[0].revents & POLLIN)
  721. {
  722. accept_client(); //accept the connection
  723. rebuild = 1; //flag a rebuild of the poll list
  724. continue; //and poll again
  725. }
  726. if(fds[0].revents & CLOSE_CONDITION) //if our server socket fails, complain
  727. {
  728. fprintf(stderr, "commserver: Server socket failed: revents = %d\n", fds[0].revents);
  729. break;
  730. }
  731. for(i=1; i <= num_clients; i++) //for each connected client
  732. {
  733. if(fds[i].revents & POLLIN) //check for input events...
  734. {
  735. retval = get_message(fds[i].fd, &msg); //if we have one, try and get the message
  736. if(retval == -1) //if that fails
  737. {
  738. purge_client(fds[i].fd); //that means the client has disconnected, so we purge them
  739. close(fds[i].fd); //close the socket
  740. rebuild = 1; //and flag a rebuild
  741. }
  742. else //if it worked
  743. {
  744. //DEBUG
  745. printf("[%lli] %s (fd:%i,pid:%i,n:%i)\n",
  746. (long long int)msg.header.usec_time,
  747. msg.header.mailbox_name,
  748. (int)msg.header.from_fd, (int)msg.header.sender, (int)msg.header.payload_length);
  749. //DEBUG
  750. route_message(fds[i].fd, &msg); //we route the message and keep on going
  751. }
  752. }
  753. if(fds[i].revents & CLOSE_CONDITION) //if we have an error condition on this socket
  754. {
  755. purge_client(fds[i].fd); //do the purge, close, and rebuild drill
  756. close(fds[i].fd);
  757. rebuild = 1;
  758. }
  759. }
  760. }
  761. cleanup(); //clean up our resources
  762. return 0;
  763. }