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