You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

727 lines
20 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. // This file is under GNU General Public License 3.0
  2. // see LICENSE.txt
  3. // generate state machine code
  4. // Copyleft (c) 2016 - 2018, p≡p foundation
  5. // Written by Volker Birk
  6. include yslt.yml2
  7. tstylesheet {
  8. include standardlib.ysl2
  9. include ./functions.ysl2
  10. template "/protocol" {
  11. document "generated/{@name}_event.h", "text"
  12. ||
  13. // This file is under GNU General Public License 3.0
  14. // see LICENSE.txt
  15. #pragma once
  16. #include "dynamic_api.h"
  17. #ifdef __cplusplus
  18. extern "C" {
  19. #endif
  20. #include "«@name».h"
  21. typedef struct «@name»_event {
  22. «@name»_PR fsm;
  23. int event;
  24. «@name»_t *msg;
  25. } «@name»_event_t;
  26. // new_«@name»_event() - allocate a new «@name»_event
  27. //
  28. // parameters:
  29. // fsm (in) finite state machine the event is for
  30. // event (in) event or None
  31. // msg (in) message to compute event from
  32. //
  33. // return value:
  34. // pointer to new event or NULL in case of failure
  35. //
  36. // caveat:
  37. // event must be valid for fsm or None
  38. // in case msg is given event will be calculated out of message
  39. DYNAMIC_API «@name»_event_t *new_«@name»_event(«@name»_PR fsm, int event, «@name»_t *msg);
  40. // free_«@name»_event() - free memory occupied by event
  41. //
  42. // parameters:
  43. // ev (in) event to free
  44. DYNAMIC_API void free_«@name»_event(«@name»_event_t *ev);
  45. #ifdef __cplusplus
  46. }
  47. #endif
  48. ||
  49. document "generated/{@name}_event.c", "text"
  50. ||
  51. // This file is under GNU General Public License 3.0
  52. // see LICENSE.txt
  53. #include "pEp_internal.h"
  54. #include "«@name»_event.h"
  55. #include "«@name»_func.h"
  56. `` for "fsm" | #include "«@name»_fsm.h"
  57. DYNAMIC_API «@name»_event_t *new_«@name»_event(«@name»_PR fsm, int event, «@name»_t *msg)
  58. {
  59. assert(fsm > 0 && (event >= 0 |`> |` msg));
  60. if (!(fsm > 0 && (event >= 0 |`> |` msg)))
  61. return NULL;
  62. «@name»_event_t *ev = («@name»_event_t *) calloc(1, sizeof(«@name»_event_t));
  63. assert(ev);
  64. if (!ev)
  65. return NULL;
  66. ev->fsm = fsm;
  67. ev->event = event;
  68. ev->msg = msg;
  69. if (msg) {
  70. switch (fsm) {
  71. `` apply "fsm", 3, mode=event
  72. default:
  73. // unknown protocol
  74. free(ev);
  75. return NULL;
  76. }
  77. }
  78. return ev;
  79. }
  80. DYNAMIC_API void free_«@name»_event(«@name»_event_t *ev)
  81. {
  82. if (ev) {
  83. free_«@name»_message(ev->msg);
  84. free(ev);
  85. }
  86. }
  87. ||
  88. document "generated/{@name}_impl.h", "text" {
  89. ||
  90. // This file is under GNU General Public License 3.0
  91. // see LICENSE.txt
  92. #pragma once
  93. #include "fsm_common.h"
  94. #include "message_api.h"
  95. #include "«@name»_event.h"
  96. #ifdef __cplusplus
  97. extern "C" {
  98. #endif
  99. // conditions
  100. ||
  101. for "func:distinctName(*//condition)"
  102. | PEP_STATUS «@name»(PEP_SESSION session, bool *result);
  103. ||
  104. // actions
  105. ||
  106. const "name", "@name";
  107. for "func:distinctName(*//action[not(starts-with(@name, 'send'))])"
  108. | PEP_STATUS «@name»(PEP_SESSION session);
  109. ||
  110. // notify state machine from event
  111. // use state to generate «@name» message if necessary
  112. PEP_STATUS «@name»_notify(
  113. PEP_SESSION session,
  114. «@name»_PR fsm,
  115. int message_type
  116. );
  117. // send message about an event to communication partners using state
  118. PEP_STATUS send_«@name»_message(
  119. PEP_SESSION session,
  120. «@name»_PR fsm,
  121. int message_type
  122. );
  123. // receive message and store it in state
  124. PEP_STATUS recv_«@name»_event(
  125. PEP_SESSION session,
  126. «@name»_event_t *ev
  127. );
  128. // state machine driver
  129. // if fsm or event set to 0 use fields in src if present
  130. PEP_STATUS «@name»_driver(
  131. PEP_SESSION session,
  132. «@name»_PR fsm,
  133. int event
  134. );
  135. PEP_STATUS inject_«@name»_event(
  136. PEP_SESSION session,
  137. «@name»_PR fsm,
  138. int event
  139. );
  140. #ifdef __cplusplus
  141. }
  142. #endif
  143. ||
  144. }
  145. document "generated/{@name}_impl.c", "text"
  146. ||
  147. // This file is under GNU General Public License 3.0
  148. // see LICENSE.txt
  149. #include "«@name»_impl.h"
  150. #include "pEp_internal.h"
  151. #include "«@name»_event.h"
  152. #include "«@name»_codec.h"
  153. #include "baseprotocol.h"
  154. `` for "fsm" | #include "«@name»_fsm.h"
  155. PEP_STATUS «@name»_driver(
  156. PEP_SESSION session,
  157. «@name»_PR fsm,
  158. int event
  159. )
  160. {
  161. assert(session && fsm);
  162. if (!(session && fsm))
  163. return PEP_ILLEGAL_VALUE;
  164. int next_state = None;
  165. do {
  166. switch (fsm) {
  167. `` apply "fsm", 3, mode=driver
  168. default:
  169. return PEP_ILLEGAL_VALUE;
  170. }
  171. } while (next_state);
  172. return PEP_STATUS_OK;
  173. }
  174. PEP_STATUS inject_«@name»_event(
  175. PEP_SESSION session,
  176. «@name»_PR fsm,
  177. int event
  178. )
  179. {
  180. «@name»_t *msg = NULL;
  181. «@name»_event_t *ev = NULL;
  182. assert(session && fsm > 0 && event > None);
  183. if (!(session && fsm > 0 && event > None))
  184. return PEP_ILLEGAL_VALUE;
  185. PEP_STATUS status = PEP_STATUS_OK;
  186. if (!session->inject_«yml:lcase(@name)»_event) {
  187. status = PEP_«yml:ucase(@name)»_NO_INJECT_CALLBACK;
  188. goto error;
  189. }
  190. if (event < Extra) {
  191. msg = new_«@name»_message(fsm, event);
  192. if (!msg) {
  193. status = PEP_OUT_OF_MEMORY;
  194. goto error;
  195. }
  196. status = update_«@name»_message(session, fsm, event, msg);
  197. if (status)
  198. goto error;
  199. }
  200. ev = («@name»_event_t *) calloc(1, sizeof(«@name»_event_t));
  201. assert(ev);
  202. if (!ev) {
  203. status = PEP_OUT_OF_MEMORY;
  204. goto error;
  205. }
  206. ev->fsm = fsm;
  207. ev->event = event;
  208. ev->msg = msg;
  209. int result = session->inject_«yml:lcase(@name)»_event(ev,
  210. session->«yml:lcase(@name)»_management);
  211. if (result) {
  212. status = PEP_STATEMACHINE_ERROR;
  213. goto error;
  214. }
  215. goto the_end;
  216. error:
  217. free(ev);
  218. free_«@name»_message(msg);
  219. the_end:
  220. return status;
  221. }
  222. PEP_STATUS «@name»_notify(
  223. PEP_SESSION session,
  224. «@name»_PR fsm,
  225. int message_type
  226. )
  227. {
  228. assert(session && fsm > 0 && message_type > 1 && message_type < Extra);
  229. if (!(session && fsm > 0 && message_type > 1 && message_type < Extra))
  230. return PEP_ILLEGAL_VALUE;
  231. PEP_STATUS status = PEP_STATUS_OK;
  232. «@name»_t *msg = new_«@name»_message(fsm, message_type);
  233. if (!msg) {
  234. status = PEP_OUT_OF_MEMORY;
  235. goto error;
  236. }
  237. status = update_«@name»_message(session, fsm, message_type, msg);
  238. if (status)
  239. goto error;
  240. goto the_end;
  241. error:
  242. free_«@name»_message(msg);
  243. the_end:
  244. return status;
  245. }
  246. PEP_STATUS send_«@name»_message(
  247. PEP_SESSION session,
  248. «@name»_PR fsm,
  249. int message_type
  250. )
  251. {
  252. PEP_STATUS status = PEP_STATUS_OK;
  253. assert(session && fsm > None && message_type > None);
  254. if (!(session && fsm > None && message_type > None))
  255. return PEP_ILLEGAL_VALUE;
  256. «@name»_t *msg = new_«@name»_message(None, None);
  257. if (!msg)
  258. return PEP_OUT_OF_MEMORY;
  259. char *data = NULL;
  260. message *m = NULL;
  261. status = update_«@name»_message(session, fsm, message_type, msg);
  262. if (status)
  263. goto the_end;
  264. size_t size = 0;
  265. status = encode_«@name»_message(msg, &data, &size);
  266. if (status)
  267. goto the_end;
  268. status = base_prepare_message(
  269. session->«yml:lcase(@name)»_state.common.from,
  270. session->«yml:lcase(@name)»_state.common.from,
  271. data,
  272. size,
  273. &m
  274. );
  275. if (status)
  276. goto the_end;
  277. status = session->messageToSend(session->«yml:lcase(@name)»_obj, m);
  278. the_end:
  279. free_message(m);
  280. free(data);
  281. free_«@name»_message(msg);
  282. return status;
  283. }
  284. PEP_STATUS recv_«@name»_event(
  285. PEP_SESSION session,
  286. «@name»_event_t *ev
  287. )
  288. {
  289. assert(session && ev);
  290. if (!(session && ev))
  291. return PEP_ILLEGAL_VALUE;
  292. PEP_STATUS status = PEP_STATUS_OK;
  293. if (ev->event < Extra) {
  294. «@name»_PR fsm = (int) None;
  295. int event = None;
  296. status = update_«@name»_state(session, ev->msg, &fsm, &event);
  297. if (status)
  298. goto error;
  299. if (ev->fsm) {
  300. if (ev->fsm != fsm |`> |` ev->event != event) {
  301. status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
  302. goto error;
  303. }
  304. }
  305. else {
  306. if (ev->event) {
  307. status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
  308. goto error;
  309. }
  310. ev->fsm = fsm;
  311. ev->event = event;
  312. }
  313. }
  314. free_«@name»_message(ev->msg);
  315. free(ev);
  316. status = «@name»_driver(session, ev->fsm, ev->event);
  317. return status;
  318. error:
  319. free_«@name»_message(ev->msg);
  320. free(ev);
  321. return status;
  322. }
  323. ||
  324. apply "fsm", 0, mode=gen;
  325. }
  326. template "fsm", mode=event
  327. {
  328. ||
  329. case «../@name»_PR_«yml:lcase(@name)»: {
  330. switch (msg->choice.«yml:lcase(@name)».payload.present) {
  331. ||
  332. for "message"
  333. ||
  334. case «../@name»__payload_PR_«yml:mixedCase(@name)»:
  335. ev->event = «@name»;
  336. break;
  337. ||
  338. ||
  339. default:
  340. // unknown message type
  341. free(ev);
  342. return NULL;
  343. }
  344. break;
  345. }
  346. ||
  347. }
  348. template "fsm", mode=driver
  349. ||
  350. case «../@name»_PR_«yml:lcase(@name)»: {
  351. int state = session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state;
  352. next_state = fsm_«@name»(session, state, event);
  353. if (next_state > None) {
  354. session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state = next_state;
  355. event = Init;
  356. }
  357. else if (next_state < None) {
  358. return PEP_STATEMACHINE_ERROR - state;
  359. }
  360. break;
  361. }
  362. ||
  363. template "fsm", mode=gen {
  364. document "generated/{@name}_fsm.h", "text" {
  365. ||
  366. // This file is under GNU General Public License 3.0
  367. // see LICENSE.txt
  368. #pragma once
  369. #include "«../@name»_impl.h"
  370. #ifdef __cplusplus
  371. extern "C" {
  372. #endif
  373. // state machine for «@name»
  374. // states
  375. typedef enum _«@name»_state {
  376. «@name»_state_None = None,
  377. «@name»_state_Init = Init,
  378. ||
  379. for "func:distinctName(state[not(@name='InitState')])"
  380. |> «@name»`if "position()!=last()" > , `
  381. ||
  382. } «@name»_state;
  383. // events
  384. typedef enum _«@name»_event {
  385. «@name»_event_None = None,
  386. «@name»_event_Init = Init,
  387. ||
  388. for "func:distinctName(state/event[not(not(../../message/@name=@name))])" {
  389. const "name", "@name";
  390. |> «$name» = «/protocol/fsm/message[@name=$name]/@id»,
  391. }
  392. for "func:distinctName(state/event[not(not(../../external/@name=@name))])" {
  393. const "name", "@name";
  394. |> «$name» = «/protocol/fsm/external[@name=$name]/@id»,
  395. }
  396. |> «@name»_event_Extra = Extra,
  397. for "func:distinctName(state/event[not(../../message/@name=@name or ../../external/@name=@name)])" {
  398. if "@name!='Init'"
  399. |> «@name»`if "position()!=last()" > , `
  400. }
  401. ||
  402. } «@name»_event;
  403. // state machine
  404. const char *«@name»_state_name(int state);
  405. // the state machine function is returning the next state in case of a
  406. // transition or None for staying
  407. «@name»_state fsm_«@name»(
  408. PEP_SESSION session,
  409. «@name»_state state,
  410. «@name»_event event
  411. );
  412. #ifdef __cplusplus
  413. }
  414. #endif
  415. ||
  416. }
  417. document "generated/{@name}_fsm.c", "text" {
  418. ||
  419. // This file is under GNU General Public License 3.0
  420. // see LICENSE.txt
  421. #include "«@name»_fsm.h"
  422. #include <stdlib.h>
  423. const char *«@name»_state_name(int state)
  424. {
  425. switch (state) {
  426. case End:
  427. return "End";
  428. case None:
  429. return "None";
  430. case Init:
  431. return "InitState";
  432. ||
  433. for "func:distinctName(state[not(@name='InitState')])" {
  434. |>> case «@name»:
  435. |>>> return "«@name»";
  436. }
  437. ||
  438. default:
  439. return "unknown state";
  440. }
  441. }
  442. static char *_str(int n, bool hex)
  443. {
  444. char *buf = calloc(1, 24);
  445. assert(buf);
  446. if (!buf)
  447. return NULL;
  448. if (hex)
  449. snprintf(buf, 24, "%.4x", n);
  450. else
  451. snprintf(buf, 24, "%d", n);
  452. return buf;
  453. }
  454. #define «@name»_ERR_LOG(t, d) log_event(session, (t), "«@name»", (d), "error")
  455. static PEP_STATUS _«@name»_ERR_LOG_int(PEP_SESSION session, char *t, int n, bool hex)
  456. {
  457. char *_buf = _str(n, hex);
  458. if (!_buf)
  459. return PEP_OUT_OF_MEMORY;
  460. PEP_STATUS status = «@name»_ERR_LOG(t, _buf);
  461. free(_buf);
  462. return status;
  463. }
  464. #define «@name»_ERR_LOG_INT(t, n) _«@name»_ERR_LOG_int(session, (t), (n), false)
  465. #define «@name»_ERR_LOG_HEX(t, n) _«@name»_ERR_LOG_int(session, (t), (n), true)
  466. #ifndef SERVICE_LOG
  467. // SERVICE LOG is meant to check session->service_log in runtime config;
  468. // for older engines log more than needed
  469. #define SERVICE_LOG(session, t, n, d) log_event((session), (t), (n), (d), "service")
  470. #endif
  471. #define «@name»_SERVICE_LOG(t, d) SERVICE_LOG(session, (t), "«@name»", (d))
  472. «@name»_state fsm_«@name»(
  473. PEP_SESSION session,
  474. «@name»_state state,
  475. «@name»_event event
  476. )
  477. {
  478. assert(session);
  479. if (!session)
  480. return invalid_state;
  481. switch (state) {
  482. case None:
  483. return «@name»_state_Init;
  484. `` apply "state", 2, mode=fsm
  485. default:
  486. «@name»_ERR_LOG_INT("invalid state", state);
  487. return invalid_state;
  488. }
  489. return None;
  490. }
  491. ||
  492. }
  493. }
  494. template "state", mode=fsm {
  495. choose {
  496. when "@name='InitState'" | case «../@name»_state_Init:
  497. otherwise | case «@name»:
  498. }
  499. ||
  500. «../@name»_SERVICE_LOG("in state", "«@name»");
  501. switch (event) {
  502. case None:
  503. «../@name»_SERVICE_LOG("received None event", "ignoring");
  504. break;
  505. ||
  506. if "not(event[@name='Init'])"
  507. ||
  508. case Init:
  509. // nothing to do
  510. break;
  511. ||
  512. ||
  513. `` apply "event", 2, mode=fsm
  514. default:
  515. «../@name»_ERR_LOG_INT("invalid event", event);
  516. return invalid_event;
  517. }
  518. break;
  519. ||
  520. }
  521. template "event", mode=fsm {
  522. | case «@name»: {
  523. if "condition|action" |> PEP_STATUS status;
  524. if "condition" |> bool result = false;
  525. if "condition|action" |
  526. ||
  527. «../../@name»_SERVICE_LOG("received event", "«@name»");
  528. `` apply "transition|action|condition" with "protocol", "../../..", with "fsm", "../.."
  529. ||
  530. if "name(*[last()])!='transition'" {
  531. |
  532. |> «../../@name»_SERVICE_LOG("remaining in state", "«../@name»");
  533. |> break;
  534. }
  535. ||
  536. }
  537. ||
  538. }
  539. template "transition" {
  540. param "fsm";
  541. ||
  542. «$fsm/@name»_SERVICE_LOG("transition to state", "«@target»");
  543. return «@target»;
  544. ||
  545. }
  546. template "action" {
  547. param "protocol";
  548. param "fsm";
  549. choose {
  550. when "starts-with(@name, 'send')" {
  551. const "name", "substring(@name, 5)";
  552. ||
  553. «$fsm/@name»_SERVICE_LOG("send message", "«$name»");
  554. status = send_«$protocol/@name»_message(session, «$fsm/@id», «$name»);
  555. ||
  556. }
  557. otherwise
  558. ||
  559. «$fsm/@name»_SERVICE_LOG("do action", "«@name»");
  560. status = «@name»(session);
  561. ||
  562. }
  563. ||
  564. if (status) {
  565. «$fsm/@name»_ERR_LOG_HEX("executing action «@name»() failed", status);
  566. return invalid_action;
  567. }
  568. ||
  569. }
  570. template "condition" {
  571. param "protocol";
  572. param "fsm";
  573. ||
  574. status = «@name»(session, &result);
  575. if (status) {
  576. «$fsm/@name»_ERR_LOG_HEX("computing condition «@name» failed", status);
  577. return invalid_condition;
  578. }
  579. if (result) {
  580. «$fsm/@name»_SERVICE_LOG("condition applies", "«@name»");
  581. ||
  582. apply "transition|action|condition"
  583. with "protocol", "$protocol", with "fsm", "$fsm";
  584. | }
  585. }
  586. }