sync/gen_statemachine.ysl2
author Volker Birk <vb@pep.foundation>
Thu, 23 Aug 2018 17:14:41 +0200
branchsync
changeset 2888 5359f8f5cc5c
parent 2885 9907d249919e
child 2889 683eba0e3d81
permissions -rw-r--r--
signalling of receiving sync message
     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         DYNAMIC_API «@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         DYNAMIC_API 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         DYNAMIC_API «@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         DYNAMIC_API 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 "message_api.h"
   128             #include "«@name»_event.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 a sync
   183             // 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 "«@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 = new_«@name»_event(None, None, msg);
   303                 if (!ev) {
   304                     status = PEP_OUT_OF_MEMORY;
   305                     goto the_end;
   306                 }
   307 
   308                 int result = session->inject_«yml:lcase(@name)»_event(ev,
   309                         session->«yml:lcase(@name)»_management);
   310                 if (result) {
   311                     status = PEP_STATEMACHINE_ERROR;
   312                     goto the_end;
   313                 }
   314                 return PEP_STATUS_OK;
   315 
   316             the_end:
   317                 free_«@name»_event(ev);
   318                 free_«@name»_message(msg);
   319                 return status;
   320             }
   321 
   322             PEP_STATUS send_«@name»_message(
   323                     PEP_SESSION session, 
   324                     «@name»_PR fsm,
   325                     int message_type
   326                 )
   327             {
   328                 PEP_STATUS status = PEP_STATUS_OK;
   329 
   330                 assert(session && fsm > None && message_type > None);
   331                 if (!(session && fsm > None && message_type > None))
   332                     return PEP_ILLEGAL_VALUE;
   333                 
   334                 «@name»_t *msg = new_«@name»_message(fsm, message_type);
   335                 if (!msg)
   336                     return PEP_OUT_OF_MEMORY;
   337 
   338                 char *data = NULL;
   339                 message *m = NULL;
   340                 identity_list *channels = NULL;
   341 
   342                 status = update_«@name»_message(session, msg);
   343                 if (status)
   344                     goto the_end;
   345 
   346                 size_t size = 0;
   347                 status = encode_«@name»_message(msg, &data, &size);
   348                 if (status)
   349                     goto the_end;
   350 
   351                 switch (message_type) {
   352                     // these messages are being broadcasted
   353                     `` for "fsm/message[@type='broadcast']" |>> case «../@name»__payload_PR_«yml:mixedCase(@name)»:
   354                         status = _own_identities_retrieve(session, &channels, PEP_idf_not_for_«yml:lcase(@name)»);
   355                         if (status)
   356                             goto the_end;
   357 
   358                         if (!(channels && channels->ident)) {
   359                             status = PEP_«yml:ucase(@name)»_NO_CHANNEL;
   360                             goto the_end;
   361                         }
   362                         break;
   363 
   364                     // these go anycast; previously used address is sticky (unicast)
   365                     `` for "fsm/message[@type='anycast']" |>> case «../@name»__payload_PR_«yml:mixedCase(@name)»:
   366                         if (!session->«yml:lcase(@name)»_state.common.from `> |`|
   367                             (session->«yml:lcase(@name)»_state.common.from->flags &
   368                             PEP_idf_not_for_«yml:lcase(@name)»)) {
   369 
   370                             // no address available yet, try to find one
   371                             status = _own_identities_retrieve(session, &channels, PEP_idf_not_for_«yml:lcase(@name)»);
   372                             if (!status)
   373                                 goto the_end;
   374                             break;
   375 
   376                             if (channels && channels->ident) {
   377                                 // only need the first one
   378                                 free_identity_list(channels->next);
   379                                 channels->next = NULL;
   380                             }
   381                             else {
   382                                 status = PEP_«yml:ucase(@name)»_NO_CHANNEL;
   383                                 goto the_end;
   384                             }
   385                         }
   386                         else {
   387                             pEp_identity *channel = identity_dup(session->«yml:lcase(@name)»_state.common.from);
   388                             if (!channel) {
   389                                 status = PEP_OUT_OF_MEMORY;
   390                                 goto the_end;
   391                             }
   392 
   393                             channels = new_identity_list(channel);
   394                             if (!channels) {
   395                                 status = PEP_OUT_OF_MEMORY;
   396                                 goto the_end;
   397                             }
   398                         }
   399 
   400                     default:
   401                         status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
   402                         goto the_end;
   403                 }
   404 
   405                 for (identity_list *li = channels; li && li->ident ; li = li->next) {
   406                     message *_m = NULL;
   407 
   408                     status = base_prepare_message(
   409                             li->ident,
   410                             li->ident,
   411                             data,
   412                             size,
   413                             &_m
   414                         );
   415                     if (status)
   416                         goto the_end;
   417                     data = NULL;
   418 
   419                     switch (message_type) {
   420                     `` for "fsm/message[@security='unencrypted']" |>>> case «../@name»__payload_PR_«yml:mixedCase(@name)»:
   421                             m = _m;
   422                             break;
   423 
   424                         default:
   425                             status = encrypt_message(session, _m, NULL, &m, PEP_enc_PEP, 0);
   426                             if (status) {
   427                                 status = PEP_SYNC_CANNOT_ENCRYPT;
   428                                 goto the_end;
   429                             }
   430                             free_message(_m);
   431                     }
   432 
   433                     status = session->messageToSend(session->«yml:lcase(@name)»_obj, m);
   434                     m = NULL;
   435                 }
   436 
   437             the_end:
   438                 free_identity_list(channels);
   439                 free_message(m);
   440                 free(data);
   441                 free_«@name»_message(msg);
   442                 return status;
   443             }
   444 
   445             PEP_STATUS recv_«@name»_event(
   446                     PEP_SESSION session,
   447                     «@name»_event_t *ev
   448                 )
   449             {
   450                 assert(session && ev);
   451                 if (!(session && ev))
   452                     return PEP_ILLEGAL_VALUE;
   453 
   454                 PEP_STATUS status = PEP_STATUS_OK;
   455                 «@name»_PR fsm = (int) None;
   456                 int event = None;
   457 
   458                 if (ev->event < Extra) {
   459                     status = update_«@name»_state(session, ev->msg, &fsm, &event);
   460                     if (status)
   461                         goto the_end;
   462 
   463                     if (ev->fsm) {
   464                         if (ev->fsm != fsm |`> |` ev->event != event) {
   465                             status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
   466                             goto the_end;
   467                         }
   468                     }
   469                     else if (ev->event) {
   470                         status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
   471                         goto the_end;
   472                     }
   473                 }
   474                 else {
   475                     fsm = ev->fsm;
   476                     event = ev->event;
   477                 }
   478 
   479                 status = «@name»_driver(session, fsm, event);
   480 
   481             the_end:
   482                 free_«@name»_event(ev);
   483                 return status;
   484             }
   485 
   486             ||
   487         }
   488 
   489         apply "fsm", 0, mode=gen;
   490     }
   491 
   492     template "fsm", mode=event
   493     {
   494     ||
   495     case «../@name»_PR_«yml:lcase(@name)»: {
   496         switch (msg->choice.«yml:lcase(@name)».payload.present) {
   497     ||
   498     for "message"
   499     ||
   500             case «../@name»__payload_PR_«yml:mixedCase(@name)»:
   501                 ev->event = «@name»;
   502                 break;
   503     ||
   504     ||
   505             default:
   506                 // unknown message type
   507                 free(ev);
   508                 return NULL;
   509         }
   510         break;
   511     }
   512 
   513     ||
   514     }
   515 
   516     template "fsm", mode=driver
   517     ||
   518     case «../@name»_PR_«yml:lcase(@name)»: {
   519         int state = session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state;
   520         next_state = fsm_«@name»(session, state, event);
   521         if (next_state > None) {
   522             session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state = next_state;
   523             event = Init;
   524         }
   525         else if (next_state < None) {
   526             return PEP_STATEMACHINE_ERROR - state;
   527         }
   528         break;
   529     }
   530 
   531     ||
   532 
   533     template "fsm", mode=gen {
   534         document "generated/{@name}_fsm.h", "text" {
   535         ||
   536         // This file is under GNU General Public License 3.0
   537         // see LICENSE.txt
   538 
   539         #pragma once
   540 
   541         #include "«../@name»_impl.h"
   542 
   543         #ifdef __cplusplus
   544         extern "C" {
   545         #endif
   546 
   547         // state machine for «@name»
   548 
   549         // states
   550 
   551         typedef enum _«@name»_state {
   552             «@name»_state_None = None,
   553             «@name»_state_Init = Init,
   554         ||
   555         for "func:distinctName(state[not(@name='InitState')])"
   556             |> «@name»`if "position()!=last()" > , `
   557         ||
   558         } «@name»_state;
   559 
   560         // events
   561 
   562         typedef enum _«@name»_event {
   563             «@name»_event_None = None,
   564             «@name»_event_Init = Init,
   565         ||
   566         for "func:distinctName(state/event[not(not(../../message/@name=@name))])" {
   567             const "name", "@name";
   568             |> «$name» = «/protocol/fsm/message[@name=$name]/@id»,
   569         }
   570         for "func:distinctName(state/event[not(not(../../external/@name=@name))])" {
   571             const "name", "@name";
   572             |> «$name» = «/protocol/fsm/external[@name=$name]/@id»,
   573         }
   574         |> «@name»_event_Extra = Extra,
   575         for "func:distinctName(state/event[not(../../message/@name=@name or ../../external/@name=@name)])" {
   576             if "@name!='Init'"
   577                 |> «@name»`if "position()!=last()" > , `
   578         }
   579         ||
   580         } «@name»_event;
   581 
   582         // state machine
   583 
   584         const char *«@name»_state_name(int state);
   585         const char *«@name»_event_name(int event);
   586 
   587         // the state machine function is returning the next state in case of a
   588         // transition or None for staying
   589 
   590         «@name»_state fsm_«@name»(
   591                 PEP_SESSION session,
   592                 «@name»_state state,
   593                 «@name»_event event
   594             );
   595 
   596         #ifdef __cplusplus
   597         }
   598         #endif
   599 
   600         ||
   601         }
   602 
   603         document "generated/{@name}_fsm.c", "text" {
   604         ||
   605         // This file is under GNU General Public License 3.0
   606         // see LICENSE.txt
   607 
   608         #include "«@name»_fsm.h"
   609         #include <stdlib.h>
   610 
   611         const char *«@name»_state_name(int state)
   612         {
   613             switch (state) {
   614                 case End:
   615                     return "End";
   616                 case None:
   617                     return "None";
   618                 case Init:
   619                     return "InitState";
   620         ||
   621         for "func:distinctName(state[not(@name='InitState')])" {
   622             |>> case «@name»:
   623             |>>> return "«@name»";
   624         }
   625         ||
   626                 default:
   627                     return "unknown state";
   628             }
   629         }
   630 
   631         const char *«@name»_event_name(int event)
   632         {
   633             switch (event) {
   634                 case None:
   635                     return "None";
   636                 case Init:
   637                     return "Init";
   638         ||
   639         for "func:distinctName(state/event[not(@name='Init')])" {
   640             |>> case «@name»:
   641             |>>> return "«@name»";
   642         }
   643         ||
   644                 default:
   645                     return "unknown event";
   646             }
   647         }
   648 
   649 
   650         static char *_str(int n, bool hex)
   651         {
   652             char *buf = calloc(1, 24);
   653             assert(buf);
   654             if (!buf)
   655                 return NULL;
   656 
   657             if (hex)
   658                 snprintf(buf, 24, "%.4x", n);
   659             else
   660                 snprintf(buf, 24, "%d", n);
   661             return buf;
   662         }
   663 
   664         #define «@name»_ERR_LOG(t, d) log_event(session, (t), "«@name»", (d), "error")
   665 
   666         static PEP_STATUS _«@name»_ERR_LOG_int(PEP_SESSION session, char *t, int n, bool hex)
   667         {
   668             char *_buf = _str(n, hex);
   669             if (!_buf)
   670                 return PEP_OUT_OF_MEMORY;
   671             PEP_STATUS status = «@name»_ERR_LOG(t, _buf);
   672             free(_buf);
   673             return status;
   674         }
   675 
   676         #define «@name»_ERR_LOG_INT(t, n) _«@name»_ERR_LOG_int(session, (t), (n), false)
   677         #define «@name»_ERR_LOG_HEX(t, n) _«@name»_ERR_LOG_int(session, (t), (n), true)
   678 
   679         #ifndef SERVICE_LOG
   680         // SERVICE LOG is meant to check session->service_log in runtime config;
   681         // for older engines log more than needed
   682         #define SERVICE_LOG(session, t, n, d) log_event((session), (t), (n), (d), "service")
   683         #endif 
   684 
   685         #define «@name»_SERVICE_LOG(t, d) SERVICE_LOG(session, (t), "«@name»", (d))
   686 
   687         «@name»_state fsm_«@name»(
   688                 PEP_SESSION session,
   689                 «@name»_state state,
   690                 «@name»_event event
   691             )
   692         {
   693             assert(session);
   694             if (!session)
   695                 return invalid_state;
   696 
   697             switch (state) {
   698                 case None:
   699                     return «@name»_state_Init;
   700                 
   701                 `` apply "state", 2, mode=fsm
   702                 default:
   703                     «@name»_ERR_LOG_INT("invalid state", state);
   704                     return invalid_state;
   705             }
   706             
   707             return None;
   708         }
   709 
   710         ||
   711         }
   712     }
   713     
   714     template "state", mode=fsm {
   715         choose {
   716             when "@name='InitState'" | case «../@name»_state_Init:
   717             otherwise | case «@name»:
   718         }
   719         ||
   720             «../@name»_SERVICE_LOG("in state", "«@name»");
   721 
   722             switch (event) {
   723                 case None:
   724                     «../@name»_SERVICE_LOG("received None event", "ignoring");
   725                     break;
   726      
   727         ||
   728         if "not(event[@name='Init'])"
   729         ||
   730                 case Init:
   731                     // nothing to do
   732                     break;
   733 
   734         ||
   735         ||
   736                 `` apply "event", 2, mode=fsm
   737                 default:
   738                     // ignore events not handled here
   739                     «../@name»_SERVICE_LOG("ignoring event", KeySync_event_name(event));
   740                     break;
   741             }
   742             break;
   743 
   744         ||
   745     }
   746 
   747     template "event", mode=fsm {
   748         | case «@name»: {
   749         if "condition|action|send" |> PEP_STATUS status;
   750         if "condition" |> bool result = false;
   751         if "condition|action|send" |
   752         ||
   753             «../../@name»_SERVICE_LOG("received event", "«@name»");
   754             `` apply "transition|action|condition|send";
   755         ||
   756         if "name(*[last()])!='transition'" {
   757             |
   758             |> «../../@name»_SERVICE_LOG("remaining in state", "«../@name»");
   759             |> break;
   760         }
   761         ||
   762         }
   763         
   764         ||
   765     }
   766 
   767     template "transition" {
   768         const "fsm", "ancestor::fsm";
   769         ||
   770 
   771         «$fsm/@name»_SERVICE_LOG("transition to state", "«@target»");
   772         return «@target»;
   773         ||
   774     }
   775 
   776     template "send" {
   777         const "fsm", "ancestor::fsm";
   778         const "protocol", "ancestor::protocol";
   779         ||
   780 
   781         «$fsm/@name»_SERVICE_LOG("send message", "«@name»");
   782         status = send_«$protocol/@name»_message(session, «$fsm/@id», «$fsm/@name»__payload_PR_«yml:mixedCase(@name)»);
   783         ||
   784     }
   785 
   786     template "action" {
   787         const "fsm", "ancestor::fsm";
   788         ||
   789 
   790         «$fsm/@name»_SERVICE_LOG("do action", "«@name»");
   791         status = «@name»(session);
   792         if (status) {
   793             «$fsm/@name»_ERR_LOG_HEX("executing action «@name»() failed", status);
   794             return invalid_action;
   795         }
   796         ||
   797     }
   798 
   799     template "condition" {
   800         const "fsm", "ancestor::fsm";
   801         ||
   802 
   803         status = «@name»(session, &result);
   804         if (status) {
   805             «$fsm/@name»_ERR_LOG_HEX("computing condition «@name» failed", status);
   806             return invalid_condition;
   807         }
   808         if (result) {
   809             «$fsm/@name»_SERVICE_LOG("condition applies", "«@name»");
   810         ||
   811         apply "transition|action|condition|send";
   812         | }
   813     }
   814 }
   815