sync/gen_statemachine.ysl2
author Volker Birk <vb@pep.foundation>
Thu, 23 Aug 2018 07:33:29 +0200
branchsync
changeset 2878 6163c699dd69
parent 2877 70189b383730
child 2879 1b90ffed2d89
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@2838
    54
        DYNAMIC_API «@name»_event_t *new_«@name»_event(«@name»_PR fsm, int event, «@name»_t *msg);
vb@2838
    55
vb@2838
    56
vb@2838
    57
        // free_«@name»_event() - free memory occupied by event
vb@2838
    58
        //
vb@2838
    59
        //  parameters:
vb@2838
    60
        //      ev (in)         event to free
vb@2838
    61
vb@2838
    62
        DYNAMIC_API void free_«@name»_event(«@name»_event_t *ev);
vb@2838
    63
vb@2838
    64
vb@2838
    65
        #ifdef __cplusplus
vb@2838
    66
        }
vb@2838
    67
        #endif
vb@2838
    68
vb@2838
    69
        ||
vb@2838
    70
vb@2838
    71
        document "generated/{@name}_event.c", "text"
vb@2838
    72
        ||
vb@2838
    73
        // This file is under GNU General Public License 3.0
vb@2838
    74
        // see LICENSE.txt
vb@2838
    75
vb@2838
    76
        #include "pEp_internal.h"
vb@2838
    77
        #include "«@name»_event.h"
vb@2838
    78
        #include "«@name»_func.h"
vb@2839
    79
        `` for "fsm" | #include "«@name»_fsm.h"
vb@2838
    80
vb@2838
    81
        DYNAMIC_API «@name»_event_t *new_«@name»_event(«@name»_PR fsm, int event, «@name»_t *msg)
