/*
* 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;
}