Blame SOURCES/0091-rdp-add-ability-to-forward-reply-to-the-client-reque.patch

b2d430
From dc115d8a6aa1a656522c4f11c89e11d61360fd05 Mon Sep 17 00:00:00 2001
b2d430
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
b2d430
Date: Tue, 28 Jun 2016 11:40:16 +0200
b2d430
Subject: [PATCH 091/102] rdp: add ability to forward reply to the client
b2d430
 request
b2d430
MIME-Version: 1.0
b2d430
Content-Type: text/plain; charset=UTF-8
b2d430
Content-Transfer-Encoding: 8bit
b2d430
b2d430
In cases where the InfoPipe servers just as a middle-man between
b2d430
the DataProvider and a client we can simply forward the reply
b2d430
reducing amount of coded needed in the InfoPipe.
b2d430
b2d430
Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
b2d430
Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
b2d430
(cherry picked from commit a40d9cc11d17d9c3c22a0462cd8c419d1e79ffb8)
b2d430
---
b2d430
 src/responder/common/data_provider/rdp.h         |  20 ++
b2d430
 src/responder/common/data_provider/rdp_message.c | 260 +++++++++++++++++------
b2d430
 src/responder/ifp/ifp_domains.c                  |  78 +------
b2d430
 3 files changed, 220 insertions(+), 138 deletions(-)
