sync/gen_statemachine.ysl2
author Hussein Kasem
Fri, 30 Nov 2018 18:21:59 +0100
branchsync
changeset 3167 e7a7a92570b0
parent 3146 6135edc3ee6b
child 3240 80ee08316899
permissions -rw-r--r--
Use the statet checked by the if
     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 require a detached signature
   629             for "message[@security='unencrypted']"
   630                 |>> case «../@name»__payload_PR_«yml:mixedCase(@name)»:
   631             ||
   632                         if (!fpr) {
   633                             status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
   634                             goto the_end;
   635                         }
   636                         break;
   637 
   638             ||
   639         }
   640         if "message[@security='untrusted']"
   641         ||
   642                 // these messages must arrive encrypted
   643         `` for "message[@security='untrusted']" |>> case «../@name»__payload_PR_«yml:mixedCase(@name)»:
   644                     if (fpr || rating < PEP_rating_reliable) {
   645                         status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
   646                         goto the_end;
   647                     }
   648                     break;
   649 
   650         ||
   651         if "message[@security='trusted']"
   652         ||
   653                 // these messages must come through a trusted channel
   654         `` for "message[@security='trusted']" |>> case «ancestor::fsm/@name»__payload_PR_«yml:mixedCase(@name)»:
   655                     if (fpr || rating < PEP_rating_trusted) {
   656                         status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
   657                         goto the_end;
   658                     }
   659                     break;
   660 
   661                 default:
   662                     status = PEP_«yml:ucase(ancestor::protocol/@name)»_ILLEGAL_MESSAGE;
   663                     goto the_end;
   664             }
   665             break;
   666 
   667         ||
   668     }
   669 
   670     template "fsm", mode=event
   671     {
   672     ||
   673     case «../@name»_PR_«yml:lcase(@name)»: {
   674         switch (msg->choice.«yml:lcase(@name)».payload.present) {
   675     ||
   676     for "message"
   677     ||
   678             case «../@name»__payload_PR_«yml:mixedCase(@name)»:
   679                 ev->event = «@name»;
   680                 break;
   681     ||
   682     ||
   683             default:
   684                 // unknown message type
   685                 free(ev);
   686                 return NULL;
   687         }
   688         break;
   689     }
   690 
   691     ||
   692     }
   693 
   694     template "fsm", mode=driver
   695     ||
   696     case «../@name»_PR_«yml:lcase(@name)»: {
   697         int state = session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state;
   698         next_state = fsm_«@name»(session, state, event);
   699         if (next_state > None) {
   700             session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state = next_state;
   701             event = Init;
   702         }
   703         else if (next_state < None) {
   704             return PEP_STATEMACHINE_ERROR - next_state;
   705         }
   706         break;
   707     }
   708 
   709     ||
   710 
   711     template "fsm", mode=gen {
   712         document "generated/{@name}_fsm.h", "text" {
   713         ||
   714         // This file is under GNU General Public License 3.0
   715         // see LICENSE.txt
   716 
   717         #pragma once
   718 
   719         #include "«../@name»_impl.h"
   720 
   721         #ifdef __cplusplus
   722         extern "C" {
   723         #endif
   724 
   725         // state machine for «@name»
   726 
   727         // states
   728 
   729         typedef enum _«@name»_state {
   730             «@name»_state_None = None,
   731             «@name»_state_Init = Init,
   732         ||
   733         for "func:distinctName(state[not(@name='InitState')])"
   734             |> «@name»`if "position()!=last()" > , `
   735         ||
   736         } «@name»_state;
   737 
   738         // events
   739 
   740         typedef enum _«@name»_event {
   741             «@name»_event_None = None,
   742             «@name»_event_Init = Init,
   743         ||
   744         for "func:distinctName(state/event[not(not(../../message/@name=@name))])" {
   745             const "name", "@name";
   746             |> «$name» = «/protocol/fsm/message[@name=$name]/@id»,
   747         }
   748         for "func:distinctName(state/event[not(not(../../external/@name=@name))])" {
   749             const "name", "@name";
   750             |> «$name» = «/protocol/fsm/external[@name=$name]/@id»,
   751         }
   752         |> «@name»_event_Extra = Extra,
   753         for "func:distinctName(state/event[not(../../message/@name=@name or ../../external/@name=@name)])" {
   754             if "@name!='Init'"
   755                 |> «@name»`if "position()!=last()" > , `
   756         }
   757         ||
   758         } «@name»_event;
   759 
   760         // state machine
   761 
   762         const char *«@name»_state_name(int state);
   763         const char *«@name»_event_name(int event);
   764 
   765         // the state machine function is returning the next state in case of a
   766         // transition or None for staying
   767 
   768         «@name»_state fsm_«@name»(
   769                 PEP_SESSION session,
   770                 «@name»_state state,
   771                 «@name»_event event
   772             );
   773 
   774         #ifdef __cplusplus
   775         }
   776         #endif
   777 
   778         ||
   779         }
   780 
   781         document "generated/{@name}_fsm.c", "text" {
   782         ||
   783         // This file is under GNU General Public License 3.0
   784         // see LICENSE.txt
   785 
   786         #include "«@name»_fsm.h"
   787         #include <stdlib.h>
   788 
   789         const char *«@name»_state_name(int state)
   790         {
   791             switch (state) {
   792                 case End:
   793                     return "End";
   794                 case None:
   795                     return "None";
   796                 case Init:
   797                     return "InitState";
   798         ||
   799         for "func:distinctName(state[not(@name='InitState')])" {
   800             |>> case «@name»:
   801             |>>> return "«@name»";
   802         }
   803         ||
   804                 default:
   805                     return "unknown state";
   806             }
   807         }
   808 
   809         const char *«@name»_event_name(int event)
   810         {
   811             switch (event) {
   812                 case None:
   813                     return "None";
   814                 case Init:
   815                     return "Init";
   816         ||
   817         for "func:distinctName(state/event[not(@name='Init')])" {
   818             |>> case «@name»:
   819             |>>> return "«@name»";
   820         }
   821         ||
   822                 default:
   823                     return "unknown event";
   824             }
   825         }
   826 
   827 
   828         static char *_str(int n, bool hex)
   829         {
   830             char *buf = calloc(1, 24);
   831             assert(buf);
   832             if (!buf)
   833                 return NULL;
   834 
   835             if (hex)
   836                 snprintf(buf, 24, "%.4x", n);
   837             else
   838                 snprintf(buf, 24, "%d", n);
   839             return buf;
   840         }
   841 
   842         #define «@name»_ERR_LOG(t, d) log_event(session, (t), "«@name»", (d), "error")
   843 
   844         static PEP_STATUS _«@name»_ERR_LOG_int(PEP_SESSION session, char *t, int n, bool hex)
   845         {
   846             char *_buf = _str(n, hex);
   847             if (!_buf)
   848                 return PEP_OUT_OF_MEMORY;
   849             PEP_STATUS status = «@name»_ERR_LOG(t, _buf);
   850             free(_buf);
   851             return status;
   852         }
   853 
   854         #define «@name»_ERR_LOG_INT(t, n) _«@name»_ERR_LOG_int(session, (t), (n), false)
   855         #define «@name»_ERR_LOG_HEX(t, n) _«@name»_ERR_LOG_int(session, (t), (n), true)
   856 
   857         #ifndef SERVICE_LOG
   858         // SERVICE LOG is meant to check session->service_log in runtime config;
   859         // for older engines log more than needed
   860         #define SERVICE_LOG(session, t, n, d) log_event((session), (t), (n), (d), "service")
   861         #endif 
   862 
   863         #define «@name»_SERVICE_LOG(t, d) SERVICE_LOG(session, (t), "«@name»", (d))
   864 
   865         «@name»_state fsm_«@name»(
   866                 PEP_SESSION session,
   867                 «@name»_state state,
   868                 «@name»_event event
   869             )
   870         {
   871             assert(session);
   872             if (!session)
   873                 return invalid_state;
   874 
   875             switch (state) {
   876                 case None:
   877                     return «@name»_state_Init;
   878                 
   879                 `` apply "state", 2, mode=fsm
   880                 default:
   881                     «@name»_ERR_LOG_INT("invalid state", state);
   882                     return invalid_state;
   883             }
   884             
   885             return None;
   886         }
   887 
   888         ||
   889         }
   890     }
   891     
   892     template "state", mode=fsm {
   893         choose {
   894             when "@name='InitState'" | case «../@name»_state_Init:
   895             otherwise | case «@name»:
   896         }
   897         ||
   898             «../@name»_SERVICE_LOG("in state", "«@name»");
   899 
   900             switch (event) {
   901                 case None:
   902                     «../@name»_SERVICE_LOG("received None event", "ignoring");
   903                     break;
   904      
   905         ||
   906         if "not(event[@name='Init'])"
   907         ||
   908                 case Init:
   909                     // nothing to do
   910                     break;
   911 
   912         ||
   913         ||
   914                 `` apply "event", 2, mode=fsm
   915                 default:
   916                     // ignore events not handled here
   917                     «../@name»_SERVICE_LOG("ignoring event", KeySync_event_name(event));
   918                     break;
   919             }
   920             break;
   921 
   922         ||
   923     }
   924 
   925     template "event", mode=fsm {
   926         | case «@name»: {
   927         if "condition|action|send" |> PEP_STATUS status;
   928         if "condition" |> bool result = false;
   929         if "condition|action|send" |
   930         ||
   931             «../../@name»_SERVICE_LOG("received event", "«@name»");
   932             `` apply "transition|action|condition|else|send";
   933         ||
   934         if "name(*[last()])!='transition'" {
   935             |
   936             |> «../../@name»_SERVICE_LOG("remaining in state", "«../@name»");
   937             |> break;
   938         }
   939         ||
   940         }
   941         
   942         ||
   943     }
   944 
   945     template "transition" {
   946         const "fsm", "ancestor::fsm";
   947         ||
   948 
   949         «$fsm/@name»_SERVICE_LOG("transition to state", "«@target»");
   950         return «@target»;
   951         ||
   952     }
   953 
   954     template "send" {
   955         const "fsm", "ancestor::fsm";
   956         const "protocol", "ancestor::protocol";
   957         ||
   958 
   959         «$fsm/@name»_SERVICE_LOG("send message", "«@name»");
   960         status = send_«$protocol/@name»_message(session, «$fsm/@id», «$fsm/@name»__payload_PR_«yml:mixedCase(@name)»);
   961         if (status == PEP_OUT_OF_MEMORY)
   962             return out_of_memory;
   963         if (status) {
   964             «$fsm/@name»_ERR_LOG_HEX("sending «@name» failed", status);
   965             return cannot_send;
   966         }
   967         ||
   968     }
   969 
   970     template "action" {
   971         const "fsm", "ancestor::fsm";
   972         ||
   973 
   974         «$fsm/@name»_SERVICE_LOG("do action", "«@name»");
   975         status = «@name»(session);
   976         if (status == PEP_OUT_OF_MEMORY)
   977             return out_of_memory;
   978         if (status) {
   979             «$fsm/@name»_ERR_LOG_HEX("executing action «@name»() failed", status);
   980             return invalid_action;
   981         }
   982         ||
   983     }
   984 
   985     template "condition" {
   986         const "fsm", "ancestor::fsm";
   987         ||
   988 
   989         status = «@name»(session, &result);
   990         if (status == PEP_OUT_OF_MEMORY)
   991             return out_of_memory;
   992         if (status) {
   993             «$fsm/@name»_ERR_LOG_HEX("computing condition «@name» failed", status);
   994             return invalid_condition;
   995         }
   996         if (result) {
   997             «$fsm/@name»_SERVICE_LOG("condition applies", "«@name»");
   998         ||
   999         apply "transition|action|condition|else|send";
  1000         | }
  1001     }
  1002 
  1003     template "else" {
  1004         if "not(name(preceding-sibling::*[last()]) = 'condition')"
  1005             error "else without if";
  1006 
  1007         | else {
  1008         |> «ancestor::fsm/@name»_SERVICE_LOG("condition does not apply", "«preceding-sibling::*[last()]/@name»");
  1009         apply "transition|action|condition|else|send";
  1010         | }
  1011     }
  1012 }
  1013