rules.c 52 KB

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