rules.c 50 KB

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