sync/gen_statemachine.ysl2
author Edouard Tisserant <edouard@pep-project.org>
Tue, 21 Feb 2017 23:08:01 +0100
changeset 1601 10e4e77e2278
parent 1597 cc039a6139cb
child 1709 dcebe0692d63
permissions -rw-r--r--
KeySync : made UUID renewal when entering group explicit in fsm, and moved it after sendGroupKeys to be sure it is accepted by peer. Also fixed bug generating else part of conditions in fsm.
     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, 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/Makefile.protocols", "text"
    18             apply "fsm", 0, mode="make";
    19         apply "fsm", 0, mode=gen;
    20     }
    21 
    22     template "fsm", mode=make
    23     ||
    24     «@filename»_fsm.c: ../sync/devicegroup.fsm
    25     \tmake -C ../«@filename»
    26     ||
    27 
    28     template "fsm", mode=gen {
    29         document "generated/{@filename}_fsm.h", "text" {
    30         ||
    31         #pragma once
    32 
    33         // state machine for «@name»
    34 
    35         #include "message_api.h"
    36         
    37         #ifdef __cplusplus
    38         extern "C" {
    39         #endif
    40 
    41         // types
    42 
    43         typedef pEp_identity * Identity;
    44         typedef stringlist_t * Stringlist;
    45 
    46         // error values
    47 
    48         typedef enum _fsm_error {
    49             // these error values are corresponding to
    50             // PEP_SYNC_STATEMACHINE_ERROR - value
    51             invalid_state = -2,
    52             invalid_event = -3,
    53             invalid_condition = -4,
    54             invalid_action = -5,
    55 
    56             // out of memory condition
    57             invalid_out_of_memory = -128
    58         } fsm_error;
    59 
    60         // conditions
    61 
    62         `` for "func:distinctName(condition)" | int «@name»(PEP_SESSION session`apply "parm", 0`);
    63 
    64         // states
    65 
    66         typedef enum _«@name»_state {
    67             // error values also in this namespace
    68             «@name»_state_invalid_state = (int) invalid_state,
    69             «@name»_state_invalid_event = (int) invalid_event,
    70             «@name»_state_invalid_condition = (int) invalid_condition,
    71             «@name»_state_invalid_action = (int) invalid_action,
    72             «@name»_state_invalid_out_of_memory = (int) invalid_out_of_memory,
    73 
    74             «@name»_state_NONE = 0,
    75         `` for "func:distinctName(state)" |> «@name»`if "position()!=last()" > , `
    76         } «@name»_state;
    77 
    78         // events
    79 
    80         typedef enum _«@name»_event {
    81             «@name»_event_NONE = 0,
    82         ||
    83         for "func:distinctName(state/event[not(not(/protocol/fsm/tag/@name=@name))])" {
    84             const "name", "@name";
    85             |> «$name» = «/protocol/fsm/tag[@name=$name]/@id»,
    86         }
    87         for "func:distinctName(state/event[not(/protocol/fsm/tag/@name=@name)])"
    88             |> «@name»`if "position()!=last()" > , `
    89         ||
    90         } «@name»_event;
    91 
    92         // actions
    93 
    94         `` const "name", "@name"
    95         `` for "func:distinctName(//action)" | PEP_STATUS «@name»(PEP_SESSION session, «$name»_state state, Identity partner, void *extra);
    96 
    97         // event injector
    98 
    99         PEP_STATUS inject_DeviceState_event(
   100             PEP_SESSION session, 
   101             DeviceState_event event,
   102             Identity partner,
   103             void *extra);
   104 
   105         // message receiver
   106         
   107         PEP_STATUS receive_DeviceState_msg(
   108                 PEP_SESSION session, 
   109                 message *src, 
   110                 PEP_rating rating, 
   111                 stringlist_t *keylist
   112             );
   113 
   114         // state machine
   115 
   116         «@name»_state fsm_«@name»(
   117                 PEP_SESSION session,
   118                 «@name»_state state,
   119                 «@name»_event event,
   120                 Identity partner,
   121                 void *extra,
   122                 time_t *timeout
   123             );
   124 
   125         // driver
   126 
   127         DYNAMIC_API PEP_STATUS fsm_«@name»_inject(
   128                 PEP_SESSION session,
   129                 «@name»_event event,
   130                 Identity partner,
   131                 void *extra,
   132                 time_t *timeout
   133             );
   134 
   135         #ifdef __cplusplus
   136         }
   137         #endif
   138 
   139         ||
   140         }
   141         document "generated/{@filename}_driver.c", "text"
   142         ||
   143         // Driver for «@name» state machine
   144 
   145         #include <assert.h>
   146         #include "pEp_internal.h"
   147 
   148 
   149         DYNAMIC_API PEP_STATUS fsm_«@name»_inject(
   150                 PEP_SESSION session,
   151                 «@name»_event event,
   152                 Identity partner,
   153                 void *extra,
   154                 time_t *timeout
   155             )
   156         {
   157             assert(session);
   158             if (!session)
   159                 return PEP_ILLEGAL_VALUE;
   160 
   161             while(true)
   162             {
   163                 «@name»_state new_state = fsm_«@name»(session,
   164                     session->«@filename»_state, event, partner, extra, timeout);
   165 
   166                 if (new_state == «@name»_state_invalid_out_of_memory)
   167                     return PEP_OUT_OF_MEMORY;
   168 
   169                 if (new_state < 0)
   170                     return PEP_SYNC_STATEMACHINE_ERROR - new_state;
   171                 
   172                 if (new_state == session->«@filename»_state)
   173                     break;
   174 
   175                 event = Init;
   176                 extra = NULL;
   177                 session->«@filename»_state = new_state;
   178             } 
   179 
   180             return PEP_STATUS_OK;
   181         }
   182 
   183         ||
   184         document "generated/{@filename}_fsm.c", "text"
   185         ||
   186         #include "pEp_internal.h"
   187         #include "«@filename»_fsm.h"
   188         #include "«@filename»_impl.h"
   189 
   190         // local definitions for «@name»'s state machine 
   191 
   192         `` apply "state", 0 mode="declStatePayload"
   193 
   194         // state machine for «@name»
   195 
   196         «@name»_state fsm_«@name»(
   197                 PEP_SESSION session,
   198                 «@name»_state state,
   199                 «@name»_event event,
   200                 Identity partner,
   201                 void *extra,
   202                 time_t *timeout
   203             )
   204         {
   205             PEP_STATUS status = PEP_STATUS_OK;
   206 
   207             switch (state) {
   208             `` apply "state", 2
   209                 default:
   210                     return («@name»_state) invalid_state;
   211             }
   212 
   213             return state;
   214         }
   215 
   216         ||
   217     }
   218 
   219     template "state" {
   220         ||
   221         case «@name»:
   222         {
   223             DEBUG_LOG("Entering FSM state", "«../@filename»_fsm.c", "state=«@name»")
   224         ||
   225 
   226         if "count(parm) > 0" 
   227         || 
   228             assert(session->sync_state_payload);
   229             if(!session->sync_state_payload) return («../@name»_state) invalid_state;
   230             `` apply "parm", 1 mode="unpackStatePayloadParm" with "stateName", "@name"
   231         ||
   232 
   233         ||
   234             *timeout = «@timeout»;
   235             switch (event) {
   236         ||
   237 
   238         if "not(event[@name='Init'])" 
   239         ||
   240                 case Init: 
   241                     DEBUG_LOG("FSM event", "«../@filename»_fsm.c, state=«@name»", "event=Init") 
   242                     break;
   243         ||
   244 
   245         apply "event", 2;
   246 
   247         ||
   248                 default:
   249                     return («../@name»_state) invalid_event;
   250             }
   251             break;
   252         }
   253         ||
   254     }
   255 
   256     function "pEp_type" {
   257         param "type";
   258 
   259         choose {
   260             when "$type = 'Identity'" > Identity
   261             when "$type = 'IdentityList'" > identity_list*
   262             when "$type = 'GroupKeys'" > group_keys_extra_t*
   263             otherwise value "$type";
   264         }
   265     }
   266 
   267     function "pEp_type_op_radix" {
   268         param "type";
   269 
   270         choose {
   271             when "$type = 'Identity'" > identity
   272             when "$type = 'IdentityList'" > identity_list
   273             when "$type = 'GroupKeys'" > group_keys_extra
   274             otherwise call "pEp_type" with "type", "$type";
   275         }
   276     }
   277 
   278     template "parm" mode="unpackStatePayloadParm" 
   279     {
   280         param "stateName";
   281         const "pEpType" call "pEp_type" with "type","name(*[1])"; 
   282         | «$pEpType» «name(*[2])» = ((«$stateName»_state_payload_t*)session->sync_state_payload)->«name(*[2])»;
   283     }
   284 
   285     template "state" mode="declStatePayload" if "count(parm) > 0"
   286     ||
   287     typedef struct _«@name»_state_payload {
   288         `` apply "parm", 1 mode="declStatePayloadParm"
   289     } «@name»_state_payload_t;
   290 
   291     ||
   292 
   293     template "parm" mode="declStatePayloadParm" {
   294         const "pEpType" call "pEp_type" with "type","name(*[1])"; 
   295         | «$pEpType» «name(*[2])»;
   296     }
   297 
   298     template "event" {
   299         ||
   300         case «@name»:
   301         {
   302             DEBUG_LOG("FSM event", "«../../@filename»_fsm.c, state=«../@name»", "event=«@name»")
   303         ||
   304         if "count(parm) > 1" {
   305             // TODO get ride of void *extra, pass per-event struct incl all params.
   306             const "extrapEpType" call "pEp_type" with "type","name(parm[2]/*[1])"; 
   307             const "extraArgName","name(parm[2]/*[2])"; 
   308             |> «$extrapEpType» «$extraArgName» = («$extrapEpType»)extra;
   309         }
   310         ||
   311         `` apply "action|transition|condition";
   312         `` if "name(*[position()=last()]) != 'transition'" |> break;
   313         }
   314         ||
   315     }
   316 
   317     template "action" {
   318         | DEBUG_LOG("FSM action", "«ancestor::fsm/@filename»_fsm.c, state=«ancestor::state/@name», event=«ancestor::event/@name»", "action=«@name»")
   319         indent(0);
   320         > status = «@name»(session, state, 
   321         choose {
   322             when "parm" > «name(parm/*)»
   323             otherwise > NULL
   324         }
   325         choose {
   326             when "count(parm) > 1" > , «name(parm[2]/*)»
   327             otherwise > , NULL
   328         }
   329         > );\n
   330         | if (status == PEP_OUT_OF_MEMORY)
   331         |> return (int) invalid_out_of_memory;
   332         | if (status != PEP_STATUS_OK)
   333         |> return (int) invalid_action;
   334     }
   335 
   336     template "condition" {
   337         | {
   338         |> int cond_result = «@name»(session`apply "parm", 0`);
   339         |> #ifndef NDEBUG
   340         |> char resstr[11] = {0,};
   341         |> snprintf(resstr,10,"result=%d",cond_result);
   342         |> #endif
   343         |> DEBUG_LOG("FSM condition", "«ancestor::fsm/@filename»_fsm.c, state=«ancestor::state/@name», event=«ancestor::event/@name», condition=«@name»", resstr)
   344         |> if (cond_result < 0)
   345         |>> return cond_result;
   346         |> if (cond_result) {
   347         apply "action|transition|condition";
   348         |> }
   349         const "else", "./following-sibling::*[position()=1][name(.)='else']";
   350         if "$else" {
   351         |> else {
   352         apply "$else/action|$else/transition|$else/condition";
   353         |> }
   354         }
   355         | }
   356     }
   357 
   358     template "parm" choose {
   359         when "count(*) = 1"
   360             > , «name(*)»
   361         otherwise
   362             > , «name(*[1])» «name(*[2])»
   363     }
   364 
   365     template "transition"{
   366         const "stateparm", "ancestor::state/child::parm";
   367         if "count($stateparm) > 0" {
   368             ||
   369             assert(session->sync_state_payload);
   370             if(!session->sync_state_payload) return («ancestor::fsm/@name»_state) invalid_state;
   371             `` apply "$stateparm", 0 mode="freeStatePayloadParm" with "stateName", "ancestor::state/@name"
   372             free(session->sync_state_payload);
   373             session->sync_state_payload = NULL;
   374             ||
   375         }
   376         if "count(parm) > 0" {
   377             const "nextstatename", "@target";
   378             const "nextstateparm", "ancestor::fsm/child::state[@name = $nextstatename]/child::parm";
   379             if "count(parm) != count($nextstateparm)" 
   380                 error > different state parameters and transition parameters count state=«ancestor::state/@name», event=«ancestor::event/@name» target=«@target»
   381             ||
   382             session->sync_state_payload = malloc(sizeof(«$nextstatename»_state_payload_t));
   383             assert(session->sync_state_payload);
   384             if(!session->sync_state_payload) return («ancestor::fsm/@name»_state) invalid_out_of_memory;
   385             ||
   386             apply "$nextstateparm", 0 mode="dupStatePayloadParm" {
   387                 with "stateName", "$nextstatename";
   388                 with "transitionParms", "parm";
   389             }
   390         }
   391         | DEBUG_LOG("FSM transition", "«ancestor::fsm/@filename»_fsm.c, state=«ancestor::state/@name», event=«ancestor::event/@name»", "target=«@target»")
   392         | return «@target»;
   393     }
   394 
   395     template "parm" mode="freeStatePayloadParm" 
   396     {
   397         param "stateName";
   398         const "pEpTypeOpRadix" call "pEp_type_op_radix" with "type","name(*[1])"; 
   399         | free_«$pEpTypeOpRadix»(((«$stateName»_state_payload_t*)session->sync_state_payload)->«name(*[2])»);
   400     }
   401 
   402     template "parm" mode="dupStatePayloadParm" 
   403     {
   404         param "stateName";
   405         param "transitionParms";
   406         const "pEpTypeOpRadix" call "pEp_type_op_radix" with "type","name(*[1])"; 
   407         const "pos", "position()";
   408         | ((«$stateName»_state_payload_t*)session->sync_state_payload)->«name(*[2])» =
   409         |     «$pEpTypeOpRadix»_dup(«name($transitionParms[$pos]/*)»);
   410     }
   411 }
   412