sync/gen_statemachine.ysl2
author Volker Birk <vb@pep.foundation>
Tue, 20 Nov 2018 17:37:00 +0100
branchsync
changeset 3144 334a6c3faacd
parent 3143 81c80e27cbf7
child 3146 6135edc3ee6b
permissions -rw-r--r--
setting sync_state.common_from
     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         typedef struct «@name» «yml:ucase(@name)»;
    31         typedef int «yml:ucase(@name)»_PR;
    32 
    33         typedef struct «@name»_event {
    34             «yml:ucase(@name)»_PR fsm;
    35             int event;
    36             «yml:ucase(@name)» *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(«yml:ucase(@name)»_PR fsm, int event, «yml:ucase(@name)» *msg);
    55 
    56         #define «yml:ucase(@name)»_TIMEOUT_EVENT new_«@name»_event(«@name»_PR_NOTHING, 0, NULL);
    57 
    58 
    59         // free_«@name»_event() - free memory occupied by event
    60         //
    61         //  parameters:
    62         //      ev (in)         event to free
    63 
    64         void free_«@name»_event(«@name»_event_t *ev);
    65 
    66 
    67         #ifdef __cplusplus
    68         }
    69         #endif
    70 
    71         ||
    72 
    73         document "generated/{@name}_event.c", "text"
    74         ||
    75         // This file is under GNU General Public License 3.0
    76         // see LICENSE.txt
    77 
    78         #include "platform.h"
    79 
    80         #include "pEp_internal.h"
    81         #include "«@name»_event.h"
    82         #include "«@name»_func.h"
    83         `` for "fsm" | #include "«@name»_fsm.h"
    84 
    85         «@name»_event_t *new_«@name»_event(«yml:ucase(@name)»_PR fsm, int event, «@name»_t *msg)
    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             #include "../asn.1/«@name».h"
   130             
   131             #define «yml:ucase(@name)»_THRESHOLD «@threshold»
   132             `` for "fsm" | #define «yml:ucase(@name)»_THRESHOLD «@threshold»
   133 
   134             #ifdef __cplusplus
   135             extern "C" {
   136             #endif
   137 
   138             // conditions
   139 
   140             ||
   141             for "func:distinctName(*//condition)"
   142                 | PEP_STATUS «@name»(PEP_SESSION session, bool *result);
   143             ||
   144 
   145             // actions
   146 
   147             ||
   148             for "func:distinctName(*//action)"
   149                 | PEP_STATUS «@name»(PEP_SESSION session);
   150             ||
   151 
   152             // timeout handler
   153             
   154             ||
   155             for "fsm[@threshold > 0]"
   156                 | PEP_STATUS «@name»TimeoutHandler(PEP_SESSION session);
   157             ||
   158 
   159             // send message about an event to communication partners using state
   160 
   161             PEP_STATUS send_«@name»_message(
   162                     PEP_SESSION session, 
   163                     «@name»_PR fsm,
   164                     int message_type
   165                 );
   166 
   167             // receive message and store it in state
   168 
   169             PEP_STATUS recv_«@name»_event(
   170                     PEP_SESSION session,
   171                     «@name»_event_t *ev
   172                 );
   173         
   174             // state machine driver
   175             // if fsm or event set to 0 use fields in src if present
   176 
   177             PEP_STATUS «@name»_driver(
   178                     PEP_SESSION session,
   179                     «@name»_PR fsm,
   180                     int event
   181                 );
   182 
   183             // API being used by the engine internally
   184 
   185             // call this if you need to signal an external event
   186 
   187             PEP_STATUS signal_«@name»_event(
   188                     PEP_SESSION session, 
   189                     «@name»_PR fsm,
   190                     int event
   191                 );
   192             
   193             // call this if you are a transport and are receiving
   194             // a «@name» message
   195 
   196             PEP_STATUS signal_«@name»_message(
   197                     PEP_SESSION session, 
   198                     PEP_rating rating,
   199                     const char *data,
   200                     size_t size,
   201                     const char *fpr
   202                 );
   203 
   204             #ifdef __cplusplus
   205             }
   206             #endif
   207 
   208             ||
   209         }
   210 
   211         document "generated/{@name}_impl.c", "text" {
   212             ||
   213             // This file is under GNU General Public License 3.0
   214             // see LICENSE.txt
   215         
   216             #include "«@name»_impl.h"
   217             #include "pEp_internal.h"
   218             #include "«@name»_event.h"
   219             #include "«yml:lcase(@name)»_codec.h"
   220             #include "baseprotocol.h"
   221             `` for "fsm" | #include "«@name»_fsm.h"
   222 
   223             `` apply "fsm", 0, mode=timeout
   224             PEP_STATUS «@name»_driver(
   225                     PEP_SESSION session,
   226                     «@name»_PR fsm,
   227                     int event
   228                 )
   229             {
   230                 assert(session);
   231                 if (!session)
   232                     return PEP_ILLEGAL_VALUE;
   233 
   234                 switch (fsm) {
   235                     case None:
   236                         if (!event) {
   237                             // timeout occured
   238                         `` for "fsm" |>>>> «../@name»_driver(session, «../@name»_PR_«yml:lcase(@name)», None);
   239                             return PEP_STATUS_OK;
   240                         }
   241                         return PEP_ILLEGAL_VALUE;
   242 
   243                     `` apply "fsm", mode=reset_state_machine;
   244                     default:
   245                         return PEP_ILLEGAL_VALUE;
   246                 }
   247 
   248                 int next_state = None;
   249                 do {
   250                     switch (fsm) {
   251                         `` apply "fsm", 3, mode=driver               
   252                         default:
   253                             return PEP_ILLEGAL_VALUE;
   254                     }
   255                 }  while (next_state);
   256 
   257                 return PEP_STATUS_OK;
   258             }
   259 
   260             PEP_STATUS signal_«@name»_event(
   261                     PEP_SESSION session, 
   262                     «@name»_PR fsm,
   263                     int event
   264                 )
   265             {
   266                 «@name»_t *msg = NULL;
   267                 «@name»_event_t *ev = NULL;
   268 
   269                 assert(session && fsm > 0 && event > None);
   270                 if (!(session && fsm > 0 && event > None))
   271                     return PEP_ILLEGAL_VALUE;
   272 
   273                 PEP_STATUS status = PEP_STATUS_OK;
   274 
   275                 if (!session->inject_«yml:lcase(@name)»_event)
   276                    return PEP_«yml:ucase(@name)»_NO_INJECT_CALLBACK;
   277 
   278                 if (event < Extra) {
   279                     msg = new_«@name»_message(fsm, event);
   280                     if (!msg) {
   281                         status = PEP_OUT_OF_MEMORY;
   282                         goto the_end;
   283                     }
   284 
   285                     status = update_«@name»_message(session, msg);
   286                     if (status)
   287                         goto the_end;
   288                 }
   289 
   290                 ev = new_«@name»_event(fsm, event, msg);
   291                 if (!ev) {
   292                     status = PEP_OUT_OF_MEMORY;
   293                     goto the_end;
   294                 }
   295 
   296                 int result = session->inject_«yml:lcase(@name)»_event(ev,
   297                         session->«yml:lcase(@name)»_management);
   298                 if (result) {
   299                     status = PEP_STATEMACHINE_ERROR;
   300                     goto the_end;
   301                 }
   302                 return PEP_STATUS_OK;
   303 
   304             the_end:
   305                 free_«@name»_event(ev);
   306                 free_«@name»_message(msg);
   307                 return status;
   308             }
   309 
   310             PEP_STATUS signal_«@name»_message(
   311                     PEP_SESSION session, 
   312                     PEP_rating rating,
   313                     const char *data,
   314                     size_t size,
   315                     const char *fpr
   316                 )
   317             {
   318                 assert(session && data && size);
   319                 if (!(session && data && size))
   320                     return PEP_ILLEGAL_VALUE;
   321 
   322                 if (!session->inject_«yml:lcase(@name)»_event)
   323                    return PEP_«yml:ucase(@name)»_NO_INJECT_CALLBACK;
   324 
   325                 «@name»_t *msg = NULL;
   326                 PEP_STATUS status = decode_«@name»_message(data, size, &msg);
   327                 if (status)
   328                     return status;
   329 
   330                 «@name»_event_t *ev = NULL;
   331 
   332                 «@name»_PR fsm = msg->present;
   333                 int event = 0;
   334 
   335                 switch (fsm) {
   336                     `` apply "fsm", 2, mode=signal_message
   337                     default:
   338                         status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
   339                         goto the_end;
   340                 }
   341 
   342                 if (fpr) {
   343                     if (session->«yml:lcase(@name)»_state.common.from->fpr)
   344                         free(session->«yml:lcase(@name)»_state.common.from->fpr);
   345                     session->«yml:lcase(@name)»_state.common.from->fpr = strdup(fpr);
   346                     assert(session->«yml:lcase(@name)»_state.common.from->fpr);
   347                     if (!session->«yml:lcase(@name)»_state.common.from->fpr) {
   348                         status = PEP_OUT_OF_MEMORY;
   349                         goto the_end;
   350                     }
   351                 }
   352 
   353                 ev = new_«@name»_event(fsm, event, msg);
   354                 if (!ev) {
   355                     status = PEP_OUT_OF_MEMORY;
   356                     goto the_end;
   357                 }
   358 
   359                 int result = session->inject_«yml:lcase(@name)»_event(ev,
   360                         session->«yml:lcase(@name)»_management);
   361                 if (result) {
   362                     status = PEP_STATEMACHINE_ERROR;
   363                     goto the_end;
   364                 }
   365 
   366                 return PEP_STATUS_OK;
   367 
   368             the_end:
   369                 free_«@name»_event(ev);
   370                 free_«@name»_message(msg);
   371                 return status;
   372             }
   373 
   374             PEP_STATUS send_«@name»_message(
   375                     PEP_SESSION session, 
   376                     «@name»_PR fsm,
   377                     int message_type
   378                 )
   379             {
   380                 PEP_STATUS status = PEP_STATUS_OK;
   381 
   382                 assert(session && fsm > None && message_type > None);
   383                 if (!(session && fsm > None && message_type > None))
   384                     return PEP_ILLEGAL_VALUE;
   385                 
   386                 «@name»_t *msg = new_«@name»_message(fsm, message_type);
   387                 if (!msg)
   388                     return PEP_OUT_OF_MEMORY;
   389 
   390                 char *data = NULL;
   391                 message *m = NULL;
   392                 identity_list *channels = NULL;
   393 
   394                 status = update_«@name»_message(session, msg);
   395                 if (status)
   396                     goto the_end;
   397 
   398                 size_t size = 0;
   399                 status = encode_«@name»_message(msg, &data, &size);
   400                 if (status)
   401                     goto the_end;
   402 
   403                 switch (message_type) {
   404                     // these messages are being broadcasted
   405                     `` for "fsm/message[@type='broadcast']" |>> case «../@name»__payload_PR_«yml:mixedCase(@name)»:
   406                         status = _own_identities_retrieve(session, &channels, PEP_idf_not_for_«yml:lcase(@name)»);
   407                         if (status)
   408                             goto the_end;
   409 
   410                         if (!(channels && channels->ident)) {
   411                             status = PEP_«yml:ucase(@name)»_NO_CHANNEL;
   412                             goto the_end;
   413                         }
   414                         break;
   415 
   416                     // these go anycast; previously used address is sticky (unicast)
   417                     `` for "fsm/message[@type='anycast']" |>> case «../@name»__payload_PR_«yml:mixedCase(@name)»:
   418                         if (!session->«yml:lcase(@name)»_state.common.from `> |`|
   419                             (session->«yml:lcase(@name)»_state.common.from->flags &
   420                             PEP_idf_not_for_«yml:lcase(@name)»)) {
   421 
   422                             // no address available yet, try to find one
   423                             status = _own_identities_retrieve(session, &channels, PEP_idf_not_for_«yml:lcase(@name)»);
   424                             if (!status)
   425                                 goto the_end;
   426                             break;
   427 
   428                             if (channels && channels->ident) {
   429                                 // only need the first one
   430                                 free_identity_list(channels->next);
   431                                 channels->next = NULL;
   432                             }
   433                             else {
   434                                 status = PEP_«yml:ucase(@name)»_NO_CHANNEL;
   435                                 goto the_end;
   436                             }
   437                         }
   438                         else {
   439                             pEp_identity *channel = identity_dup(session->«yml:lcase(@name)»_state.common.from);
   440                             if (!channel) {
   441                                 status = PEP_OUT_OF_MEMORY;
   442                                 goto the_end;
   443                             }
   444 
   445                             channels = new_identity_list(channel);
   446                             if (!channels) {
   447                                 status = PEP_OUT_OF_MEMORY;
   448                                 goto the_end;
   449                             }
   450                         }
   451 
   452                     default:
   453                         status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
   454                         goto the_end;
   455                 }
   456 
   457                 for (identity_list *li = channels; li && li->ident ; li = li->next) {
   458                     message *_m = NULL;
   459                     char *_data = NULL;
   460                     
   461                     _data = malloc(size);
   462                     assert(_data);
   463                     if (!_data) {
   464                         status = PEP_OUT_OF_MEMORY;
   465                         goto the_end;
   466                     }
   467                     memcpy(_data, data, size);
   468 
   469                     switch (message_type) {
   470                     `` for "fsm/message[@security='unencrypted']" |>>> case «../@name»__payload_PR_«yml:mixedCase(@name)»:
   471                             status = base_prepare_message(
   472                                     session,
   473                                     li->ident,
   474                                     li->ident,
   475                                     _data,
   476                                     size,
   477                                     li->ident->fpr,
   478                                     &_m
   479                                 );
   480                             if (status) {
   481                                 free(_data);
   482                                 goto the_end;
   483                             }
   484                             attach_own_key(session, _m);
   485                             m = _m;
   486                             break;
   487 
   488                         default:
   489                             status = base_prepare_message(
   490                                     session,
   491                                     li->ident,
   492                                     li->ident,
   493                                     _data,
   494                                     size,
   495                                     NULL,
   496                                     &_m
   497                                 );
   498                             if (status) {
   499                                 free(_data);
   500                                 goto the_end;
   501                             }
   502                             status = encrypt_message(session, _m, NULL, &m, PEP_enc_PEP, 0);
   503                             if (status) {
   504                                 status = PEP_«yml:ucase(@name)»_CANNOT_ENCRYPT;
   505                                 goto the_end;
   506                             }
   507                             free_message(_m);
   508                     }
   509 
   510                     status = session->messageToSend(m);
   511                     m = NULL;
   512                 }
   513 
   514             the_end:
   515                 free_identity_list(channels);
   516                 free_message(m);
   517                 free(data);
   518                 free_«@name»_message(msg);
   519                 return status;
   520             }
   521 
   522             PEP_STATUS recv_«@name»_event(
   523                     PEP_SESSION session,
   524                     «@name»_event_t *ev
   525                 )
   526             {
   527                 assert(session && ev);
   528                 if (!(session && ev))
   529                     return PEP_ILLEGAL_VALUE;
   530 
   531                 PEP_STATUS status = PEP_STATUS_OK;
   532                 «@name»_PR fsm = (int) None;
   533                 int event = None;
   534 
   535                 if (ev->event > None && ev->event < Extra) {
   536                     status = update_«@name»_state(session, ev->msg, &fsm, &event);
   537                     if (status)
   538                         goto the_end;
   539 
   540                     if (ev->fsm) {
   541                         if (ev->fsm != fsm |`> |` ev->event != event) {
   542                             status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
   543                             goto the_end;
   544                         }
   545                     }
   546                     else if (ev->event) {
   547                         status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
   548                         goto the_end;
   549                     }
   550                 }
   551                 else {
   552                     fsm = ev->fsm;
   553                     event = ev->event;
   554                 }
   555 
   556                 status = «@name»_driver(session, fsm, event);
   557 
   558             the_end:
   559                 free_«@name»_event(ev);
   560                 return status;
   561             }
   562 
   563             ||
   564         }
   565 
   566         apply "fsm", 0, mode=gen;
   567     }
   568 
   569     template "fsm", mode=timeout
   570     ||
   571     static bool _«@name»_timeout(int state)
   572     {
   573         static int last_state = None;
   574         static time_t switch_time = 0;
   575 
   576         if (state > Init) {
   577             if (state == last_state) {
   578                 if (time(NULL) - switch_time > «yml:ucase(@name)»_THRESHOLD) {
   579                     last_state = None;
   580                     switch_time = 0;
   581                     return true;
   582                 }
   583             }
   584             else {
   585                 last_state = state;
   586                 switch_time = time(NULL);
   587             }
   588         }
   589         else {
   590             last_state = None;
   591             switch_time = 0;
   592         }
   593 
   594         return false;
   595     }
   596 
   597     ||
   598 
   599     template "fsm", mode=reset_state_machine
   600     ||
   601         case «../@name»_PR_«yml:lcase(@name)»: {
   602             int state = session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state;
   603             switch (state) {
   604                 `` for "state[@name!='InitState' and @timeout != 'off']" |>>> case «@name»:
   605                     if (_«@name»_timeout(state)) {
   606                         session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state = Init;
   607                         event = Init;
   608                         `` if "@threshold > 0" |>>>>> «@name»TimeoutHandler(session);
   609                     }
   610                     break;
   611                 
   612                 default:
   613                     _«@name»_timeout(None);
   614             }
   615             break;
   616         }
   617 
   618     ||
   619 
   620     template "fsm", mode=signal_message
   621     {
   622         ||
   623         case «../@name»_PR_«yml:lcase(@name)»:
   624             event = msg->choice.«yml:lcase(@name)».payload.present;
   625             switch (event) {
   626         ||
   627         if "message[@security='unencrypted']" {
   628             |         // these messages are going untested
   629             for "message[@security='unencrypted']"
   630                 |>> case «../@name»__payload_PR_«yml:mixedCase(@name)»:
   631             ||
   632                         break;
   633 
   634             ||
   635         }
   636         if "message[@security='untrusted']"
   637         ||
   638                 // these messages must arrive encrypted
   639         `` for "message[@security='untrusted']" |>> case «../@name»__payload_PR_«yml:mixedCase(@name)»:
   640                     if (rating < PEP_rating_reliable) {
   641                         status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
   642                         goto the_end;
   643                     }
   644                     break;
   645 
   646         ||
   647         if "message[@security='trusted']"
   648         ||
   649                 // these messages must come through a trusted channel
   650         `` for "message[@security='trusted']" |>> case «ancestor::fsm/@name»__payload_PR_«yml:mixedCase(@name)»:
   651                     if (rating < PEP_rating_trusted) {
   652                         status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
   653                         goto the_end;
   654                     }
   655                     break;
   656 
   657                 default:
   658                     status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
   659                     goto the_end;
   660             }
   661             break;
   662 
   663         ||
   664     }
   665 
   666     template "fsm", mode=event
   667     {
   668     ||
   669     case «../@name»_PR_«yml:lcase(@name)»: {
   670         switch (msg->choice.«yml:lcase(@name)».payload.present) {
   671     ||
   672     for "message"
   673     ||
   674             case «../@name»__payload_PR_«yml:mixedCase(@name)»:
   675                 ev->event = «@name»;
   676                 break;
   677     ||
   678     ||
   679             default:
   680                 // unknown message type
   681                 free(ev);
   682                 return NULL;
   683         }
   684         break;
   685     }
   686 
   687     ||
   688     }
   689 
   690     template "fsm", mode=driver
   691     ||
   692     case «../@name»_PR_«yml:lcase(@name)»: {
   693         int state = session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state;
   694         next_state = fsm_«@name»(session, state, event);
   695         if (next_state > None) {
   696             session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state = next_state;
   697             event = Init;
   698         }
   699         else if (next_state < None) {
   700             return PEP_STATEMACHINE_ERROR - state;
   701         }
   702         break;
   703     }
   704 
   705     ||
   706 
   707     template "fsm", mode=gen {
   708         document "generated/{@name}_fsm.h", "text" {
   709         ||
   710         // This file is under GNU General Public License 3.0
   711         // see LICENSE.txt
   712 
   713         #pragma once
   714 
   715         #include "«../@name»_impl.h"
   716 
   717         #ifdef __cplusplus
   718         extern "C" {
   719         #endif
   720 
   721         // state machine for «@name»
   722 
   723         // states
   724 
   725         typedef enum _«@name»_state {
   726             «@name»_state_None = None,
   727             «@name»_state_Init = Init,
   728         ||
   729         for "func:distinctName(state[not(@name='InitState')])"
   730             |> «@name»`if "position()!=last()" > , `
   731         ||
   732         } «@name»_state;
   733 
   734         // events
   735 
   736         typedef enum _«@name»_event {
   737             «@name»_event_None = None,
   738             «@name»_event_Init = Init,
   739         ||
   740         for "func:distinctName(state/event[not(not(../../message/@name=@name))])" {
   741             const "name", "@name";
   742             |> «$name» = «/protocol/fsm/message[@name=$name]/@id»,
   743         }
   744         for "func:distinctName(state/event[not(not(../../external/@name=@name))])" {
   745             const "name", "@name";
   746             |> «$name» = «/protocol/fsm/external[@name=$name]/@id»,
   747         }
   748         |> «@name»_event_Extra = Extra,
   749         for "func:distinctName(state/event[not(../../message/@name=@name or ../../external/@name=@name)])" {
   750             if "@name!='Init'"
   751                 |> «@name»`if "position()!=last()" > , `
   752         }
   753         ||
   754         } «@name»_event;
   755 
   756         // state machine
   757 
   758         const char *«@name»_state_name(int state);
   759         const char *«@name»_event_name(int event);
   760 
   761         // the state machine function is returning the next state in case of a
   762         // transition or None for staying
   763 
   764         «@name»_state fsm_«@name»(
   765                 PEP_SESSION session,
   766                 «@name»_state state,
   767                 «@name»_event event
   768             );
   769 
   770         #ifdef __cplusplus
   771         }
   772         #endif
   773 
   774         ||
   775         }
   776 
   777         document "generated/{@name}_fsm.c", "text" {
   778         ||
   779         // This file is under GNU General Public License 3.0
   780         // see LICENSE.txt
   781 
   782         #include "«@name»_fsm.h"
   783         #include <stdlib.h>
   784 
   785         const char *«@name»_state_name(int state)
   786         {
   787             switch (state) {
   788                 case End:
   789                     return "End";
   790                 case None:
   791                     return "None";
   792                 case Init:
   793                     return "InitState";
   794         ||
   795         for "func:distinctName(state[not(@name='InitState')])" {
   796             |>> case «@name»:
   797             |>>> return "«@name»";
   798         }
   799         ||
   800                 default:
   801                     return "unknown state";
   802             }
   803         }
   804 
   805         const char *«@name»_event_name(int event)
   806         {
   807             switch (event) {
   808                 case None:
   809                     return "None";
   810                 case Init:
   811                     return "Init";
   812         ||
   813         for "func:distinctName(state/event[not(@name='Init')])" {
   814             |>> case «@name»:
   815             |>>> return "«@name»";
   816         }
   817         ||
   818                 default:
   819                     return "unknown event";
   820             }
   821         }
   822 
   823 
   824         static char *_str(int n, bool hex)
   825         {
   826             char *buf = calloc(1, 24);
   827             assert(buf);
   828             if (!buf)
   829                 return NULL;
   830 
   831             if (hex)
   832                 snprintf(buf, 24, "%.4x", n);
   833             else
   834                 snprintf(buf, 24, "%d", n);
   835             return buf;
   836         }
   837 
   838         #define «@name»_ERR_LOG(t, d) log_event(session, (t), "«@name»", (d), "error")
   839 
   840         static PEP_STATUS _«@name»_ERR_LOG_int(PEP_SESSION session, char *t, int n, bool hex)
   841         {
   842             char *_buf = _str(n, hex);
   843             if (!_buf)
   844                 return PEP_OUT_OF_MEMORY;
   845             PEP_STATUS status = «@name»_ERR_LOG(t, _buf);
   846             free(_buf);
   847             return status;
   848         }
   849 
   850         #define «@name»_ERR_LOG_INT(t, n) _«@name»_ERR_LOG_int(session, (t), (n), false)
   851         #define «@name»_ERR_LOG_HEX(t, n) _«@name»_ERR_LOG_int(session, (t), (n), true)
   852 
   853         #ifndef SERVICE_LOG
   854         // SERVICE LOG is meant to check session->service_log in runtime config;
   855         // for older engines log more than needed
   856         #define SERVICE_LOG(session, t, n, d) log_event((session), (t), (n), (d), "service")
   857         #endif 
   858 
   859         #define «@name»_SERVICE_LOG(t, d) SERVICE_LOG(session, (t), "«@name»", (d))
   860 
   861         «@name»_state fsm_«@name»(
   862                 PEP_SESSION session,
   863                 «@name»_state state,
   864                 «@name»_event event
   865             )
   866         {
   867             assert(session);
   868             if (!session)
   869                 return invalid_state;
   870 
   871             switch (state) {
   872                 case None:
   873                     return «@name»_state_Init;
   874                 
   875                 `` apply "state", 2, mode=fsm
   876                 default:
   877                     «@name»_ERR_LOG_INT("invalid state", state);
   878                     return invalid_state;
   879             }
   880             
   881             return None;
   882         }
   883 
   884         ||
   885         }
   886     }
   887     
   888     template "state", mode=fsm {
   889         choose {
   890             when "@name='InitState'" | case «../@name»_state_Init:
   891             otherwise | case «@name»:
   892         }
   893         ||
   894             «../@name»_SERVICE_LOG("in state", "«@name»");
   895 
   896             switch (event) {
   897                 case None:
   898                     «../@name»_SERVICE_LOG("received None event", "ignoring");
   899                     break;
   900      
   901         ||
   902         if "not(event[@name='Init'])"
   903         ||
   904                 case Init:
   905                     // nothing to do
   906                     break;
   907 
   908         ||
   909         ||
   910                 `` apply "event", 2, mode=fsm
   911                 default:
   912                     // ignore events not handled here
   913                     «../@name»_SERVICE_LOG("ignoring event", KeySync_event_name(event));
   914                     break;
   915             }
   916             break;
   917 
   918         ||
   919     }
   920 
   921     template "event", mode=fsm {
   922         | case «@name»: {
   923         if "condition|action|send" |> PEP_STATUS status;
   924         if "condition" |> bool result = false;
   925         if "condition|action|send" |
   926         ||
   927             «../../@name»_SERVICE_LOG("received event", "«@name»");
   928             `` apply "transition|action|condition|else|send";
   929         ||
   930         if "name(*[last()])!='transition'" {
   931             |
   932             |> «../../@name»_SERVICE_LOG("remaining in state", "«../@name»");
   933             |> break;
   934         }
   935         ||
   936         }
   937         
   938         ||
   939     }
   940 
   941     template "transition" {
   942         const "fsm", "ancestor::fsm";
   943         ||
   944 
   945         «$fsm/@name»_SERVICE_LOG("transition to state", "«@target»");
   946         return «@target»;
   947         ||
   948     }
   949 
   950     template "send" {
   951         const "fsm", "ancestor::fsm";
   952         const "protocol", "ancestor::protocol";
   953         ||
   954 
   955         «$fsm/@name»_SERVICE_LOG("send message", "«@name»");
   956         status = send_«$protocol/@name»_message(session, «$fsm/@id», «$fsm/@name»__payload_PR_«yml:mixedCase(@name)»);
   957         if (status == PEP_OUT_OF_MEMORY)
   958             return out_of_memory;
   959         if (status) {
   960             «$fsm/@name»_ERR_LOG_HEX("sending «@name» failed", status);
   961             return cannot_send;
   962         }
   963         ||
   964     }
   965 
   966     template "action" {
   967         const "fsm", "ancestor::fsm";
   968         ||
   969 
   970         «$fsm/@name»_SERVICE_LOG("do action", "«@name»");
   971         status = «@name»(session);
   972         if (status == PEP_OUT_OF_MEMORY)
   973             return out_of_memory;
   974         if (status) {
   975             «$fsm/@name»_ERR_LOG_HEX("executing action «@name»() failed", status);
   976             return invalid_action;
   977         }
   978         ||
   979     }
   980 
   981     template "condition" {
   982         const "fsm", "ancestor::fsm";
   983         ||
   984 
   985         status = «@name»(session, &result);
   986         if (status == PEP_OUT_OF_MEMORY)
   987             return out_of_memory;
   988         if (status) {
   989             «$fsm/@name»_ERR_LOG_HEX("computing condition «@name» failed", status);
   990             return invalid_condition;
   991         }
   992         if (result) {
   993             «$fsm/@name»_SERVICE_LOG("condition applies", "«@name»");
   994         ||
   995         apply "transition|action|condition|else|send";
   996         | }
   997     }
   998 
   999     template "else" {
  1000         if "not(name(preceding-sibling::*[last()]) = 'condition')"
  1001             error "else without if";
  1002 
  1003         | else {
  1004         |> «ancestor::fsm/@name»_SERVICE_LOG("condition does not apply", "«preceding-sibling::*[last()]/@name»");
  1005         apply "transition|action|condition|else|send";
  1006         | }
  1007     }
  1008 }
  1009