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