b2d430
b2d430
diff --git a/src/responder/common/data_provider/rdp.h b/src/responder/common/data_provider/rdp.h
b2d430
index 8a3ec803d8ea7914e2a54661e31dec084586582a..f0aed179a3d33de0462591e6c0bcd3c518105707 100644
b2d430
--- a/src/responder/common/data_provider/rdp.h
b2d430
+++ b/src/responder/common/data_provider/rdp.h
b2d430
@@ -54,6 +54,26 @@ errno_t _rdp_message_recv(struct tevent_req *req,
b2d430
 #define rdp_message_recv(req, ...)                                         \
b2d430
     _rdp_message_recv(req, ##__VA_ARGS__, DBUS_TYPE_INVALID)
b2d430
 
b2d430
+/**
b2d430
+ * Send D-Bus message to Data Provider but instead of returning the reply
b2d430
+ * to the caller it forwards the reply to the client request. No further
b2d430
+ * processing is required by the caller. In case of a failure the client
b2d430
+ * request is freed since there is nothing we can do.
b2d430
+ */
b2d430
+void _rdp_message_send_and_reply(struct sbus_request *sbus_req,
b2d430
+                                 struct resp_ctx *rctx,
b2d430
+                                 struct sss_domain_info *domain,
b2d430
+                                 const char *path,
b2d430
+                                 const char *iface,
b2d430
+                                 const char *method,
b2d430
+                                 int first_arg_type,
b2d430
+                                 ...);
b2d430
+
b2d430
+#define rdp_message_send_and_reply(sbus_req, rctx, domain, path, iface,       \
b2d430
+                                   method, ...)                               \
b2d430
+    _rdp_message_send_and_reply(sbus_req, rctx, domain, path, iface, method,  \
b2d430
+                                ##__VA_ARGS__, DBUS_TYPE_INVALID)
b2d430
+
b2d430
 errno_t rdp_register_client(struct be_conn *be_conn,
b2d430
                             const char *client_name);
b2d430
 
b2d430
diff --git a/src/responder/common/data_provider/rdp_message.c b/src/responder/common/data_provider/rdp_message.c
b2d430
index 78af6f8967b378536b6456274fbcac4b609d1033..e226401567e4a1b2b9784a9aba21540ff5f0bc8d 100644
b2d430
--- a/src/responder/common/data_provider/rdp_message.c
b2d430
+++ b/src/responder/common/data_provider/rdp_message.c
b2d430
@@ -53,104 +53,72 @@ static errno_t rdp_error_to_errno(DBusError *error)
b2d430
     return EIO;
b2d430
 }
b2d430
 
b2d430
-struct rdp_message_state {
b2d430
-    struct DBusMessage *reply;
b2d430
-};
b2d430
-
b2d430
-static int rdp_message_state_destructor(struct rdp_message_state *state)
b2d430
+static errno_t
b2d430
+rdp_message_send_internal(struct resp_ctx *rctx,
b2d430
+                          struct sss_domain_info *domain,
b2d430
+                          DBusPendingCallNotifyFunction notify_fn,
b2d430
+                          void *notify_fn_data,
b2d430
+                          const char *path,
b2d430
+                          const char *iface,
b2d430
+                          const char *method,
b2d430
+                          int first_arg_type,
b2d430
+                          va_list va)
b2d430
 {
b2d430
-    if (state->reply != NULL) {
b2d430
-        dbus_message_unref(state->reply);
b2d430
-    }
b2d430
-
b2d430
-    return 0;
b2d430
-}
b2d430
-
b2d430
-static void rdp_message_done(DBusPendingCall *pending, void *ptr);
b2d430
-
b2d430
-struct tevent_req *_rdp_message_send(TALLOC_CTX *mem_ctx,
b2d430
-                                     struct resp_ctx *rctx,
b2d430
-                                     struct sss_domain_info *domain,
b2d430
-                                     const char *path,
b2d430
-                                     const char *iface,
b2d430
-                                     const char *method,
b2d430
-                                     int first_arg_type,
b2d430
-                                     ...)
b2d430
-{
b2d430
-    struct rdp_message_state *state;
b2d430
     struct be_conn *be_conn;
b2d430
-    struct tevent_req *req;
b2d430
-    DBusMessage *msg;
b2d430
+    DBusMessage *msg = NULL;
b2d430
     dbus_bool_t bret;
b2d430
     errno_t ret;
b2d430
-    va_list va;
b2d430
-
b2d430
-    req = tevent_req_create(mem_ctx, &state, struct rdp_message_state);
b2d430
-    if (req == NULL) {
b2d430
-        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
b2d430
-        return NULL;
b2d430
-    }
b2d430
-
b2d430
-    talloc_set_destructor(state, rdp_message_state_destructor);
b2d430
 
b2d430
     ret = sss_dp_get_domain_conn(rctx, domain->conn_name, &be_conn);
b2d430
     if (ret != EOK) {
b2d430
         DEBUG(SSSDBG_CRIT_FAILURE, "BUG: The Data Provider connection for "
b2d430
               "%s is not available!\n", domain->name);
b2d430
-        ret = ERR_INTERNAL;
b2d430
-        goto immediately;
b2d430
+        goto done;
b2d430
     }
b2d430
 
b2d430
     msg = dbus_message_new_method_call(NULL, path, iface, method);
b2d430
     if (msg == NULL) {
b2d430
         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create message\n");
b2d430
         ret = ENOMEM;
b2d430
-        goto immediately;
b2d430
+        goto done;
b2d430
     }
b2d430
 
b2d430
-    va_start(va, first_arg_type);
b2d430
     bret = dbus_message_append_args_valist(msg, first_arg_type, va);
b2d430
-    va_end(va);
b2d430
     if (!bret) {
b2d430
         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build message\n");
b2d430
         ret = EIO;
b2d430
-        goto immediately;
b2d430
+        goto done;
b2d430
     }
b2d430
 
b2d430
     DEBUG(SSSDBG_TRACE_FUNC, "DP Request: %s %s.%s\n", path, iface, method);
b2d430
 
b2d430
-    ret = sbus_conn_send(be_conn->conn, msg, 30000,
b2d430
-                         rdp_message_done, req, NULL);
b2d430
+    ret = sbus_conn_send(be_conn->conn, msg, 3000,
b2d430
+                         notify_fn, notify_fn_data, NULL);
b2d430
     if (ret != EOK) {
b2d430
         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to contact Data Provider "
b2d430
               "[%d]: %s\n", ret, sss_strerror(ret));
b2d430
-        goto immediately;
b2d430
+        goto done;
b2d430
     }
b2d430
 
b2d430
-    return req;
b2d430
+    ret = EOK;
b2d430
 
b2d430
-immediately:
b2d430
-    if (ret == EOK) {
b2d430
-        tevent_req_done(req);
b2d430
-    } else {
b2d430
-        tevent_req_error(req, ret);
b2d430
+done:
b2d430
+    if (msg != NULL) {
b2d430
+        dbus_message_unref(msg);
b2d430
     }
b2d430
-    tevent_req_post(req, rctx->ev);
b2d430
 
b2d430
-    return req;
b2d430
+    return ret;
b2d430
 }
b2d430
 
b2d430
-static void rdp_message_done(DBusPendingCall *pending, void *ptr)
b2d430
+static errno_t rdp_process_pending_call(DBusPendingCall *pending,
b2d430
+                                        DBusMessage **_reply)
b2d430
 {
b2d430
-    struct rdp_message_state *state;
b2d430
-    DBusMessage *reply = NULL;
b2d430
-    struct tevent_req *req;
b2d430
-    DBusError error;
b2d430
+    DBusMessage *reply;
b2d430
     dbus_bool_t bret;
b2d430
+    DBusError error;
b2d430
     errno_t ret;
b2d430
 
b2d430
-    req = talloc_get_type(ptr, struct tevent_req);
b2d430
-    state = tevent_req_data(req, struct rdp_message_state);
b2d430
+    *_reply = NULL;
b2d430
 
b2d430
     dbus_error_init(&error);
b2d430
 
b2d430
@@ -165,9 +133,8 @@ static void rdp_message_done(DBusPendingCall *pending, void *ptr)
b2d430
     switch (dbus_message_get_type(reply)) {
b2d430
     case DBUS_MESSAGE_TYPE_METHOD_RETURN:
b2d430
         DEBUG(SSSDBG_TRACE_FUNC, "DP Success\n");
b2d430
-        state->reply = reply;
b2d430
         ret = EOK;
b2d430
-        goto done;
b2d430
+        break;
b2d430
 
b2d430
     case DBUS_MESSAGE_TYPE_ERROR:
b2d430
         bret = dbus_set_error_from_message(&error, reply);
b2d430
@@ -180,28 +147,105 @@ static void rdp_message_done(DBusPendingCall *pending, void *ptr)
b2d430
         DEBUG(SSSDBG_CRIT_FAILURE, "DP Error [%s]: %s\n",
b2d430
               error.name, (error.message == NULL ? "(null)" : error.message));
b2d430
         ret = rdp_error_to_errno(&error);
b2d430
-        goto done;
b2d430
+        break;
b2d430
     default:
b2d430
+        dbus_message_unref(reply);
b2d430
         DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected type?\n");
b2d430
         ret = ERR_INTERNAL;
b2d430
         goto done;
b2d430
     }
b2d430
 
b2d430
-    ret = ERR_INTERNAL;
b2d430
+    *_reply = reply;
b2d430
 
b2d430
 done:
b2d430
     dbus_pending_call_unref(pending);
b2d430
     dbus_error_free(&error);
b2d430
 
b2d430
+    return ret;
b2d430
+}
b2d430
+
b2d430
+struct rdp_message_state {
b2d430
+    struct DBusMessage *reply;
b2d430
+};
b2d430
+
b2d430
+static int rdp_message_state_destructor(struct rdp_message_state *state)
b2d430
+{
b2d430
+    if (state->reply != NULL) {
b2d430
+        dbus_message_unref(state->reply);
b2d430
+    }
b2d430
+
b2d430
+    return 0;
b2d430
+}
b2d430
+
b2d430
+static void rdp_message_done(DBusPendingCall *pending, void *ptr);
b2d430
+
b2d430
+struct tevent_req *_rdp_message_send(TALLOC_CTX *mem_ctx,
b2d430
+                                     struct resp_ctx *rctx,
b2d430
+                                     struct sss_domain_info *domain,
b2d430
+                                     const char *path,
b2d430
+                                     const char *iface,
b2d430
+                                     const char *method,
b2d430
+                                     int first_arg_type,
b2d430
+                                     ...)
b2d430
+{
b2d430
+    struct rdp_message_state *state;
b2d430
+    struct tevent_req *req;
b2d430
+    errno_t ret;
b2d430
+    va_list va;
b2d430
+
b2d430
+    req = tevent_req_create(mem_ctx, &state, struct rdp_message_state);
b2d430
+    if (req == NULL) {
b2d430
+        DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create() failed\n");
b2d430
+        return NULL;
b2d430
+    }
b2d430
+
b2d430
+    talloc_set_destructor(state, rdp_message_state_destructor);
b2d430
+
b2d430
+    va_start(va, first_arg_type);
b2d430
+    ret = rdp_message_send_internal(rctx, domain, rdp_message_done, req,
b2d430
+                                    path, iface, method, first_arg_type, va);
b2d430
+    va_end(va);
b2d430
+    if (ret != EOK) {
b2d430
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to contact Data Provider "
b2d430
+              "[%d]: %s\n", ret, sss_strerror(ret));
b2d430
+        goto immediately;
b2d430
+    }
b2d430
+
b2d430
+    return req;
b2d430
+
b2d430
+immediately:
b2d430
     if (ret == EOK) {
b2d430
         tevent_req_done(req);
b2d430
+    } else {
b2d430
+        tevent_req_error(req, ret);
b2d430
+    }
b2d430
+    tevent_req_post(req, rctx->ev);
b2d430
+
b2d430
+    return req;
b2d430
+}
b2d430
+
b2d430
+static void rdp_message_done(DBusPendingCall *pending, void *ptr)
b2d430
+{
b2d430
+    struct rdp_message_state *state;
b2d430
+    struct tevent_req *req;
b2d430
+    errno_t ret;
b2d430
+
b2d430
+    req = talloc_get_type(ptr, struct tevent_req);
b2d430
+    state = tevent_req_data(req, struct rdp_message_state);
b2d430
+
b2d430
+    ret = rdp_process_pending_call(pending, &state->reply);
b2d430
+    if (ret != EOK) {
b2d430
+        if (state->reply != NULL) {
b2d430
+            dbus_message_unref(state->reply);
b2d430
+        }
b2d430
+
b2d430
+        state->reply = NULL;
b2d430
+
b2d430
+        tevent_req_error(req, ret);
b2d430
         return;
b2d430
     }
b2d430
 
b2d430
-    if (reply != NULL) {
b2d430
-        dbus_message_unref(reply);
b2d430
-    }
b2d430
-    tevent_req_error(req, ret);
b2d430
+    tevent_req_done(req);
b2d430
 }