vb@2838
    82
        {
vb@2838
    83
            assert(fsm > 0 && (event >= 0 |`> |` msg));
vb@2838
    84
            if (!(fsm > 0 && (event >= 0 |`> |` msg)))
vb@2838
    85
                return NULL;
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@2838
   109
        DYNAMIC_API 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@2870
   127
            #include "message_api.h"
vb@2870
   128
            #include "«@name»_event.h"
vb@2870
   129
            
vb@2870
   130
            #ifdef __cplusplus
vb@2870
   131
            extern "C" {
vb@2870
   132
            #endif
vb@2870
   133
vb@2870
   134
            // conditions
vb@2870
   135
vb@2870
   136
            ||
vb@2870
   137
            for "func:distinctName(*//condition)"
vb@2870
   138
                | PEP_STATUS «@name»(PEP_SESSION session, bool *result);
vb@2870
   139
            ||
vb@2870
   140
vb@2870
   141
            // actions
vb@2870
   142
vb@2870
   143
            ||
vb@2870
   144
            for "func:distinctName(*//action)"
vb@2870
   145
                | PEP_STATUS «@name»(PEP_SESSION session);
vb@2870
   146
            ||
vb@2870
   147
vb@2870
   148
            // notify state machine from event
vb@2870
   149
            // use state to generate «@name» message if necessary
vb@2870
   150
vb@2870
   151
            PEP_STATUS «@name»_notify(
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
            // send message about an event to communication partners using state
vb@2870
   158
vb@2870
   159
            PEP_STATUS send_«@name»_message(
vb@2870
   160
                    PEP_SESSION session, 
vb@2870
   161
                    «@name»_PR fsm,
vb@2870
   162
                    int message_type
vb@2870
   163
                );
vb@2870
   164
vb@2870
   165
            // receive message and store it in state
vb@2870
   166
vb@2870
   167
            PEP_STATUS recv_«@name»_event(
vb@2870
   168
                    PEP_SESSION session,
vb@2870
   169
                    «@name»_event_t *ev
vb@2870
   170
                );
krista@2271
   171
        
vb@2870
   172
            // state machine driver
vb@2870
   173
            // if fsm or event set to 0 use fields in src if present
krista@2271
   174
vb@2870
   175
            PEP_STATUS «@name»_driver(
vb@2870
   176
                    PEP_SESSION session,
vb@2870
   177
                    «@name»_PR fsm,
vb@2870
   178
                    int event
vb@2870
   179
                );
krista@2271
   180
vb@2870
   181
            PEP_STATUS inject_«@name»_event(
vb@2870
   182
                    PEP_SESSION session, 
vb@2870
   183
                    «@name»_PR fsm,
vb@2870
   184
                    int event
vb@2870
   185
                );
vb@2834
   186
vb@2829
   187
vb@2870
   188
            #ifdef __cplusplus
vb@2870
   189
            }
vb@2870
   190
            #endif
vb@2829
   191
vb@2870
   192
            ||
vb@2829
   193
        }
vb@2829
   194
vb@2870
   195
        document "generated/{@name}_impl.c", "text" {
vb@2870
   196
            ||
vb@2870
   197
            // This file is under GNU General Public License 3.0
vb@2870
   198
            // see LICENSE.txt
vb@2870
   199
        
vb@2870
   200
            #include "«@name»_impl.h"
vb@2870
   201
            #include "pEp_internal.h"
vb@2870
   202
            #include "«@name»_event.h"
vb@2870
   203
            #include "«@name»_codec.h"
vb@2870
   204
            #include "baseprotocol.h"
vb@2870
   205
            `` for "fsm" | #include "«@name»_fsm.h"
vb@2829
   206
vb@2870
   207
            PEP_STATUS «@name»_driver(
vb@2870
   208
                    PEP_SESSION session,
vb@2870
   209
                    «@name»_PR fsm,
vb@2870
   210
                    int event
vb@2870
   211
                )
vb@2870
   212
            {
vb@2870
   213
                assert(session && fsm);
vb@2870
   214
                if (!(session && fsm))
vb@2870
   215
                    return PEP_ILLEGAL_VALUE;
vb@2829
   216
vb@2870
   217
                int next_state = None;
vb@2870
   218
                do {
vb@2870
   219
                    switch (fsm) {
vb@2870
   220
                        `` apply "fsm", 3, mode=driver               
vb@2870
   221
                        default:
vb@2870
   222
                            return PEP_ILLEGAL_VALUE;
vb@2870
   223
                    }
vb@2870
   224
                }  while (next_state);
vb@2829
   225
vb@2870
   226
                return PEP_STATUS_OK;
vb@2829
   227
            }
vb@2829
   228
vb@2870
   229
            PEP_STATUS inject_«@name»_event(
vb@2870
   230
                    PEP_SESSION session, 
vb@2870
   231
                    «@name»_PR fsm,
vb@2870
   232
                    int event
vb@2870
   233
                )
vb@2870
   234
            {
vb@2870
   235
                «@name»_t *msg = NULL;
vb@2870
   236
                «@name»_event_t *ev = NULL;
vb@2870
   237
vb@2870
   238
                assert(session && fsm > 0 && event > None);
vb@2870
   239
                if (!(session && fsm > 0 && event > None))
vb@2870
   240
                    return PEP_ILLEGAL_VALUE;
vb@2870
   241
vb@2870
   242
                PEP_STATUS status = PEP_STATUS_OK;
vb@2870
   243
vb@2870
   244
                if (!session->inject_«yml:lcase(@name)»_event) {
vb@2870
   245
                   status = PEP_«yml:ucase(@name)»_NO_INJECT_CALLBACK;
vb@2870
   246
                   goto error;
vb@2870
   247
                }
vb@2870
   248
vb@2870
   249
                if (event < Extra) {
vb@2870
   250
                    msg = new_«@name»_message(fsm, event);
vb@2870
   251
                    if (!msg) {
vb@2870
   252
                        status = PEP_OUT_OF_MEMORY;
vb@2870
   253
                        goto error;
vb@2870
   254
                    }
vb@2870
   255
vb@2870
   256
                    status = update_«@name»_message(session, msg);
vb@2870
   257
                    if (status)
vb@2870
   258
                        goto error;
vb@2870
   259
                }
vb@2870
   260
vb@2870
   261
                ev = («@name»_event_t *) calloc(1, sizeof(«@name»_event_t));
vb@2870
   262
                assert(ev);
vb@2870
   263
                if (!ev) {
vb@2870
   264
                    status = PEP_OUT_OF_MEMORY;
vb@2870
   265
                    goto error;
vb@2870
   266
                }
vb@2870
   267
                
vb@2870
   268
                ev->fsm = fsm;
vb@2870
   269
                ev->event = event;
vb@2870
   270
                ev->msg = msg;
vb@2870
   271
vb@2870
   272
                int result = session->inject_«yml:lcase(@name)»_event(ev,
vb@2870
   273
                        session->«yml:lcase(@name)»_management);
vb@2870
   274
                if (result) {
vb@2870
   275
                    status = PEP_STATEMACHINE_ERROR;
vb@2870
   276
                    goto error;
vb@2870
   277
                }
vb@2870
   278
vb@2870
   279
                goto the_end;
vb@2870
   280
vb@2870
   281
            error:
vb@2870
   282
                free(ev);
vb@2870
   283
                free_«@name»_message(msg);
vb@2870
   284
vb@2870
   285
            the_end:
vb@2870
   286
                return status;
vb@2870
   287
            }
vb@2870
   288
vb@2870
   289
            PEP_STATUS «@name»_notify(
vb@2870
   290
                    PEP_SESSION session, 
vb@2870
   291
                    «@name»_PR fsm,
vb@2870
   292
                    int message_type
vb@2870
   293
                )
vb@2870
   294
            {
vb@2870
   295
                assert(session && fsm > 0 && message_type > 1 && message_type < Extra);
vb@2870
   296
                if (!(session && fsm > 0 && message_type > 1 && message_type < Extra))
vb@2870
   297
                    return PEP_ILLEGAL_VALUE;
vb@2870
   298
vb@2870
   299
                PEP_STATUS status = PEP_STATUS_OK;
vb@2870
   300
vb@2870
   301
                «@name»_t *msg = new_«@name»_message(fsm, message_type);
vb@2829
   302
                if (!msg) {
vb@2829
   303
                    status = PEP_OUT_OF_MEMORY;
vb@2829
   304
                    goto error;
vb@2829
   305
                }
vb@2829
   306
vb@2854
   307
                status = update_«@name»_message(session, msg);
vb@2829
   308
                if (status)
vb@2829
   309
                    goto error;
vb@2870
   310
vb@2870
   311
                goto the_end;
vb@2870
   312
vb@2870
   313
            error:
vb@2870
   314
                free_«@name»_message(msg);
vb@2870
   315
vb@2870
   316
            the_end:
vb@2870
   317
                return status;
vb@2829
   318
            }
vb@2829
   319
vb@2870
   320
            PEP_STATUS send_«@name»_message(
vb@2870
   321
                    PEP_SESSION session, 
vb@2870
   322
                    «@name»_PR fsm,
vb@2870
   323
                    int message_type
vb@2870
   324
                )
vb@2870
   325
            {
vb@2870
   326
                PEP_STATUS status = PEP_STATUS_OK;
vb@2829
   327
vb@2870
   328
                assert(session && fsm > None && message_type > None);
vb@2870
   329
                if (!(session && fsm > None && message_type > None))
vb@2870
   330
                    return PEP_ILLEGAL_VALUE;
vb@2870
   331
                
vb@2870
   332
                «@name»_t *msg = new_«@name»_message(fsm, message_type);
vb@2870
   333
                if (!msg)
vb@2870
   334
                    return PEP_OUT_OF_MEMORY;
vb@2870
   335
vb@2870
   336
                char *data = NULL;
vb@2870
   337
                message *m = NULL;
vb@2870
   338
                identity_list *channels = NULL;
vb@2870
   339
vb@2870
   340
                status = update_«@name»_message(session, msg);
vb@2870
   341
                if (status)
vb@2870
   342
                    goto the_end;
vb@2870
   343
vb@2870
   344
                size_t size = 0;
vb@2870
   345
                status = encode_«@name»_message(msg, &data, &size);
vb@2870
   346
                if (status)
vb@2870
   347
                    goto the_end;
vb@2870
   348
vb@2870
   349
                switch (message_type) {
vb@2871
   350
                    // these messages are being broadcasted
vb@2877
   351
                    `` for "fsm/message[@type='broadcast']" |>> case «../@name»__payload_PR_«yml:mixedCase(@name)»:
vb@2871
   352
                        status = _own_identities_retrieve(session, &channels, PEP_idf_not_for_«yml:lcase(@name)»);
vb@2875
   353
                        if (status)
vb@2870
   354
                            goto the_end;
vb@2871
   355
vb@2871
   356
                        if (!(channels && channels->ident)) {
vb@2871
   357
                            status = PEP_«yml:ucase(@name)»_NO_CHANNEL;
vb@2871
   358
                            goto the_end;
vb@2871
   359
                        }
vb@2870
   360
                        break;
vb@2870
   361
vb@2877
   362
                    // these go anycast; previously used address is sticky (unicast)
vb@2877
   363
                    `` for "fsm/message[@type='anycast']" |>> case «../@name»__payload_PR_«yml:mixedCase(@name)»:
vb@2870
   364
                        if (!session->«yml:lcase(@name)»_state.common.from `> |`|
vb@2870
   365
                            (session->«yml:lcase(@name)»_state.common.from->flags &
vb@2871
   366
                            PEP_idf_not_for_«yml:lcase(@name)»)) {
vb@2870
   367
vb@2870
   368
                            // no address available yet, try to find one
vb@2871
   369
                            status = _own_identities_retrieve(session, &channels, PEP_idf_not_for_«yml:lcase(@name)»);
vb@2870
   370
                            if (!status)
vb@2870
   371
                                goto the_end;
vb@2870
   372
                            break;
vb@2870
   373
vb@2870
   374
                            if (channels && channels->ident) {
vb@2870
   375
                                // only need the first one
vb@2870
   376
                                free_identity_list(channels->next);
vb@2870
   377
                                channels->next = NULL;
vb@2870
   378
                            }
vb@2870
   379
                            else {
vb@2871
   380
                                status = PEP_«yml:ucase(@name)»_NO_CHANNEL;
vb@2870
   381
                                goto the_end;
vb@2870
   382
                            }
vb@2870
   383
                        }
vb@2870
   384
                        else {
vb@2870
   385
                            pEp_identity *channel = identity_dup(session->«yml:lcase(@name)»_state.common.from);
vb@2870
   386
                            if (!channel) {
vb@2870
   387
                                status = PEP_OUT_OF_MEMORY;
vb@2870
   388
                                goto the_end;
vb@2870
   389
                            }
vb@2870
   390
vb@2870
   391
                            channels = new_identity_list(channel);
vb@2870
   392
                            if (!channels) {
vb@2870
   393
                                status = PEP_OUT_OF_MEMORY;
vb@2870
   394
                                goto the_end;
vb@2870
   395
                            }
vb@2870
   396
                        }
vb@2877
   397
vb@2877
   398
                    default:
vb@2877
   399
                        status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
vb@2877
   400
                        goto the_end;
vb@2870
   401
                }
vb@2870
   402
vb@2870
   403
                for (identity_list *li = channels; li && li->ident ; li = li->next) {
vb@2878
   404
                    message *_m = NULL;
vb@2878
   405
vb@2870
   406
                    status = base_prepare_message(
vb@2870
   407
                            li->ident,
vb@2870
   408
                            li->ident,
vb@2870
   409
                            data,
vb@2870
   410
                            size,
vb@2878
   411
                            &_m
vb@2870
   412
                        );
vb@2870
   413
                    if (status)
vb@2870
   414
                        goto the_end;
vb@2875
   415
                    data = NULL;
vb@2875
   416
vb@2878
   417
                    switch (message_type) {
vb@2878
   418
                    `` for "fsm/message[@security='unencrypted']" |>>> case «../@name»__payload_PR_«yml:mixedCase(@name)»:
vb@2878
   419
                            m = _m;
vb@2878
   420
                            break;
vb@2878
   421
vb@2878
   422
                        default:
vb@2878
   423
                            status = encrypt_message(session, _m, NULL, &m, PEP_enc_PEP, 0);
vb@2878
   424
                            if (status)
vb@2878
   425
                                goto the_end;
vb@2878
   426
                            free_message(_m);
vb@2878
   427
                    }
vb@2878
   428
vb@2870
   429
                    status = session->messageToSend(session->«yml:lcase(@name)»_obj, m);
vb@2870
   430
                    m = NULL;
vb@2870
   431
                }
vb@2870
   432
vb@2870
   433
            the_end:
vb@2870
   434
                free_identity_list(channels);
vb@2870
   435
                free_message(m);
vb@2870
   436
                free(data);
vb@2870
   437
                free_«@name»_message(msg);
vb@2870
   438
                return status;
vb@2829
   439
            }
vb@2829
   440
vb@2870
   441
            PEP_STATUS recv_«@name»_event(
vb@2870
   442
                    PEP_SESSION session,
vb@2870
   443
                    «@name»_event_t *ev
vb@2870
   444
                )
vb@2870
   445
            {
vb@2870
   446
                assert(session && ev);
vb@2870
   447
                if (!(session && ev))
vb@2870
   448
                    return PEP_ILLEGAL_VALUE;
vb@2829
   449
vb@2870
   450
                PEP_STATUS status = PEP_STATUS_OK;
vb@2829
   451
vb@2870
   452
                if (ev->event < Extra) {
vb@2870
   453
                    «@name»_PR fsm = (int) None;
vb@2870
   454
                    int event = None;
vb@2829
   455
vb@2870
   456
                    status = update_«@name»_state(session, ev->msg, &fsm, &event);
vb@2870
   457
                    if (status)
vb@2870
   458
                        goto error;
vb@2829
   459
vb@2870
   460
                    if (ev->fsm) {
vb@2870
   461
                        if (ev->fsm != fsm |`> |` ev->event != event) {
vb@2870
   462
                            status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
vb@2870
   463
                            goto error;
vb@2870
   464
                        }
vb@2870
   465
                    }
vb@2870
   466
                    else {
vb@2870
   467
                        if (ev->event) {
vb@2870
   468
                            status = PEP_«yml:ucase(@name)»_ILLEGAL_MESSAGE;
vb@2870
   469
                            goto error;
vb@2870
   470
                        }
vb@2870
   471
                        ev->fsm = fsm;
vb@2870
   472
                        ev->event = event;
vb@2870
   473
                    }
vb@2870
   474
                }
vb@2829
   475
vb@2870
   476
                free_«@name»_message(ev->msg);
vb@2870
   477
                free(ev);
vb@2870
   478
                status = «@name»_driver(session, ev->fsm, ev->event);
vb@2870
   479
                return status;
vb@2870
   480
vb@2870
   481
            error:
vb@2870
   482
                free_«@name»_message(ev->msg);
vb@2870
   483
                free(ev);
vb@2870
   484
                return status;
vb@2829
   485
            }
vb@2829
   486
vb@2870
   487
            ||
vb@2829
   488
        }
vb@2829
   489
vb@2829
   490
        apply "fsm", 0, mode=gen;
vb@2829
   491
    }
