/* * Copyright (c) 2019 Clementine Computing LLC. * * This file is part of PopuFare. * * PopuFare is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * PopuFare is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with PopuFare. If not, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "commhub.h" int print_replayable_message(struct message_record *msg) { int i; printf("%s:",msg->header.mailbox_name); for(i=0; i < msg->header.payload_length; i++) { printf(" %02X", msg->payload[i]); } printf("\n"); return 0; } int print_decoded_message(struct message_record *msg) { int binflag, i; printf("%lld: %d->%s (%d bytes):", msg->header.usec_time, msg->header.sender, msg->header.mailbox_name, msg->header.payload_length); if(!strncmp(msg->header.mailbox_name, MAILBOX_GPS_STATUS, MAILBOX_NAME_MAX)) { gps_status *s = (gps_status *)msg->payload; printf("Position: %f,%f %3.1f deg @ %.1f M/sec Quality: %d sats (%s)", s->lat, s->lon, s->heading, s->velocity, s->num_sats, (s->gps_good?"OK":"NOT OK") ); } else if(!strncmp(msg->header.mailbox_name, MAILBOX_STOP_STATUS, MAILBOX_NAME_MAX)) { stop_status *s = (stop_status *)msg->payload; printf("Paddle %d Route %d Trip %d Stop %d (%f,%f) \"%s\"", s->paddle, s->route, s->trip, s->stop, s->lat, s->lon, s->stopname); } else if(!strncmp(msg->header.mailbox_name, MAILBOX_PASS_STATUS, MAILBOX_NAME_MAX)) { pass_status *s = (pass_status *)msg->payload; char *flush_str; switch(s->flush_status) { case FLUSH_STATUS_NORMAL: flush_str="NORMAL"; break; case FLUSH_STATUS_LEGACY: flush_str="LEGACY"; break; case FLUSH_STATUS_DOWNLOAD: flush_str="DOWNLOAD"; break; case FLUSH_STATUS_APPLY: flush_str="APPLY"; break; case FLUSH_STATUS_WRITE: flush_str="WRITE"; break; default: flush_str="???"; break; } printf("Flush Status = %d (%s) Progress = %d%% LastSync = %d LastAck = %d", s->flush_status, flush_str, s->progress_indicator, (int)s->last_sync_time, (int)s->last_ack_time); } else if(!strncmp(msg->header.mailbox_name, MAILBOX_BILL_STATUS, MAILBOX_NAME_MAX)) { bill_status *s = (bill_status *)msg->payload; printf("LastSync = %d LastAck = %d", (int)s->last_sync_time, (int)s->last_ack_time); } else if(!strncmp(msg->header.mailbox_name, MAILBOX_PIU_MESSAGE, MAILBOX_NAME_MAX)) { piu_message *s = (piu_message *)msg->payload; printf("Priority = %d Line = %d Duration = %d (0 = make default) Message = \"%s\"", s->priority, s->line, s->seconds, s->message); } else if(!strncmp(msg->header.mailbox_name, MAILBOX_SET_PADDLE, MAILBOX_NAME_MAX) || !strncmp(msg->header.mailbox_name, MAILBOX_PADDLE_ACK, MAILBOX_NAME_MAX)) { set_paddle_req *s = (set_paddle_req *)msg->payload; printf("Requested = %d Result = %d", s->request, s->result); } else if(!strncmp(msg->header.mailbox_name, MAILBOX_RULE_CALL, MAILBOX_NAME_MAX)) { driver_rulecall *s = (driver_rulecall *)msg->payload; printf("Rule = \"%s\" Parameter = \"%s\"", s->rulename, s->ruleparam); } else { binflag = 0; for(i = 0; i < msg->header.payload_length; i++) { switch(msg->payload[i]) { case '\0': case '\t': case '\r': case '\n': break; default: if( (msg->payload[i] < ' ') || (msg->payload[i] > '~') ) { binflag = 1; } break; } } if(binflag) { for(i = 0; i < msg->header.payload_length; i++) { printf("%02x ", msg->payload[i]); } } else { printf("\""); for(i = 0; i < msg->header.payload_length; i++) { switch(msg->payload[i]) { case '\0': printf("\\0"); break; case '"': printf("\\\""); break; case '\r': printf("\\r"); break; case '\n': printf("\\n"); break; case '\t': printf("\\t"); break; case '\\': printf("\\\\"); break; default: printf("%c", msg->payload[i]); break; } } printf("\""); } } printf("\n"); return 0; } #define CLIENT_MODE_WIRETAP (0x0001) //Enter Wiretap Mode #define CLIENT_MODE_RAWOUT (0x0002) //Format output in replayable raw format #define CLIENT_MODE_TRANSMIT_FILE (0x0004) //Transmit messages from a specific file #define CLIENT_MODE_TRANSMIT_STDIN (0x0008) //Transmit messages from standard in #define CLIENT_MODE_TRANSMIT_RATE (0x0010) //Wait between transmits #define CLIENT_MODE_QUIT_AFTER_EOF (0x0020) //Quit after EOF on our transmit source #define CLIENT_MODE_RECONNECT (0x0040) //Reconnect if disconnected from IPC hub #define CLIENT_MODE_VERBOSE (0x0080) //In verbose mode we generate lots more commentary to stderr #define CLIENT_MODE_MATCH_REGEX (0x0100) //In Match Regex mode we only display mailboxes that match regex #define CLIENT_MODE_EXCLUDE_REGEX (0x0200) //In Exclude Regex mode we exclude mailboxes that match regex //Flags setting our mode of operation (see CLIENT_MODE_* above) // int mode_flags = 0; //This is properly initialized in process_command_line() FILE *infile = NULL; //If we are transmitting from a file, here's our handle to it... long long int last_tx = 0; //Start out with a last-transmit time of "never" long long int tx_rate_limit = 0; //Wait at least tx_rate_limit milliseconds between sends int hub_fd = -1; //File descriptor of our communication hub regex_t match_expr; //Mailbox name match regex (compiled) regex_t exclude_expr; //Mailbox name exclude regex (compiled) #define FLAG_WIRETAP_ON ("-W") //Turns Wiretap mode on (default) #define FLAG_WIRETAP_OFF ("-w") //Turns Wiretap mode off #define FLAG_RAWMODE_ON ("-R") //Turns Raw Replayable mode on #define FLAG_RAWMODE_OFF ("-r") //Turns Raw Replayable mode off (default) #define FLAG_TX_FILE ("-f") //Selects a transmit file (other than stdin) #define FLAG_TX_STDIN ("-s") //Selects transmits to come from standard input #define FLAG_TX_RATE ("-t") //Selects a delay between transmits (in milliseconds) #define FLAG_TX_QUIT_EOF ("-Q") //Selects the behavior of quitting after EOF on our input file (default) #define FLAG_TX_LISTEN_EOF ("-q") //Selects the behavior of hanging out and listending after EOF on input file #define FLAG_RECONNECT_ON ("-X") //Reconnect when disconnected from the IPC hub. #define FLAG_RECONNECT_OFF ("-x") //Don't reconnect when disconnected from the IPC hub. #define FLAG_VERBOSE_ON ("-V") //Set verbose mode ON (all status messages to stderr) #define FLAG_VERBOSE_OFF ("-v") //Set verbose mode OFF (no status messages to stderr) #define FLAG_MATCH_REGEX ("-m") //Set mailbox name match regex #define FLAG_EXCLUDE_REGEX ("-M") //Set mailbox exclude regex #define STATUS_MSG(fmt, ...) if(mode_flags & CLIENT_MODE_VERBOSE){fprintf(stderr, fmt, ## __VA_ARGS__);} void quit_gracefully(int exitcode) { if(infile) { fclose(infile); infile = NULL; } if(hub_fd >= 0) { close(hub_fd); hub_fd = -1; } if(mode_flags & CLIENT_MODE_MATCH_REGEX) { regfree(&match_expr); } if(mode_flags & CLIENT_MODE_EXCLUDE_REGEX) { regfree(&exclude_expr); } STATUS_MSG("Quitting.\n"); exit(exitcode); } void usage(char *progname) { fprintf(stderr, "Usage: %s [-w | -W] [-r | -R] [-x | -X] [ [-q | -Q] [-s | -f filespec] [-t msec] ] [-m match_regex | -M exclude_regex]\n\n", progname); fprintf(stderr, "\t -W = Enable Wiretap. (default)\n"); fprintf(stderr, "\t -w = Disable Wiretap.\n"); fprintf(stderr, "\t -R = Enable Raw/Replayable output.\n"); fprintf(stderr, "\t -r = Disable Raw/Replayable output. (default)\n"); fprintf(stderr, "\t -s = Enable transmission of raw format messages from standard in.\n"); fprintf(stderr, "\t -f filespec = Enable transmission of raw format messages from specified regular file.\n"); fprintf(stderr, "\t -t usec = Rate limit transmitted messages by waiting usec milliseconds between transmits.\n"); fprintf(stderr, "\t -Q = Quit after sending the last message in our input file. (default)\n"); fprintf(stderr, "\t -q = Listen forever after sending the last message in our input file.\n"); fprintf(stderr, "\t -X = Reconnect if disconnected from IPC hub.\n"); fprintf(stderr, "\t -x = Don't attempt to reconnect if disconnected. (default)\n"); fprintf(stderr, "\t -V = Set verbose mode (all status messages to stderr).\n"); fprintf(stderr, "\t -v = Clear verbose mode (no status messages to stderr). (default)\n"); fprintf(stderr, "\t -m match_regex = Only display messages to mailbox names that match supplied regex.\n"); fprintf(stderr, "\t -M exclude_regex = Ignore received messages to mailbox names that match supplied regex.\n"); fprintf(stderr, "\n"); fprintf(stderr, "\tThe -f and -s options are mutually exclusive. If both the\n"); fprintf(stderr, "-w and -W options or both the -r and -R options are specified\n"); fprintf(stderr, "the one furthest to the right will take precedence. The same\n"); fprintf(stderr, "holds true for -Q/-q and -X/-x as well as -V/-v.\n"); fprintf(stderr, "\n"); fprintf(stderr, "When invoked with no flags, it is equivalent to:\n"); fprintf(stderr, "\t%s -W -r -x\n", progname); fprintf(stderr, "\n"); fprintf(stderr, "\tNo transmission source is enabled by default, thus allowing\n"); fprintf(stderr, "the task to be backgrounded without having it stop for input from the terminal.\n"); fprintf(stderr, "It should also be noted that when not in a transmit mode, the -q and -Q flags as\n"); fprintf(stderr, "well as the -t flag are considered invalid.\n"); fprintf(stderr, "\n"); } void process_command_line(int argc, char **argv) { mode_flags = CLIENT_MODE_WIRETAP | CLIENT_MODE_QUIT_AFTER_EOF; int expect_file = 0; int expect_time = 0; int expect_match = 0; int expect_exclude = 0; int i; int retval; int qeof = 0; //flag to see if a quit-after-eof flag is processed (either way) so we can give //an error if it is not done in a transmit mode... char errorbuffer[1024]={0}; for(i = 1; i < argc; i++) { if(expect_file) { infile = fopen(argv[i], "rb"); if(!infile) { fprintf(stderr, "Cannot open file specified by -f (\"%s\") for reading!\n", argv[i]); quit_gracefully(1); } expect_file = 0; } else if(expect_time) { retval = sscanf(argv[i], "%lld", &tx_rate_limit); if(retval != 1) { fprintf(stderr, "Expecting integer number of milliseconds after -t, got \"%s\"!\n", argv[i]); quit_gracefully(1); } expect_time = 0; } else if(expect_match) { retval = regcomp(&match_expr, argv[i], REG_EXTENDED | REG_NOSUB); if(retval) { regerror(retval, &match_expr, errorbuffer, sizeof(errorbuffer)); errorbuffer[sizeof(errorbuffer) - 1] = '\0'; fprintf(stderr, "Error compiling regular expression supplied by -m: %s\n", errorbuffer); quit_gracefully(1); } else { expect_match = 0; mode_flags |= CLIENT_MODE_MATCH_REGEX; } } else if(expect_exclude) { retval = regcomp(&exclude_expr, argv[i], REG_EXTENDED | REG_NOSUB); if(retval) { regerror(retval, &exclude_expr, errorbuffer, sizeof(errorbuffer)); errorbuffer[sizeof(errorbuffer) - 1] = '\0'; fprintf(stderr, "Error compiling regular expression supplied by -M: %s\n", errorbuffer); quit_gracefully(1); } else { expect_exclude = 0; mode_flags |= CLIENT_MODE_EXCLUDE_REGEX; } } else { if(!strcmp(argv[i], FLAG_WIRETAP_ON)) { mode_flags |= CLIENT_MODE_WIRETAP; } else if(!strcmp(argv[i], FLAG_WIRETAP_OFF)) { mode_flags &= ~CLIENT_MODE_WIRETAP; } else if(!strcmp(argv[i], FLAG_RAWMODE_ON)) { mode_flags |= CLIENT_MODE_RAWOUT; } else if(!strcmp(argv[i], FLAG_RAWMODE_OFF)) { mode_flags &= ~CLIENT_MODE_RAWOUT; } else if(!strcmp(argv[i], FLAG_TX_QUIT_EOF)) { mode_flags |= CLIENT_MODE_QUIT_AFTER_EOF; qeof |= 1; } else if(!strcmp(argv[i], FLAG_TX_LISTEN_EOF)) { mode_flags &= ~CLIENT_MODE_QUIT_AFTER_EOF; qeof |= 1; } else if(!strcmp(argv[i], FLAG_RECONNECT_ON)) { mode_flags |= CLIENT_MODE_RECONNECT; } else if(!strcmp(argv[i], FLAG_RECONNECT_OFF)) { mode_flags &= ~CLIENT_MODE_RECONNECT; } else if(!strcmp(argv[i], FLAG_VERBOSE_ON)) { mode_flags |= CLIENT_MODE_VERBOSE; } else if(!strcmp(argv[i], FLAG_VERBOSE_OFF)) { mode_flags &= ~CLIENT_MODE_VERBOSE; } else if(!strcmp(argv[i], FLAG_TX_STDIN)) { if(mode_flags & CLIENT_MODE_TRANSMIT_FILE) { fprintf(stderr, "Conflicting Flags (-f and -s)!\n"); usage(argv[0]); quit_gracefully(1); } else { mode_flags |= CLIENT_MODE_TRANSMIT_STDIN; } } else if(!strcmp(argv[i], FLAG_TX_FILE)) { if(mode_flags & CLIENT_MODE_TRANSMIT_FILE) { fprintf(stderr, "Only one -f flag is valid per command line!\n"); usage(argv[0]); quit_gracefully(1); } if(mode_flags & CLIENT_MODE_TRANSMIT_STDIN) { fprintf(stderr, "Conflicting Flags (-f and -s)!\n"); usage(argv[0]); quit_gracefully(1); } else { mode_flags |= CLIENT_MODE_TRANSMIT_FILE; expect_file = 1; } } else if(!strcmp(argv[i], FLAG_TX_RATE)) { mode_flags |= CLIENT_MODE_TRANSMIT_RATE; expect_time = 1; } else if(!strcmp(argv[i], FLAG_MATCH_REGEX)) { if(mode_flags & (CLIENT_MODE_MATCH_REGEX | CLIENT_MODE_EXCLUDE_REGEX)) { fprintf(stderr, "Only one -m/-M flag allowed per command line!\n"); usage(argv[0]); quit_gracefully(1); } expect_match = 1; } else if(!strcmp(argv[i], FLAG_EXCLUDE_REGEX)) { if(mode_flags & (CLIENT_MODE_MATCH_REGEX | CLIENT_MODE_EXCLUDE_REGEX)) { fprintf(stderr, "Only one -m/-M flag allowed per command line!\n"); usage(argv[0]); quit_gracefully(1); } expect_exclude = 1; } else { fprintf(stderr, "Unknown option \"%s\"!\n", argv[i]); usage(argv[0]); quit_gracefully(1); } } } if(expect_file) //If we've hit the end of our argument list and we're left hanging looking for a filename { fprintf(stderr,"Input file expected following -f flag!\n"); usage(argv[0]); quit_gracefully(1); } if(expect_time) //If we're left hanging looking for a rate-limit time { fprintf(stderr,"Delay time in milliseconds expected following -t flag!\n"); usage(argv[0]); quit_gracefully(1); } if(expect_match) //If we're left hanging waiting for a regex for -m { fprintf(stderr,"Regular expression expected following -m flag!\n"); usage(argv[0]); quit_gracefully(1); } if(expect_exclude) //If we're left hanging waiting for a regex for -m { fprintf(stderr,"Regular expression expected following -M flag!\n"); usage(argv[0]); quit_gracefully(1); } //If we're not in a transmit mode, certain options are not valid... if(!( mode_flags & (CLIENT_MODE_TRANSMIT_FILE | CLIENT_MODE_TRANSMIT_STDIN))) { if(mode_flags & CLIENT_MODE_TRANSMIT_RATE) //Transmit rate limit isn't valid without transmit source { fprintf(stderr,"Transmit rate limit specified without transmit source!\n"); usage(argv[0]); quit_gracefully(1); } if(qeof) { fprintf(stderr,"The -q and -Q flags are not valid if we are not in a transmit mode!\n"); usage(argv[0]); quit_gracefully(1); } } return; } int do_server_connect(char *progname) { hub_fd = connect_to_message_server(progname); if( hub_fd < 0 ) { STATUS_MSG("Cannot connect to IPC hub!\n"); if( !(mode_flags & CLIENT_MODE_RECONNECT) ) { quit_gracefully(0); } return -1; } else if(mode_flags & CLIENT_MODE_WIRETAP) { struct message_record msg; STATUS_MSG("Entering WIRETAP mode!\n"); prepare_message(&msg, MAILBOX_WIRETAP, "ON", 2); send_message(hub_fd,&msg); } return 0; } long long int get_msec() { return get_usec_time() / 1000; } //---------------------------------------- #define SKIP_WHITESPACE(trav,c) \ while(trav && *trav) \ { \ c = *trav; \ \ if( (c == ' ') || (c == '\t') ||\ (c == '\r') || (c == '\n') )\ { \ trav++; \ } \ else \ { \ break; \ } \ } //---------------------------------------- int parse_raw_message(char *rawmsg, struct message_record *msg) { char mailbox_name[MAILBOX_NAME_MAX + 1] = {0}; unsigned char payload[MAX_PAYLOAD_LENGTH] = {0}; int payload_len = 0; int i; char *mbx = rawmsg; char c; unsigned char accum; SKIP_WHITESPACE(mbx,c); if( (*mbx == '\0') || (*mbx == '#') ) { return -1; } for(i = 0; i <= MAILBOX_NAME_MAX; i++) { mailbox_name[i] = mbx[i]; if((mbx[i] == ':') || (mbx[i] == '\0') || //if we hit a : (field terminator), nul (end sentinel) (mbx[i] == ' ') || (mbx[i] == '\t') || //or any of the usual suspects (whitespace characters) (mbx[i] == '\r') || (mbx[i] == '\n')) //we want to terminate the mailbox name and continue... { mailbox_name[i] = '\0'; break; } } mbx += i; //skip past the mailbox name SKIP_WHITESPACE(mbx,c); //Clean off any trailing whitespace that we may have left over if(*mbx++ != ':') //if the next non-whitespace character isn't the field separator... { STATUS_MSG("Malformed input line: \"%s\"\n", rawmsg); return -1; } payload_len = 0; accum = 0; i = 0; while(*mbx) { switch(*mbx) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': accum <<= 4; accum |= (*mbx) - '0'; //ascii to numeric hex i++; mbx++; break; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': accum <<= 4; accum |= (*mbx) - 'W'; //ascii to lower case hex i++; mbx++; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': accum <<= 4; accum |= (*mbx) - '7'; //ascii to i++; mbx++; break; case ' ': case '\t': case '\r': case '\n': mbx++; if(i > 0) //if we have an octet started { i = 2; //whitespace ends the current octet } break; default: STATUS_MSG("Illegal character '%c' in raw message (expecting hex or whitespace)!\n", *mbx); return -1; break; } //switch(*mbx) if(i >= 2) //If we've snagged ourselves both nybbles of an octet { payload[payload_len] = accum; //store the octet accum = 0; //reset our accumulator i = 0; //and nybble counter payload_len++; //advance our payload_len variable to be accurate if(payload_len >= MAX_PAYLOAD_LENGTH) //test against our maximum length... { payload_len = MAX_PAYLOAD_LENGTH; //we've hit our maximum payload size... break; //we're done accumulating this message } } } //while(*mbx) return prepare_message(msg, mailbox_name, payload, payload_len); } #define FULL_POLL_TIME (1000) int main(int argc, char **argv) { char linebuffer[TOTAL_MESSAGE_SIZE * 4]; struct message_record msg; struct message_record outgoing; int retval; int polltime; int transmit_pending = 0; struct pollfd fds[2] = {{0}}; //File descriptor set int nfds = 0; int end_of_input = 0; //-------------------------------- signal(SIGINT, quit_gracefully); signal(SIGTERM, quit_gracefully); process_command_line(argc, argv); do_server_connect(argv[0]); while(1) { if((mode_flags & CLIENT_MODE_TRANSMIT_FILE) && (infile != NULL)) { if(feof(infile)) { end_of_input = 1; } } //If we've hit the end of the line for our input, and we are flagged to exit at that point, if( end_of_input && (mode_flags & CLIENT_MODE_QUIT_AFTER_EOF) ) { quit_gracefully(0); } if(hub_fd < 0) { sleep(1); STATUS_MSG("Attempting to reconnect to IPC hub!\n"); do_server_connect(argv[0]); continue; } nfds = 0; fds[nfds].fd = hub_fd; fds[nfds].events = POLLIN; fds[nfds].revents = 0; nfds++; polltime = FULL_POLL_TIME; //If we are in rate-limited transmit mode if(mode_flags & CLIENT_MODE_TRANSMIT_RATE) { //Calculate how many milliseconds before our next scheduled transmit window long long int delta = tx_rate_limit - (get_msec() - last_tx); // If that time is less than polltime milliseconds away, shorten polltime so we can //drop out of poll, add our input file descriptor, and try polling again to see if we have //any input we can make into a message to transmit. if( (delta > 0) && (delta < polltime) ) { polltime = delta; } } if( !(mode_flags & CLIENT_MODE_TRANSMIT_RATE) || //if we are either NOT rate limiting ((get_msec() - last_tx) >= tx_rate_limit) //or we are, but it is time to transmit again ) { if(!transmit_pending && !end_of_input) //if there is no transmit pending and we are due one, we want to gather input... { //(unless of course, we've hit EOF on our current input source) if(mode_flags & CLIENT_MODE_TRANSMIT_STDIN) //if we are transmitting from stdin, add it to the poll { fds[nfds].fd = STDIN_FILENO; fds[nfds].events = POLLIN; fds[nfds].revents = 0; nfds++; } else if((mode_flags & CLIENT_MODE_TRANSMIT_FILE) && //if we are transmitting from a regular file, add it (infile != NULL)) //(unless it's been closed) { //even though it should always report ready, it prevents fds[nfds].fd = fileno(infile); //us having to handle a stupid special case... fds[nfds].events = POLLIN; fds[nfds].revents = 0; nfds++; } } polltime = FULL_POLL_TIME; } if(transmit_pending) //If we have assembled an entire message to transmit, { fds[0].events |= POLLOUT; //flag poll() to tell us when the IPC hub is ready to receive it. } retval = poll(fds, nfds, polltime); //Poll waiting for something to happen (up to 1 second) if(retval < 0) //if poll() returns an error other than EINTR, that's really bad news. { int errnum = errno; if(errnum == EINTR) //If we were interrupted by a signal { continue; //just go and try it again... } STATUS_MSG("Poll returned error %d (%s)\n", errnum, strerror(errnum)); quit_gracefully(1); } else if(retval > 0) //If there were more than zero flagged file descriptors { if(fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { STATUS_MSG("Connection to IPC hub has been died.\n"); close(hub_fd); hub_fd = -1; if(mode_flags & CLIENT_MODE_RECONNECT) //If this client has been asked to reconnect when possible, { continue; //Continue the while(1) which will attempt reconnect } else //on the other hand, if this client hasn't been asked to reconnect, that's time to quit { quit_gracefully(0); } } else { if(fds[0].revents & POLLIN) //If we have input available from the IPC hub { retval = get_message(hub_fd, &msg); //try and fetch it,,, if(retval) //if that fails { //complain to the user STATUS_MSG("Error returned by get_message(), connection to IPC hub suspect!\n"); //and ditch the connection close(hub_fd); hub_fd = -1; continue; } else //on success... we have a message... { int skip; skip = 0; if(mode_flags & CLIENT_MODE_MATCH_REGEX) //if we're in -m mode (display only matching) { //if the match_expr regex returns REG_NOMATCH on the mailbox name, we want to skip this message if(regexec(&match_expr, msg.header.mailbox_name, 0, NULL, 0) == REG_NOMATCH) { skip = 1; } } if(mode_flags & CLIENT_MODE_EXCLUDE_REGEX) //if we're int -M mode (display only non-matching) { //if the reject_expr regex returns 0 (match) on the mailbox name, we want to skip this message if(regexec(&exclude_expr, msg.header.mailbox_name, 0, NULL, 0) == 0) { skip = 1; } } if(!skip) { if(mode_flags & CLIENT_MODE_RAWOUT) //if we are in raw output mode, { //generate the raw/replayable version of the message (machine readable) print_replayable_message(&msg); } else //otherwise { //generate the decoded version of the message (human readable) print_decoded_message(&msg); } } } } if(fds[0].revents & POLLOUT) //if we a timeslot to transmit and we've asked to talk { send_message(hub_fd, &outgoing); //that means that we've filled our output message transmit_pending = 0; //flag this message as non-pending. last_tx = get_msec(); //and record our last transmit time } } if(nfds > 1) { if(fds[1].revents & POLLIN) { FILE *f = stdin; if(mode_flags & CLIENT_MODE_TRANSMIT_FILE) { f = infile; } //If we both have a file to fetch from, and can fetch something besides EOF if( (f != NULL) && (fgets(linebuffer, sizeof(linebuffer), f) != NULL) ) { //If we can parse that into something we understand, flag it for transmission transmit_pending = (parse_raw_message(linebuffer, &outgoing) == 0); } else { end_of_input = 1; } } else if(fds[1].revents & (POLLERR | POLLHUP | POLLNVAL)) { STATUS_MSG("EOF encountered reading from input.\n"); end_of_input = 1; if(mode_flags & CLIENT_MODE_TRANSMIT_FILE) { fclose(infile); infile = NULL; continue; } } } } } return 0; }