/*
    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/sbus_private.h"
#include "sbus/sbus_interface_declarations.h"
#include "responder/ifp/ifp_iface/sbus_ifp_arguments.h"
#include "responder/ifp/ifp_iface/sbus_ifp_invokers.h"

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;
}

struct _sbus_ifp_invoke_in__out__state {
    struct {
        enum sbus_handler_type type;
        void *data;
        errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *);
        struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *);
        errno_t (*recv)(TALLOC_CTX *, struct tevent_req *);
    } handler;

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

static void
_sbus_ifp_invoke_in__out__step
    (struct tevent_context *ev,
     struct tevent_timer *te,
     struct timeval tv,
     void *private_data);

static void
_sbus_ifp_invoke_in__out__done
   (struct tevent_req *subreq);

struct tevent_req *
_sbus_ifp_invoke_in__out__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_ifp_invoke_in__out__state *state;
    struct tevent_req *req;
    const char *key;
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in__out__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;

    ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in__out__step, req);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_request_key(state, keygen, sbus_req, NULL, &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_ifp_invoke_in__out__step
   (struct tevent_context *ev,
    struct tevent_timer *te,
    struct timeval tv,
    void *private_data)
{
    struct _sbus_ifp_invoke_in__out__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_ifp_invoke_in__out__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);
        if (ret != EOK) {
            goto done;
        }

        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);
        if (subreq == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
            ret = ENOMEM;
            goto done;
        }

        tevent_req_set_callback(subreq, _sbus_ifp_invoke_in__out__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_ifp_invoke_in__out__done(struct tevent_req *subreq)
{
    struct _sbus_ifp_invoke_in__out__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_ifp_invoke_in__out__state);

    ret = state->handler.recv(state, subreq);
    talloc_zfree(subreq);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
    return;
}

struct _sbus_ifp_invoke_in__out_ao_state {
    struct _sbus_ifp_invoker_args_ao out;
    struct {
        enum sbus_handler_type type;
        void *data;
        errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char ***);
        struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *);
        errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char ***);
    } handler;

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

static void
_sbus_ifp_invoke_in__out_ao_step
    (struct tevent_context *ev,
     struct tevent_timer *te,
     struct timeval tv,
     void *private_data);

static void
_sbus_ifp_invoke_in__out_ao_done
   (struct tevent_req *subreq);

struct tevent_req *
_sbus_ifp_invoke_in__out_ao_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_ifp_invoke_in__out_ao_state *state;
    struct tevent_req *req;
    const char *key;
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in__out_ao_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;

    ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in__out_ao_step, req);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_request_key(state, keygen, sbus_req, NULL, &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_ifp_invoke_in__out_ao_step
   (struct tevent_context *ev,
    struct tevent_timer *te,
    struct timeval tv,
    void *private_data)
{
    struct _sbus_ifp_invoke_in__out_ao_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_ifp_invoke_in__out_ao_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, &state->out.arg0);
        if (ret != EOK) {
            goto done;
        }

        ret = _sbus_ifp_invoker_write_ao(state->write_iterator, &state->out);
        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);
        if (subreq == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
            ret = ENOMEM;
            goto done;
        }

        tevent_req_set_callback(subreq, _sbus_ifp_invoke_in__out_ao_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_ifp_invoke_in__out_ao_done(struct tevent_req *subreq)
{
    struct _sbus_ifp_invoke_in__out_ao_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_ifp_invoke_in__out_ao_state);

    ret = state->handler.recv(state, subreq, &state->out.arg0);
    talloc_zfree(subreq);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    ret = _sbus_ifp_invoker_write_ao(state->write_iterator, &state->out);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
    return;
}

struct _sbus_ifp_invoke_in__out_as_state {
    struct _sbus_ifp_invoker_args_as out;
    struct {
        enum sbus_handler_type type;
        void *data;
        errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char ***);
        struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *);
        errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char ***);
    } handler;

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

static void
_sbus_ifp_invoke_in__out_as_step
    (struct tevent_context *ev,
     struct tevent_timer *te,
     struct timeval tv,
     void *private_data);

static void
_sbus_ifp_invoke_in__out_as_done
   (struct tevent_req *subreq);

struct tevent_req *
_sbus_ifp_invoke_in__out_as_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_ifp_invoke_in__out_as_state *state;
    struct tevent_req *req;
    const char *key;
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in__out_as_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;

    ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in__out_as_step, req);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_request_key(state, keygen, sbus_req, NULL, &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_ifp_invoke_in__out_as_step
   (struct tevent_context *ev,
    struct tevent_timer *te,
    struct timeval tv,
    void *private_data)
{
    struct _sbus_ifp_invoke_in__out_as_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_ifp_invoke_in__out_as_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, &state->out.arg0);
        if (ret != EOK) {
            goto done;
        }

        ret = _sbus_ifp_invoker_write_as(state->write_iterator, &state->out);
        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);
        if (subreq == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
            ret = ENOMEM;
            goto done;
        }

        tevent_req_set_callback(subreq, _sbus_ifp_invoke_in__out_as_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_ifp_invoke_in__out_as_done(struct tevent_req *subreq)
{
    struct _sbus_ifp_invoke_in__out_as_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_ifp_invoke_in__out_as_state);

    ret = state->handler.recv(state, subreq, &state->out.arg0);
    talloc_zfree(subreq);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    ret = _sbus_ifp_invoker_write_as(state->write_iterator, &state->out);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
    return;
}

struct _sbus_ifp_invoke_in__out_b_state {
    struct _sbus_ifp_invoker_args_b out;
    struct {
        enum sbus_handler_type type;
        void *data;
        errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, bool*);
        struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *);
        errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, bool*);
    } handler;

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

static void
_sbus_ifp_invoke_in__out_b_step
    (struct tevent_context *ev,
     struct tevent_timer *te,
     struct timeval tv,
     void *private_data);

static void
_sbus_ifp_invoke_in__out_b_done
   (struct tevent_req *subreq);

struct tevent_req *
_sbus_ifp_invoke_in__out_b_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_ifp_invoke_in__out_b_state *state;
    struct tevent_req *req;
    const char *key;
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in__out_b_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;

    ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in__out_b_step, req);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_request_key(state, keygen, sbus_req, NULL, &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_ifp_invoke_in__out_b_step
   (struct tevent_context *ev,
    struct tevent_timer *te,
    struct timeval tv,
    void *private_data)
{
    struct _sbus_ifp_invoke_in__out_b_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_ifp_invoke_in__out_b_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, &state->out.arg0);
        if (ret != EOK) {
            goto done;
        }

        ret = _sbus_ifp_invoker_write_b(state->write_iterator, &state->out);
        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);
        if (subreq == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
            ret = ENOMEM;
            goto done;
        }

        tevent_req_set_callback(subreq, _sbus_ifp_invoke_in__out_b_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_ifp_invoke_in__out_b_done(struct tevent_req *subreq)
{
    struct _sbus_ifp_invoke_in__out_b_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_ifp_invoke_in__out_b_state);

    ret = state->handler.recv(state, subreq, &state->out.arg0);
    talloc_zfree(subreq);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    ret = _sbus_ifp_invoker_write_b(state->write_iterator, &state->out);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
    return;
}

struct _sbus_ifp_invoke_in__out_ifp_extra_state {
    struct _sbus_ifp_invoker_args_ifp_extra out;
    struct {
        enum sbus_handler_type type;
        void *data;
        errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, hash_table_t **);
        struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *);
        errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, hash_table_t **);
    } handler;

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

static void
_sbus_ifp_invoke_in__out_ifp_extra_step
    (struct tevent_context *ev,
     struct tevent_timer *te,
     struct timeval tv,
     void *private_data);

static void
_sbus_ifp_invoke_in__out_ifp_extra_done
   (struct tevent_req *subreq);

struct tevent_req *
_sbus_ifp_invoke_in__out_ifp_extra_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_ifp_invoke_in__out_ifp_extra_state *state;
    struct tevent_req *req;
    const char *key;
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in__out_ifp_extra_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;

    ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in__out_ifp_extra_step, req);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_request_key(state, keygen, sbus_req, NULL, &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_ifp_invoke_in__out_ifp_extra_step
   (struct tevent_context *ev,
    struct tevent_timer *te,
    struct timeval tv,
    void *private_data)
{
    struct _sbus_ifp_invoke_in__out_ifp_extra_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_ifp_invoke_in__out_ifp_extra_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, &state->out.arg0);
        if (ret != EOK) {
            goto done;
        }

        ret = _sbus_ifp_invoker_write_ifp_extra(state->write_iterator, &state->out);
        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);
        if (subreq == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
            ret = ENOMEM;
            goto done;
        }

        tevent_req_set_callback(subreq, _sbus_ifp_invoke_in__out_ifp_extra_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_ifp_invoke_in__out_ifp_extra_done(struct tevent_req *subreq)
{
    struct _sbus_ifp_invoke_in__out_ifp_extra_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_ifp_invoke_in__out_ifp_extra_state);

    ret = state->handler.recv(state, subreq, &state->out.arg0);
    talloc_zfree(subreq);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    ret = _sbus_ifp_invoker_write_ifp_extra(state->write_iterator, &state->out);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
    return;
}

struct _sbus_ifp_invoke_in__out_o_state {
    struct _sbus_ifp_invoker_args_o out;
    struct {
        enum sbus_handler_type type;
        void *data;
        errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char **);
        struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *);
        errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char **);
    } handler;

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

static void
_sbus_ifp_invoke_in__out_o_step
    (struct tevent_context *ev,
     struct tevent_timer *te,
     struct timeval tv,
     void *private_data);

static void
_sbus_ifp_invoke_in__out_o_done
   (struct tevent_req *subreq);

struct tevent_req *
_sbus_ifp_invoke_in__out_o_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_ifp_invoke_in__out_o_state *state;
    struct tevent_req *req;
    const char *key;
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in__out_o_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;

    ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in__out_o_step, req);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_request_key(state, keygen, sbus_req, NULL, &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_ifp_invoke_in__out_o_step
   (struct tevent_context *ev,
    struct tevent_timer *te,
    struct timeval tv,
    void *private_data)
{
    struct _sbus_ifp_invoke_in__out_o_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_ifp_invoke_in__out_o_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, &state->out.arg0);
        if (ret != EOK) {
            goto done;
        }

        ret = _sbus_ifp_invoker_write_o(state->write_iterator, &state->out);
        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);
        if (subreq == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
            ret = ENOMEM;
            goto done;
        }

        tevent_req_set_callback(subreq, _sbus_ifp_invoke_in__out_o_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_ifp_invoke_in__out_o_done(struct tevent_req *subreq)
{
    struct _sbus_ifp_invoke_in__out_o_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_ifp_invoke_in__out_o_state);

    ret = state->handler.recv(state, subreq, &state->out.arg0);
    talloc_zfree(subreq);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    ret = _sbus_ifp_invoker_write_o(state->write_iterator, &state->out);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
    return;
}

struct _sbus_ifp_invoke_in__out_s_state {
    struct _sbus_ifp_invoker_args_s out;
    struct {
        enum sbus_handler_type type;
        void *data;
        errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char **);
        struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *);
        errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char **);
    } handler;

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

static void
_sbus_ifp_invoke_in__out_s_step
    (struct tevent_context *ev,
     struct tevent_timer *te,
     struct timeval tv,
     void *private_data);

static void
_sbus_ifp_invoke_in__out_s_done
   (struct tevent_req *subreq);

struct tevent_req *
_sbus_ifp_invoke_in__out_s_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_ifp_invoke_in__out_s_state *state;
    struct tevent_req *req;
    const char *key;
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in__out_s_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;

    ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in__out_s_step, req);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_request_key(state, keygen, sbus_req, NULL, &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_ifp_invoke_in__out_s_step
   (struct tevent_context *ev,
    struct tevent_timer *te,
    struct timeval tv,
    void *private_data)
{
    struct _sbus_ifp_invoke_in__out_s_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_ifp_invoke_in__out_s_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, &state->out.arg0);
        if (ret != EOK) {
            goto done;
        }

        ret = _sbus_ifp_invoker_write_s(state->write_iterator, &state->out);
        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);
        if (subreq == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
            ret = ENOMEM;
            goto done;
        }

        tevent_req_set_callback(subreq, _sbus_ifp_invoke_in__out_s_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_ifp_invoke_in__out_s_done(struct tevent_req *subreq)
{
    struct _sbus_ifp_invoke_in__out_s_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_ifp_invoke_in__out_s_state);

    ret = state->handler.recv(state, subreq, &state->out.arg0);
    talloc_zfree(subreq);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    ret = _sbus_ifp_invoker_write_s(state->write_iterator, &state->out);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
    return;
}

struct _sbus_ifp_invoke_in__out_u_state {
    struct _sbus_ifp_invoker_args_u out;
    struct {
        enum sbus_handler_type type;
        void *data;
        errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, uint32_t*);
        struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *);
        errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, uint32_t*);
    } handler;

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

static void
_sbus_ifp_invoke_in__out_u_step
    (struct tevent_context *ev,
     struct tevent_timer *te,
     struct timeval tv,
     void *private_data);

static void
_sbus_ifp_invoke_in__out_u_done
   (struct tevent_req *subreq);

struct tevent_req *
_sbus_ifp_invoke_in__out_u_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_ifp_invoke_in__out_u_state *state;
    struct tevent_req *req;
    const char *key;
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in__out_u_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;

    ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in__out_u_step, req);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_request_key(state, keygen, sbus_req, NULL, &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_ifp_invoke_in__out_u_step
   (struct tevent_context *ev,
    struct tevent_timer *te,
    struct timeval tv,
    void *private_data)
{
    struct _sbus_ifp_invoke_in__out_u_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_ifp_invoke_in__out_u_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, &state->out.arg0);
        if (ret != EOK) {
            goto done;
        }

        ret = _sbus_ifp_invoker_write_u(state->write_iterator, &state->out);
        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);
        if (subreq == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
            ret = ENOMEM;
            goto done;
        }

        tevent_req_set_callback(subreq, _sbus_ifp_invoke_in__out_u_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_ifp_invoke_in__out_u_done(struct tevent_req *subreq)
{
    struct _sbus_ifp_invoke_in__out_u_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_ifp_invoke_in__out_u_state);

    ret = state->handler.recv(state, subreq, &state->out.arg0);
    talloc_zfree(subreq);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    ret = _sbus_ifp_invoker_write_u(state->write_iterator, &state->out);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
    return;
}

struct _sbus_ifp_invoke_in_s_out_ao_state {
    struct _sbus_ifp_invoker_args_s *in;
    struct _sbus_ifp_invoker_args_ao out;
    struct {
        enum sbus_handler_type type;
        void *data;
        errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, const char ***);
        struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *);
        errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char ***);
    } handler;

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

static void
_sbus_ifp_invoke_in_s_out_ao_step
    (struct tevent_context *ev,
     struct tevent_timer *te,
     struct timeval tv,
     void *private_data);

static void
_sbus_ifp_invoke_in_s_out_ao_done
   (struct tevent_req *subreq);

struct tevent_req *
_sbus_ifp_invoke_in_s_out_ao_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_ifp_invoke_in_s_out_ao_state *state;
    struct tevent_req *req;
    const char *key;
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in_s_out_ao_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;

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

    ret = _sbus_ifp_invoker_read_s(state, read_iterator, state->in);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in_s_out_ao_step, req);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_request_key(state, keygen, sbus_req, state->in, &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_ifp_invoke_in_s_out_ao_step
   (struct tevent_context *ev,
    struct tevent_timer *te,
    struct timeval tv,
    void *private_data)
{
    struct _sbus_ifp_invoke_in_s_out_ao_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_ifp_invoke_in_s_out_ao_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, state->in->arg0, &state->out.arg0);
        if (ret != EOK) {
            goto done;
        }

        ret = _sbus_ifp_invoker_write_ao(state->write_iterator, &state->out);
        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, state->in->arg0);
        if (subreq == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
            ret = ENOMEM;
            goto done;
        }

        tevent_req_set_callback(subreq, _sbus_ifp_invoke_in_s_out_ao_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_ifp_invoke_in_s_out_ao_done(struct tevent_req *subreq)
{
    struct _sbus_ifp_invoke_in_s_out_ao_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_ifp_invoke_in_s_out_ao_state);

    ret = state->handler.recv(state, subreq, &state->out.arg0);
    talloc_zfree(subreq);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    ret = _sbus_ifp_invoker_write_ao(state->write_iterator, &state->out);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
    return;
}

struct _sbus_ifp_invoke_in_s_out_as_state {
    struct _sbus_ifp_invoker_args_s *in;
    struct _sbus_ifp_invoker_args_as out;
    struct {
        enum sbus_handler_type type;
        void *data;
        errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, const char ***);
        struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *);
        errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char ***);
    } handler;

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

static void
_sbus_ifp_invoke_in_s_out_as_step
    (struct tevent_context *ev,
     struct tevent_timer *te,
     struct timeval tv,
     void *private_data);

static void
_sbus_ifp_invoke_in_s_out_as_done
   (struct tevent_req *subreq);

struct tevent_req *
_sbus_ifp_invoke_in_s_out_as_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_ifp_invoke_in_s_out_as_state *state;
    struct tevent_req *req;
    const char *key;
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in_s_out_as_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;

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

    ret = _sbus_ifp_invoker_read_s(state, read_iterator, state->in);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in_s_out_as_step, req);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_request_key(state, keygen, sbus_req, state->in, &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_ifp_invoke_in_s_out_as_step
   (struct tevent_context *ev,
    struct tevent_timer *te,
    struct timeval tv,
    void *private_data)
{
    struct _sbus_ifp_invoke_in_s_out_as_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_ifp_invoke_in_s_out_as_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, state->in->arg0, &state->out.arg0);
        if (ret != EOK) {
            goto done;
        }

        ret = _sbus_ifp_invoker_write_as(state->write_iterator, &state->out);
        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, state->in->arg0);
        if (subreq == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
            ret = ENOMEM;
            goto done;
        }

        tevent_req_set_callback(subreq, _sbus_ifp_invoke_in_s_out_as_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_ifp_invoke_in_s_out_as_done(struct tevent_req *subreq)
{
    struct _sbus_ifp_invoke_in_s_out_as_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_ifp_invoke_in_s_out_as_state);

    ret = state->handler.recv(state, subreq, &state->out.arg0);
    talloc_zfree(subreq);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    ret = _sbus_ifp_invoker_write_as(state->write_iterator, &state->out);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
    return;
}

struct _sbus_ifp_invoke_in_s_out_o_state {
    struct _sbus_ifp_invoker_args_s *in;
    struct _sbus_ifp_invoker_args_o out;
    struct {
        enum sbus_handler_type type;
        void *data;
        errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, const char **);
        struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *);
        errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char **);
    } handler;

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

static void
_sbus_ifp_invoke_in_s_out_o_step
    (struct tevent_context *ev,
     struct tevent_timer *te,
     struct timeval tv,
     void *private_data);

static void
_sbus_ifp_invoke_in_s_out_o_done
   (struct tevent_req *subreq);

struct tevent_req *
_sbus_ifp_invoke_in_s_out_o_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_ifp_invoke_in_s_out_o_state *state;
    struct tevent_req *req;
    const char *key;
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in_s_out_o_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;

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

    ret = _sbus_ifp_invoker_read_s(state, read_iterator, state->in);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in_s_out_o_step, req);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_request_key(state, keygen, sbus_req, state->in, &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_ifp_invoke_in_s_out_o_step
   (struct tevent_context *ev,
    struct tevent_timer *te,
    struct timeval tv,
    void *private_data)
{
    struct _sbus_ifp_invoke_in_s_out_o_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_ifp_invoke_in_s_out_o_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, state->in->arg0, &state->out.arg0);
        if (ret != EOK) {
            goto done;
        }

        ret = _sbus_ifp_invoker_write_o(state->write_iterator, &state->out);
        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, state->in->arg0);
        if (subreq == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
            ret = ENOMEM;
            goto done;
        }

        tevent_req_set_callback(subreq, _sbus_ifp_invoke_in_s_out_o_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_ifp_invoke_in_s_out_o_done(struct tevent_req *subreq)
{
    struct _sbus_ifp_invoke_in_s_out_o_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_ifp_invoke_in_s_out_o_state);

    ret = state->handler.recv(state, subreq, &state->out.arg0);
    talloc_zfree(subreq);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    ret = _sbus_ifp_invoker_write_o(state->write_iterator, &state->out);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
    return;
}

struct _sbus_ifp_invoke_in_s_out_s_state {
    struct _sbus_ifp_invoker_args_s *in;
    struct _sbus_ifp_invoker_args_s out;
    struct {
        enum sbus_handler_type type;
        void *data;
        errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, const char **);
        struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *);
        errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char **);
    } handler;

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

static void
_sbus_ifp_invoke_in_s_out_s_step
    (struct tevent_context *ev,
     struct tevent_timer *te,
     struct timeval tv,
     void *private_data);

static void
_sbus_ifp_invoke_in_s_out_s_done
   (struct tevent_req *subreq);

struct tevent_req *
_sbus_ifp_invoke_in_s_out_s_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_ifp_invoke_in_s_out_s_state *state;
    struct tevent_req *req;
    const char *key;
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in_s_out_s_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;

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

    ret = _sbus_ifp_invoker_read_s(state, read_iterator, state->in);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in_s_out_s_step, req);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_request_key(state, keygen, sbus_req, state->in, &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_ifp_invoke_in_s_out_s_step
   (struct tevent_context *ev,
    struct tevent_timer *te,
    struct timeval tv,
    void *private_data)
{
    struct _sbus_ifp_invoke_in_s_out_s_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_ifp_invoke_in_s_out_s_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, state->in->arg0, &state->out.arg0);
        if (ret != EOK) {
            goto done;
        }

        ret = _sbus_ifp_invoker_write_s(state->write_iterator, &state->out);
        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, state->in->arg0);
        if (subreq == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
            ret = ENOMEM;
            goto done;
        }

        tevent_req_set_callback(subreq, _sbus_ifp_invoke_in_s_out_s_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_ifp_invoke_in_s_out_s_done(struct tevent_req *subreq)
{
    struct _sbus_ifp_invoke_in_s_out_s_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_ifp_invoke_in_s_out_s_state);

    ret = state->handler.recv(state, subreq, &state->out.arg0);
    talloc_zfree(subreq);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    ret = _sbus_ifp_invoker_write_s(state->write_iterator, &state->out);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
    return;
}

struct _sbus_ifp_invoke_in_sas_out_raw_state {
    struct _sbus_ifp_invoker_args_sas *in;
    struct {
        enum sbus_handler_type type;
        void *data;
        errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, const char **, DBusMessageIter *);
        struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *, const char **, DBusMessageIter *);
        errno_t (*recv)(TALLOC_CTX *, struct tevent_req *);
    } handler;

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

static void
_sbus_ifp_invoke_in_sas_out_raw_step
    (struct tevent_context *ev,
     struct tevent_timer *te,
     struct timeval tv,
     void *private_data);

static void
_sbus_ifp_invoke_in_sas_out_raw_done
   (struct tevent_req *subreq);

struct tevent_req *
_sbus_ifp_invoke_in_sas_out_raw_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_ifp_invoke_in_sas_out_raw_state *state;
    struct tevent_req *req;
    const char *key;
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in_sas_out_raw_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;

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

    ret = _sbus_ifp_invoker_read_sas(state, read_iterator, state->in);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in_sas_out_raw_step, req);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_request_key(state, keygen, sbus_req, state->in, &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_ifp_invoke_in_sas_out_raw_step
   (struct tevent_context *ev,
    struct tevent_timer *te,
    struct timeval tv,
    void *private_data)
{
    struct _sbus_ifp_invoke_in_sas_out_raw_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_ifp_invoke_in_sas_out_raw_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, state->in->arg0, state->in->arg1, state->write_iterator);
        if (ret != EOK) {
            goto done;
        }

        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, state->in->arg0, state->in->arg1, state->write_iterator);
        if (subreq == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
            ret = ENOMEM;
            goto done;
        }

        tevent_req_set_callback(subreq, _sbus_ifp_invoke_in_sas_out_raw_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_ifp_invoke_in_sas_out_raw_done(struct tevent_req *subreq)
{
    struct _sbus_ifp_invoke_in_sas_out_raw_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_ifp_invoke_in_sas_out_raw_state);

    ret = state->handler.recv(state, subreq);
    talloc_zfree(subreq);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
    return;
}

struct _sbus_ifp_invoke_in_ss_out_o_state {
    struct _sbus_ifp_invoker_args_ss *in;
    struct _sbus_ifp_invoker_args_o out;
    struct {
        enum sbus_handler_type type;
        void *data;
        errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, const char *, const char **);
        struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *, const char *);
        errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char **);
    } handler;

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

static void
_sbus_ifp_invoke_in_ss_out_o_step
    (struct tevent_context *ev,
     struct tevent_timer *te,
     struct timeval tv,
     void *private_data);

static void
_sbus_ifp_invoke_in_ss_out_o_done
   (struct tevent_req *subreq);

struct tevent_req *
_sbus_ifp_invoke_in_ss_out_o_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_ifp_invoke_in_ss_out_o_state *state;
    struct tevent_req *req;
    const char *key;
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in_ss_out_o_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;

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

    ret = _sbus_ifp_invoker_read_ss(state, read_iterator, state->in);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in_ss_out_o_step, req);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_request_key(state, keygen, sbus_req, state->in, &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_ifp_invoke_in_ss_out_o_step
   (struct tevent_context *ev,
    struct tevent_timer *te,
    struct timeval tv,
    void *private_data)
{
    struct _sbus_ifp_invoke_in_ss_out_o_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_ifp_invoke_in_ss_out_o_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, state->in->arg0, state->in->arg1, &state->out.arg0);
        if (ret != EOK) {
            goto done;
        }

        ret = _sbus_ifp_invoker_write_o(state->write_iterator, &state->out);
        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, state->in->arg0, state->in->arg1);
        if (subreq == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
            ret = ENOMEM;
            goto done;
        }

        tevent_req_set_callback(subreq, _sbus_ifp_invoke_in_ss_out_o_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_ifp_invoke_in_ss_out_o_done(struct tevent_req *subreq)
{
    struct _sbus_ifp_invoke_in_ss_out_o_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_ifp_invoke_in_ss_out_o_state);

    ret = state->handler.recv(state, subreq, &state->out.arg0);
    talloc_zfree(subreq);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    ret = _sbus_ifp_invoker_write_o(state->write_iterator, &state->out);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
    return;
}

struct _sbus_ifp_invoke_in_ssu_out_ao_state {
    struct _sbus_ifp_invoker_args_ssu *in;
    struct _sbus_ifp_invoker_args_ao out;
    struct {
        enum sbus_handler_type type;
        void *data;
        errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, const char *, uint32_t, const char ***);
        struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *, const char *, uint32_t);
        errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char ***);
    } handler;

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

static void
_sbus_ifp_invoke_in_ssu_out_ao_step
    (struct tevent_context *ev,
     struct tevent_timer *te,
     struct timeval tv,
     void *private_data);

static void
_sbus_ifp_invoke_in_ssu_out_ao_done
   (struct tevent_req *subreq);

struct tevent_req *
_sbus_ifp_invoke_in_ssu_out_ao_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_ifp_invoke_in_ssu_out_ao_state *state;
    struct tevent_req *req;
    const char *key;
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in_ssu_out_ao_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;

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

    ret = _sbus_ifp_invoker_read_ssu(state, read_iterator, state->in);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in_ssu_out_ao_step, req);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_request_key(state, keygen, sbus_req, state->in, &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_ifp_invoke_in_ssu_out_ao_step
   (struct tevent_context *ev,
    struct tevent_timer *te,
    struct timeval tv,
    void *private_data)
{
    struct _sbus_ifp_invoke_in_ssu_out_ao_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_ifp_invoke_in_ssu_out_ao_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, state->in->arg0, state->in->arg1, state->in->arg2, &state->out.arg0);
        if (ret != EOK) {
            goto done;
        }

        ret = _sbus_ifp_invoker_write_ao(state->write_iterator, &state->out);
        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, state->in->arg0, state->in->arg1, state->in->arg2);
        if (subreq == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
            ret = ENOMEM;
            goto done;
        }

        tevent_req_set_callback(subreq, _sbus_ifp_invoke_in_ssu_out_ao_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_ifp_invoke_in_ssu_out_ao_done(struct tevent_req *subreq)
{
    struct _sbus_ifp_invoke_in_ssu_out_ao_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_ifp_invoke_in_ssu_out_ao_state);

    ret = state->handler.recv(state, subreq, &state->out.arg0);
    talloc_zfree(subreq);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    ret = _sbus_ifp_invoker_write_ao(state->write_iterator, &state->out);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
    return;
}

struct _sbus_ifp_invoke_in_su_out_ao_state {
    struct _sbus_ifp_invoker_args_su *in;
    struct _sbus_ifp_invoker_args_ao out;
    struct {
        enum sbus_handler_type type;
        void *data;
        errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, const char *, uint32_t, const char ***);
        struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, const char *, uint32_t);
        errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char ***);
    } handler;

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

static void
_sbus_ifp_invoke_in_su_out_ao_step
    (struct tevent_context *ev,
     struct tevent_timer *te,
     struct timeval tv,
     void *private_data);

static void
_sbus_ifp_invoke_in_su_out_ao_done
   (struct tevent_req *subreq);

struct tevent_req *
_sbus_ifp_invoke_in_su_out_ao_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_ifp_invoke_in_su_out_ao_state *state;
    struct tevent_req *req;
    const char *key;
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in_su_out_ao_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;

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

    ret = _sbus_ifp_invoker_read_su(state, read_iterator, state->in);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in_su_out_ao_step, req);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_request_key(state, keygen, sbus_req, state->in, &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_ifp_invoke_in_su_out_ao_step
   (struct tevent_context *ev,
    struct tevent_timer *te,
    struct timeval tv,
    void *private_data)
{
    struct _sbus_ifp_invoke_in_su_out_ao_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_ifp_invoke_in_su_out_ao_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, state->in->arg0, state->in->arg1, &state->out.arg0);
        if (ret != EOK) {
            goto done;
        }

        ret = _sbus_ifp_invoker_write_ao(state->write_iterator, &state->out);
        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, state->in->arg0, state->in->arg1);
        if (subreq == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
            ret = ENOMEM;
            goto done;
        }

        tevent_req_set_callback(subreq, _sbus_ifp_invoke_in_su_out_ao_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_ifp_invoke_in_su_out_ao_done(struct tevent_req *subreq)
{
    struct _sbus_ifp_invoke_in_su_out_ao_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_ifp_invoke_in_su_out_ao_state);

    ret = state->handler.recv(state, subreq, &state->out.arg0);
    talloc_zfree(subreq);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    ret = _sbus_ifp_invoker_write_ao(state->write_iterator, &state->out);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
    return;
}

struct _sbus_ifp_invoke_in_u_out_o_state {
    struct _sbus_ifp_invoker_args_u *in;
    struct _sbus_ifp_invoker_args_o out;
    struct {
        enum sbus_handler_type type;
        void *data;
        errno_t (*sync)(TALLOC_CTX *, struct sbus_request *, void *, uint32_t, const char **);
        struct tevent_req * (*send)(TALLOC_CTX *, struct tevent_context *, struct sbus_request *, void *, uint32_t);
        errno_t (*recv)(TALLOC_CTX *, struct tevent_req *, const char **);
    } handler;

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

static void
_sbus_ifp_invoke_in_u_out_o_step
    (struct tevent_context *ev,
     struct tevent_timer *te,
     struct timeval tv,
     void *private_data);

static void
_sbus_ifp_invoke_in_u_out_o_done
   (struct tevent_req *subreq);

struct tevent_req *
_sbus_ifp_invoke_in_u_out_o_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_ifp_invoke_in_u_out_o_state *state;
    struct tevent_req *req;
    const char *key;
    errno_t ret;

    req = tevent_req_create(mem_ctx, &state, struct _sbus_ifp_invoke_in_u_out_o_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;

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

    ret = _sbus_ifp_invoker_read_u(state, read_iterator, state->in);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_invoker_schedule(state, ev, _sbus_ifp_invoke_in_u_out_o_step, req);
    if (ret != EOK) {
        goto done;
    }

    ret = sbus_request_key(state, keygen, sbus_req, state->in, &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_ifp_invoke_in_u_out_o_step
   (struct tevent_context *ev,
    struct tevent_timer *te,
    struct timeval tv,
    void *private_data)
{
    struct _sbus_ifp_invoke_in_u_out_o_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_ifp_invoke_in_u_out_o_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, state->in->arg0, &state->out.arg0);
        if (ret != EOK) {
            goto done;
        }

        ret = _sbus_ifp_invoker_write_o(state->write_iterator, &state->out);
        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, state->in->arg0);
        if (subreq == NULL) {
            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create subrequest!\n");
            ret = ENOMEM;
            goto done;
        }

        tevent_req_set_callback(subreq, _sbus_ifp_invoke_in_u_out_o_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_ifp_invoke_in_u_out_o_done(struct tevent_req *subreq)
{
    struct _sbus_ifp_invoke_in_u_out_o_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_ifp_invoke_in_u_out_o_state);

    ret = state->handler.recv(state, subreq, &state->out.arg0);
    talloc_zfree(subreq);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    ret = _sbus_ifp_invoker_write_o(state->write_iterator, &state->out);
    if (ret != EOK) {
        tevent_req_error(req, ret);
        return;
    }

    tevent_req_done(req);
    return;
}