b2d430
 
b2d430
 errno_t _rdp_message_recv(struct tevent_req *req,
b2d430
@@ -241,3 +285,85 @@ done:
b2d430
     return ret;
b2d430
 }
b2d430
 
b2d430
+static void rdp_message_send_and_reply_done(DBusPendingCall *pending,
b2d430
+                                            void *ptr);
b2d430
+
b2d430
+void _rdp_message_send_and_reply(struct sbus_request *sbus_req,
b2d430
+                                 struct resp_ctx *rctx,
b2d430
+                                 struct sss_domain_info *domain,
b2d430
+                                 const char *path,
b2d430
+                                 const char *iface,
b2d430
+                                 const char *method,
b2d430
+                                 int first_arg_type,
b2d430
+                                 ...)
b2d430
+{
b2d430
+    errno_t ret;
b2d430
+    va_list va;
b2d430
+
b2d430
+    va_start(va, first_arg_type);
b2d430
+    ret = rdp_message_send_internal(rctx, domain,
b2d430
+                                    rdp_message_send_and_reply_done, sbus_req,
b2d430
+                                    path, iface, method, first_arg_type, va);
b2d430
+    va_end(va);
b2d430
+
b2d430
+    if (ret != EOK) {
b2d430
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to contact Data Provider "
b2d430
+              "[%d]: %s\n", ret, sss_strerror(ret));
b2d430
+        talloc_free(sbus_req);
b2d430
+    }
b2d430
+}
b2d430
+
b2d430
+static void rdp_message_send_and_reply_done(DBusPendingCall *pending,
b2d430
+                                            void *ptr)
b2d430
+{
b2d430
+    struct sbus_request *sbus_req;
b2d430
+    DBusMessage *reply = NULL;
b2d430
+    dbus_uint32_t serial;
b2d430
+    const char *sender;
b2d430
+    dbus_bool_t dbret;
b2d430
+    errno_t ret;
b2d430
+
b2d430
+    sbus_req = talloc_get_type(ptr, struct sbus_request);
b2d430
+
b2d430
+    ret = rdp_process_pending_call(pending, &reply);
b2d430
+    if (reply == NULL) {
b2d430
+        /* Something bad happened. Just kill the request. */
b2d430
+        ret = EIO;
b2d430
+        goto done;
b2d430
+    }
b2d430
+
b2d430
+    /* Otherwise we have a valid reply and we do not care about returned
b2d430
+     * value. We set destination and serial in reply to point to the original
b2d430
+     * client request. */
b2d430
+
b2d430
+    sender = dbus_message_get_sender(sbus_req->message);
b2d430
+    serial = dbus_message_get_serial(sbus_req->message);
b2d430
+
b2d430
+    dbret = dbus_message_set_destination(reply, sender);
b2d430
+    if (dbret == false) {
b2d430
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set reply sender!\n");
b2d430
+        ret = EIO;
b2d430
+        goto done;
b2d430
+    }
b2d430
+
b2d430
+    dbret = dbus_message_set_reply_serial(reply, serial);
b2d430
+    if (dbret == false) {
b2d430
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set reply serial!\n");
b2d430
+        ret = EIO;
b2d430
+        goto done;
b2d430
+    }
b2d430
+
b2d430
+    sbus_request_finish(sbus_req, reply);
b2d430
+
b2d430
+    ret = EOK;
b2d430
+
b2d430
+done:
b2d430
+    if (reply != NULL) {
b2d430
+        dbus_message_unref(reply);
b2d430
+    }
b2d430
+
b2d430
+    if (ret != EOK) {
b2d430
+        /* Something bad happend, just kill the request. */
b2d430
+        talloc_free(sbus_req);
b2d430
+    }
b2d430
+}
b2d430
diff --git a/src/responder/ifp/ifp_domains.c b/src/responder/ifp/ifp_domains.c
b2d430
index 5333b25275e0847015f9cdd294eab5dbdda32f6c..8bfd39feb39822921ea703d8a89ac372e0ad5410 100644
b2d430
--- a/src/responder/ifp/ifp_domains.c
b2d430
+++ b/src/responder/ifp/ifp_domains.c
b2d430
@@ -538,14 +538,11 @@ void ifp_dom_get_parent_domain(struct sbus_request *dbus_req,
b2d430
                                dom->parent->name);
