summaryrefslogtreecommitdiffstats
path: root/src/sbus/codegen/templates/invokers.c.tpl
blob: a1c2b311e6cb80f14eb1162dac4a815c04a422a0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
<template name="file-header">
    /*
        Generated by sbus code generator

        Copyright (C) 2017 Red Hat

        This program is free software; you can redistribute it and/or modify
        it under the terms of the GNU General Public License as published by
        the Free Software Foundation; either version 3 of the License, or
        (at your option) any later version.

        This program is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.

        You should have received a copy of the GNU General Public License
        along with this program.  If not, see <http://www.gnu.org/licenses/>.
    */

    #include <errno.h>
    #include <talloc.h>
    #include <tevent.h>
    #include <dbus/dbus.h>

    #include "${sbus-path}/sbus_private.h"
    #include "${sbus-path}/sbus_interface_declarations.h"
    #include "${header:arguments}"
    #include "${header:invokers}"

    static errno_t
    sbus_invoker_schedule(TALLOC_CTX *mem_ctx,
                          struct tevent_context *ev,
                          void *handler,
                          void *private_data)
    {
        /* Schedule invoker as a timed event so it is processed after other
         * event types. This will give dispatcher a chance to catch more
         * messages before this invoker is triggered and therefore it will
         * allow to potentially chain more request into one, especially for
         * synchronous handlers. */

        struct tevent_timer *te;
        struct timeval tv;

        tv = tevent_timeval_current_ofs(0, 5);
        te = tevent_add_timer(ev, mem_ctx, tv, handler, private_data);
        if (te == NULL) {
            /* There is not enough memory to create a timer. We can't do
             * anything about it. */
            DEBUG(SSSDBG_OP_FAILURE, "Could not add invoker event!\n");
            return ENOMEM;
        }

        return EOK;
    }

</template>