vb@2829
   492
vb@2838
   493
    template "fsm", mode=event
vb@2838
   494
    {
vb@2838
   495
    ||
vb@2843
   496
    case «../@name»_PR_«yml:lcase(@name)»: {
vb@2839
   497
        switch (msg->choice.«yml:lcase(@name)».payload.present) {
vb@2838
   498
    ||
vb@2838
   499
    for "message"
vb@2838
   500
    ||
vb@2838
   501
            case «../@name»__payload_PR_«yml:mixedCase(@name)»:
vb@2838
   502
                ev->event = «@name»;
vb@2838
   503
                break;
vb@2838
   504
    ||
vb@2838
   505
    ||
vb@2838
   506
            default:
vb@2838
   507
                // unknown message type
vb@2838
   508
                free(ev);
vb@2838
   509
                return NULL;
vb@2838
   510
        }
vb@2838
   511
        break;
vb@2838
   512
    }
vb@2838
   513
vb@2838
   514
    ||
vb@2838
   515
    }
vb@2838
   516
vb@2829
   517
    template "fsm", mode=driver
vb@2829
   518
    ||
vb@2843
   519
    case «../@name»_PR_«yml:lcase(@name)»: {
vb@2839
   520
        int state = session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state;
vb@2839
   521
        next_state = fsm_«@name»(session, state, event);
vb@2838
   522
        if (next_state > None) {
vb@2838
   523
            session->«yml:lcase(../@name)»_state.«yml:lcase(@name)».state = next_state;
vb@2838
   524
            event = Init;
vb@2838
   525
        }
vb@2838
   526
        else if (next_state < None) {
vb@2829
   527
            return PEP_STATEMACHINE_ERROR - state;
vb@2838
   528
        }
vb@2829
   529
        break;
vb@2829
   530
    }
