Blob Blame History Raw
From 7f4199c2d4dc9147be436005d75e03fc468f5349 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Mon, 27 Jun 2016 13:56:13 +0200
Subject: [PATCH 096/102] sssctl: print active server and server list
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Resolves:
https://fedorahosted.org/sssd/ticket/3069

Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
(cherry picked from commit bd4c2ed5aec7f57ea04500f0e43f151eedfdde45)
---
 src/providers/data_provider/dp_iface.c           |   6 +-
 src/providers/data_provider/dp_iface.h           |   8 +
 src/providers/data_provider/dp_iface.xml         |   8 +
 src/providers/data_provider/dp_iface_failover.c  | 297 ++++++++++++++++++++++-
 src/providers/data_provider/dp_iface_generated.c |  52 ++++
 src/providers/data_provider/dp_iface_generated.h |  10 +
 src/providers/fail_over.c                        |  42 ++++
 src/providers/fail_over.h                        |   4 +
 src/responder/ifp/ifp_domains.c                  |  48 ++++
 src/responder/ifp/ifp_domains.h                  |   8 +
 src/responder/ifp/ifp_iface.c                    |   4 +-
 src/responder/ifp/ifp_iface.xml                  |  10 +
 src/responder/ifp/ifp_iface_generated.c          |  52 ++++
 src/responder/ifp/ifp_iface_generated.h          |  10 +
 src/tools/sssctl/sssctl_domains.c                | 182 +++++++++++++-
 15 files changed, 722 insertions(+), 19 deletions(-)

