sync/gen_statemachine.ysl2
author Volker Birk <vb@pep.foundation>
Wed, 29 Aug 2018 21:49:28 +0200
branchsync
changeset 2903 33549a7c7191
parent 2899 63b619aef131
child 2907 92f22b19b09e
permissions -rw-r--r--
safeguards
     1 // This file is under GNU General Public License 3.0
     2 // see LICENSE.txt
     3 
     4 // generate state machine code
     5 
     6 // Copyleft (c) 2016 - 2018, p≡p foundation
     7 
     8 // Written by Volker Birk
     9 
    10 include yslt.yml2
    11 
    12 tstylesheet {
    13     include standardlib.ysl2
    14     include ./functions.ysl2
    15 
    16     template "/protocol" {
    17         document "generated/{@name}_event.h", "text"
    18         ||
    19         // This file is under GNU General Public License 3.0
    20         // see LICENSE.txt
    21 
    22         #pragma once
    23 
    24         #include "dynamic_api.h"
    25 
    26         #ifdef __cplusplus
    27         extern "C" {
    28         #endif
    29 
    30         #include "«@name».h"
    31 
    32 
    33         typedef struct «@name»_event {
    34             «@name»_PR fsm;
    35             int event;
    36             «@name»_t *msg;
    37         } «@name»_event_t;
    38 
    39  
    40         // new_«@name»_event() - allocate a new «@name»_event
    41         //
    42         //  parameters:
    43         //      fsm (in)        finite state machine the event is for
    44         //      event (in)      event or None
    45         //      msg (in)        message to compute event from
    46         //
    47         //  return value:
    48         //      pointer to new event or NULL in case of failure
    49         //
    50         //  caveat:
    51         //      event must be valid for fsm or None
    52         //      in case msg is given event will be calculated out of message
    53 
    54         «@name»_event_t *new_«@name»_event(«@name»_PR fsm, int event, «@name»_t *msg);
    55 
    56 
    57         // free_«@name»_event() - free memory occupied by event
    58         //
    59         //  parameters:
    60         //      ev (in)         event to free
    61 
    62         void free_«@name»_event(«@name»_event_t *ev);
    63 
    64 
    65         #ifdef __cplusplus
    66         }
    67         #endif
    68 
    69         ||
    70 
    71         document "generated/{@name}_event.c", "text"
    72         ||
    73         // This file is under GNU General Public License 3.0
    74         // see LICENSE.txt
    75 
    76         #include "pEp_internal.h"
    77         #include "«@name»_event.h"
    78         #include "«@name»_func.h"
    79         `` for "fsm" | #include "«@name»_fsm.h"
    80 
    81         «@name»_event_t *new_«@name»_event(«@name»_PR fsm, int event, «@name»_t *msg)
    82         {
    83             assert(fsm > 0 && (event >= 0 |`> |` msg));
    84             if (!(fsm > 0 && (event >= 0 |`> |` msg)))
    85                 return NULL;
    86 
    87             «@name»_event_t *ev = («@name»_event_t *) calloc(1, sizeof(«@name»_event_t));
    88             assert(ev);
    89             if (!ev)
    90                 return NULL;
    91 
    92             ev->fsm = fsm;
    93             ev->event = event;
    94             ev->msg = msg;
    95 
    96             if (msg) {
    97                 switch (fsm) {
    98                     `` apply "fsm", 3, mode=event
    99                     default:
   100                         // unknown protocol
   101                         free(ev);
   102                         return NULL;
   103                 }
   104             }
   105 
   106             return ev;
   107         }
   108 
   109         void free_«@name»_event(«@name»_event_t *ev)
   110         {
   111             if (ev) {
   112                 free_«@name»_message(ev->msg);
   113                 free(ev);
   114             }
   115         }
   116 
   117         ||
   118 
   119         document "generated/{@name}_impl.h", "text" {
   120             ||
   121             // This file is under GNU General Public License 3.0
   122             // see LICENSE.txt
   123 
   124             #pragma once
   125 
   126             #include "fsm_common.h"
   127             #include "«@name»_event.h"
   128             #include "message_api.h"
   129             
   130             #ifdef __cplusplus
   131             extern "C" {
   132             #endif
   133 
   134             // conditions
   135 
   136             ||
   137             for "func:distinctName(*//condition)"
   138                 | PEP_STATUS «@name»(PEP_SESSION session, bool *result);
   139             ||
   140 
   141             // actions
   142 
   143             ||
   144             for "func:distinctName(*//action)"
   145                 | PEP_STATUS «@name»(PEP_SESSION session);
   146             ||
   147 
   148             // send message about an event to communication partners using state
   149 
   150             PEP_STATUS send_«@name»_message(
   151                     PEP_SESSION session, 
   152                     «@name»_PR fsm,
   153                     int message_type
   154                 );
   155 
   156             // receive message and store it in state
   157 
   158             PEP_STATUS recv_«@name»_event(
   159                     PEP_SESSION session,
   160                     «@name»_event_t *ev
   161                 );
   162         
   163             // state machine driver
   164             // if fsm or event set to 0 use fields in src if present
   165 
   166             PEP_STATUS «@name»_driver(
   167                     PEP_SESSION session,
   168                     «@name»_PR fsm,
   169                     int event
   170                 );
   171 
   172             // API being used by the engine internally
   173 
   174             // call this if you need to signal an external event
   175 
   176             PEP_STATUS signal_«@name»_event(
   177                     PEP_SESSION session, 
   178                     «@name»_PR fsm,
   179                     int event
   180                 );
   181             
   182             // call this if you are a transport and are receiving
   183             // a «@name» message
   184 
   185             PEP_STATUS signal_«@name»_message(
   186                     PEP_SESSION session, 
   187                     PEP_rating rating,
   188                     const char *data,
   189                     size_t size
   190                 );
   191 
   192             #ifdef __cplusplus
   193             }
   194             #endif
   195 
   196             ||
   197         }
   198 
   199         document "generated/{@name}_impl.c", "text" {
   200             ||
   201             // This file is under GNU General Public License 3.0
   202             // see LICENSE.txt
   203         
   204             #include "«@name»_impl.h"
   205             #include "pEp_internal.h"
   206             #include "«@name»_event.h"
   207             #include "«yml:lcase(@name)»_codec.h"
   208             #include "baseprotocol.h"
   209             `` for "fsm" | #include "«@name»_fsm.h"
   210 
   211             PEP_STATUS «@name»_driver(
   212                     PEP_SESSION session,
   213                     «@name»_PR fsm,
   214                     int event
   215                 )
   216             {
   217                 assert(session && fsm);
   218                 if (!(session && fsm))
   219                     return PEP_ILLEGAL_VALUE;
   220 
   221                 int next_state = None;
   222                 do {
   223                     switch (fsm) {
   224                         `` apply "fsm", 3, mode=driver               
   225                         default:
   226                             return PEP_ILLEGAL_VALUE;
   227                     }
   228                 }  while (next_state);
   229 
   230                 return PEP_STATUS_OK;
   231             }
   232 
   233             PEP_STATUS signal_«@name»_event(
   234                     PEP_SESSION session, 
   235                     «@name»_PR fsm,
   236                     int event
   237                 )
   238             {
   239                 «@name»_t *msg = NULL;
   240                 «@name»_event_t *ev = NULL;
   241 
   242                 assert(session && fsm > 0 && event > None);
   243                 if (!(session && fsm > 0 && event > None))
   244                     return PEP_ILLEGAL_VALUE;
   245 
   246                 PEP_STATUS status = PEP_STATUS_OK;
   247 
   248                 if (!session->inject_«yml:lcase(@name)»_event)
   249                    return PEP_«yml:ucase(@name)»_NO_INJECT_CALLBACK;
   250 
   251                 if (event < Extra) {
   252                     msg = new_«@name»_message(fsm, event);
   253                     if (!msg) {
   254                         status = PEP_OUT_OF_MEMORY;
   255                         goto the_end;
   256                     }
   257 
   258                     status = update_«@name»_message(session, msg);
   259                     if (status)
   260                         goto the_end;
   261                 }
   262 
   263                 ev = new_«@name»_event(fsm, event, msg);
   264                 if (!ev) {
   265                     status = PEP_OUT_OF_MEMORY;
   266                     goto the_end;
   267                 }
   268 
   269                 int result = session->inject_«yml:lcase(@name)»_event(ev,
   270                         session->«yml:lcase(@name)»_management);
   271                 if (result) {
   272                     status = PEP_STATEMACHINE_ERROR;
   273                     goto the_end;
   274                 }
   275                 return PEP_STATUS_OK;
   276 
   277             the_end:
   278                 free_«@name»_event(ev);
   279                 free_«@name»_message(msg);
   280                 return status;
   281             }
   282 
   283             PEP_STATUS signal_«@name»_message(
   284                     PEP_SESSION session, 
   285                     PEP_rating rating,
   286                     const char *data,
   287                     size_t size
   288                 )
   289             {
   290                 assert(session && data && size);
   291                 if (!(session && data && size))
   292                     return PEP_ILLEGAL_VALUE;
   293 
   294                 if (!session->inject_«yml:lcase(@name)»_event)
   295                    return PEP_«yml:ucase(@name)»_NO_INJECT_CALLBACK;
   296 
   297                 «@name»_t *msg = NULL;
   298                 PEP_STATUS status = decode_«@name»_message(data, size, &msg);
   299                 if (status)
   300                     return status;
   301 
   302                 «@name»_event_t *ev = NULL;
   303 
   304                 switch (msg->present) {
   305                     `` apply "fsm", 2, mode=signal_message
   306                     default:
   307                         status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
   308                         goto the_end;
   309                 }
   310 
   311                 ev = new_«@name»_event(None, None, msg);
   312                 if (!ev) {
   313                     status = PEP_OUT_OF_MEMORY;
   314                     goto the_end;
   315                 }
   316 
   317                 int result = session->inject_«yml:lcase(@name)»_event(ev,
   318                         session->«yml:lcase(@name)»_management);
   319                 if (result) {
   320                     status = PEP_STATEMACHINE_ERROR;
   321                     goto the_end;
   322                 }
   323                 return PEP_STATUS_OK;
   324 
   325             the_end:
   326                 free_«@name»_event(ev);
   327                 free_«@name»_message(msg);
   328                 return status;
   329             }
   330 
   331             PEP_STATUS send_«@name»_message(
   332                     PEP_SESSION session, 
   333                     «@name»_PR fsm,
   334                     int message_type
   335                 )
   336             {
   337                 PEP_STATUS status = PEP_STATUS_OK;
   338 
   339                 assert(session && fsm > None && message_type > None);
   340                 if (!(session && fsm > None && message_type > None))
   341                     return PEP_ILLEGAL_VALUE;
   342                 
   343                 «@name»_t *msg = new_«@name»_message(fsm, message_type);
   344                 if (!msg)
   345                     return PEP_OUT_OF_MEMORY;
   346 
   347                 char *data = NULL;
   348                 message *m = NULL;
   349                 identity_list *channels = NULL;
   350 
   351                 status = update_«@name»_message(session, msg);
   352                 if (status)
   353                     goto the_end;
   354 
   355                 size_t size = 0;
   356                 status = encode_«@name»_message(msg, &data, &size);
   357                 if (status)
   358                     goto the_end;
   359 
   360                 switch (message_type) {
   361                     // these messages are being broadcasted
   362                     `` for "fsm/message[@type='broadcast']" |>> case «../@name»__payload_PR_«yml:mixedCase(@name)»:
   363                         status = _own_identities_retrieve(session, &channels, PEP_idf_not_for_«yml:lcase(@name)»);
   364                         if (status)
   365                             goto the_end;
   366 
   367                         if (!(channels && channels->ident)) {
   368                             status = PEP_«yml:ucase(@name)»_NO_CHANNEL;
   369                             goto the_end;
   370                         }
   371                         break;
   372 
   373                     // these go anycast; previously used address is sticky (unicast)
   374                     `` for "fsm/message[@type='anycast']" |>> case «../@name»__payload_PR_«yml:mixedCase(@name)»:
   375                         if (!session->«yml:lcase(@name)»_state.common.from `> |`|
   376                             (session->«yml:lcase(@name)»_state.common.from->flags &
   377                             PEP_idf_not_for_«yml:lcase(@name)»)) {
   378 
   379                             // no address available yet, try to find one
   380                             status = _own_identities_retrieve(session, &channels, PEP_idf_not_for_«yml:lcase(@name)»);
   381                             if (!status)
   382                                 goto the_end;
   383                             break;
   384 
   385                             if (channels && channels->ident) {
   386                                 // only need the first one
   387                                 free_identity_list(channels->next);
   388                                 channels->next = NULL;
   389                             }
   390                             else {
   391                                 status = PEP_«yml:ucase(@name)»_NO_CHANNEL;
   392                                 goto the_end;
   393                             }
   394                         }
   395                         else {
   396                             pEp_identity *channel = identity_dup(session->«yml:lcase(@name)»_state.common.from);
   397                             if (!channel) {
   398                                 status = PEP_OUT_OF_MEMORY;
   399                                 goto the_end;
   400                             }
   401 
   402                             channels = new_identity_list(channel);
   403                             if (!channels) {
   404                                 status = PEP_OUT_OF_MEMORY;
   405                                 goto the_end;
   406                             }
   407                         }
   408 
   409                     default:
   410                         status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
   411                         goto the_end;
   412                 }
   413 
   414                 for (identity_list *li = channels; li && li->ident ; li = li->next) {
   415                     message *_m = NULL;
   416 
   417                     status = base_prepare_message(
   418                             li->ident,
   419                             li->ident,
   420                             data,
   421                             size,
   422                             &_m
   423                         );
   424                     if (status)
   425                         goto the_end;
   426                     data = NULL;
   427 
   428                     switch (message_type) {
   429                     `` for "fsm/message[@security='unencrypted']" |>>> case «../@name»__payload_PR_«yml:mixedCase(@name)»:
   430                             m = _m;
   431                             break;
   432 
   433                         default:
   434                             status = encrypt_message(session, _m, NULL, &m, PEP_enc_PEP, 0);
   435                             if (status) {
   436                                 status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
   437                                 goto the_end;
   438                             }
   439                             free_message(_m);
   440                     }
   441 
   442                     status = session->messageToSend(m);
   443                     m = NULL;
   444                 }
   445 
   446             the_end:
   447                 free_identity_list(channels);
   448                 free_message(m);
   449                 free(data);
   450                 free_«@name»_message(msg);
   451                 return status;
   452             }
   453 
   454             PEP_STATUS recv_«@name»_event(
   455                     PEP_SESSION session,
   456                     «@name»_event_t *ev
   457                 )
   458             {
   459                 assert(session && ev);
   460                 if (!(session && ev))
   461                     return PEP_ILLEGAL_VALUE;
   462 
   463                 PEP_STATUS status = PEP_STATUS_OK;
   464                 «@name»_PR fsm = (int) None;
   465                 int event = None;
   466 
   467                 if (ev->event < Extra) {
   468                     status = update_«@name»_state(session, ev->msg, &fsm, &event);
   469                     if (status)
   470                         goto the_end;
   471 
   472                     if (ev->fsm) {
   473                         if (ev->fsm != fsm |`> |` ev->event != event) {
   474                             status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
   475                             goto the_end;
   476                         }
   477                     }
   478                     else if (ev->event) {
   479                         status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
   480                         goto the_end;
   481                     }
   482                 }
   483                 else {
   484                     fsm = ev->fsm;
   485                     event = ev->event;
   486                 }
   487 
   488                 status = «@name»_driver(session, fsm, event);
   489 
   490             the_end:
   491                 free_«@name»_event(ev);
   492                 return status;
   493             }
   494 
   495             ||
   496         }
   497 
   498         apply "fsm", 0, mode=gen;
   499     }
   500 
   501     template "fsm", mode=signal_message
   502     {
   503         ||
   504         case «../@name»_PR_«yml:lcase(@name)»:
   505             switch (msg->choice.«yml:lcase(@name)».payload.present) {
   506         ||
   507         if "message[@security='unencrypted']" {
   508             |         // these messages are going untested
   509             for "message[@security='unencrypted']"
   510                 |>> case «../@name»__payload_PR_«yml:mixedCase(@name)»:
   511             ||
   512                        break;
   513 
   514             ||
   515         }
   516         if "message[@security='untrusted']"
   517         ||
   518                 // these messages must arrive encrypted
   519         `` for "message[@security='untrusted']" |>> case «../@name»__payload_PR_«yml:mixedCase(@name)»:
   520                     if (rating < PEP_rating_reliable) {
   521                         status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
   522                         goto the_end;
   523                     }
   524                     break;
   525 
   526         ||
   527         if "message[@security='trusted']"
   528         ||
   529                 // these messages must come through a trusted channel
   530         `` for "message[@security='trusted']" |>> case «ancestor::fsm/@name»__payload_PR_«yml:mixedCase(@name)»:
   531                     if (rating < PEP_rating_trusted) {
   532                         status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
   533                         goto the_end;
   534                     }
   535                     break;
   536 
   537                 default:
   538                     status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
   539                     goto the_end;
   540             }
   541             break;
   542 
   543         ||
   544     }
   545 
   546     template "fsm", mode=event
   547     {
   548     ||
   549     case «../@name»_PR_«yml:lcase(@name)»: {
   550         switch (msg->choice.«yml:lcase(@name)».payload.present) {
   551     ||
   552     for "message"
   553     ||
   554             case «../@name»__payload_PR_«yml:mixedCase(@name)»:
   555                 ev->event = «@name»;
   556                 break;
   557     ||
   558     ||
   559             default:
   560                 // unknown message type
   561                 free(ev);
   562                 return NULL;
   563         }
   564         break;
   565     }
   566 
   567     ||
   568     }
   569 
   570     template "fsm", mode=driver
   571     ||
   572     case «../@name»_PR_«yml:lcase(@name)»: {
   573         int state = session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state;
   574         next_state = fsm_«@name»(session, state, event);
   575         if (next_state > None) {
   576             session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state = next_state;
   577             event = Init;
   578         }
   579         else if (next_state < None) {
   580             return PEP_STATEMACHINE_ERROR - state;
   581         }
   582         break;
   583     }
   584 
   585     ||
   586 
   587     template "fsm", mode=gen {
   588         document "generated/{@name}_fsm.h", "text" {
   589         ||
   590         // This file is under GNU General Public License 3.0
   591         // see LICENSE.txt
   592 
   593         #pragma once
   594 
   595         #include "«../@name»_impl.h"
   596 
   597         #ifdef __cplusplus
   598         extern "C" {
   599         #endif
   600 
   601         // state machine for «@name»
   602 
   603         // states
   604 
   605         typedef enum _«@name»_state {
   606             «@name»_state_None = None,
   607             «@name»_state_Init = Init,
   608         ||
   609         for "func:distinctName(state[not(@name='InitState')])"
   610             |> «@name»`if "position()!=last()" > , `
   611         ||
   612         } «@name»_state;
   613 
   614         // events
   615 
   616         typedef enum _«@name»_event {
   617             «@name»_event_None = None,
   618             «@name»_event_Init = Init,
   619         ||
   620         for "func:distinctName(state/event[not(not(../../message/@name=@name))])" {
   621             const "name", "@name";
   622             |> «$name» = «/protocol/fsm/message[@name=$name]/@id»,
   623         }
   624         for "func:distinctName(state/event[not(not(../../external/@name=@name))])" {
   625             const "name", "@name";
   626             |> «$name» = «/protocol/fsm/external[@name=$name]/@id»,
   627         }
   628         |> «@name»_event_Extra = Extra,
   629         for "func:distinctName(state/event[not(../../message/@name=@name or ../../external/@name=@name)])" {
   630             if "@name!='Init'"
   631                 |> «@name»`if "position()!=last()" > , `
   632         }
   633         ||
   634         } «@name»_event;
   635 
   636         // state machine
   637 
   638         const char *«@name»_state_name(int state);
   639         const char *«@name»_event_name(int event);
   640 
   641         // the state machine function is returning the next state in case of a
   642         // transition or None for staying
   643 
   644         «@name»_state fsm_«@name»(
   645                 PEP_SESSION session,
   646                 «@name»_state state,
   647                 «@name»_event event
   648             );
   649 
   650         #ifdef __cplusplus
   651         }
   652         #endif
   653 
   654         ||
   655         }
   656 
   657         document "generated/{@name}_fsm.c", "text" {
   658         ||
   659         // This file is under GNU General Public License 3.0
   660         // see LICENSE.txt
   661 
   662         #include "«@name»_fsm.h"
   663         #include <stdlib.h>
   664 
   665         const char *«@name»_state_name(int state)
   666         {
   667             switch (state) {
   668                 case End:
   669                     return "End";
   670                 case None:
   671                     return "None";
   672                 case Init:
   673                     return "InitState";
   674         ||
   675         for "func:distinctName(state[not(@name='InitState')])" {
   676             |>> case «@name»:
   677             |>>> return "«@name»";
   678         }
   679         ||
   680                 default:
   681                     return "unknown state";
   682             }
   683         }
   684 
   685         const char *«@name»_event_name(int event)
   686         {
   687             switch (event) {
   688                 case None:
   689                     return "None";
   690                 case Init:
   691                     return "Init";
   692         ||
   693         for "func:distinctName(state/event[not(@name='Init')])" {
   694             |>> case «@name»:
   695             |>>> return "«@name»";
   696         }
   697         ||
   698                 default:
   699                     return "unknown event";
   700             }
   701         }
   702 
   703 
   704         static char *_str(int n, bool hex)
   705         {
   706             char *buf = calloc(1, 24);
   707             assert(buf);
   708             if (!buf)
   709                 return NULL;
   710 
   711             if (hex)
   712                 snprintf(buf, 24, "%.4x", n);
   713             else
   714                 snprintf(buf, 24, "%d", n);
   715             return buf;
   716         }
   717 
   718         #define «@name»_ERR_LOG(t, d) log_event(session, (t), "«@name»", (d), "error")
   719 
   720         static PEP_STATUS _«@name»_ERR_LOG_int(PEP_SESSION session, char *t, int n, bool hex)
   721         {
   722             char *_buf = _str(n, hex);
   723             if (!_buf)
   724                 return PEP_OUT_OF_MEMORY;
   725             PEP_STATUS status = «@name»_ERR_LOG(t, _buf);
   726             free(_buf);
   727             return status;
   728         }
   729 
   730         #define «@name»_ERR_LOG_INT(t, n) _«@name»_ERR_LOG_int(session, (t), (n), false)
   731         #define «@name»_ERR_LOG_HEX(t, n) _«@name»_ERR_LOG_int(session, (t), (n), true)
   732 
   733         #ifndef SERVICE_LOG
   734         // SERVICE LOG is meant to check session->service_log in runtime config;
   735         // for older engines log more than needed
   736         #define SERVICE_LOG(session, t, n, d) log_event((session), (t), (n), (d), "service")
   737         #endif 
   738 
   739         #define «@name»_SERVICE_LOG(t, d) SERVICE_LOG(session, (t), "«@name»", (d))
   740 
   741         «@name»_state fsm_«@name»(
   742                 PEP_SESSION session,
   743                 «@name»_state state,
   744                 «@name»_event event
   745             )
   746         {
   747             assert(session);
   748             if (!session)
   749                 return invalid_state;
   750 
   751             switch (state) {
   752                 case None:
   753                     return «@name»_state_Init;
   754                 
   755                 `` apply "state", 2, mode=fsm
   756                 default:
   757                     «@name»_ERR_LOG_INT("invalid state", state);
   758                     return invalid_state;
   759             }
   760             
   761             return None;
   762         }
   763 
   764         ||
   765         }
   766     }
   767     
   768     template "state", mode=fsm {
   769         choose {
   770             when "@name='InitState'" | case «../@name»_state_Init:
   771             otherwise | case «@name»:
   772         }
   773         ||
   774             «../@name»_SERVICE_LOG("in state", "«@name»");
   775 
   776             switch (event) {
   777                 case None:
   778                     «../@name»_SERVICE_LOG("received None event", "ignoring");
   779                     break;
   780      
   781         ||
   782         if "not(event[@name='Init'])"
   783         ||
   784                 case Init:
   785                     // nothing to do
   786                     break;
   787 
   788         ||
   789         ||
   790                 `` apply "event", 2, mode=fsm
   791                 default:
   792                     // ignore events not handled here
   793                     «../@name»_SERVICE_LOG("ignoring event", KeySync_event_name(event));
   794                     break;
   795             }
   796             break;
   797 
   798         ||
   799     }
   800 
   801     template "event", mode=fsm {
   802         | case «@name»: {
   803         if "condition|action|send" |> PEP_STATUS status;
   804         if "condition" |> bool result = false;
   805         if "condition|action|send" |
   806         ||
   807             «../../@name»_SERVICE_LOG("received event", "«@name»");
   808             `` apply "transition|action|condition|send";
   809         ||
   810         if "name(*[last()])!='transition'" {
   811             |
   812             |> «../../@name»_SERVICE_LOG("remaining in state", "«../@name»");
   813             |> break;
   814         }
   815         ||
   816         }
   817         
   818         ||
   819     }
   820 
   821     template "transition" {
   822         const "fsm", "ancestor::fsm";
   823         ||
   824 
   825         «$fsm/@name»_SERVICE_LOG("transition to state", "«@target»");
   826         return «@target»;
   827         ||
   828     }
   829 
   830     template "send" {
   831         const "fsm", "ancestor::fsm";
   832         const "protocol", "ancestor::protocol";
   833         ||
   834 
   835         «$fsm/@name»_SERVICE_LOG("send message", "«@name»");
   836         status = send_«$protocol/@name»_message(session, «$fsm/@id», «$fsm/@name»__payload_PR_«yml:mixedCase(@name)»);
   837         ||
   838     }
   839 
   840     template "action" {
   841         const "fsm", "ancestor::fsm";
   842         ||
   843 
   844         «$fsm/@name»_SERVICE_LOG("do action", "«@name»");
   845         status = «@name»(session);
   846         if (status) {
   847             «$fsm/@name»_ERR_LOG_HEX("executing action «@name»() failed", status);
   848             return invalid_action;
   849         }
   850         ||
   851     }
   852 
   853     template "condition" {
   854         const "fsm", "ancestor::fsm";
   855         ||
   856 
   857         status = «@name»(session, &result);
   858         if (status) {
   859             «$fsm/@name»_ERR_LOG_HEX("computing condition «@name» failed", status);
   860             return invalid_condition;
   861         }
   862         if (result) {
   863             «$fsm/@name»_SERVICE_LOG("condition applies", "«@name»");
   864         ||
   865         apply "transition|action|condition|send";
   866         | }
   867     }
   868 }
   869