vb@2829
   531
vb@2829
   532
    ||
vb@2829
   533
vb@2829
   534
    template "fsm", mode=gen {
vb@2829
   535
        document "generated/{@name}_fsm.h", "text" {
vb@2829
   536
        ||
vb@2829
   537
        // This file is under GNU General Public License 3.0
vb@2829
   538
        // see LICENSE.txt
vb@2829
   539
vb@2829
   540
        #pragma once
vb@2829
   541
vb@2829
   542
        #include "«../@name»_impl.h"
vb@2829
   543
vb@2829
   544
        #ifdef __cplusplus
vb@2829
   545
        extern "C" {
vb@2829
   546
        #endif
vb@2829
   547
vb@2829
   548
        // state machine for «@name»
krista@2271
   549
krista@2271
   550
        // states
krista@2271
   551
krista@2271
   552
        typedef enum _«@name»_state {
vb@2829
   553
            «@name»_state_None = None,
vb@2829
   554
            «@name»_state_Init = Init,
vb@2829
   555
        ||
vb@2829
   556
        for "func:distinctName(state[not(@name='InitState')])"
vb@2829
   557
            |> «@name»`if "position()!=last()" > , `
vb@2829
   558
        ||
krista@2271
   559
        } «@name»_state;
krista@2271
   560
krista@2271
   561
        // events
krista@2271
   562
krista@2271
   563
        typedef enum _«@name»_event {
vb@2829
   564
            «@name»_event_None = None,
vb@2829
   565
            «@name»_event_Init = Init,
krista@2271
   566
        ||
vb@2829
   567
        for "func:distinctName(state/event[not(not(../../message/@name=@name))])" {
krista@2271
   568
            const "name", "@name";
vb@2829
   569
            |> «$name» = «/protocol/fsm/message[@name=$name]/@id»,
krista@2271
   570
        }
vb@2830
   571
        for "func:distinctName(state/event[not(not(../../external/@name=@name))])" {
vb@2830
   572
            const "name", "@name";
vb@2830
   573
            |> «$name» = «/protocol/fsm/external[@name=$name]/@id»,
vb@2830
   574
        }
vb@2829
   575
        |> «@name»_event_Extra = Extra,
vb@2830
   576
        for "func:distinctName(state/event[not(../../message/@name=@name or ../../external/@name=@name)])" {
vb@2829
   577
            if "@name!='Init'"
vb@2829
   578
                |> «@name»`if "position()!=last()" > , `
vb@2829
   579
        }
krista@2271
   580
        ||
krista@2271
   581
        } «@name»_event;
krista@2271
   582
vb@2829
   583
        // state machine
krista@2271
   584
vb@2829
   585
        const char *«@name»_state_name(int state);
krista@2271
   586
vb@2838
   587
        // the state machine function is returning the next state in case of a
vb@2838
   588
        // transition or None for staying
vb@2838
   589
krista@2271
   590
        «@name»_state fsm_«@name»(
krista@2271
   591
                PEP_SESSION session,
krista@2271
   592
                «@name»_state state,
vb@2829
   593
                «@name»_event event
krista@2271
   594
            );
krista@2271
   595
krista@2271
   596
        #ifdef __cplusplus
krista@2271
   597
        }
krista@2271
   598
        #endif
krista@2271
   599
krista@2271
   600
        ||
krista@2271
   601
        }
vb@2829
   602
vb@2829
   603
        document "generated/{@name}_fsm.c", "text" {
krista@2271
   604
        ||
vb@2829
   605
        // This file is under GNU General Public License 3.0
vb@2829
   606
        // see LICENSE.txt
krista@2271
   607
vb@2829
   608
        #include "«@name»_fsm.h"
vb@2847
   609
        #include <stdlib.h>
krista@2271
   610
vb@2829
   611
        const char *«@name»_state_name(int state)
vb@2829
   612
        {
vb@2829
   613
            switch (state) {
vb@2829
   614
                case End:
vb@2829
   615
                    return "End";
vb@2829
   616
                case None:
vb@2829
   617
                    return "None";
vb@2829
   618
                case Init:
vb@2829
   619
                    return "InitState";
vb@2829
   620
        ||
vb@2829
   621
        for "func:distinctName(state[not(@name='InitState')])" {
vb@2829
   622
            |>> case «@name»:
vb@2829
   623
            |>>> return "«@name»";
vb@2829
   624
        }
vb@2829
   625
        ||
vb@2829
   626
                default:
vb@2829
   627
                    return "unknown state";
vb@2829
   628
            }
vb@2829
   629
        }
krista@2271
   630
vb@2829
   631
        static char *_str(int n, bool hex)
vb@2829
   632
        {
vb@2829
   633
            char *buf = calloc(1, 24);
vb@2829
   634
            assert(buf);
vb@2829
   635
            if (!buf)
vb@2829
   636
                return NULL;
vb@2829
   637
vb@2829
   638
            if (hex)
vb@2829
   639
                snprintf(buf, 24, "%.4x", n);
vb@2829
   640
            else
vb@2829
   641
                snprintf(buf, 24, "%d", n);
vb@2829
   642
            return buf;
vb@2829
   643
        }
vb@2829
   644
vb@2829
   645
        #define «@name»_ERR_LOG(t, d) log_event(session, (t), "«@name»", (d), "error")
vb@2829
   646
vb@2829
   647
        static PEP_STATUS _«@name»_ERR_LOG_int(PEP_SESSION session, char *t, int n, bool hex)
vb@2829
   648
        {
vb@2829
   649
            char *_buf = _str(n, hex);
vb@2829
   650
            if (!_buf)
vb@2829
   651
                return PEP_OUT_OF_MEMORY;
vb@2829
   652
            PEP_STATUS status = «@name»_ERR_LOG(t, _buf);
vb@2829
   653
            free(_buf);
vb@2829
   654
            return status;
vb@2829
   655
        }
vb@2829
   656
vb@2829
   657
        #define «@name»_ERR_LOG_INT(t, n) _«@name»_ERR_LOG_int(session, (t), (n), false)
vb@2829
   658
        #define «@name»_ERR_LOG_HEX(t, n) _«@name»_ERR_LOG_int(session, (t), (n), true)
vb@2829
   659
vb@2829
   660
        #ifndef SERVICE_LOG
vb@2829
   661
        // SERVICE LOG is meant to check session->service_log in runtime config;
vb@2829
   662
        // for older engines log more than needed
vb@2829
   663
        #define SERVICE_LOG(session, t, n, d) log_event((session), (t), (n), (d), "service")
vb@2829
   664
        #endif 
vb@2829
   665
vb@2829
   666
        #define «@name»_SERVICE_LOG(t, d) SERVICE_LOG(session, (t), "«@name»", (d))
vb@2829
   667
vb@2829
   668
        «@name»_state fsm_«@name»(
krista@2271
   669
                PEP_SESSION session,
vb@2829
   670
                «@name»_state state,
vb@2829
   671
                «@name»_event event
krista@2271
   672
            )
krista@2271
   673
        {
krista@2271
   674
            assert(session);
krista@2271
   675
            if (!session)
vb@2829
   676
                return invalid_state;
krista@2271
   677
krista@2271
   678
            switch (state) {
vb@2829
   679
                case None:
vb@2838
   680
                    return «@name»_state_Init;
vb@2829
   681
                
vb@2829
   682
                `` apply "state", 2, mode=fsm
krista@2271
   683
                default:
vb@2838
   684
                    «@name»_ERR_LOG_INT("invalid state", state);
vb@2829
   685
                    return invalid_state;
krista@2271
   686
            }
vb@2829
   687
            
vb@2838
   688
            return None;
krista@2271
   689
        }
krista@2271
   690
krista@2271
   691
        ||
vb@2829
   692
        }
vb@2829
   693
    }