diff --git a/src/providers/data_provider/dp_iface.c b/src/providers/data_provider/dp_iface.c
index 8ed7274f0dd7b59598e2cf21e0dd59d16666df0b..4b2b0ddca68be8899f7285b4d881a91444b99362 100644
--- a/src/providers/data_provider/dp_iface.c
+++ b/src/providers/data_provider/dp_iface.c
@@ -42,8 +42,10 @@ struct iface_dp_backend iface_dp_backend = {
 };
 
 struct iface_dp_failover iface_dp_failover = {
-    {&iface_dp_failover_meta, 0},
-    .ListServices = dp_failover_list_services
+    { &iface_dp_failover_meta, 0 },
+    .ListServices = dp_failover_list_services,
+    .ActiveServer = dp_failover_active_server,
+    .ListServers = dp_failover_list_servers
 };
 
 static struct sbus_iface_map dp_map[] = {
diff --git a/src/providers/data_provider/dp_iface.h b/src/providers/data_provider/dp_iface.h
index 76e623d21c413fd68f8f3c9a91ea32fd707dc54d..5c6f0eb2f5dd68b63bda389e6fdd2446ca9efb21 100644
--- a/src/providers/data_provider/dp_iface.h
+++ b/src/providers/data_provider/dp_iface.h
@@ -69,4 +69,12 @@ errno_t dp_failover_list_services(struct sbus_request *sbus_req,
                                   void *dp_cli,
                                   const char *domname);
 
+errno_t dp_failover_active_server(struct sbus_request *sbus_req,
+                                  void *dp_cli,
+                                  const char *service_name);
+
+errno_t dp_failover_list_servers(struct sbus_request *sbus_req,
+                                 void *dp_cli,
+                                 const char *service_name);
+
 #endif /* DP_IFACE_H_ */
diff --git a/src/providers/data_provider/dp_iface.xml b/src/providers/data_provider/dp_iface.xml
index eab7fc0f1500bf8890030352421da62c134115b9..992848a048ef9fe813d6ae05bbcabd0913ecb277 100644
--- a/src/providers/data_provider/dp_iface.xml
+++ b/src/providers/data_provider/dp_iface.xml
@@ -22,6 +22,14 @@
             <arg name="domain_name" type="s" direction="in" />
             <arg name="services" type="as" direction="out" />
         </method>
+        <method name="ActiveServer">
+            <arg name="service_name" type="s" direction="in" />
+            <arg name="server" type="s" direction="out" />
+        </method>
+        <method name="ListServers">
+            <arg name="service_name" type="s" direction="in" />
+            <arg name="servers" type="as" direction="out" />
+        </method>
     </interface>
 
     <interface name="org.freedesktop.sssd.dataprovider">
diff --git a/src/providers/data_provider/dp_iface_failover.c b/src/providers/data_provider/dp_iface_failover.c
index 038791088eeab7e9c5923996db77d2a107ff067d..7d95ffdd627604eb8c7e1b2882bf1665f792b660 100644
--- a/src/providers/data_provider/dp_iface_failover.c
+++ b/src/providers/data_provider/dp_iface_failover.c
@@ -28,20 +28,208 @@
 #include "providers/backend.h"
 #include "util/util.h"
 
+static errno_t
+dp_failover_list_services_ldap(struct be_ctx *be_ctx,
+                               const char **services,
+                               int *_count)
+{
+    struct be_svc_data *svc;
+    int count;
+
+    count = 0;
+    DLIST_FOR_EACH(svc, be_ctx->be_fo->svcs) {
+        services[count] = talloc_strdup(services, svc->name);
+        if (services[count] == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+            return ENOMEM;
+        }
+        count++;
+    }
+
+    *_count = count;
+
+    return EOK;
+}
+
+static errno_t
+dp_failover_list_services_ad(struct be_ctx *be_ctx,
+                             struct sss_domain_info *domain,
+                             const char **services,
+                             int *_count)
+{
+    char *fo_svc_name = NULL;
+    struct be_svc_data *svc;
+    errno_t ret;
+    int count;
+
+    fo_svc_name = talloc_asprintf(services, "sd_%s", domain->name);
+    if (fo_svc_name == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    count = 0;
+    DLIST_FOR_EACH(svc, be_ctx->be_fo->svcs) {
+        /* Drop each sd_gc_* since this service is not used with AD at all,
+         * we only connect to AD_GC for global catalog. */
+        if (strncasecmp(svc->name, "sd_gc_", strlen("sd_gc_")) == 0) {
+            continue;
+        }
+
+        /* Drop all subdomain services for different domain. */
+        if (strncasecmp(svc->name, "sd_", strlen("sd_")) == 0) {
+            if (!IS_SUBDOMAIN(domain)) {
+                continue;
+            }
+
+            if (strcasecmp(svc->name, fo_svc_name) != 0) {
+                continue;
+            }
+        }
+
+        if (IS_SUBDOMAIN(domain)) {
+            /* Drop AD since we connect to subdomain.com for LDAP. */
+            if (strcasecmp(svc->name, "AD") == 0) {
+                continue;
+            }
+        }
+
+        services[count] = talloc_strdup(services, svc->name);
+        if (services[count] == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+            ret = ENOMEM;
+            goto done;
+        }
+        count++;
+    }
+
+    *_count = count;
+
+    ret = EOK;
+
+done:
+    talloc_free(fo_svc_name);
+    return ret;
+}
+
+static errno_t
+dp_failover_list_services_ipa(struct be_ctx *be_ctx,
+                              struct sss_domain_info *domain,
+                              const char **services,
+                              int *_count)
+{
+    struct be_svc_data *svc;
+    char *fo_svc_name = NULL;
+    char *fo_gc_name = NULL;
+    errno_t ret;
+    int count;
+
+    fo_svc_name = talloc_asprintf(services, "sd_%s", domain->name);
+    if (fo_svc_name == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    fo_gc_name = talloc_asprintf(services, "sd_gc_%s", domain->name);
+    if (fo_gc_name == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    count = 0;
+    DLIST_FOR_EACH(svc, be_ctx->be_fo->svcs) {
+        /* Drop all subdomain services for different domain. */
+        if (strncasecmp(svc->name, "sd_", strlen("sd_")) == 0) {
+            if (!IS_SUBDOMAIN(domain)) {
+                continue;
+            }
+
+            if (strcasecmp(svc->name, fo_svc_name) != 0
+                    && strcasecmp(svc->name, fo_gc_name) != 0) {
+                continue;
+            }
+        }
+
+        services[count] = talloc_strdup(services, svc->name);
+        if (services[count] == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+            return ENOMEM;
+        }
+        count++;
+    }
+
+    *_count = count;
+
+    ret = EOK;
+
+done:
+    talloc_free(fo_svc_name);
+    talloc_free(fo_gc_name);
+
+    return ret;
+}
+
+enum dp_fo_svc_type {
+    DP_FO_SVC_LDAP = 0,
+    DP_FO_SVC_AD = 1,
+    DP_FO_SVC_IPA = 1 << 1,
+    DP_FO_SVC_MIXED = DP_FO_SVC_AD | DP_FO_SVC_IPA
+};
+
 errno_t dp_failover_list_services(struct sbus_request *sbus_req,
                                   void *dp_cli,
                                   const char *domname)
 {
+    enum dp_fo_svc_type svc_type = DP_FO_SVC_LDAP;
+    struct sss_domain_info *domain;
     struct be_ctx *be_ctx;
     struct be_svc_data *svc;
     const char **services;
     int num_services;
+    errno_t ret;
 
     be_ctx = dp_client_be(dp_cli);
 
+    if (SBUS_IS_STRING_EMPTY(domname)) {
+        domain = be_ctx->domain;
+    } else {
+        domain = find_domain_by_name(be_ctx->domain, domname, false);
+        if (domain == NULL) {
+            sbus_request_reply_error(sbus_req, SBUS_ERROR_UNKNOWN_DOMAIN,
+                                     "Unknown domain %s", domname);
+            return EOK;
+        }
+    }
+
+    /**
+     * Returning list of failover services is currently rather difficult
+     * since there is only one failover context for the whole backend.
+     *
+     * The list of services for the given domain depends on whether it is
+     * a master domain or a subdomain and whether we are using IPA, AD or
+     * LDAP backend.
+     *
+     * For LDAP we just return everything we have.
+     * For AD master domain we return AD, AD_GC.
+     * For AD subdomain we return subdomain.com, AD_GC.
+     * For IPA in client mode we return IPA.
+     * For IPA in server mode we return IPA for master domain and
+     * subdomain.com, gc_subdomain.com for subdomain.
+     *
+     * We also return everything else for all cases if any other service
+     * such as kerberos is configured separately.
+     */
+
+    /* Allocate enough space. */
     num_services = 0;
     DLIST_FOR_EACH(svc, be_ctx->be_fo->svcs) {
         num_services++;
+
+        if (strcasecmp(svc->name, "AD") == 0) {
+            svc_type |= DP_FO_SVC_AD;
+        } else if (strcasecmp(svc->name, "IPA") == 0) {
+            svc_type |= DP_FO_SVC_IPA;
+        }
     }
 
     services = talloc_zero_array(sbus_req, const char *, num_services);
@@ -50,17 +238,108 @@ errno_t dp_failover_list_services(struct sbus_request *sbus_req,
         return ENOMEM;
     }
 
-    num_services = 0;
-    DLIST_FOR_EACH(svc, be_ctx->be_fo->svcs) {
-        services[num_services] = talloc_strdup(services, svc->name);
-        if (services[num_services] == NULL) {
-            DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
-            talloc_free(services);
-            return ENOMEM;
-        }
-        num_services++;
+    /* Fill the list. */
+    switch (svc_type) {
+    case DP_FO_SVC_LDAP:
+    case DP_FO_SVC_MIXED:
+        ret = dp_failover_list_services_ldap(be_ctx, services, &num_services);
+        break;
+    case DP_FO_SVC_AD:
+        ret = dp_failover_list_services_ad(be_ctx, domain,
+                                           services, &num_services);
+        break;
+    case DP_FO_SVC_IPA:
+        ret = dp_failover_list_services_ipa(be_ctx, domain,
+                                            services, &num_services);
+        break;
+    default:
+        ret = ERR_INTERNAL;
+        break;
+    }
+
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create service list [%d]: %s\n",
+              ret, sss_strerror(ret));
+        talloc_free(services);
+        return ret;
     }
 
     iface_dp_failover_ListServices_finish(sbus_req, services, num_services);
     return EOK;
 }
+
+errno_t dp_failover_active_server(struct sbus_request *sbus_req,
+                                  void *dp_cli,
+                                  const char *service_name)
+{
+    struct be_ctx *be_ctx;
+    struct be_svc_data *svc;
+    const char *server;
+    bool found = false;
+
+    be_ctx = dp_client_be(dp_cli);
+
+    DLIST_FOR_EACH(svc, be_ctx->be_fo->svcs) {
+        if (strcmp(svc->name, service_name) == 0) {
+            found = true;
+            break;
+        }
+    }
+
+    if (!found) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get server name\n");
+        sbus_request_reply_error(sbus_req, SBUS_ERROR_NOT_FOUND,
+                                 "Unknown service name");
+        return EOK;
+    }
+
+    if (svc->last_good_srv == NULL) {
+        server = "";
+    } else {
+        server = fo_get_server_name(svc->last_good_srv);
+        if (server == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get server name\n");
+            sbus_request_reply_error(sbus_req, SBUS_ERROR_INTERNAL,
+                                     "Unable to get server name");
+            return EOK;
+        }
+    }
+
+    iface_dp_failover_ActiveServer_finish(sbus_req, server);
+    return EOK;
+}
+
+errno_t dp_failover_list_servers(struct sbus_request *sbus_req,
+                                 void *dp_cli,
+                                 const char *service_name)
+{
+    struct be_ctx *be_ctx;
+    struct be_svc_data *svc;
+    const char **servers;
+    bool found = false;
+    size_t count;
+
+    be_ctx = dp_client_be(dp_cli);
+
+    DLIST_FOR_EACH(svc, be_ctx->be_fo->svcs) {
+        if (strcmp(svc->name, service_name) == 0) {
+            found = true;
+            break;
+        }
+    }
+
+    if (!found) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get server list\n");
+        sbus_request_reply_error(sbus_req, SBUS_ERROR_NOT_FOUND,
+                                 "Unknown service name");
+        return EOK;
+    }
+
+    servers = fo_svc_server_list(sbus_req, svc->fo_service, &count);
+    if (servers == NULL) {
+        return ENOMEM;
+    }
+
+    iface_dp_failover_ListServers_finish(sbus_req, servers, (int)count);
+    return EOK;
+}
diff --git a/src/providers/data_provider/dp_iface_generated.c b/src/providers/data_provider/dp_iface_generated.c
index 7b36fd8aaeebb976a511c5592b1dd0ae28e9bb8a..fd2acb4f4bd8cf1dcbe8842cccc6dc2077fc83a2 100644
--- a/src/providers/data_provider/dp_iface_generated.c
+++ b/src/providers/data_provider/dp_iface_generated.c
@@ -111,6 +111,44 @@ int iface_dp_failover_ListServices_finish(struct sbus_request *req, const char *
                                          DBUS_TYPE_INVALID);
 }
 