b2d430
 }
b2d430
 
b2d430
-static void ifp_domains_domain_is_online_done(struct tevent_req *req);
b2d430
-
b2d430
 int ifp_domains_domain_is_online(struct sbus_request *sbus_req,
b2d430
                                  void *data)
b2d430
 {
b2d430
     struct ifp_ctx *ifp_ctx;
b2d430
     struct sss_domain_info *dom;
b2d430
-    struct tevent_req *req;
b2d430
     DBusError *error;
b2d430
 
b2d430
     ifp_ctx = talloc_get_type(data, struct ifp_ctx);
b2d430
@@ -558,49 +555,18 @@ int ifp_domains_domain_is_online(struct sbus_request *sbus_req,
b2d430
         return EOK;
b2d430
     }
b2d430
 
b2d430
-    req = rdp_message_send(sbus_req, ifp_ctx->rctx, dom, DP_PATH,
b2d430
-                           IFACE_DP_BACKEND, IFACE_DP_BACKEND_ISONLINE,
b2d430
-                           DBUS_TYPE_STRING, &dom->name);
b2d430
-    if (req == NULL) {
b2d430
-        return ENOMEM;
b2d430
-    }
b2d430
-
b2d430
-    tevent_req_set_callback(req, ifp_domains_domain_is_online_done, sbus_req);
b2d430
+    rdp_message_send_and_reply(sbus_req, ifp_ctx->rctx, dom, DP_PATH,
b2d430
+                               IFACE_DP_BACKEND, IFACE_DP_BACKEND_ISONLINE,
b2d430
+                               DBUS_TYPE_STRING, &dom->name);
b2d430
 
b2d430
     return EOK;
b2d430
 }