vb@2829
   694
    
vb@2829
   695
    template "state", mode=fsm {
vb@2829
   696
        choose {
vb@2829
   697
            when "@name='InitState'" | case «../@name»_state_Init:
vb@2829
   698
            otherwise | case «@name»:
vb@2829
   699
        }
vb@2829
   700
        ||
vb@2829
   701
            «../@name»_SERVICE_LOG("in state", "«@name»");
vb@2829
   702
vb@2829
   703
            switch (event) {
vb@2829
   704
                case None:
vb@2829
   705
                    «../@name»_SERVICE_LOG("received None event", "ignoring");
vb@2838
   706
                    break;
vb@2829
   707
     
vb@2838
   708
        ||
vb@2838
   709
        if "not(event[@name='Init'])"
vb@2838
   710
        ||
vb@2838
   711
                case Init:
vb@2838
   712
                    // nothing to do
vb@2838
   713
                    break;
vb@2838
   714
vb@2838
   715
        ||
vb@2838
   716
        ||
vb@2829
   717
                `` apply "event", 2, mode=fsm
vb@2829
   718
                default:
vb@2838
   719
                    «../@name»_ERR_LOG_INT("invalid event", event);
vb@2829
   720
                    return invalid_event;
vb@2829
   721
            }
vb@2829
   722
            break;
vb@2829
   723
vb@2829
   724
        ||
krista@2271
   725
    }
