debugcli.c 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993
  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/types.h>
  21. #include <sys/socket.h>
  22. #include <sys/un.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <unistd.h>
  26. #include <poll.h>
  27. #include <errno.h>
  28. #include <time.h>
  29. #include <string.h>
  30. #include <signal.h>
  31. #include <regex.h>
  32. #include "commhub.h"
  33. int print_replayable_message(struct message_record *msg)
  34. {
  35. int i;
  36. printf("%s:",msg->header.mailbox_name);
  37. for(i=0; i < msg->header.payload_length; i++)
  38. {
  39. printf(" %02X", msg->payload[i]);
  40. }
  41. printf("\n");
  42. return 0;
  43. }
  44. int print_decoded_message(struct message_record *msg)
  45. {
  46. int binflag, i;
  47. printf("%lld: %d->%s (%d bytes):", msg->header.usec_time, msg->header.sender, msg->header.mailbox_name, msg->header.payload_length);
  48. if(!strncmp(msg->header.mailbox_name, MAILBOX_GPS_STATUS, MAILBOX_NAME_MAX))
  49. {
  50. gps_status *s = (gps_status *)msg->payload;
  51. printf("Position: %f,%f %3.1f deg @ %.1f M/sec Quality: %d sats (%s)",
  52. s->lat, s->lon, s->heading, s->velocity, s->num_sats, (s->gps_good?"OK":"NOT OK") );
  53. }
  54. else if(!strncmp(msg->header.mailbox_name, MAILBOX_STOP_STATUS, MAILBOX_NAME_MAX))
  55. {
  56. stop_status *s = (stop_status *)msg->payload;
  57. 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);
  58. }
  59. else if(!strncmp(msg->header.mailbox_name, MAILBOX_PASS_STATUS, MAILBOX_NAME_MAX))
  60. {
  61. pass_status *s = (pass_status *)msg->payload;
  62. char *flush_str;
  63. switch(s->flush_status)
  64. {
  65. case FLUSH_STATUS_NORMAL:
  66. flush_str="NORMAL";
  67. break;
  68. case FLUSH_STATUS_LEGACY:
  69. flush_str="LEGACY";
  70. break;
  71. case FLUSH_STATUS_DOWNLOAD:
  72. flush_str="DOWNLOAD";
  73. break;
  74. case FLUSH_STATUS_APPLY:
  75. flush_str="APPLY";
  76. break;
  77. case FLUSH_STATUS_WRITE:
  78. flush_str="WRITE";
  79. break;
  80. default:
  81. flush_str="???";
  82. break;
  83. }
  84. 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);
  85. }
  86. else if(!strncmp(msg->header.mailbox_name, MAILBOX_BILL_STATUS, MAILBOX_NAME_MAX))
  87. {
  88. bill_status *s = (bill_status *)msg->payload;
  89. printf("LastSync = %d LastAck = %d", (int)s->last_sync_time, (int)s->last_ack_time);
  90. }
  91. else if(!strncmp(msg->header.mailbox_name, MAILBOX_PIU_MESSAGE, MAILBOX_NAME_MAX))
  92. {
  93. piu_message *s = (piu_message *)msg->payload;
  94. printf("Priority = %d Line = %d Duration = %d (0 = make default) Message = \"%s\"", s->priority, s->line, s->seconds, s->message);
  95. }
  96. else if(!strncmp(msg->header.mailbox_name, MAILBOX_SET_PADDLE, MAILBOX_NAME_MAX) || !strncmp(msg->header.mailbox_name, MAILBOX_PADDLE_ACK, MAILBOX_NAME_MAX))
  97. {
  98. set_paddle_req *s = (set_paddle_req *)msg->payload;
  99. printf("Requested = %d Result = %d", s->request, s->result);
  100. }
  101. else if(!strncmp(msg->header.mailbox_name, MAILBOX_RULE_CALL, MAILBOX_NAME_MAX))
  102. {
  103. driver_rulecall *s = (driver_rulecall *)msg->payload;
  104. printf("Rule = \"%s\" Parameter = \"%s\"", s->rulename, s->ruleparam);
  105. }
  106. else
  107. {
  108. binflag = 0;
  109. for(i = 0; i < msg->header.payload_length; i++)
  110. {
  111. switch(msg->payload[i])
  112. {
  113. case '\0':
  114. case '\t':
  115. case '\r':
  116. case '\n':
  117. break;
  118. default:
  119. if( (msg->payload[i] < ' ') || (msg->payload[i] > '~') )
  120. {
  121. binflag = 1;
  122. }
  123. break;
  124. }
  125. }
  126. if(binflag)
  127. {
  128. for(i = 0; i < msg->header.payload_length; i++)
  129. {
  130. printf("%02x ", msg->payload[i]);
  131. }
  132. }
  133. else
  134. {
  135. printf("\"");
  136. for(i = 0; i < msg->header.payload_length; i++)
  137. {
  138. switch(msg->payload[i])
  139. {
  140. case '\0':
  141. printf("\\0");
  142. break;
  143. case '"':
  144. printf("\\\"");
  145. break;
  146. case '\r':
  147. printf("\\r");
  148. break;
  149. case '\n':
  150. printf("\\n");
  151. break;
  152. case '\t':
  153. printf("\\t");
  154. break;
  155. case '\\':
  156. printf("\\\\");
  157. break;
  158. default:
  159. printf("%c", msg->payload[i]);
  160. break;
  161. }
  162. }
  163. printf("\"");
  164. }
  165. }
  166. printf("\n");
  167. return 0;
  168. }
  169. #define CLIENT_MODE_WIRETAP (0x0001) //Enter Wiretap Mode
  170. #define CLIENT_MODE_RAWOUT (0x0002) //Format output in replayable raw format
  171. #define CLIENT_MODE_TRANSMIT_FILE (0x0004) //Transmit messages from a specific file
  172. #define CLIENT_MODE_TRANSMIT_STDIN (0x0008) //Transmit messages from standard in
  173. #define CLIENT_MODE_TRANSMIT_RATE (0x0010) //Wait between transmits
  174. #define CLIENT_MODE_QUIT_AFTER_EOF (0x0020) //Quit after EOF on our transmit source
  175. #define CLIENT_MODE_RECONNECT (0x0040) //Reconnect if disconnected from IPC hub
  176. #define CLIENT_MODE_VERBOSE (0x0080) //In verbose mode we generate lots more commentary to stderr
  177. #define CLIENT_MODE_MATCH_REGEX (0x0100) //In Match Regex mode we only display mailboxes that match regex
  178. #define CLIENT_MODE_EXCLUDE_REGEX (0x0200) //In Exclude Regex mode we exclude mailboxes that match regex
  179. //Flags setting our mode of operation (see CLIENT_MODE_* above)
  180. //
  181. int mode_flags = 0; //This is properly initialized in process_command_line()
  182. FILE *infile = NULL; //If we are transmitting from a file, here's our handle to it...
  183. long long int last_tx = 0; //Start out with a last-transmit time of "never"
  184. long long int tx_rate_limit = 0; //Wait at least tx_rate_limit milliseconds between sends
  185. int hub_fd = -1; //File descriptor of our communication hub
  186. regex_t match_expr; //Mailbox name match regex (compiled)
  187. regex_t exclude_expr; //Mailbox name exclude regex (compiled)
  188. #define FLAG_WIRETAP_ON ("-W") //Turns Wiretap mode on (default)
  189. #define FLAG_WIRETAP_OFF ("-w") //Turns Wiretap mode off
  190. #define FLAG_RAWMODE_ON ("-R") //Turns Raw Replayable mode on
  191. #define FLAG_RAWMODE_OFF ("-r") //Turns Raw Replayable mode off (default)
  192. #define FLAG_TX_FILE ("-f") //Selects a transmit file (other than stdin)
  193. #define FLAG_TX_STDIN ("-s") //Selects transmits to come from standard input
  194. #define FLAG_TX_RATE ("-t") //Selects a delay between transmits (in milliseconds)
  195. #define FLAG_TX_QUIT_EOF ("-Q") //Selects the behavior of quitting after EOF on our input file (default)
  196. #define FLAG_TX_LISTEN_EOF ("-q") //Selects the behavior of hanging out and listending after EOF on input file
  197. #define FLAG_RECONNECT_ON ("-X") //Reconnect when disconnected from the IPC hub.
  198. #define FLAG_RECONNECT_OFF ("-x") //Don't reconnect when disconnected from the IPC hub.
  199. #define FLAG_VERBOSE_ON ("-V") //Set verbose mode ON (all status messages to stderr)
  200. #define FLAG_VERBOSE_OFF ("-v") //Set verbose mode OFF (no status messages to stderr)
  201. #define FLAG_MATCH_REGEX ("-m") //Set mailbox name match regex
  202. #define FLAG_EXCLUDE_REGEX ("-M") //Set mailbox exclude regex
  203. #define STATUS_MSG(fmt, ...) if(mode_flags & CLIENT_MODE_VERBOSE){fprintf(stderr, fmt, ## __VA_ARGS__);}
  204. void quit_gracefully(int exitcode)
  205. {
  206. if(infile)
  207. {
  208. fclose(infile);
  209. infile = NULL;
  210. }
  211. if(hub_fd >= 0)
  212. {
  213. close(hub_fd);
  214. hub_fd = -1;
  215. }
  216. if(mode_flags & CLIENT_MODE_MATCH_REGEX)
  217. {
  218. regfree(&match_expr);
  219. }
  220. if(mode_flags & CLIENT_MODE_EXCLUDE_REGEX)
  221. {
  222. regfree(&exclude_expr);
  223. }
  224. STATUS_MSG("Quitting.\n");
  225. exit(exitcode);
  226. }
  227. void usage(char *progname)
  228. {
  229. 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);
  230. fprintf(stderr, "\t -W = Enable Wiretap. (default)\n");
  231. fprintf(stderr, "\t -w = Disable Wiretap.\n");
  232. fprintf(stderr, "\t -R = Enable Raw/Replayable output.\n");
  233. fprintf(stderr, "\t -r = Disable Raw/Replayable output. (default)\n");
  234. fprintf(stderr, "\t -s = Enable transmission of raw format messages from standard in.\n");
  235. fprintf(stderr, "\t -f filespec = Enable transmission of raw format messages from specified regular file.\n");
  236. fprintf(stderr, "\t -t usec = Rate limit transmitted messages by waiting usec milliseconds between transmits.\n");
  237. fprintf(stderr, "\t -Q = Quit after sending the last message in our input file. (default)\n");
  238. fprintf(stderr, "\t -q = Listen forever after sending the last message in our input file.\n");
  239. fprintf(stderr, "\t -X = Reconnect if disconnected from IPC hub.\n");
  240. fprintf(stderr, "\t -x = Don't attempt to reconnect if disconnected. (default)\n");
  241. fprintf(stderr, "\t -V = Set verbose mode (all status messages to stderr).\n");
  242. fprintf(stderr, "\t -v = Clear verbose mode (no status messages to stderr). (default)\n");
  243. fprintf(stderr, "\t -m match_regex = Only display messages to mailbox names that match supplied regex.\n");
  244. fprintf(stderr, "\t -M exclude_regex = Ignore received messages to mailbox names that match supplied regex.\n");
  245. fprintf(stderr, "\n");
  246. fprintf(stderr, "\tThe -f and -s options are mutually exclusive. If both the\n");
  247. fprintf(stderr, "-w and -W options or both the -r and -R options are specified\n");
  248. fprintf(stderr, "the one furthest to the right will take precedence. The same\n");
  249. fprintf(stderr, "holds true for -Q/-q and -X/-x as well as -V/-v.\n");
  250. fprintf(stderr, "\n");
  251. fprintf(stderr, "When invoked with no flags, it is equivalent to:\n");
  252. fprintf(stderr, "\t%s -W -r -x\n", progname);
  253. fprintf(stderr, "\n");
  254. fprintf(stderr, "\tNo transmission source is enabled by default, thus allowing\n");
  255. fprintf(stderr, "the task to be backgrounded without having it stop for input from the terminal.\n");
  256. fprintf(stderr, "It should also be noted that when not in a transmit mode, the -q and -Q flags as\n");
  257. fprintf(stderr, "well as the -t flag are considered invalid.\n");
  258. fprintf(stderr, "\n");
  259. }
  260. void process_command_line(int argc, char **argv)
  261. {
  262. mode_flags = CLIENT_MODE_WIRETAP | CLIENT_MODE_QUIT_AFTER_EOF;
  263. int expect_file = 0;
  264. int expect_time = 0;
  265. int expect_match = 0;
  266. int expect_exclude = 0;
  267. int i;
  268. int retval;
  269. int qeof = 0; //flag to see if a quit-after-eof flag is processed (either way) so we can give
  270. //an error if it is not done in a transmit mode...
  271. char errorbuffer[1024]={0};
  272. for(i = 1; i < argc; i++)
  273. {
  274. if(expect_file)
  275. {
  276. infile = fopen(argv[i], "rb");
  277. if(!infile)
  278. {
  279. fprintf(stderr, "Cannot open file specified by -f (\"%s\") for reading!\n", argv[i]);
  280. quit_gracefully(1);
  281. }
  282. expect_file = 0;
  283. }
  284. else if(expect_time)
  285. {
  286. retval = sscanf(argv[i], "%lld", &tx_rate_limit);
  287. if(retval != 1)
  288. {
  289. fprintf(stderr, "Expecting integer number of milliseconds after -t, got \"%s\"!\n", argv[i]);
  290. quit_gracefully(1);
  291. }
  292. expect_time = 0;
  293. }
  294. else if(expect_match)
  295. {
  296. retval = regcomp(&match_expr, argv[i], REG_EXTENDED | REG_NOSUB);
  297. if(retval)
  298. {
  299. regerror(retval, &match_expr, errorbuffer, sizeof(errorbuffer));
  300. errorbuffer[sizeof(errorbuffer) - 1] = '\0';
  301. fprintf(stderr, "Error compiling regular expression supplied by -m: %s\n", errorbuffer);
  302. quit_gracefully(1);
  303. }
  304. else
  305. {
  306. expect_match = 0;
  307. mode_flags |= CLIENT_MODE_MATCH_REGEX;
  308. }
  309. }
  310. else if(expect_exclude)
  311. {
  312. retval = regcomp(&exclude_expr, argv[i], REG_EXTENDED | REG_NOSUB);
  313. if(retval)
  314. {
  315. regerror(retval, &exclude_expr, errorbuffer, sizeof(errorbuffer));
  316. errorbuffer[sizeof(errorbuffer) - 1] = '\0';
  317. fprintf(stderr, "Error compiling regular expression supplied by -M: %s\n", errorbuffer);
  318. quit_gracefully(1);
  319. }
  320. else
  321. {
  322. expect_exclude = 0;
  323. mode_flags |= CLIENT_MODE_EXCLUDE_REGEX;
  324. }
  325. }
  326. else
  327. {
  328. if(!strcmp(argv[i], FLAG_WIRETAP_ON))
  329. {
  330. mode_flags |= CLIENT_MODE_WIRETAP;
  331. }
  332. else if(!strcmp(argv[i], FLAG_WIRETAP_OFF))
  333. {
  334. mode_flags &= ~CLIENT_MODE_WIRETAP;
  335. }
  336. else if(!strcmp(argv[i], FLAG_RAWMODE_ON))
  337. {
  338. mode_flags |= CLIENT_MODE_RAWOUT;
  339. }
  340. else if(!strcmp(argv[i], FLAG_RAWMODE_OFF))
  341. {
  342. mode_flags &= ~CLIENT_MODE_RAWOUT;
  343. }
  344. else if(!strcmp(argv[i], FLAG_TX_QUIT_EOF))
  345. {
  346. mode_flags |= CLIENT_MODE_QUIT_AFTER_EOF;
  347. qeof |= 1;
  348. }
  349. else if(!strcmp(argv[i], FLAG_TX_LISTEN_EOF))
  350. {
  351. mode_flags &= ~CLIENT_MODE_QUIT_AFTER_EOF;
  352. qeof |= 1;
  353. }
  354. else if(!strcmp(argv[i], FLAG_RECONNECT_ON))
  355. {
  356. mode_flags |= CLIENT_MODE_RECONNECT;
  357. }
  358. else if(!strcmp(argv[i], FLAG_RECONNECT_OFF))
  359. {
  360. mode_flags &= ~CLIENT_MODE_RECONNECT;
  361. }
  362. else if(!strcmp(argv[i], FLAG_VERBOSE_ON))
  363. {
  364. mode_flags |= CLIENT_MODE_VERBOSE;
  365. }
  366. else if(!strcmp(argv[i], FLAG_VERBOSE_OFF))
  367. {
  368. mode_flags &= ~CLIENT_MODE_VERBOSE;
  369. }
  370. else if(!strcmp(argv[i], FLAG_TX_STDIN))
  371. {
  372. if(mode_flags & CLIENT_MODE_TRANSMIT_FILE)
  373. {
  374. fprintf(stderr, "Conflicting Flags (-f and -s)!\n");
  375. usage(argv[0]);
  376. quit_gracefully(1);
  377. }
  378. else
  379. {
  380. mode_flags |= CLIENT_MODE_TRANSMIT_STDIN;
  381. }
  382. }
  383. else if(!strcmp(argv[i], FLAG_TX_FILE))
  384. {
  385. if(mode_flags & CLIENT_MODE_TRANSMIT_FILE)
  386. {
  387. fprintf(stderr, "Only one -f flag is valid per command line!\n");
  388. usage(argv[0]);
  389. quit_gracefully(1);
  390. }
  391. if(mode_flags & CLIENT_MODE_TRANSMIT_STDIN)
  392. {
  393. fprintf(stderr, "Conflicting Flags (-f and -s)!\n");
  394. usage(argv[0]);
  395. quit_gracefully(1);
  396. }
  397. else
  398. {
  399. mode_flags |= CLIENT_MODE_TRANSMIT_FILE;
  400. expect_file = 1;
  401. }
  402. }
  403. else if(!strcmp(argv[i], FLAG_TX_RATE))
  404. {
  405. mode_flags |= CLIENT_MODE_TRANSMIT_RATE;
  406. expect_time = 1;
  407. }
  408. else if(!strcmp(argv[i], FLAG_MATCH_REGEX))
  409. {
  410. if(mode_flags & (CLIENT_MODE_MATCH_REGEX | CLIENT_MODE_EXCLUDE_REGEX))
  411. {
  412. fprintf(stderr, "Only one -m/-M flag allowed per command line!\n");
  413. usage(argv[0]);
  414. quit_gracefully(1);
  415. }
  416. expect_match = 1;
  417. }
  418. else if(!strcmp(argv[i], FLAG_EXCLUDE_REGEX))
  419. {
  420. if(mode_flags & (CLIENT_MODE_MATCH_REGEX | CLIENT_MODE_EXCLUDE_REGEX))
  421. {
  422. fprintf(stderr, "Only one -m/-M flag allowed per command line!\n");
  423. usage(argv[0]);
  424. quit_gracefully(1);
  425. }
  426. expect_exclude = 1;
  427. }
  428. else
  429. {
  430. fprintf(stderr, "Unknown option \"%s\"!\n", argv[i]);
  431. usage(argv[0]);
  432. quit_gracefully(1);
  433. }
  434. }
  435. }
  436. if(expect_file) //If we've hit the end of our argument list and we're left hanging looking for a filename
  437. {
  438. fprintf(stderr,"Input file expected following -f flag!\n");
  439. usage(argv[0]);
  440. quit_gracefully(1);
  441. }
  442. if(expect_time) //If we're left hanging looking for a rate-limit time
  443. {
  444. fprintf(stderr,"Delay time in milliseconds expected following -t flag!\n");
  445. usage(argv[0]);
  446. quit_gracefully(1);
  447. }
  448. if(expect_match) //If we're left hanging waiting for a regex for -m
  449. {
  450. fprintf(stderr,"Regular expression expected following -m flag!\n");
  451. usage(argv[0]);
  452. quit_gracefully(1);
  453. }
  454. if(expect_exclude) //If we're left hanging waiting for a regex for -m
  455. {
  456. fprintf(stderr,"Regular expression expected following -M flag!\n");
  457. usage(argv[0]);
  458. quit_gracefully(1);
  459. }
  460. //If we're not in a transmit mode, certain options are not valid...
  461. if(!( mode_flags & (CLIENT_MODE_TRANSMIT_FILE | CLIENT_MODE_TRANSMIT_STDIN)))
  462. {
  463. if(mode_flags & CLIENT_MODE_TRANSMIT_RATE) //Transmit rate limit isn't valid without transmit source
  464. {
  465. fprintf(stderr,"Transmit rate limit specified without transmit source!\n");
  466. usage(argv[0]);
  467. quit_gracefully(1);
  468. }
  469. if(qeof)
  470. {
  471. fprintf(stderr,"The -q and -Q flags are not valid if we are not in a transmit mode!\n");
  472. usage(argv[0]);
  473. quit_gracefully(1);
  474. }
  475. }
  476. return;
  477. }
  478. int do_server_connect(char *progname)
  479. {
  480. hub_fd = connect_to_message_server(progname);
  481. if( hub_fd < 0 )
  482. {
  483. STATUS_MSG("Cannot connect to IPC hub!\n");
  484. if( !(mode_flags & CLIENT_MODE_RECONNECT) )
  485. {
  486. quit_gracefully(0);
  487. }
  488. return -1;
  489. }
  490. else if(mode_flags & CLIENT_MODE_WIRETAP)
  491. {
  492. struct message_record msg;
  493. STATUS_MSG("Entering WIRETAP mode!\n");
  494. prepare_message(&msg, MAILBOX_WIRETAP, "ON", 2);
  495. send_message(hub_fd,&msg);
  496. }
  497. return 0;
  498. }
  499. long long int get_msec()
  500. {
  501. return get_usec_time() / 1000;
  502. }
  503. //----------------------------------------
  504. #define SKIP_WHITESPACE(trav,c) \
  505. while(trav && *trav) \
  506. { \
  507. c = *trav; \
  508. \
  509. if( (c == ' ') || (c == '\t') ||\
  510. (c == '\r') || (c == '\n') )\
  511. { \
  512. trav++; \
  513. } \
  514. else \
  515. { \
  516. break; \
  517. } \
  518. }
  519. //----------------------------------------
  520. int parse_raw_message(char *rawmsg, struct message_record *msg)
  521. {
  522. char mailbox_name[MAILBOX_NAME_MAX + 1] = {0};
  523. unsigned char payload[MAX_PAYLOAD_LENGTH] = {0};
  524. int payload_len = 0;
  525. int i;
  526. char *mbx = rawmsg;
  527. char c;
  528. unsigned char accum;
  529. SKIP_WHITESPACE(mbx,c);
  530. if( (*mbx == '\0') || (*mbx == '#') )
  531. {
  532. return -1;
  533. }
  534. for(i = 0; i <= MAILBOX_NAME_MAX; i++)
  535. {
  536. mailbox_name[i] = mbx[i];
  537. if((mbx[i] == ':') || (mbx[i] == '\0') || //if we hit a : (field terminator), nul (end sentinel)
  538. (mbx[i] == ' ') || (mbx[i] == '\t') || //or any of the usual suspects (whitespace characters)
  539. (mbx[i] == '\r') || (mbx[i] == '\n')) //we want to terminate the mailbox name and continue...
  540. {
  541. mailbox_name[i] = '\0';
  542. break;
  543. }
  544. }
  545. mbx += i; //skip past the mailbox name
  546. SKIP_WHITESPACE(mbx,c); //Clean off any trailing whitespace that we may have left over
  547. if(*mbx++ != ':') //if the next non-whitespace character isn't the field separator...
  548. {
  549. STATUS_MSG("Malformed input line: \"%s\"\n", rawmsg);
  550. return -1;
  551. }
  552. payload_len = 0;
  553. accum = 0;
  554. i = 0;
  555. while(*mbx)
  556. {
  557. switch(*mbx)
  558. {
  559. case '0':
  560. case '1':
  561. case '2':
  562. case '3':
  563. case '4':
  564. case '5':
  565. case '6':
  566. case '7':
  567. case '8':
  568. case '9':
  569. accum <<= 4;
  570. accum |= (*mbx) - '0'; //ascii to numeric hex
  571. i++;
  572. mbx++;
  573. break;
  574. case 'a':
  575. case 'b':
  576. case 'c':
  577. case 'd':
  578. case 'e':
  579. case 'f':
  580. accum <<= 4;
  581. accum |= (*mbx) - 'W'; //ascii to lower case hex
  582. i++;
  583. mbx++;
  584. break;
  585. case 'A':
  586. case 'B':
  587. case 'C':
  588. case 'D':
  589. case 'E':
  590. case 'F':
  591. accum <<= 4;
  592. accum |= (*mbx) - '7'; //ascii to
  593. i++;
  594. mbx++;
  595. break;
  596. case ' ':
  597. case '\t':
  598. case '\r':
  599. case '\n':
  600. mbx++;
  601. if(i > 0) //if we have an octet started
  602. {
  603. i = 2; //whitespace ends the current octet
  604. }
  605. break;
  606. default:
  607. STATUS_MSG("Illegal character '%c' in raw message (expecting hex or whitespace)!\n", *mbx);
  608. return -1;
  609. break;
  610. } //switch(*mbx)
  611. if(i >= 2) //If we've snagged ourselves both nybbles of an octet
  612. {
  613. payload[payload_len] = accum; //store the octet
  614. accum = 0; //reset our accumulator
  615. i = 0; //and nybble counter
  616. payload_len++; //advance our payload_len variable to be accurate
  617. if(payload_len >= MAX_PAYLOAD_LENGTH) //test against our maximum length...
  618. {
  619. payload_len = MAX_PAYLOAD_LENGTH; //we've hit our maximum payload size...
  620. break; //we're done accumulating this message
  621. }
  622. }
  623. } //while(*mbx)
  624. return prepare_message(msg, mailbox_name, payload, payload_len);
  625. }
  626. #define FULL_POLL_TIME (1000)
  627. int main(int argc, char **argv)
  628. {
  629. char linebuffer[TOTAL_MESSAGE_SIZE * 4];
  630. struct message_record msg;
  631. struct message_record outgoing;
  632. int retval;
  633. int polltime;
  634. int transmit_pending = 0;
  635. struct pollfd fds[2] = {{0}}; //File descriptor set
  636. int nfds = 0;
  637. int end_of_input = 0;
  638. //--------------------------------
  639. signal(SIGINT, quit_gracefully);
  640. signal(SIGTERM, quit_gracefully);
  641. process_command_line(argc, argv);
  642. do_server_connect(argv[0]);
  643. while(1)
  644. {
  645. if((mode_flags & CLIENT_MODE_TRANSMIT_FILE) && (infile != NULL))
  646. {
  647. if(feof(infile))
  648. {
  649. end_of_input = 1;
  650. }
  651. }
  652. //If we've hit the end of the line for our input, and we are flagged to exit at that point,
  653. if( end_of_input && (mode_flags & CLIENT_MODE_QUIT_AFTER_EOF) )
  654. {
  655. quit_gracefully(0);
  656. }
  657. if(hub_fd < 0)
  658. {
  659. sleep(1);
  660. STATUS_MSG("Attempting to reconnect to IPC hub!\n");
  661. do_server_connect(argv[0]);
  662. continue;
  663. }
  664. nfds = 0;
  665. fds[nfds].fd = hub_fd;
  666. fds[nfds].events = POLLIN;
  667. fds[nfds].revents = 0;
  668. nfds++;
  669. polltime = FULL_POLL_TIME;
  670. //If we are in rate-limited transmit mode
  671. if(mode_flags & CLIENT_MODE_TRANSMIT_RATE)
  672. {
  673. //Calculate how many milliseconds before our next scheduled transmit window
  674. long long int delta = tx_rate_limit - (get_msec() - last_tx);
  675. // If that time is less than polltime milliseconds away, shorten polltime so we can
  676. //drop out of poll, add our input file descriptor, and try polling again to see if we have
  677. //any input we can make into a message to transmit.
  678. if( (delta > 0) && (delta < polltime) )
  679. {
  680. polltime = delta;
  681. }
  682. }
  683. if( !(mode_flags & CLIENT_MODE_TRANSMIT_RATE) || //if we are either NOT rate limiting
  684. ((get_msec() - last_tx) >= tx_rate_limit) //or we are, but it is time to transmit again
  685. )
  686. {
  687. if(!transmit_pending && !end_of_input) //if there is no transmit pending and we are due one, we want to gather input...
  688. { //(unless of course, we've hit EOF on our current input source)
  689. if(mode_flags & CLIENT_MODE_TRANSMIT_STDIN) //if we are transmitting from stdin, add it to the poll
  690. {
  691. fds[nfds].fd = STDIN_FILENO;
  692. fds[nfds].events = POLLIN;
  693. fds[nfds].revents = 0;
  694. nfds++;
  695. }
  696. else if((mode_flags & CLIENT_MODE_TRANSMIT_FILE) && //if we are transmitting from a regular file, add it
  697. (infile != NULL)) //(unless it's been closed)
  698. { //even though it should always report ready, it prevents
  699. fds[nfds].fd = fileno(infile); //us having to handle a stupid special case...
  700. fds[nfds].events = POLLIN;
  701. fds[nfds].revents = 0;
  702. nfds++;
  703. }
  704. }
  705. polltime = FULL_POLL_TIME;
  706. }
  707. if(transmit_pending) //If we have assembled an entire message to transmit,
  708. {
  709. fds[0].events |= POLLOUT; //flag poll() to tell us when the IPC hub is ready to receive it.
  710. }
  711. retval = poll(fds, nfds, polltime); //Poll waiting for something to happen (up to 1 second)
  712. if(retval < 0) //if poll() returns an error other than EINTR, that's really bad news.
  713. {
  714. int errnum = errno;
  715. if(errnum == EINTR) //If we were interrupted by a signal
  716. {
  717. continue; //just go and try it again...
  718. }
  719. STATUS_MSG("Poll returned error %d (%s)\n", errnum, strerror(errnum));
  720. quit_gracefully(1);
  721. }
  722. else if(retval > 0) //If there were more than zero flagged file descriptors
  723. {
  724. if(fds[0].revents & (POLLERR | POLLHUP | POLLNVAL))
  725. {
  726. STATUS_MSG("Connection to IPC hub has been died.\n");
  727. close(hub_fd);
  728. hub_fd = -1;
  729. if(mode_flags & CLIENT_MODE_RECONNECT) //If this client has been asked to reconnect when possible,
  730. {
  731. continue; //Continue the while(1) which will attempt reconnect
  732. }
  733. else //on the other hand, if this client hasn't been asked to reconnect, that's time to quit
  734. {
  735. quit_gracefully(0);
  736. }
  737. }
  738. else
  739. {
  740. if(fds[0].revents & POLLIN) //If we have input available from the IPC hub
  741. {
  742. retval = get_message(hub_fd, &msg); //try and fetch it,,,
  743. if(retval) //if that fails
  744. {
  745. //complain to the user
  746. STATUS_MSG("Error returned by get_message(), connection to IPC hub suspect!\n");
  747. //and ditch the connection
  748. close(hub_fd);
  749. hub_fd = -1;
  750. continue;
  751. }
  752. else //on success... we have a message...
  753. {
  754. int skip;
  755. skip = 0;
  756. if(mode_flags & CLIENT_MODE_MATCH_REGEX) //if we're in -m mode (display only matching)
  757. {
  758. //if the match_expr regex returns REG_NOMATCH on the mailbox name, we want to skip this message
  759. if(regexec(&match_expr, msg.header.mailbox_name, 0, NULL, 0) == REG_NOMATCH)
  760. {
  761. skip = 1;
  762. }
  763. }
  764. if(mode_flags & CLIENT_MODE_EXCLUDE_REGEX) //if we're int -M mode (display only non-matching)
  765. {
  766. //if the reject_expr regex returns 0 (match) on the mailbox name, we want to skip this message
  767. if(regexec(&exclude_expr, msg.header.mailbox_name, 0, NULL, 0) == 0)
  768. {
  769. skip = 1;
  770. }
  771. }
  772. if(!skip)
  773. {
  774. if(mode_flags & CLIENT_MODE_RAWOUT) //if we are in raw output mode,
  775. {
  776. //generate the raw/replayable version of the message (machine readable)
  777. print_replayable_message(&msg);
  778. }
  779. else //otherwise
  780. {
  781. //generate the decoded version of the message (human readable)
  782. print_decoded_message(&msg);
  783. }
  784. }
  785. }
  786. }
  787. if(fds[0].revents & POLLOUT) //if we a timeslot to transmit and we've asked to talk
  788. {
  789. send_message(hub_fd, &outgoing); //that means that we've filled our output message
  790. transmit_pending = 0; //flag this message as non-pending.
  791. last_tx = get_msec(); //and record our last transmit time
  792. }
  793. }
  794. if(nfds > 1)
  795. {
  796. if(fds[1].revents & POLLIN)
  797. {
  798. FILE *f = stdin;
  799. if(mode_flags & CLIENT_MODE_TRANSMIT_FILE)
  800. {
  801. f = infile;
  802. }
  803. //If we both have a file to fetch from, and can fetch something besides EOF
  804. if( (f != NULL) && (fgets(linebuffer, sizeof(linebuffer), f) != NULL) )
  805. {
  806. //If we can parse that into something we understand, flag it for transmission
  807. transmit_pending = (parse_raw_message(linebuffer, &outgoing) == 0);
  808. }
  809. else
  810. {
  811. end_of_input = 1;
  812. }
  813. }
  814. else if(fds[1].revents & (POLLERR | POLLHUP | POLLNVAL))
  815. {
  816. STATUS_MSG("EOF encountered reading from input.\n");
  817. end_of_input = 1;
  818. if(mode_flags & CLIENT_MODE_TRANSMIT_FILE)
  819. {
  820. fclose(infile);
  821. infile = NULL;
  822. continue;
  823. }
  824. }
  825. }
  826. }
  827. }
  828. return 0;
  829. }