b2d430
 
b2d430
-static void ifp_domains_domain_is_online_done(struct tevent_req *req)
b2d430
-{
b2d430
-    struct sbus_request *sbus_req;
b2d430
-    DBusError *error;
b2d430
-    bool is_online;
b2d430
-    errno_t ret;
b2d430
-
b2d430
-    sbus_req = tevent_req_callback_data(req, struct sbus_request);
b2d430
-
b2d430
-    ret = rdp_message_recv(req, DBUS_TYPE_BOOLEAN, &is_online);
b2d430
-    if (ret != EOK) {
b2d430
-        DEBUG(SSSDBG_OP_FAILURE, "Unable to get online status [%d]: %s\n",
b2d430
-              ret, sss_strerror(ret));
b2d430
-        error = sbus_error_new(sbus_req, DBUS_ERROR_FAILED,
b2d430
-                               "Unable to get online status [%d]: %s",
b2d430
-                               ret, sss_strerror(ret));
b2d430
-        sbus_request_fail_and_finish(sbus_req, error);
b2d430
-        return;
b2d430
-    }
b2d430
-
b2d430
-    iface_ifp_domains_domain_IsOnline_finish(sbus_req, is_online);
b2d430
-}
b2d430
-
b2d430
-static void ifp_domains_domain_list_services_done(struct tevent_req *req);
b2d430
-
b2d430
 int ifp_domains_domain_list_services(struct sbus_request *sbus_req,
b2d430
                                      void *data)