<template name="invoker">
    struct _sbus_invoke_in_${input-signature}_out_${output-signature}_state {
        <toggle name="if-input-arguments">
        struct _sbus_invoker_args_${input-signature} *in;
        </toggle>
        <toggle name="if-output-arguments">
        struct _sbus_invoker_args_${output-signature} out;
        </toggle>
        struct {
            enum sbus_handler_type type;
            void *data;
            errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *<loop name="in">, ${type}</loop><loop name="in-raw">, ${type}</loop><loop name="out">, ${type}</loop><loop name="out-raw">, ${type}</loop>);
            struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *<loop name="in">, ${type}</loop><loop name="in-raw">, ${type}</loop><loop name="out-raw">, ${type}</loop>);
            errno_t (*recv)(TALLOC_CTX *, struct tevent_req *<loop name="out">, ${type}</loop>);
        } handler;

        struct sbus_request *sbus_req;
        DBusMessageIter *read_iterator;
        DBusMessageIter *write_iterator;
    };

    static void
    _sbus_invoke_in_${input-signature}_out_${output-signature}_step
        (struct tevent_context *ev,
         struct tevent_timer *te,
         struct timeval tv,
         void *private_data);

    static void
    _sbus_invoke_in_${input-signature}_out_${output-signature}_done
       (struct tevent_req *subreq);

    struct tevent_req *
    _sbus_invoke_in_${input-signature}_out_${output-signature}_send
       (TALLOC_CTX *mem_ctx,
        struct tevent_context *ev,
        struct sbus_request *sbus_req,
        sbus_invoker_keygen keygen,
        const struct sbus_handler *handler,
        DBusMessageIter *read_iterator,
        DBusMessageIter *write_iterator,
        const char **_key)
    {
        struct _sbus_invoke_in_${input-signature}_out_${output-signature}_state *state;
        struct tevent_req *req;
        const char *key;
        errno_t ret;

        req = tevent_req_create(mem_ctx, &state, struct _sbus_invoke_in_${input-signature}_out_${output-signature}_state);
        if (req == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tevent request!\n");
            return NULL;
        }

        state->handler.type = handler->type;
        state->handler.data = handler->data;
        state->handler.sync = handler->sync;
        state->handler.send = handler->async_send;
        state->handler.recv = handler->async_recv;

        state->sbus_req = sbus_req;
        state->read_iterator = read_iterator;
        state->write_iterator = write_iterator;

        <toggle name="if-input-arguments">
        state->in = talloc_zero(state, struct _sbus_invoker_args_${input-signature});
        if (state->in == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE,
                  "Unable to allocate space for input parameters!\n");
            ret = ENOMEM;
            goto done;
        }

        ret = _sbus_invoker_read_${input-signature}(state, read_iterator, state->in);
        if (ret != EOK) {
            goto done;
        }

        </toggle>
        ret = sbus_invoker_schedule(state, ev, _sbus_invoke_in_${input-signature}_out_${output-signature}_step, req);
        if (ret != EOK) {
            goto done;
        }

        ret = sbus_request_key(state, keygen, sbus_req,<toggle name="if-input-arguments"> state->in<or> NULL</toggle>, &key);
        if (ret != EOK) {
            goto done;
        }

        if (_key != NULL) {
            *_key = talloc_steal(mem_ctx, key);
        }

        ret = EAGAIN;

    done:
        if (ret != EAGAIN) {
            tevent_req_error(req, ret);
            tevent_req_post(req, ev);
        }

        return req;
    }

    static void _sbus_invoke_in_${input-signature}_out_${output-signature}_step
       (struct tevent_context *ev,
        struct tevent_timer *te,
        struct timeval tv,
        void *private_data)
    {
        struct _sbus_invoke_in_${input-signature}_out_${output-signature}_state *state;
        struct tevent_req *subreq;
        struct tevent_req *req;
        errno_t ret;

        req = talloc_get_type(private_data, struct tevent_req);
        state = tevent_req_data(req, struct _sbus_invoke_in_${input-signature}_out_${output-signature}_state);

        switch (state->handler.type) {
        case SBUS_HANDLER_SYNC:
            if (state->handler.sync == NULL) {
                DEBUG(SSSDBG_CRIT_FAILURE, "Bug: sync handler is not specified!\n");
                ret = ERR_INTERNAL;
                goto done;
            }

            ret = state->handler.sync(state, state->sbus_req, state->handler.data<loop name="in">, state->in->arg${index}</loop><loop name="in-raw">, state->read_iterator</loop><loop name="out">, &state->out.arg${index}</loop><loop name="out-raw">, state->write_iterator</loop>);
            if (ret != EOK) {
                goto done;
            }

            <toggle name="if-output-arguments">
            ret = _sbus_invoker_write_${output-signature}(state->write_iterator, &state->out);
            </toggle>
            goto done;
        case SBUS_HANDLER_ASYNC:
            if (state->handler.send == NULL || state->handler.recv == NULL) {
                DEBUG(SSSDBG_CRIT_FAILURE, "Bug: async handler is not specified!\n");
                ret = ERR_INTERNAL;
                goto done;
            }

            subreq = state->handler.send(state, ev, state->sbus_req, state->handler.data<loop name="in">, state->in->arg${index}</loop><loop name="in-raw">, state->read_iterator</loop><loop name="out-raw">, state->write_iterator</loop>);
            if (subreq == NULL) {
                DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
                ret = ENOMEM;
                goto done;
            }

            tevent_req_set_callback(subreq, _sbus_invoke_in_${input-signature}_out_${output-signature}_done, req);
            ret = EAGAIN;
            goto done;
        }

        ret = ERR_INTERNAL;

    done:
        if (ret == EOK) {
            tevent_req_done(req);
        } else if (ret != EAGAIN) {
            tevent_req_error(req, ret);
        }
    }

    static void _sbus_invoke_in_${input-signature}_out_${output-signature}_done(struct tevent_req *subreq)
    {
        struct _sbus_invoke_in_${input-signature}_out_${output-signature}_state *state;
        struct tevent_req *req;
        errno_t ret;

        req = tevent_req_callback_data(subreq, struct tevent_req);
        state = tevent_req_data(req, struct _sbus_invoke_in_${input-signature}_out_${output-signature}_state);

        ret = state->handler.recv(state, subreq<loop name="out">, &state->out.arg${index}</loop>);
        talloc_zfree(subreq);
        if (ret != EOK) {
            tevent_req_error(req, ret);
            return;
        }

        <toggle name="if-output-arguments">
        ret = _sbus_invoker_write_${output-signature}(state->write_iterator, &state->out);
        if (ret != EOK) {
            tevent_req_error(req, ret);
            return;
        }

        </toggle>
        tevent_req_done(req);
        return;
    }

</template>