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