b2d430
 {
b2d430
     struct ifp_ctx *ifp_ctx;
b2d430
     struct sss_domain_info *dom;
b2d430
-    struct tevent_req *req;
b2d430
     DBusError *error;
b2d430
 
b2d430
     ifp_ctx = talloc_get_type(data, struct ifp_ctx);
b2d430
@@ -613,40 +579,10 @@ int ifp_domains_domain_list_services(struct sbus_request *sbus_req,
b2d430
         return EOK;
b2d430
     }
b2d430
 
b2d430
-    req = rdp_message_send(sbus_req, ifp_ctx->rctx, dom, DP_PATH,
b2d430
-                           IFACE_DP_FAILOVER, IFACE_DP_FAILOVER_LISTSERVICES,
b2d430
-                           DBUS_TYPE_STRING, &dom->name);
b2d430
-    if (req == NULL) {
b2d430
-        return ENOMEM;
b2d430
-    }
b2d430
-
b2d430
-    tevent_req_set_callback(req, ifp_domains_domain_list_services_done, sbus_req);
b2d430
+    rdp_message_send_and_reply(sbus_req, ifp_ctx->rctx, dom, DP_PATH,
b2d430
+                               IFACE_DP_FAILOVER,
b2d430
+                               IFACE_DP_FAILOVER_LISTSERVICES,
b2d430
+                               DBUS_TYPE_STRING, &dom->name);
b2d430
 
b2d430
     return EOK;
b2d430
 }
b2d430
-
b2d430
-static void ifp_domains_domain_list_services_done(struct tevent_req *req)
b2d430
-{
b2d430
-    struct sbus_request *sbus_req;
b2d430
-    DBusError *error;
b2d430
-    int num_services;
b2d430
-    const char **services;
b2d430
-    errno_t ret;
b2d430
-
b2d430
-    sbus_req = tevent_req_callback_data(req, struct sbus_request);
b2d430
-
b2d430
-    ret = rdp_message_recv(req, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
b2d430
-                           &services, &num_services);
b2d430
-    if (ret != EOK) {
b2d430
-        DEBUG(SSSDBG_OP_FAILURE, "Unable to get failover services [%d]: %s\n",
b2d430
-              ret, sss_strerror(ret));
b2d430
-        error = sbus_error_new(sbus_req, DBUS_ERROR_FAILED,
b2d430
-                               "Unable to get failover services [%d]: %s",
b2d430
-                               ret, sss_strerror(ret));
b2d430
-        sbus_request_fail_and_finish(sbus_req, error);
b2d430
-        return;
b2d430
-    }
b2d430
-
b2d430
-    iface_ifp_domains_domain_ListServices_finish(sbus_req, services,
b2d430
-                                                 num_services);
b2d430
-}
b2d430
-- 
b2d430
2.4.11
b2d430