| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993 |
- /*
- * Copyright (c) 2019 Clementine Computing LLC.
- *
- * This file is part of PopuFare.
- *
- * PopuFare is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * PopuFare is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with PopuFare. If not, see <https://www.gnu.org/licenses/>.
- *
- */
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <sys/un.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <poll.h>
- #include <errno.h>
- #include <time.h>
- #include <string.h>
- #include <signal.h>
- #include <regex.h>
- #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;
- }
|