krista@2271
   726
vb@2829
   727
    template "event", mode=fsm {
vb@2829
   728
        | case «@name»: {
vb@2865
   729
        if "condition|action|send" |> PEP_STATUS status;
vb@2829
   730
        if "condition" |> bool result = false;
vb@2865
   731
        if "condition|action|send" |
krista@2271
   732
        ||
vb@2829
   733
            «../../@name»_SERVICE_LOG("received event", "«@name»");
vb@2865
   734
            `` apply "transition|action|condition|send";
vb@2829
   735
        ||
vb@2829
   736
        if "name(*[last()])!='transition'" {
vb@2829
   737
            |
vb@2843
   738
            |> «../../@name»_SERVICE_LOG("remaining in state", "«../@name»");
vb@2829
   739
            |> break;
vb@2829
   740
        }
vb@2829
   741
        ||
vb@2829
   742
        }
vb@2829
   743
        
vb@2829
   744
        ||
vb@2829
   745
    }
vb@2829
   746
vb@2829
   747
    template "transition" {
vb@2865
   748
        const "fsm", "ancestor::fsm";
krista@2271
   749
        ||
krista@2271
   750
vb@2829
   751
        «$fsm/@name»_SERVICE_LOG("transition to state", "«@target»");
vb@2838
   752
        return «@target»;
krista@2271
   753
        ||
vb@2829
   754
    }
