rules.c 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <unistd.h>
  6. #include <errno.h>
  7. #include <string.h>
  8. #include <time.h>
  9. #include "tinyscheme1.39/scheme-private.h"
  10. #include "tinyscheme1.39/scheme.h"
  11. #include "../common/common_defs.h"
  12. #include "../common/gpsmath.h"
  13. #include "../commhub/commhub.h"
  14. #include "../commhub/client_utils.h"
  15. #include "passdb_slim.h"
  16. #include "rules.h"
  17. #include "rfid_decoder.h"
  18. #define BUFFERSIZE (1024)
  19. //Communication hub file descriptor...
  20. extern int commhub_fd;
  21. logical_card_id_t apb_cache[APB_CACHE_SIZE] = {0}; //Array to hold the anti-passback cache
  22. time_t apb_time[APB_CACHE_SIZE] = {0}; //Array to hold the accept time of each row of the APB cache
  23. int apb_idx = 0; //Index of the next record to fill in the anti-passback cache
  24. double apb_lat = 0; //Last accept latitude for the anti-passback cache
  25. double apb_lon = 0; //Last accept longitude " " " "
  26. int apb_route = 0; //Last accept route
  27. int apb_trip = 0; // trip
  28. int apb_stop = 0; // stop
  29. //This function clears the entire anti-passback cache
  30. int apb_clear()
  31. {
  32. apb_idx = 0;
  33. return 0;
  34. }
  35. //This function updates the location data for the anti-passback cache
  36. static void update_apb_location()
  37. {
  38. apb_route = stop_stat.route;
  39. apb_trip = stop_stat.trip;
  40. apb_stop = stop_stat.stop;
  41. apb_lat = effective_lat();
  42. apb_lon = effective_lon();
  43. }
  44. // This function examines the state of the gps_stat and stop_stat structures and clears the anti-passback cache if the bus has
  45. //either moved at least APB_DIST_EXPIRE meters since the last accept, or if the route, trip, or stop have changed.
  46. // If the cache is cleared as the result of this function call, the cache state is updated so that it reflects the location
  47. //data as they stood when the flush was completed. If there is nothing in the cache, the location is simply updated.
  48. int apb_flush_if_needed()
  49. {
  50. float distance;
  51. // struct message_record msg;
  52. if(apb_idx == 0) //If the cache is empty, we can skip a lot of this stuff
  53. {
  54. // format_trace_message(&msg,"APBFIN: apb_idx == 0"); //WOOF
  55. // if(commhub_fd) send_message(commhub_fd, &msg);
  56. update_apb_location();
  57. return 0;
  58. }
  59. //See if our route/trip/stop tuple has changed (if so we can skip the expensive GPS math)
  60. if( (apb_route != stop_stat.route) || (apb_trip != stop_stat.trip) || (apb_stop != stop_stat.stop) )
  61. {
  62. // format_trace_message(&msg,"APBFIN: r/t/s != old r/t/s"); //WOOF
  63. // if(commhub_fd) send_message(commhub_fd, &msg);
  64. update_apb_location();
  65. apb_clear();
  66. return 1;
  67. }
  68. else //if not, see if the bus has at least moved
  69. {
  70. distance = GPS_Dist(apb_lat, apb_lon, effective_lat(), effective_lon());
  71. if(distance >= APB_DIST_EXPIRE)
  72. {
  73. // format_trace_message(&msg,"APBFIN: distance > threshold"); //WOOF
  74. // if(commhub_fd) send_message(commhub_fd, &msg);
  75. update_apb_location();
  76. apb_clear();
  77. return 1;
  78. }
  79. }
  80. // format_trace_message(&msg,"APBFIN: drop-through"); //WOOF
  81. // if(commhub_fd) send_message(commhub_fd, &msg);
  82. return 0;
  83. }
  84. //Add a rider to the anti-passback cache...
  85. int apb_add(logical_card_id_t id)
  86. {
  87. //See if we need to auto-flush. If not, update the location marker of the APB cache to be "here"
  88. if( !apb_flush_if_needed() )
  89. {
  90. update_apb_location();
  91. }
  92. //If we have space for more riders...
  93. if(apb_idx < APB_CACHE_SIZE)
  94. {
  95. //set the time, logical_card_id, and increment the index counter
  96. apb_time[apb_idx] = time(NULL);
  97. apb_cache[apb_idx++] = id;
  98. return 0;
  99. }
  100. else
  101. {
  102. return -1;
  103. }
  104. }
  105. // Look up a rider in the anti-passback cache. If the rider is not present, this function returns zero.
  106. //If the rider is present, this function returns the number of seconds since they last rode this bus.
  107. int apb_lookup(logical_card_id_t id)
  108. {
  109. int i, j;
  110. int age;
  111. //Loop through all the entries in our anti-passback cache
  112. for(i=0; i < apb_idx; i++)
  113. {
  114. if(apb_cache[i] == id) //if we've found one that matches this rider's ID number
  115. {
  116. age = time(NULL) - apb_time[i]; //figure out how old it is
  117. if(age <= 0) //If the timestamp is very new
  118. {
  119. return 1; //pretend it's 1 second old (since 0 means "not found")
  120. }
  121. else if(age < APB_ROW_EXPIRE) //If the timestamp is not considered stale (older than APB_ROW_EXPIRE seconds...)
  122. {
  123. return age; //otherwise, return the age
  124. }
  125. else //If the timestamp _is_ stale, we want to purge just this row...
  126. {
  127. //We're deleting a row, so the index shrinks by one
  128. apb_idx--;
  129. //Bump any further entries back one (this is inefficient, but it almost never happens so we can eat it)
  130. for(j = i; j < apb_idx; j++)
  131. {
  132. apb_time[j] = apb_time[j + 1];
  133. apb_cache[j] = apb_cache[j + 1];
  134. }
  135. return 0; //Since the row we found expired, pretend it was never in there to being with
  136. }
  137. }
  138. }
  139. return 0; //return 0 if we didn't find the row...
  140. }
  141. // Takes a token as a first parameter, and a list of prohibited characters as a second.
  142. //returns 1 if the first string contains any character from the second string.
  143. //
  144. // This is used to filter out credit card data from magstripe swipes. This function could later
  145. //become much smarter and use regular expressions or some similar craziness, but for the moment this
  146. //simple solution can handle every case we've seen to date.
  147. static inline int magstripe_scrub_test(char *token, char *triggers)
  148. {
  149. char *p, *q;
  150. if(! (token && triggers) ) //Don't even fuck with NULL pointers...
  151. return 0;
  152. p = token; //Set up a travelling pointer on to our token
  153. while(*p) //While there's any more token
  154. {
  155. q = triggers; //Set up our travelling pointer to our trigger string
  156. while(*q) //if there are any more triggers
  157. {
  158. if(*q == *p) //and we've hit one!
  159. {
  160. return 1; //return SCRUB.
  161. }
  162. else //otherwise,
  163. {
  164. q++; //try the next trigger
  165. }
  166. }
  167. p++; //if this character of the token made it through unscathed, try the next
  168. }
  169. return 0; //by default, return 0 (DON'T scrub)
  170. }
  171. // This function does a "smart" magstripe lookup by looking up the token found
  172. //on track1, then track2, then track3 individually (ignoring tracks 1 and 2 if they
  173. //contain credit-card style field-delimiters), returning the index of the rider
  174. //in the riders database attached to ctx, or -1 if not found.
  175. // If the return is -1 (not found) final_token is filled with the whole list of
  176. //tracks that were attempted (with credit-card looking tracks blanked out).
  177. // If the return is >= 0 (record found) final_token is filled with the token that
  178. //was used to generate the match.
  179. //int smart_find_mag(passdb_context *ctx, char *token, char *final_token)
  180. int smart_find_mag(passdb_slim_context *ctx, char *token, char *final_token)
  181. {
  182. char t1[BUFFERSIZE] = {0};
  183. char t2[BUFFERSIZE] = {0};
  184. char t3[BUFFERSIZE] = {0};
  185. enum
  186. {
  187. GOT_TRACK_1 = 1,
  188. GOT_TRACK_2 = 2,
  189. GOT_TRACK_3 = 4
  190. };
  191. int track_map = 0;
  192. char *trav;
  193. int retval;
  194. int i, idx;
  195. trav = token;
  196. idx = i = 0;
  197. while(*trav)
  198. {
  199. switch(*trav)
  200. {
  201. case '%': //begin track 1
  202. idx = 1;
  203. track_map |= GOT_TRACK_1;
  204. i = 0;
  205. trav++;
  206. break;
  207. case ';': //begin track 2
  208. // FIXME: Kludge. Our readers report a 2nd track2 for track2.
  209. //the following if/else block handles that, so if there are two track2's,
  210. //it silently makes the second one into a track3.
  211. //
  212. if( !(track_map & GOT_TRACK_2) ) //if we have no track2
  213. {
  214. idx = 2; //this track2 is the REAL track2
  215. track_map |= GOT_TRACK_2;
  216. }
  217. else //otherwise, if we have already seen a track2
  218. {
  219. idx = 3; //pretend the 2nd track2 is really a track3
  220. track_map |= GOT_TRACK_3;
  221. }
  222. i = 0;
  223. trav++;
  224. break;
  225. case '+': //begin track 3
  226. idx = 3;
  227. track_map |= GOT_TRACK_3;
  228. i = 0;
  229. trav++;
  230. break;
  231. case '?': //end of track
  232. switch(idx)
  233. {
  234. case 1:
  235. t1[i++]='\0';
  236. break;
  237. case 2:
  238. t2[i++]='\0';
  239. break;
  240. case 3:
  241. t3[i++]='\0';
  242. break;
  243. default:
  244. break;
  245. }
  246. idx = 0;
  247. trav++;
  248. break;
  249. default:
  250. switch(idx)
  251. {
  252. case 1:
  253. t1[i++]=*trav;
  254. break;
  255. case 2:
  256. t2[i++]=*trav;
  257. break;
  258. case 3:
  259. t3[i++]=*trav;
  260. break;
  261. default:
  262. break;
  263. }
  264. trav++;
  265. break;
  266. }
  267. }
  268. #ifdef SCRUB_MAGSTRIPE_TRACK1
  269. //If there are any credit-card style field delimiters on track 1, null it out so we don't save any credit card #s
  270. if( magstripe_scrub_test(t1, T1_SCRUB_LIST) )
  271. {
  272. t1[0] = '\0';
  273. }
  274. #endif
  275. #ifdef SCRUB_MAGSTRIPE_TRACK2
  276. //If there are any credit-card style field delimiters on track 2, null it out so we don't save any credit card #s
  277. if( magstripe_scrub_test(t2, T2_SCRUB_LIST) )
  278. {
  279. t2[0] = '\0';
  280. }
  281. #endif
  282. #ifdef SCRUB_MAGSTRIPE_TRACK3
  283. //If there are any credit-card style field delimiters on track 3, null it out so we don't save any credit card #s
  284. if( magstripe_scrub_test(t3, T3_SCRUB_LIST) )
  285. {
  286. t3[0] = '\0';
  287. }
  288. #endif
  289. //------------------------- If we have a non-blank value for Track 1
  290. if(t1[0] != '\0')
  291. {
  292. sprintf(final_token,"1:%s", t1); //Create a Track N hash key using the value on Track N
  293. //"N:trackNdata"
  294. retval = find_mag_in_hash(ctx, final_token); //look for a match
  295. if(retval >= 0) //if we get it, use it.
  296. {
  297. return retval;
  298. }
  299. }
  300. //------------------------- If we have a non-blank value for Track 1
  301. if(t2[0] != '\0')
  302. {
  303. sprintf(final_token,"2:%s", t2); //Create a Track N hash key using the value on Track N
  304. //"N:trackNdata"
  305. retval = find_mag_in_hash(ctx, final_token); //look for a match
  306. if(retval >= 0) //if we get it, use it.
  307. {
  308. return retval;
  309. }
  310. }
  311. //------------------------- If we have a non-blank value for Track 3
  312. if(t3[0] != '\0')
  313. {
  314. sprintf(final_token,"3:%s", t3); //Create a Track N hash key using the value on Track N
  315. //"N:trackNdata"
  316. retval = find_mag_in_hash(ctx, final_token); //look for a match
  317. if(retval >= 0) //if we get it, use it.
  318. {
  319. return retval;
  320. }
  321. }
  322. #ifdef ALLOW_WILDCARD_MAGSTRIPE_TRACK
  323. //In each of these cases, we do a hash lookup for each non-blank track, but we create special hash keys
  324. //which look up rows where the server has specified that ANY track will match if the data string matches
  325. //These keys are in the form of "0:trackNdata", and after they match, they get rewritten so they appear
  326. //in the credential data field of the billing record with a leading zero followed by the track that
  327. //actually matched the wildcard. For example "02:track2data" would mean that the data on Track 2 was
  328. //the first to match an all-tracks-wild card record.
  329. //This is useful for cards which may have dodgey enough encoding that (and believe it or not, we've seen this)
  330. //the reader reports the same card on successive swipes as having some given data on track 2 or track 3. This
  331. //is an artifact of the fact that the readers in the current system are buggy in that they report track 3 as a second
  332. //track 2, so on a card that counts on using track 3, but has a not-reliably-readable track 2, the actual value on
  333. //track 3 will get reported as a track 2 value. This is immensely silly, but in the infinite wisdom of the firmware
  334. //folks at UIC America, for compatibility with older systems, track 3 really *ought* to be a second track 2. *sigh*
  335. if(t1[0] != '\0') //if a track is non-blank
  336. {
  337. sprintf(final_token,"0:%s", t1); //look it up with a track number of 0 (ANY)
  338. retval = find_mag_in_hash(ctx, final_token);
  339. if(retval >= 0) //if we found something...
  340. {
  341. sprintf(final_token,"01:%s", t1); //rewrite the credential to report wildcard match on THIS track
  342. return retval; //and use the value
  343. }
  344. }
  345. if(t2[0] != '\0')
  346. {
  347. sprintf(final_token,"0:%s", t2);
  348. retval = find_mag_in_hash(ctx, final_token);
  349. if(retval >= 0)
  350. {
  351. sprintf(final_token,"02:%s", t2);
  352. return retval;
  353. }
  354. }
  355. if(t3[0] != '\0')
  356. {
  357. sprintf(final_token,"0:%s", t3);
  358. retval = find_mag_in_hash(ctx, final_token);
  359. if(retval >= 0)
  360. {
  361. sprintf(final_token,"03:%s", t3);
  362. return retval;
  363. }
  364. }
  365. #endif
  366. sprintf(final_token,"1:%s/2:%s/3:%s", t1, t2, t3);
  367. return -1;
  368. }
  369. // This function does a "smart" RFID lookup by translating the raw RFID format (NN|XXXX...)
  370. //where NN is the number of bits in hex, and XXXX... is the bitstring in hex (right justified
  371. //to the nearest nibble boundary) and processing it based on the number of bits and a list of
  372. //known formats until it either matches a known format or is determined not to match any of them.
  373. // If a match is found, the return value will be >= 0 and indicate the index of the rider in
  374. //the database that ctx is attached to. If no match is found, -1 is returned. In either case
  375. //final_token is filled with the string representing the decoded decimal RFID value "nbits:site:id"
  376. //or if the token was not in a known format, "nbits:rawval?".
  377. //int smart_find_rf(passdb_context *ctx, char *token, char *final_token)
  378. int smart_find_rf(passdb_slim_context *ctx, char *token, char *final_token)
  379. {
  380. unsigned long long site;
  381. unsigned long long id;
  382. unsigned int nbits;
  383. int retval;
  384. retval = decode_rfid_string(token, &site, &id, &nbits);
  385. if(retval < 0)
  386. {
  387. sprintf(final_token,"%s", token);
  388. return -1;
  389. }
  390. else
  391. {
  392. sprintf(final_token, "%d:%lld:%lld", nbits, site, id);
  393. return find_rf_in_hash(ctx, final_token);
  394. }
  395. }
  396. //-------------------------------------------------------------- TEMPORARY OVERLAY DATA -----------------------------------------------------
  397. typedef struct rule_param_lookaside_struct
  398. {
  399. seq_t seq;
  400. logical_card_id_t id;
  401. char rule_param[PARAM_LEN];
  402. struct rule_param_lookaside_struct *next;
  403. } rule_param_lookaside;
  404. static rule_param_lookaside *local_param = NULL;
  405. #define FIND_LOOKASIDE_GUTS(p, q, id) {q = NULL; p = local_param; while(p) { if(p->id == (id)) break; q = p; p = p->next; }}
  406. int set_lookaside(seq_t seq, logical_card_id_t id, char *param)
  407. {
  408. rule_param_lookaside *p, *q;
  409. FIND_LOOKASIDE_GUTS(p, q, id);
  410. if(!p)
  411. {
  412. p = (rule_param_lookaside *)malloc(sizeof(rule_param_lookaside));
  413. if(!p)
  414. {
  415. fprintf(stderr, "Cannot allocate memory for lookaside buffer!\n");
  416. fflush(stderr);
  417. return -1;
  418. }
  419. p->next = NULL;
  420. p->seq = seq;
  421. p->id = id;
  422. if(q)
  423. {
  424. q->next = p;
  425. }
  426. else
  427. {
  428. local_param = p;
  429. }
  430. }
  431. strncpy(p->rule_param, param, PARAM_LEN - 1);
  432. p->rule_param[PARAM_LEN - 1] = '\0';
  433. return 0;
  434. }
  435. int check_lookaside(seq_t delete_if_older, logical_card_id_t id, char *param)
  436. {
  437. rule_param_lookaside *p, *q;
  438. FIND_LOOKASIDE_GUTS(p, q, id);
  439. if(p)
  440. {
  441. if(p->seq < delete_if_older)
  442. {
  443. if(q)
  444. {
  445. q->next = p->next;
  446. }
  447. else
  448. {
  449. local_param = p->next;
  450. }
  451. free(p);
  452. return 0;
  453. }
  454. strncpy(param, p->rule_param, PARAM_LEN - 1);
  455. param[PARAM_LEN - 1] = '\0';
  456. return 1;
  457. }
  458. else
  459. {
  460. return 0;
  461. }
  462. }
  463. //-------------------------------------------------------------------------------------------------------------------------------------------
  464. //--------------------------------------BELOW HERE, WE HANDLE RULE PROCESSING LOGIC BY FEEDING THE SCHEME INTERPRETER RULE CALLS AND SEEING
  465. //--------------------------------------WHICH CALLBACK (ACCEPT/REJECT) IT CALLS. "Here Be Dragons..."
  466. //-------------------------------------------------------------------------------------------------------------------------------------------
  467. /*
  468. A C programmer's crash course in the main working data structure of the scheme programmer...
  469. The base data structure is a pair of pointers. While they can be arranged in all sorts of ways (lists, trees, etc...) the list is the base form.
  470. The first pointer is called the CAR pointer (Content Address Register) and the second the CDR (Content Data Register). This has no bearing on how
  471. they're used, but that's what they're called. By convention in a list, you have your CAR pointer pointing to a list item (a garbage collected cell of
  472. managed memory), and your CDR pointer pointing to the next pair in the list. In the case of trees and nested lists you'll have things like the second
  473. example below where the CAR pointer points at a pair rather than a value, in which case we have a sub-list.
  474. the operations that manipulate lists at their most basic are these three, cons, car, and cdr. cons constructs a list, so the following:
  475. (cons 1 (cons 2 (cons 3 '()))) will return (1 2 3)
  476. car and cdr get the pointers contained in the CAR and CDR of a pair, so (car '(1 2 3)) will return 1, wheras (cdr '(1 2 3)) will return (2 3).
  477. (cdr '(1)) will return NIL.
  478. '(1 2 3.1 4) works out to:
  479. car cdr
  480. ___________ ___________ ___________ ___________
  481. | | | | | | | | | | | |
  482. | * | *--|--->| * | *--|--->| * | *--|--->| * | NIL |
  483. |__|__|_____| |__|__|_____| |__|__|_____| |__|__|_____|
  484. | | | |
  485. v v v v
  486. 1 2 3.1 4
  487. '(a b (c d) e) works out to:
  488. car cdr
  489. ___________ ___________ ___________ ___________
  490. | | | | | | | | | | | |
  491. | * | *--|--->| * | *--|--->| * | *--|--->| * | NIL |
  492. |__|__|_____| |__|__|_____| |__|__|_____| |__|__|_____|
  493. | | | |
  494. v v | v
  495. |
  496. 'a' 'b' | 'e'
  497. |
  498. V
  499. ___________ ___________
  500. | | | | | |
  501. | * | *--|--->| * | NIL |
  502. |__|__|_____| |__|__|_____|
  503. | |
  504. v v
  505. 'c' 'd'
  506. The file scheme.h defines the interface we use below to bind C functions to Scheme symbols. Most of the work in the below functions is in decoding the
  507. above data structures into something we can use in C. This is a cumbersome process in C, but not unmanagable. The arguments of any our our wrapper
  508. functions are two items, first a pointer to the interpreter context in which the function has been called, and second a pointer to a list of arguments.
  509. this pointer is in the form of the first list above, so the first CAR pointer points at the first argument, and the CDR points at the rest of the list.
  510. */
  511. //Message structures to fill with messages resuling from rule processing
  512. struct message_record bill_msg;
  513. struct message_record user_msg;
  514. struct message_record rider_msg;
  515. //List of feedback flags which will be set during rule processing
  516. static int scheme_error = 0;
  517. static int accept_flag = 0;
  518. static int add_to_passback_flag = 0;
  519. //List of state variables which need to be bound by the environment before calling a rule
  520. static char *rname = NULL;
  521. static char *rparam = NULL;
  522. static char *cred = NULL;
  523. static logical_card_id_t logical_card_id = 0;
  524. static seq_t seq_num;
  525. pointer debug_print_nonl(scheme *sc, pointer args) //this is used to print scheme data structures out to stdout, it's recursive and hairy, but for
  526. { //debugging it's worth its weight in gold.
  527. pointer car,trav;
  528. if(args != sc->NIL) //if we have arguments to process
  529. {
  530. trav=args;
  531. while(trav != sc->NIL) //walk the arguments list
  532. {
  533. if(trav != args)
  534. printf(" "); //if we're not on our first argument, put a space between
  535. car=pair_car(trav); //get the CAR pointer
  536. if(is_pair(car)) //if it points to a pair, we're looking at a sublist
  537. {
  538. printf("("); //print the parens
  539. debug_print_nonl(sc,car); //and recurse
  540. printf(")"); //close 'em
  541. }
  542. else //if it's not a pair, find out what sort of a value it is
  543. {
  544. if(is_string(car)) //try string
  545. {
  546. printf("\"%s\"",string_value(car));
  547. }
  548. else if(is_symbol(car)) //then symbol
  549. {
  550. printf("'%s",symname(car));
  551. }
  552. else if(is_integer(car)) //int
  553. {
  554. printf("%ld",ivalue(car));
  555. }
  556. else if(is_real(car)) //or real
  557. {
  558. printf("%f",rvalue(car));
  559. }
  560. else
  561. {
  562. printf("?");
  563. }
  564. }
  565. trav=pair_cdr(trav); //advance our travelling pointer
  566. }
  567. }
  568. return sc->NIL;
  569. }
  570. pointer debug_print(scheme *sc, pointer args)
  571. {
  572. debug_print_nonl(sc,args);
  573. printf("\n");
  574. return sc->NIL;
  575. }
  576. pointer tell_rider(scheme *sc, pointer args)
  577. {
  578. format_piu_message(&rider_msg, 1, PIU_PRIORITY_FARE, PASSENGER_MESSAGE_DURATION, "%s", string_value(pair_car(args)));
  579. if(commhub_fd >= 0)
  580. {
  581. send_message(commhub_fd, &rider_msg);
  582. }
  583. return sc->NIL;
  584. }
  585. pointer accept_fare_no_passback(scheme *sc, pointer args) //this function provides a scheme interface to accept a fare
  586. {
  587. pointer trav,car;
  588. int idx = 0;
  589. char reason[BILLING_REASON_LEN] = {0};
  590. int cash_val = 0;
  591. int user_msg_good = 0;
  592. trav = args;
  593. while(trav != sc->NIL) //while we have more data
  594. {
  595. car=pair_car(trav); //the car pointer should contain the parameter
  596. trav=pair_cdr(trav); //the cdr pointer points at the rest of the list
  597. switch(idx)
  598. {
  599. case 0: //First parameter is the accept reason
  600. strncpy(reason, string_value(car), BILLING_REASON_LEN);
  601. reason[BILLING_REASON_LEN - 1] = '\0';
  602. break;
  603. case 1: //Second parameter is the display reason
  604. format_driver_message(&user_msg, LOGLEVEL_ACCEPT, "%s %s", ACCEPT_STR, string_value(car));
  605. user_msg_good = 1;
  606. break;
  607. case 2: //Third parameter is the cash value if any...
  608. cash_val = ivalue(car);
  609. break;
  610. default:
  611. break;
  612. }
  613. idx++;
  614. if(idx > 2)
  615. break;
  616. }
  617. format_billing_message(&bill_msg, ACCEPT_STR, rname, rparam, reason, cred, logical_card_id, cash_val);
  618. if(commhub_fd >= 0)
  619. {
  620. send_message(commhub_fd, &bill_msg);
  621. if(user_msg_good)
  622. {
  623. send_message(commhub_fd, &user_msg);
  624. }
  625. accept_flag = 1;
  626. add_to_passback_flag = 0;
  627. }
  628. else
  629. {
  630. //TODO: What the hell do we do now?
  631. }
  632. return sc->NIL;
  633. }
  634. pointer drop_vault(scheme *sc, pointer args)
  635. {
  636. prepare_message(&user_msg, MAILBOX_VAULT_DROP, "", 0);
  637. if(commhub_fd >= 0)
  638. {
  639. send_message(commhub_fd, &user_msg);
  640. }
  641. return sc->NIL;
  642. }
  643. pointer accept_fare(scheme *sc, pointer args) //this function provides a scheme interface to accept a fare
  644. {
  645. pointer trav,car;
  646. int idx = 0;
  647. char reason[BILLING_REASON_LEN] = {0};
  648. int cash_val = 0;
  649. int user_msg_good = 0;
  650. trav = args;
  651. while(trav != sc->NIL) //while we have more data
  652. {
  653. car = pair_car(trav); //the car pointer should contain the parameter
  654. trav = pair_cdr(trav); //the cdr pointer points at the rest of the list
  655. switch(idx)
  656. {
  657. case 0: //First parameter is the accept reason
  658. strncpy(reason, string_value(car), BILLING_REASON_LEN);
  659. reason[BILLING_REASON_LEN - 1] = '\0';
  660. break;
  661. case 1: //Second parameter is the display reason
  662. format_driver_message(&user_msg, LOGLEVEL_ACCEPT, "%s %s", ACCEPT_STR, string_value(car));
  663. user_msg_good = 1;
  664. break;
  665. case 2: //Third parameter is the cash value if any...
  666. cash_val = ivalue(car);
  667. break;
  668. default:
  669. break;
  670. }
  671. idx++;
  672. if(idx > 2)
  673. break;
  674. }
  675. format_billing_message(&bill_msg, ACCEPT_STR, rname, rparam, reason, cred, logical_card_id, cash_val);
  676. if(commhub_fd >= 0)
  677. {
  678. send_message(commhub_fd, &bill_msg);
  679. if(user_msg_good)
  680. {
  681. send_message(commhub_fd, &user_msg);
  682. }
  683. accept_flag = 1;
  684. add_to_passback_flag = 1;
  685. }
  686. else
  687. {
  688. //TODO: What the hell do we do now?
  689. }
  690. return sc->NIL;
  691. }
  692. pointer reject_fare(scheme *sc, pointer args) //this function provides a scheme interface to reject a fare
  693. {
  694. pointer trav,car;
  695. int idx = 0;
  696. char reason[BILLING_REASON_LEN] = {0};
  697. int cash_val = 0;
  698. int user_msg_good = 0;
  699. trav = args;
  700. while(trav != sc->NIL) //while we have more data
  701. {
  702. car=pair_car(trav); //the car pointer should contain the parameter
  703. trav=pair_cdr(trav); //the cdr pointer points at the rest of the list
  704. switch(idx)
  705. {
  706. case 0: //First parameter is the accept reason
  707. strncpy(reason, string_value(car), BILLING_REASON_LEN);
  708. reason[BILLING_REASON_LEN - 1] = '\0';
  709. break;
  710. case 1: //Second parameter is the display reason
  711. format_driver_message(&user_msg, LOGLEVEL_REJECT, "%s %s", REJECT_STR, string_value(car));
  712. user_msg_good = 1;
  713. break;
  714. default:
  715. break;
  716. }
  717. idx++;
  718. if(idx > 1)
  719. break;
  720. }
  721. format_billing_message(&bill_msg, REJECT_STR, rname, rparam, reason, cred, logical_card_id, cash_val);
  722. if(commhub_fd >= 0)
  723. {
  724. send_message(commhub_fd, &bill_msg);
  725. if(user_msg_good)
  726. {
  727. send_message(commhub_fd, &user_msg);
  728. }
  729. accept_flag = 0;
  730. add_to_passback_flag = 0;
  731. }
  732. else
  733. {
  734. //TODO: What the hell do we do now?
  735. }
  736. return sc->NIL;
  737. }
  738. pointer handle_rule_error(scheme *sc, pointer args) //this error handler allows any errors or exceptions like dereferencing NIL's or
  739. { //syntax errors in the scheme rules to be reported up to the driver (and also keep them from
  740. char err[1024]; //causing all hell to break loose). Think of it as a catch block.
  741. struct message_record logmsg;
  742. pointer car = pair_car(args); //get the pointer to our first argument's value (the error message)
  743. pointer cadr = pair_car(pair_cdr(args)); //get the pointer to our next argument (the optional identifier)
  744. scheme_error=1; //set the error flag
  745. if(is_symbol(cadr))
  746. {
  747. sprintf(err,"\"%s\" %s", string_value(car), symname(cadr)); //spit that argument out on the UI
  748. }
  749. else if(is_string(cadr))
  750. {
  751. sprintf(err,"\"%s\" \"%s\"", string_value(car), string_value(cadr)); //spit that argument out on the UI
  752. }
  753. else
  754. {
  755. sprintf(err,"\"%s\" <unknown>", string_value(car)); //spit that argument out on the UI
  756. }
  757. //Construct a diagnostic log message for this error
  758. format_log_message(&logmsg, LOGLEVEL_ERROR, "Scheme Error: %s", err);
  759. if(commhub_fd >= 0) //If we have a connection to the commhub
  760. {
  761. send_message(commhub_fd, &logmsg); //send this driver message to the log
  762. }
  763. return args; // Although it's not documented, if the error handler does not return its arguments the
  764. //scheme interpreter throws a SEGV so don't mess with this return value unless you're very brave
  765. //or have some profound insight into the inner workings of the scheme interpreter that you are
  766. //pretty sure will never go obsolete with version upgrades...
  767. }
  768. pointer get_stopnum(scheme *sc, pointer args)
  769. {
  770. return mk_integer(sc,stop_stat.stop);
  771. }
  772. pointer get_routenum(scheme *sc, pointer args) //returns the human-friendly route 30, 92, etc...
  773. {
  774. return mk_integer(sc,(int)(stop_stat.route / 100));
  775. }
  776. pointer get_longroutenum(scheme *sc, pointer args) //returns the "internal" route number 3000 = 30 outbound, 3010 = 30 inbound, etc...
  777. {
  778. return mk_integer(sc,stop_stat.route);
  779. }
  780. pointer get_tripnum(scheme *sc, pointer args)
  781. {
  782. return mk_integer(sc,stop_stat.trip);
  783. }
  784. pointer get_time(scheme *sc, pointer args) //this function returns the time in the form (hour minute) example (13 5) 1:05pm
  785. {
  786. time_t t;
  787. struct tm comp;
  788. t=time(NULL);
  789. localtime_r(&t,&comp);
  790. return cons(sc,mk_integer(sc,comp.tm_hour),cons(sc,mk_integer(sc,comp.tm_min),sc->NIL));
  791. }
  792. pointer get_date(scheme *sc, pointer args) //this function returns the date in the form (year month day) example (2007 5 17) May 17th 2007
  793. {
  794. time_t t;
  795. struct tm comp;
  796. t=time(NULL);
  797. localtime_r(&t,&comp);
  798. return cons(sc,mk_integer(sc,comp.tm_year + 1900),cons(sc,mk_integer(sc,comp.tm_mon + 1),cons(sc,mk_integer(sc,comp.tm_mday),sc->NIL)));
  799. }
  800. pointer get_day_of_week(scheme *sc, pointer args) //this function returns a number 1-7 inclusive, 1=Sunday 7=Saturday
  801. {
  802. time_t t;
  803. struct tm comp;
  804. t=time(NULL);
  805. localtime_r(&t,&comp);
  806. return mk_integer(sc,comp.tm_wday + 1);
  807. }
  808. pointer get_gps(scheme *sc, pointer args) //this function returns the current GPS coordinates (latitude longitude) where S < 0 > N and W < 0 > E
  809. {
  810. if(gps_stat.gps_good)
  811. {
  812. return cons(sc,mk_real(sc,gps_stat.lat),cons(sc,mk_real(sc,gps_stat.lon),sc->NIL));
  813. }
  814. else
  815. {
  816. return cons(sc,mk_real(sc,stop_stat.lat),cons(sc,mk_real(sc,stop_stat.lon),sc->NIL));
  817. }
  818. }
  819. pointer get_rulename(scheme *sc, pointer args) //returns the name of the current rule (useful for billing logs)
  820. {
  821. if(rname)
  822. {
  823. return mk_string(sc,rname);
  824. }
  825. else
  826. {
  827. return sc->NIL;
  828. }
  829. }
  830. pointer get_ruleparam(scheme *sc, pointer args) //returns the parameter of the current rule
  831. {
  832. if(rparam)
  833. {
  834. return mk_string(sc,rparam);
  835. }
  836. else
  837. {
  838. return sc->NIL;
  839. }
  840. }
  841. pointer scheme_set_lookaside(scheme *sc, pointer args)
  842. {
  843. char buffer[PARAM_LEN] ={0};
  844. pointer arg;
  845. if(args == sc->NIL)
  846. {
  847. return sc->NIL;
  848. }
  849. arg = pair_car(args);
  850. if(arg != sc->NIL)
  851. {
  852. if(is_string(arg)) //try string
  853. {
  854. set_lookaside(seq_num, logical_card_id, string_value(arg));
  855. return sc->NIL;
  856. }
  857. else if(is_integer(arg)) //int
  858. {
  859. sprintf(buffer, "%ld", ivalue(arg));
  860. set_lookaside(seq_num, logical_card_id, buffer);
  861. return sc->NIL;
  862. }
  863. else if(is_real(arg)) //or real
  864. {
  865. sprintf(buffer, "%f", rvalue(arg));
  866. set_lookaside(seq_num, logical_card_id, buffer);
  867. return sc->NIL;
  868. }
  869. }
  870. return sc->NIL;
  871. }
  872. pointer scheme_get_lookaside(scheme *sc, pointer args)
  873. {
  874. char buffer[PARAM_LEN] = {0};
  875. if(check_lookaside(seq_num, logical_card_id, buffer))
  876. {
  877. return mk_string(sc, buffer);
  878. }
  879. else
  880. {
  881. return sc->NIL;
  882. }
  883. }
  884. pointer get_lookaside_param(scheme *sc, pointer args)
  885. {
  886. pointer foo;
  887. foo = scheme_get_lookaside(sc, args);
  888. if(foo != sc->NIL)
  889. {
  890. // printf("Using estimate!\n");
  891. return foo;
  892. }
  893. else
  894. {
  895. // printf("Using real data!\n");
  896. return get_ruleparam(sc, args);
  897. }
  898. }
  899. pointer scheme_strtok(scheme *sc, pointer args)
  900. {
  901. pointer p, q, ret;
  902. char *tmp = NULL;
  903. char *delim = NULL;
  904. char *token = NULL;
  905. char chop_buffer[LINE_BUFFER_SIZE] = {0};
  906. p = q = ret = sc->NIL;
  907. if(!is_string(pair_car(args)))
  908. {
  909. return sc->NIL;
  910. }
  911. if(!is_string(pair_car(pair_cdr(args))))
  912. {
  913. return sc->NIL;
  914. }
  915. strncpy(chop_buffer, string_value(pair_car(args)), LINE_BUFFER_SIZE);
  916. chop_buffer[LINE_BUFFER_SIZE - 1] = '\0';
  917. delim = string_value(pair_car(pair_cdr(args)));
  918. token = strtok_r(chop_buffer, delim, &tmp);
  919. while(token)
  920. {
  921. if(q == sc->NIL)
  922. {
  923. q = ret = cons(sc, mk_string(sc, token), sc->NIL);
  924. }
  925. else
  926. {
  927. p = cons(sc, mk_string(sc, token), sc->NIL);
  928. set_cdr(q, p);
  929. q = p;
  930. }
  931. token = strtok_r(NULL, delim, &tmp);
  932. }
  933. return ret;
  934. }
  935. pointer gps_distance(scheme *sc, pointer args) //this function takes two sets of coordinates (as returned by get_gps) and returns a distance in meters
  936. {
  937. double la1,lo1,la2,lo2;
  938. double dist;
  939. pointer p1,p2;
  940. if(!is_pair(args))
  941. return sc->NIL;
  942. p1=pair_car(args);
  943. if(!is_pair(pair_cdr(args)))
  944. return sc->NIL;
  945. p2=pair_car(pair_cdr(args));
  946. if(!is_real(pair_car(p1)))
  947. return sc->NIL;
  948. if(!is_real(pair_car(pair_cdr(p1))))
  949. return sc->NIL;
  950. if(!is_real(pair_car(p2)))
  951. return sc->NIL;
  952. if(!is_real(pair_car(pair_cdr(p2))))
  953. return sc->NIL;
  954. la1=rvalue(pair_car(p1));
  955. lo1=rvalue(pair_car(pair_cdr(p1)));
  956. la2=rvalue(pair_car(p2));
  957. lo2=rvalue(pair_car(pair_cdr(p2)));
  958. dist=GPS_Dist(la1,lo1,la2,lo2);
  959. // printf("Distace: (%f %f) -> (%f %f) = %f m\n",la1,lo1,la2,lo2,dist);
  960. return mk_real(sc,dist);
  961. }
  962. scheme scm={0};
  963. int rules_loaded_flag = 0;
  964. #define SCHEME_INTERNAL_ERROR(x) ((x)->retcode != 0)
  965. int rules_loaded()
  966. {
  967. return rules_loaded_flag;
  968. }
  969. int unload_rules()
  970. {
  971. if(rules_loaded_flag)
  972. {
  973. scheme_deinit(&scm);
  974. }
  975. rules_loaded_flag = 0;
  976. return 0;
  977. }
  978. int load_rules(char *filename)
  979. {
  980. FILE *f;
  981. struct message_record logmsg;
  982. scheme_init(&scm);
  983. // This is of vital importance... Failure to do so will cause error messages that
  984. //don't get caught by the *error-hook* exception handler due to a bug in tinyscheme
  985. //to try and output to a zero-length string output port which will then cause a SIGSEGV
  986. //and undignified exit-and-respawn of passdb. When this happens we loose the error message
  987. //itself, so the rule error becomes a hell of a lot harder to debug.
  988. scheme_set_output_port_file(&scm, stdout);
  989. f=fopen(SCHEME_INIT_FILE,"rb"); //digest the standard library
  990. if(!f)
  991. {
  992. //complain violently if it fails to exist
  993. format_log_message(&logmsg, LOGLEVEL_ERROR, "File error loading %s library.", SCHEME_INIT_FILE);
  994. if(commhub_fd >= 0) //If we have a connection to the commhub
  995. {
  996. send_message(commhub_fd, &logmsg); //send this driver message to the log
  997. }
  998. return -1;
  999. }
  1000. scheme_error=0; //clear our error callback flag
  1001. scheme_load_file(&scm,f); //try and load it
  1002. fclose(f); //close the file
  1003. if( SCHEME_INTERNAL_ERROR(&scm) )
  1004. {
  1005. scheme_deinit(&scm);
  1006. //complain violently if it fails to parse
  1007. format_log_message(&logmsg, LOGLEVEL_ERROR, "Scheme error loading %s library.", SCHEME_INIT_FILE);
  1008. if(commhub_fd >= 0) //If we have a connection to the commhub
  1009. {
  1010. send_message(commhub_fd, &logmsg); //send this driver message to the log
  1011. }
  1012. return -1;
  1013. }
  1014. //set up our error handler so we can detect bogus or broken rule loads
  1015. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"debug_print"),mk_foreign_func(&scm, debug_print));
  1016. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"handle_rule_error"),mk_foreign_func(&scm, handle_rule_error));
  1017. scheme_load_string(&scm,"(define *error-hook* handle_rule_error)");
  1018. //and initialize all of the symbols for our wrapper functions
  1019. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"stopnum"),mk_foreign_func(&scm, get_stopnum));
  1020. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"routenum"),mk_foreign_func(&scm, get_routenum));
  1021. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"longroutenum"),mk_foreign_func(&scm, get_longroutenum));
  1022. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"tripnum"),mk_foreign_func(&scm, get_tripnum));
  1023. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"gps"),mk_foreign_func(&scm, get_gps));
  1024. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"rule"),mk_foreign_func(&scm, get_rulename));
  1025. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"param"),mk_foreign_func(&scm, get_ruleparam));
  1026. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"get_lookaside"),mk_foreign_func(&scm, scheme_get_lookaside));
  1027. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"set_lookaside"),mk_foreign_func(&scm, scheme_set_lookaside));
  1028. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"lookaside_param"),mk_foreign_func(&scm, get_lookaside_param));
  1029. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"gettime"),mk_foreign_func(&scm, get_time));
  1030. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"day_of_week"),mk_foreign_func(&scm, get_day_of_week));
  1031. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"getdate"),mk_foreign_func(&scm, get_date));
  1032. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"gps_distance"),mk_foreign_func(&scm, gps_distance));
  1033. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"accept_fare"),mk_foreign_func(&scm, accept_fare));
  1034. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"accept_fare_no_passback"),mk_foreign_func(&scm, accept_fare_no_passback));
  1035. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"reject_fare"),mk_foreign_func(&scm, reject_fare));
  1036. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"tell_rider"),mk_foreign_func(&scm, tell_rider));
  1037. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"drop_vault"),mk_foreign_func(&scm, drop_vault));
  1038. scheme_define(&scm,scm.global_env,mk_symbol(&scm,"strtok"),mk_foreign_func(&scm, scheme_strtok));
  1039. f=fopen(filename,"rb"); //open the rules file
  1040. if(!f)
  1041. {
  1042. scheme_deinit(&scm);
  1043. //complain violently if it fails to exist
  1044. format_log_message(&logmsg, LOGLEVEL_ERROR, "File error loading rules from %s", filename);
  1045. if(commhub_fd >= 0) //If we have a connection to the commhub
  1046. {
  1047. send_message(commhub_fd, &logmsg); //send this driver message to the log
  1048. }
  1049. return -1;
  1050. }
  1051. scheme_error=0;
  1052. scheme_load_file(&scm,f);
  1053. if( scheme_error || SCHEME_INTERNAL_ERROR(&scm) )
  1054. {
  1055. scheme_deinit(&scm);
  1056. //complain violently if it fails to parse
  1057. format_log_message(&logmsg, LOGLEVEL_ERROR, "Scheme error loading rules from %s", filename);
  1058. if(commhub_fd >= 0) //If we have a connection to the commhub
  1059. {
  1060. send_message(commhub_fd, &logmsg); //send this driver message to the log
  1061. }
  1062. return -1;
  1063. }
  1064. fclose(f);
  1065. rules_loaded_flag = 1;
  1066. return 0;
  1067. }
  1068. #ifdef TRACE_NEXT_CALL_OF_FAILED_RULE
  1069. char trace_this_rule[RULENAME_LEN + 1] = {0};
  1070. #endif
  1071. static inline void do_low_level_rulecall(char *rulename)
  1072. {
  1073. char rulefunc[RULENAME_LEN * 2] = {0};
  1074. #ifdef TRACE_NEXT_CALL_OF_FAILED_RULE
  1075. if(strcmp(rulename, trace_this_rule)) //if this IS NOT our trace target, process it normally...
  1076. {
  1077. #endif
  1078. //Generate a function call to the full procedure name ("(rule_[whatever_rname_is])");
  1079. sprintf(rulefunc, "(rule_%s)", rulename);
  1080. //Go and load the string containing the function call in the current context as if it were input from the console
  1081. scheme_load_string(&scm, rulefunc);
  1082. #ifdef TRACE_NEXT_CALL_OF_FAILED_RULE
  1083. if(scheme_error || SCHEME_INTERNAL_ERROR(&scm) ) //if we are in a trace mode, remember that this call incurred errors
  1084. {
  1085. strncpy(trace_this_rule, rulename, RULENAME_LEN); //and save its rule name, so next time we go
  1086. //to execute it, we'll execute it with trace
  1087. //enabled...
  1088. }
  1089. }
  1090. else //otherwise, if this IS our trace target, set up tracing and process it with tracing on...
  1091. {
  1092. #ifdef TRACE_RULE_CALL_TO_FILE
  1093. FILE *f;
  1094. char trace_dest_name[RULENAME_LEN + 16];
  1095. sprintf(trace_dest_name, "/tmp/%s.trace", rulename);
  1096. f = fopen(trace_dest_name, "wb");
  1097. if(!f)
  1098. {
  1099. f = stderr;
  1100. }
  1101. #define TRACE_DEST (f)
  1102. #else
  1103. #define TRACE_DEST (stderr)
  1104. #endif
  1105. setlinebuf(TRACE_DEST);
  1106. scheme_set_output_port_file(&scm, TRACE_DEST);
  1107. fprintf(TRACE_DEST, "---- Start Tracing rule %s ----\n", rulename);
  1108. fflush(TRACE_DEST);
  1109. scm.tracing = 1;
  1110. //Generate a function call to the full procedure name ("(rule_[whatever_rname_is])");
  1111. sprintf(rulefunc, "(rule_%s)", rname);
  1112. //Go and load the string containing the function call in the current context as if it were input from the console
  1113. scheme_load_string(&scm, rulefunc);
  1114. scm.tracing = 0;
  1115. fprintf(TRACE_DEST, "\n---- Done Tracing rule %s ----\n", rulename);
  1116. fflush(TRACE_DEST);
  1117. scheme_set_output_port_file(&scm, stdout);
  1118. #ifdef TRACE_RULE_CALL_TO_FILE
  1119. if(f != stderr)
  1120. {
  1121. fclose(f);
  1122. }
  1123. #endif
  1124. }
  1125. #endif
  1126. }
  1127. static inline void log_rule_failure()
  1128. {
  1129. struct message_record foo;
  1130. format_log_message(&foo, LOGLEVEL_ERROR, "Failed rule call rule: \"%s\" cred: \"%s\"", rname, cred); //log the failure
  1131. if(commhub_fd >= 0) //If we have a connection to the commhub
  1132. {
  1133. send_message(commhub_fd, &foo); //send this driver message to the log
  1134. }
  1135. }
  1136. int process_driver_rulecall(struct driver_rulecall_struct *drc)
  1137. {
  1138. if(!drc)
  1139. return -1;
  1140. // Populate all of our local variables with the values from the NULL rider, except those supplied by the driver's UI
  1141. //interaction. These are the variables that the scheme callbacks will use to figure out which rule, credential,
  1142. //logical_card_id, sequence_number, etc... applies to this ride.
  1143. // The rest of that type of parameter (route, trip, stop, date, time, gps fix, etc...)
  1144. //are either calculated on the spot (for date and time), or pulled from the data structures which are updated via IPC by
  1145. //the modules responsible for tracking GPS location and paddle data...
  1146. rname = drc->rulename;
  1147. rparam = drc->ruleparam;
  1148. logical_card_id = 0;
  1149. seq_num = 0;
  1150. cred = "";
  1151. // Populate all of our flags which are set inside the scheme callbacks, saying whether we are to accept or reject a rider,
  1152. //whether they need to be added to the passback cache, and whether any errors occurred during the processing of the scheme
  1153. //rule.
  1154. accept_flag = 0;
  1155. add_to_passback_flag = 0;
  1156. scheme_error = 0;
  1157. // This function call actually makes the request of the scheme interpreter to go and load the requested rule. This is
  1158. //where the trace wrapping happens, such that a rule that has recently failed will be executed with debug/trace functionality
  1159. //enabled for subsequent sessions in the hope that we'll catch an error condition in the diagnostic log that we can use to
  1160. //sanely debug the broken rule. (provided the symbol TRACE_NEXT_CALL_OF_FAILED_RULE has been #define'd).
  1161. do_low_level_rulecall(rname);
  1162. if(scheme_error || SCHEME_INTERNAL_ERROR(&scm))
  1163. {
  1164. // Log this failure with the offending rule and credential in the diagnostic log
  1165. log_rule_failure();
  1166. // Also, notify the driver of the rule failure (which should cause the driver to manually accept, and
  1167. //hopefully also report the error message to dispatch.
  1168. format_driver_message(&user_msg, LOGLEVEL_REJECT, "Rule execution error in: %s", rname);
  1169. if(commhub_fd >= 0) //If we have a connection to the commhub
  1170. {
  1171. send_message(commhub_fd, &user_msg); //send this driver message to the driver
  1172. }
  1173. return -1;
  1174. }
  1175. return 0;
  1176. }
  1177. //int process_rider(passdb_context *ctx, int rider_index, char *credential)
  1178. int process_rider(passdb_slim_context *ctx, int rider_index, char *credential)
  1179. {
  1180. rider_record rr;
  1181. int age;
  1182. if(!ctx)
  1183. return -1;
  1184. if(rider_index < 0)
  1185. return -1;
  1186. //if(rider_index >= NUM_STORED_PASSES)
  1187. // return -1;
  1188. //if(rider_index >= (2*ctx->hash_modulus) ) //oof...not sure what to do here
  1189. // return -1;
  1190. //flush the passback cache if it needs it...
  1191. apb_flush_if_needed();
  1192. make_rider_record( ctx, &rr, rider_index );
  1193. //search the anti-passback cache for this credential. If it exists, see how old it is.
  1194. //age = apb_lookup(ctx->riders[rider_index].id);
  1195. age = apb_lookup(rr.id);
  1196. // If it has an age of 0, it isn't in the passback cache. If the age is >= 1, that means that it is age seconds old
  1197. //so, effectively, if this credential represents a passback, do the following:
  1198. if(age)
  1199. {
  1200. //Send messages to tell the passenger that they've incurred a passback reject.
  1201. format_piu_message(&rider_msg, 1, PIU_PRIORITY_FARE, PASSENGER_MESSAGE_DURATION, "%s %s %ds", REJECT_STR, PASSBACK_STR, age);
  1202. //Same for the driver, but we get a longer string...
  1203. format_driver_message(&user_msg, LOGLEVEL_REJECT, "Passback (%d sec)", age);
  1204. //And similar for the billing log...
  1205. //format_billing_message(&bill_msg, REJECT_STR, "PASSBACK", "", "Passback", cred, ctx->riders[rider_index].id, 0);
  1206. format_billing_message(&bill_msg, REJECT_STR, "PASSBACK", "", "Passback", cred, rr.id, 0);
  1207. if(commhub_fd >= 0) //If we have a connection to the commhub
  1208. {
  1209. send_message(commhub_fd, &bill_msg); //send the billing message
  1210. send_message(commhub_fd, &user_msg); //send the driver message
  1211. send_message(commhub_fd, &rider_msg); //send the rider message
  1212. }
  1213. return 0;
  1214. }
  1215. // Populate all of our local variables with the values from the selected rider. These are the variables
  1216. //that the scheme callbacks will use to figure out which rule, credential, logical_card_id, sequence_number, etc...
  1217. //applies to this ride. The rest of that type of parameter (route, trip, stop, date, time, gps fix, etc...)
  1218. //are either calculated on the spot (for date and time), or pulled from the data structures which are updated via IPC by
  1219. //the modules responsible for tracking GPS location and paddle data...
  1220. /*
  1221. rname = ctx->riders[rider_index].rule_name;
  1222. rparam = ctx->riders[rider_index].rule_param;
  1223. logical_card_id = ctx->riders[rider_index].id;
  1224. seq_num = ctx->riders[rider_index].seq;
  1225. */
  1226. rname = rr.rule_name;
  1227. rparam = rr.rule_param;
  1228. logical_card_id = rr.id;
  1229. seq_num = rr.seq;
  1230. cred = credential;
  1231. // Populate all of our flags which are set inside the scheme callbacks, saying whether we are to accept or reject a rider,
  1232. //whether they need to be added to the passback cache, and whether any errors occurred during the processing of the scheme
  1233. //rule.
  1234. accept_flag = 0;
  1235. add_to_passback_flag = 0;
  1236. scheme_error = 0;
  1237. // This function call actually makes the request of the scheme interpreter to go and load the requested rule. This is
  1238. //where the trace wrapping happens, such that a rule that has recently failed will be executed with debug/trace functionality
  1239. //enabled for subsequent sessions in the hope that we'll catch an error condition in the diagnostic log that we can use to
  1240. //sanely debug the broken rule. (provided the symbol TRACE_NEXT_CALL_OF_FAILED_RULE has been #define'd).
  1241. do_low_level_rulecall(rname);
  1242. if(scheme_error || SCHEME_INTERNAL_ERROR(&scm)) //If an scheme interpreter error ocurred while processing a rule
  1243. {
  1244. // Log this failure with the offending rule and credential in the diagnostic log
  1245. log_rule_failure();
  1246. // Also, notify the driver of the rule failure (which should cause the driver to manually accept, and
  1247. //hopefully also report the error message to dispatch.
  1248. format_driver_message(&user_msg, LOGLEVEL_REJECT, "Rule execution error in: %s", rname);
  1249. if(commhub_fd >= 0) //If we have a connection to the commhub
  1250. {
  1251. send_message(commhub_fd, &user_msg); //send this driver message to the driver
  1252. }
  1253. return -1;
  1254. }
  1255. if(add_to_passback_flag)
  1256. {
  1257. //apb_add(ctx->riders[rider_index].id);
  1258. apb_add(rr.id);
  1259. }
  1260. return 0;
  1261. }