+/* arguments for org.freedesktop.sssd.DataProvider.Failover.ActiveServer */
+const struct sbus_arg_meta iface_dp_failover_ActiveServer__in[] = {
+    { "service_name", "s" },
+    { NULL, }
+};
+
+/* arguments for org.freedesktop.sssd.DataProvider.Failover.ActiveServer */
+const struct sbus_arg_meta iface_dp_failover_ActiveServer__out[] = {
+    { "server", "s" },
+    { NULL, }
+};
+
+int iface_dp_failover_ActiveServer_finish(struct sbus_request *req, const char *arg_server)
+{
+   return sbus_request_return_and_finish(req,
+                                         DBUS_TYPE_STRING, &arg_server,
+                                         DBUS_TYPE_INVALID);
+}
+
+/* arguments for org.freedesktop.sssd.DataProvider.Failover.ListServers */
+const struct sbus_arg_meta iface_dp_failover_ListServers__in[] = {
+    { "service_name", "s" },
+    { NULL, }
+};
+
+/* arguments for org.freedesktop.sssd.DataProvider.Failover.ListServers */
+const struct sbus_arg_meta iface_dp_failover_ListServers__out[] = {
+    { "servers", "as" },
+    { NULL, }
+};
+
+int iface_dp_failover_ListServers_finish(struct sbus_request *req, const char *arg_servers[], int len_servers)
+{
+   return sbus_request_return_and_finish(req,
+                                         DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &arg_servers, len_servers,
+                                         DBUS_TYPE_INVALID);
+}
+
 /* methods for org.freedesktop.sssd.DataProvider.Failover */
 const struct sbus_method_meta iface_dp_failover__methods[] = {
     {
@@ -120,6 +158,20 @@ const struct sbus_method_meta iface_dp_failover__methods[] = {
         offsetof(struct iface_dp_failover, ListServices),
         invoke_s_method,
     },
+    {
+        "ActiveServer", /* name */
+        iface_dp_failover_ActiveServer__in,
+        iface_dp_failover_ActiveServer__out,
+        offsetof(struct iface_dp_failover, ActiveServer),
+        invoke_s_method,
+    },
+    {
+        "ListServers", /* name */
+        iface_dp_failover_ListServers__in,
+        iface_dp_failover_ListServers__out,
+        offsetof(struct iface_dp_failover, ListServers),
+        invoke_s_method,
+    },
     { NULL, }
 };
 
diff --git a/src/providers/data_provider/dp_iface_generated.h b/src/providers/data_provider/dp_iface_generated.h
index 977ab3bae803ca002162b02d0c3d9677779983f4..7c2216aa27022769c707d80b59e9b436e72d1739 100644
--- a/src/providers/data_provider/dp_iface_generated.h
+++ b/src/providers/data_provider/dp_iface_generated.h
@@ -22,6 +22,8 @@
 /* constants for org.freedesktop.sssd.DataProvider.Failover */
 #define IFACE_DP_FAILOVER "org.freedesktop.sssd.DataProvider.Failover"
 #define IFACE_DP_FAILOVER_LISTSERVICES "ListServices"
+#define IFACE_DP_FAILOVER_ACTIVESERVER "ActiveServer"
+#define IFACE_DP_FAILOVER_LISTSERVERS "ListServers"
 
 /* constants for org.freedesktop.sssd.dataprovider */
 #define IFACE_DP "org.freedesktop.sssd.dataprovider"
@@ -72,11 +74,19 @@ int iface_dp_backend_IsOnline_finish(struct sbus_request *req, bool arg_status);
 struct iface_dp_failover {
     struct sbus_vtable vtable; /* derive from sbus_vtable */
     int (*ListServices)(struct sbus_request *req, void *data, const char *arg_domain_name);
+    int (*ActiveServer)(struct sbus_request *req, void *data, const char *arg_service_name);
+    int (*ListServers)(struct sbus_request *req, void *data, const char *arg_service_name);
 };
 
 /* finish function for ListServices */
 int iface_dp_failover_ListServices_finish(struct sbus_request *req, const char *arg_services[], int len_services);
 
+/* finish function for ActiveServer */
+int iface_dp_failover_ActiveServer_finish(struct sbus_request *req, const char *arg_server);
+
+/* finish function for ListServers */
+int iface_dp_failover_ListServers_finish(struct sbus_request *req, const char *arg_servers[], int len_servers);
+
 /* vtable for org.freedesktop.sssd.dataprovider */
 struct iface_dp {
     struct sbus_vtable vtable; /* derive from sbus_vtable */
diff --git a/src/providers/fail_over.c b/src/providers/fail_over.c
index 1d88d2aa54bfdebd4b648e2b13fa8d03e2be3973..8ab39f27f77e19e601855632196006a8dbbdf136 100644
--- a/src/providers/fail_over.c
+++ b/src/providers/fail_over.c
@@ -1660,6 +1660,48 @@ bool fo_svc_has_server(struct fo_service *service, struct fo_server *server)
     return false;
 }
 
+const char **fo_svc_server_list(TALLOC_CTX *mem_ctx,
+                                struct fo_service *service,
+                                size_t *_count)
+{
+    const char **list;
+    const char *server;
+    struct fo_server *srv;
+    size_t count;
+
+    count = 0;
+    DLIST_FOR_EACH(srv, service->server_list) {
+        count++;
+    }
+
+    list = talloc_zero_array(mem_ctx, const char *, count + 1);
+    if (list == NULL) {
+        return NULL;
+    }
+
+    count = 0;
+    DLIST_FOR_EACH(srv, service->server_list) {
+        server = fo_get_server_name(srv);
+        if (server == NULL) {
+            /* _srv_ */
+            continue;
+        }
+
+        list[count] = talloc_strdup(list, server);
+        if (list[count] == NULL) {
+            talloc_free(list);
+            return NULL;
+        }
+        count++;
+    }
+
+    if (_count != NULL) {
+        *_count = count;
+    }
+
+    return list;
+}
+
 bool fo_set_srv_lookup_plugin(struct fo_ctx *ctx,
                               fo_srv_lookup_plugin_send_t send_fn,
                               fo_srv_lookup_plugin_recv_t recv_fn,
diff --git a/src/providers/fail_over.h b/src/providers/fail_over.h
index f24b5715f13931965400c20562a1578aaf756908..d70212fb7ea569b9c47bba36704aa8ae18754cbb 100644
--- a/src/providers/fail_over.h
+++ b/src/providers/fail_over.h
@@ -212,6 +212,10 @@ struct fo_server *fo_get_active_server(struct fo_service *service);
 
 bool fo_svc_has_server(struct fo_service *service, struct fo_server *server);
 
+const char **fo_svc_server_list(TALLOC_CTX *mem_ctx,
+                                struct fo_service *service,
+                                size_t *_count);
+
 /*
  * pvt will be talloc_stealed to ctx
  */
diff --git a/src/responder/ifp/ifp_domains.c b/src/responder/ifp/ifp_domains.c
index ff690ed6a7d5519979d242a4d5dadd08aff50347..977bbfcbe818f08873ce072d34fdcf900cabf52f 100644
--- a/src/responder/ifp/ifp_domains.c
+++ b/src/responder/ifp/ifp_domains.c
@@ -582,3 +582,51 @@ int ifp_domains_domain_list_services(struct sbus_request *sbus_req,
 
     return EOK;
 }
+
+int ifp_domains_domain_active_server(struct sbus_request *sbus_req,
+                                     void *data,
+                                     const char *service)
+{
+    struct ifp_ctx *ifp_ctx;
+    struct sss_domain_info *dom;
+
+    ifp_ctx = talloc_get_type(data, struct ifp_ctx);
+
+    dom = get_domain_info_from_req(sbus_req, data);
+    if (dom == NULL) {
+        sbus_request_reply_error(sbus_req, SBUS_ERROR_UNKNOWN_DOMAIN,
+                                 "Unknown domain");
+        return EOK;
+    }
+
+    rdp_message_send_and_reply(sbus_req, ifp_ctx->rctx, dom, DP_PATH,
+                               IFACE_DP_FAILOVER,
+                               IFACE_DP_FAILOVER_ACTIVESERVER,
+                               DBUS_TYPE_STRING, &service);
+
+    return EOK;
+}
+
+int ifp_domains_domain_list_servers(struct sbus_request *sbus_req,
+                                    void *data,
+                                    const char *service)
+{
+    struct ifp_ctx *ifp_ctx;
+    struct sss_domain_info *dom;
+
+    ifp_ctx = talloc_get_type(data, struct ifp_ctx);
+
+    dom = get_domain_info_from_req(sbus_req, data);
+    if (dom == NULL) {
+        sbus_request_reply_error(sbus_req, SBUS_ERROR_UNKNOWN_DOMAIN,
+                                 "Unknown domain");
+        return EOK;
+    }
+
+    rdp_message_send_and_reply(sbus_req, ifp_ctx->rctx, dom, DP_PATH,
+                               IFACE_DP_FAILOVER,
+                               IFACE_DP_FAILOVER_LISTSERVERS,
+                               DBUS_TYPE_STRING, &service);
+
+    return EOK;
+}
diff --git a/src/responder/ifp/ifp_domains.h b/src/responder/ifp/ifp_domains.h
index 91645e60701f8f75e89a42e93e2c066def67b018..621ba6158e285911cb8298cef212219dfd3afec8 100644
--- a/src/responder/ifp/ifp_domains.h
+++ b/src/responder/ifp/ifp_domains.h
@@ -100,4 +100,12 @@ int ifp_domains_domain_is_online(struct sbus_request *sbus_req,
 int ifp_domains_domain_list_services(struct sbus_request *sbus_req,
                                      void *data);
 
+int ifp_domains_domain_active_server(struct sbus_request *sbus_req,
+                                     void *data,
+                                     const char *service);
+
+int ifp_domains_domain_list_servers(struct sbus_request *sbus_req,
+                                    void *data,
+                                    const char *service);
+
 #endif /* IFP_DOMAINS_H_ */
diff --git a/src/responder/ifp/ifp_iface.c b/src/responder/ifp/ifp_iface.c
index 90bb52b2ccf5207034abbe12bddbfa1eeaf875f7..e6ddc687ba9db878ee39fee5868d1f924d58482d 100644
--- a/src/responder/ifp/ifp_iface.c
+++ b/src/responder/ifp/ifp_iface.c
@@ -81,7 +81,9 @@ struct iface_ifp_domains iface_ifp_domains = {
 struct iface_ifp_domains_domain iface_ifp_domains_domain = {
     { &iface_ifp_domains_domain_meta, 0 },
     .IsOnline = ifp_domains_domain_is_online,
-    .ListServices = ifp_domains_domain_list_services
+    .ListServices = ifp_domains_domain_list_services,
+    .ActiveServer = ifp_domains_domain_active_server,
+    .ListServers = ifp_domains_domain_list_servers
 };
 
 struct iface_ifp_users iface_ifp_users = {
diff --git a/src/responder/ifp/ifp_iface.xml b/src/responder/ifp/ifp_iface.xml
index 7f6f47299deeba4b1baa23d1e63ee7bb17304a59..25b104ad70c0fd84b6c0fe9dbb0dc6e6439c1376 100644
--- a/src/responder/ifp/ifp_iface.xml
+++ b/src/responder/ifp/ifp_iface.xml
@@ -112,6 +112,16 @@
         <method name="ListServices">
             <arg name="services" type="as" direction="out" />
         </method>
+
+        <method name="ActiveServer">
+            <arg name="service" type="s" direction="in" />
+            <arg name="server" type="s" direction="out" />
+        </method>
+
+        <method name="ListServers">
+            <arg name="service_name" type="s" direction="in" />
+            <arg name="servers" type="as" direction="out" />
+        </method>
     </interface>
 
     <interface name="org.freedesktop.sssd.infopipe.Cache">
diff --git a/src/responder/ifp/ifp_iface_generated.c b/src/responder/ifp/ifp_iface_generated.c
index 4d3bb5727b03ae64adad14fcdbb3eb5366edb406..6156ca2947434f301d206232f83cfc0647007707 100644
--- a/src/responder/ifp/ifp_iface_generated.c
+++ b/src/responder/ifp/ifp_iface_generated.c
@@ -558,6 +558,44 @@ int iface_ifp_domains_domain_ListServices_finish(struct sbus_request *req, const
                                          DBUS_TYPE_INVALID);
 }
 
+/* arguments for org.freedesktop.sssd.infopipe.Domains.Domain.ActiveServer */
+const struct sbus_arg_meta iface_ifp_domains_domain_ActiveServer__in[] = {
+    { "service", "s" },
+    { NULL, }
+};
+
+/* arguments for org.freedesktop.sssd.infopipe.Domains.Domain.ActiveServer */
+const struct sbus_arg_meta iface_ifp_domains_domain_ActiveServer__out[] = {
+    { "server", "s" },
+    { NULL, }
+};
+
+int iface_ifp_domains_domain_ActiveServer_finish(struct sbus_request *req, const char *arg_server)
+{
+   return sbus_request_return_and_finish(req,
+                                         DBUS_TYPE_STRING, &arg_server,
+                                         DBUS_TYPE_INVALID);
+}
+
+/* arguments for org.freedesktop.sssd.infopipe.Domains.Domain.ListServers */
+const struct sbus_arg_meta iface_ifp_domains_domain_ListServers__in[] = {
+    { "service_name", "s" },
+    { NULL, }
+};
+
+/* arguments for org.freedesktop.sssd.infopipe.Domains.Domain.ListServers */
+const struct sbus_arg_meta iface_ifp_domains_domain_ListServers__out[] = {
+    { "servers", "as" },
+    { NULL, }
+};
+
+int iface_ifp_domains_domain_ListServers_finish(struct sbus_request *req, const char *arg_servers[], int len_servers)
+{
+   return sbus_request_return_and_finish(req,
+                                         DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &arg_servers, len_servers,
+                                         DBUS_TYPE_INVALID);
+}
+
 /* methods for org.freedesktop.sssd.infopipe.Domains.Domain */
 const struct sbus_method_meta iface_ifp_domains_domain__methods[] = {
     {
@@ -574,6 +612,20 @@ const struct sbus_method_meta iface_ifp_domains_domain__methods[] = {
         offsetof(struct iface_ifp_domains_domain, ListServices),
         NULL, /* no invoker */
     },
+    {
+        "ActiveServer", /* name */
+        iface_ifp_domains_domain_ActiveServer__in,
+        iface_ifp_domains_domain_ActiveServer__out,
+        offsetof(struct iface_ifp_domains_domain, ActiveServer),
+        invoke_s_method,
+    },
+    {
+        "ListServers", /* name */
+        iface_ifp_domains_domain_ListServers__in,
+        iface_ifp_domains_domain_ListServers__out,
+        offsetof(struct iface_ifp_domains_domain, ListServers),
+        invoke_s_method,
+    },
     { NULL, }
 };
 
diff --git a/src/responder/ifp/ifp_iface_generated.h b/src/responder/ifp/ifp_iface_generated.h
index 2eff57410e5292a05818050b96eb85aa3a4f2e16..141348249d2da5447fa04495564a8c6a55d67a1b 100644
--- a/src/responder/ifp/ifp_iface_generated.h
+++ b/src/responder/ifp/ifp_iface_generated.h
@@ -58,6 +58,8 @@
 #define IFACE_IFP_DOMAINS_DOMAIN "org.freedesktop.sssd.infopipe.Domains.Domain"
 #define IFACE_IFP_DOMAINS_DOMAIN_ISONLINE "IsOnline"
 #define IFACE_IFP_DOMAINS_DOMAIN_LISTSERVICES "ListServices"
+#define IFACE_IFP_DOMAINS_DOMAIN_ACTIVESERVER "ActiveServer"
+#define IFACE_IFP_DOMAINS_DOMAIN_LISTSERVERS "ListServers"
 
 /* constants for org.freedesktop.sssd.infopipe.Cache */
 #define IFACE_IFP_CACHE "org.freedesktop.sssd.infopipe.Cache"
@@ -215,6 +217,8 @@ struct iface_ifp_domains_domain {
     struct sbus_vtable vtable; /* derive from sbus_vtable */
     int (*IsOnline)(struct sbus_request *req, void *data);
     int (*ListServices)(struct sbus_request *req, void *data);
+    int (*ActiveServer)(struct sbus_request *req, void *data, const char *arg_service);
+    int (*ListServers)(struct sbus_request *req, void *data, const char *arg_service_name);
 };
 
 /* finish function for IsOnline */
@@ -223,6 +227,12 @@ int iface_ifp_domains_domain_IsOnline_finish(struct sbus_request *req, bool arg_
 /* finish function for ListServices */
 int iface_ifp_domains_domain_ListServices_finish(struct sbus_request *req, const char *arg_services[], int len_services);
 
+/* finish function for ActiveServer */
+int iface_ifp_domains_domain_ActiveServer_finish(struct sbus_request *req, const char *arg_server);
+
+/* finish function for ListServers */
+int iface_ifp_domains_domain_ListServers_finish(struct sbus_request *req, const char *arg_servers[], int len_servers);
+
 /* vtable for org.freedesktop.sssd.infopipe.Cache */
 struct iface_ifp_cache {
     struct sbus_vtable vtable; /* derive from sbus_vtable */
diff --git a/src/tools/sssctl/sssctl_domains.c b/src/tools/sssctl/sssctl_domains.c
index 40962792b84eabeb2c142f158184b17180a01669..545ed95f4415da597b191146409ea6ba028f36f8 100644
--- a/src/tools/sssctl/sssctl_domains.c
+++ b/src/tools/sssctl/sssctl_domains.c
@@ -112,6 +112,155 @@ done:
     return ret;
 }
 
+static const char *proper_service_name(const char *service)
+{
+    if (strcasecmp(service, "AD_GC") == 0) {
+        return "AD Global Catalog";
+    } else if (strcasecmp(service, "AD") == 0) {
+        return "AD Domain Controller";
+    } else if (strncasecmp(service, "sd_gc_", strlen("sd_gc_")) == 0) {
+        return "AD Global Catalog";
+    } else if (strncasecmp(service, "sd_", strlen("sd_")) == 0) {
+        return "AD Domain Controller";
+    }
+
+    return service;
+}
+
+static errno_t sssctl_domain_status_active_server(struct sss_tool_ctx *tool_ctx,
+                                                  sss_sifp_ctx *sifp,
+                                                  const char *domain_path)
+{
+    TALLOC_CTX *tmp_ctx;
+    sss_sifp_error error;
+    DBusMessage *reply;
+    const char *server;
+    const char **services;
+    int num_services;
+    errno_t ret;
+    int i;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+        return ENOMEM;
+    }
+
+    error = sssctl_sifp_send(tmp_ctx, sifp, &reply, domain_path,
+                             IFACE_IFP_DOMAINS_DOMAIN,
+                             IFACE_IFP_DOMAINS_DOMAIN_LISTSERVICES);
+    if (error != SSS_SIFP_OK) {
+        sssctl_sifp_error(sifp, error, "Unable to list services");
+        ret = EIO;
+        goto done;
+    }
+
+    ret = sbus_parse_reply(reply, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
+                           &services, &num_services);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    printf(_("Active servers:\n"));
+    for (i = 0; i < num_services; i++) {
+        error = sssctl_sifp_send(tmp_ctx, sifp, &reply, domain_path,
+                                 IFACE_IFP_DOMAINS_DOMAIN,
+                                 IFACE_IFP_DOMAINS_DOMAIN_ACTIVESERVER,
+                                 DBUS_TYPE_STRING, &services[i]);
+        if (error != SSS_SIFP_OK) {
+            sssctl_sifp_error(sifp, error, "Unable to get active server");
+            ret = EIO;
+            goto done;
+        }
+
+        ret = sbus_parse_reply(reply, DBUS_TYPE_STRING, &server);
+        if (ret != EOK) {
+            goto done;
+        }
+
+        server = SBUS_IS_STRING_EMPTY(server) ? _("not connected") : server;
+        printf("%s: %s\n", proper_service_name(services[i]), server);
+    }
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+static errno_t sssctl_domain_status_server_list(struct sss_tool_ctx *tool_ctx,
+                                                sss_sifp_ctx *sifp,
+                                                const char *domain_path)
+{
+    TALLOC_CTX *tmp_ctx;
+    sss_sifp_error error;
+    DBusMessage *reply;
+    const char **servers;
+    int num_servers;
+    const char **services;
+    int num_services;
+    errno_t ret;
+    int i, j;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+        return ENOMEM;
+    }
+
+    error = sssctl_sifp_send(tmp_ctx, sifp, &reply, domain_path,
+                             IFACE_IFP_DOMAINS_DOMAIN,
+                             IFACE_IFP_DOMAINS_DOMAIN_LISTSERVICES);
+    if (error != SSS_SIFP_OK) {
+        sssctl_sifp_error(sifp, error, "Unable to list services");
+        ret = EIO;
+        goto done;
+    }
+
+    ret = sbus_parse_reply(reply, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
+                           &services, &num_services);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    for (i = 0; i < num_services; i++) {
+        printf(_("Discovered %s servers:\n"), proper_service_name(services[i]));
+        error = sssctl_sifp_send(tmp_ctx, sifp, &reply, domain_path,
+                                 IFACE_IFP_DOMAINS_DOMAIN,
+                                 IFACE_IFP_DOMAINS_DOMAIN_LISTSERVERS,
+                                 DBUS_TYPE_STRING, &services[i]);
+        if (error != SSS_SIFP_OK) {
+            sssctl_sifp_error(sifp, error, "Unable to get active server");
+            ret = EIO;
+            goto done;
+        }
+
+        ret = sbus_parse_reply(reply, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
+                               &servers, &num_servers);
+        if (ret != EOK) {
+            goto done;
+        }
+
+        if (num_servers == 0) {
+            puts(_("None so far.\n"));
+            continue;
+        }
+
+        for (j = 0; j < num_servers; j++) {
+            printf("- %s\n", servers[j]);
+        }
+
+        printf("\n");
+    }
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
 struct sssctl_domain_status_opts {
     const char *domain;
     int online;
@@ -135,11 +284,8 @@ errno_t sssctl_domain_status(struct sss_cmdline *cmdline,
     /* Parse command line. */
     struct poptOption options[] = {
         {"online", 'o', POPT_ARG_NONE , &opts.online, 0, _("Show online status"), NULL },
-        /*
-        {"last-requests", 'l', POPT_ARG_NONE, &opts.last, 0, _("Show last requests that went to data provider"), NULL },
         {"active-server", 'a', POPT_ARG_NONE, &opts.active, 0, _("Show information about active server"), NULL },
         {"servers", 'r', POPT_ARG_NONE, &opts.servers, 0, _("Show list of discovered servers"), NULL },
-        */
         {"start", 's', POPT_ARG_NONE, &opts.force_start, 0, _("Start SSSD if it is not running"), NULL },
         POPT_TABLEEND
     };
@@ -175,10 +321,32 @@ errno_t sssctl_domain_status(struct sss_cmdline *cmdline,
         return EFAULT;
     }
 
-    ret = sssctl_domain_status_online(tool_ctx, sifp, path);
-    if (ret != EOK) {
-        fprintf(stderr, _("Unable to get online status\n"));
-        return ret;
+    if (opts.online) {
+        ret = sssctl_domain_status_online(tool_ctx, sifp, path);
+        if (ret != EOK) {
+            fprintf(stderr, _("Unable to get online status\n"));
+            return ret;
+        }
+
+        printf("\n");
+    }
+
+    if (opts.active) {
+        ret = sssctl_domain_status_active_server(tool_ctx, sifp, path);
+        if (ret != EOK) {
+            fprintf(stderr, _("Unable to get online status\n"));
+            return ret;
+        }
+
+        printf("\n");
+    }
+
+    if (opts.servers) {
+        ret = sssctl_domain_status_server_list(tool_ctx, sifp, path);
+        if (ret != EOK) {
+            fprintf(stderr, _("Unable to get server list\n"));
+            return ret;
+        }
     }
 
     return EOK;
-- 
2.4.11