krista@2271
   755
vb@2865
   756
    template "send" {
vb@2865
   757
        const "fsm", "ancestor::fsm";
vb@2865
   758
        const "protocol", "ancestor::protocol";
vb@2865
   759
        ||
vb@2865
   760
vb@2865
   761
        «$fsm/@name»_SERVICE_LOG("send message", "«@name»");
vb@2865
   762
        status = send_«$protocol/@name»_message(session, «$fsm/@id», «$fsm/@name»__payload_PR_«yml:mixedCase(@name)»);
vb@2865
   763
        ||
vb@2865
   764
    }
vb@2865
   765
vb@2829
   766
    template "action" {
vb@2865
   767
        const "fsm", "ancestor::fsm";
vb@2865
   768
        ||
vb@2829
   769
vb@2865
   770
        «$fsm/@name»_SERVICE_LOG("do action", "«@name»");
vb@2865
   771
        status = «@name»(session);
vb@2829
   772
        if (status) {
vb@2829
   773
            «$fsm/@name»_ERR_LOG_HEX("executing action «@name»() failed", status);
vb@2829
   774
            return invalid_action;
krista@2271
   775
        }
krista@2271
   776
        ||
krista@2271
   777
    }
krista@2271
   778
vb@2829
   779
    template "condition" {
vb@2865
   780
        const "fsm", "ancestor::fsm";
krista@2271
   781
        ||
krista@2271
   782
vb@2829
   783
        status = «@name»(session, &result);
vb@2829
   784
        if (status) {
vb@2829
   785
            «$fsm/@name»_ERR_LOG_HEX("computing condition «@name» failed", status);
vb@2829
   786
            return invalid_condition;
krista@2271
   787
        }
vb@2829
   788
        if (result) {
vb@2843
   789
            «$fsm/@name»_SERVICE_LOG("condition applies", "«@name»");
krista@2271
   790
        ||
vb@2865
   791
        apply "transition|action|condition|send";
krista@2271
   792
        | }
krista@2271
   793
    }
krista@2271
   794
}
krista@2271
   795