From 6f1082722a3fffb1cecb001830987ded1893aede Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgallagh@redhat.com>
Date: Mon, 23 Jan 2012 22:00:30 -0500
Subject: [PATCH 12/12] PROXY: add support for enumerating services

---
 src/providers/proxy/proxy.h          |    8 ++
 src/providers/proxy/proxy_id.c       |    7 ++
 src/providers/proxy/proxy_init.c     |   33 +++++++
 src/providers/proxy/proxy_services.c |  170 ++++++++++++++++++++++++++++++++++
 4 files changed, 218 insertions(+), 0 deletions(-)

diff --git a/src/providers/proxy/proxy.h b/src/providers/proxy/proxy.h
index 15fdea42341eb1cafdf001e55df47429801a35ba..6e6179c8d9445c4a172a4498180a0131992672f1 100644
--- a/src/providers/proxy/proxy.h
+++ b/src/providers/proxy/proxy.h
@@ -81,6 +81,11 @@ struct proxy_nss_ops {
                                         struct servent *result,
                                         char *buffer, size_t buflen,
                                         int *errnop);
+    enum nss_status (*setservent)(void);
+    enum nss_status (*getservent_r)(struct servent *result,
+                                    char *buffer, size_t buflen,
+                                    int *errnop);
+    enum nss_status (*endservent)(void);
 };
 
 struct authtok_conv {
@@ -165,5 +170,8 @@ errno_t get_serv_byport(struct proxy_id_ctx *ctx,
                         struct sysdb_ctx *sysdb,
                         struct sss_domain_info *dom,
                         const char *be_filter);
+errno_t enum_services(struct proxy_id_ctx *ctx,
+                      struct sysdb_ctx *sysdb,
+                      struct sss_domain_info *dom);
 
 #endif /* __PROXY_H__ */
diff --git a/src/providers/proxy/proxy_id.c b/src/providers/proxy/proxy_id.c
index 898c0ba31b2e9c31e1446ac96b32701e288be3a0..5188e073da0860ec3d473ac936236e8f66acf4bc 100644
--- a/src/providers/proxy/proxy_id.c
+++ b/src/providers/proxy/proxy_id.c
@@ -1190,6 +1190,13 @@ void proxy_get_account_info(struct be_req *breq)
             ret = get_serv_byport(ctx, sysdb, domain, ar->filter_value);
             break;
         case BE_FILTER_ENUM:
+            if (!ctx->ops.setservent
+                    || !ctx->ops.getservent_r
+                    || !ctx->ops.endservent) {
+                return proxy_reply(breq, DP_ERR_FATAL,
+                                   ENODEV, "Services are not supported");
+            }
+            ret = enum_services(ctx, sysdb, domain);
             break;
         default:
             return proxy_reply(breq, DP_ERR_FATAL,
diff --git a/src/providers/proxy/proxy_init.c b/src/providers/proxy/proxy_init.c
index b28e65a57e92e79aee60538e9b2880540c1743f3..d43550bfaff0792b29b849f51b3714e4b27c3e55 100644
--- a/src/providers/proxy/proxy_init.c
+++ b/src/providers/proxy/proxy_init.c
@@ -259,6 +259,39 @@ int sssm_proxy_id_init(struct be_ctx *bectx,
                dlerror()));
     }
 
+    ctx->ops.setservent = proxy_dlsym(ctx->handle,
+                                      "_nss_%s_setservent",
+                                      libname);
+    if (!ctx->ops.setservent) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              ("Failed to load _nss_%s_setservent, error: %s. "
+               "The library does not support services.\n",
+               libname,
+               dlerror()));
+    }
+
+    ctx->ops.getservent_r = proxy_dlsym(ctx->handle,
+                                        "_nss_%s_getservent_r",
+                                        libname);
+    if (!ctx->ops.getservent_r) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              ("Failed to load _nss_%s_getservent_r, error: %s. "
+               "The library does not support services.\n",
+               libname,
+               dlerror()));
+    }
+
+    ctx->ops.endservent = proxy_dlsym(ctx->handle,
+                                      "_nss_%s_endservent",
+                                      libname);
+    if (!ctx->ops.endservent) {
+        DEBUG(SSSDBG_MINOR_FAILURE,
+              ("Failed to load _nss_%s_endservent, error: %s. "
+               "The library does not support services.\n",
+               libname,
+               dlerror()));
+    }
+
     *ops = &proxy_id_ops;
     *pvt_data = ctx;
     ret = EOK;
diff --git a/src/providers/proxy/proxy_services.c b/src/providers/proxy/proxy_services.c
index 6707d81f0eb3e61bcddeb4d997ce3c44026cc0fb..55135b73e74a3dc91412c6b0e9169a13b01877bb 100644
--- a/src/providers/proxy/proxy_services.c
+++ b/src/providers/proxy/proxy_services.c
@@ -262,3 +262,173 @@ done:
     talloc_free(tmp_ctx);
     return ret;
 }
+
+static errno_t
+get_const_aliases(TALLOC_CTX *mem_ctx,
+                  char **aliases,
+                  const char ***const_aliases)
+{
+    errno_t ret;
+    size_t i, num_aliases;
+    const char **alias_list = NULL;
+
+    for (num_aliases = 0; aliases[num_aliases]; num_aliases++);
+
+    alias_list = talloc_zero_array(mem_ctx, const char *, num_aliases + 1);
+    if (!alias_list) return ENOMEM;
+
+    for (i = 0; aliases[i]; i++) {
+        alias_list[i] = talloc_strdup(alias_list, aliases[i]);
+        if (!alias_list[i]) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+
+    ret = EOK;
+    *const_aliases = alias_list;
+
+done:
+    if (ret != EOK) {
+        talloc_zfree(alias_list);
+    }
+    return ret;
+}
+
+errno_t
+enum_services(struct proxy_id_ctx *ctx,
+              struct sysdb_ctx *sysdb,
+              struct sss_domain_info *dom)
+{
+    TALLOC_CTX *tmpctx;
+    bool in_transaction = false;
+    struct servent *svc;
+    enum nss_status status;
+    size_t buflen;
+    char *buffer;
+    char *newbuf;
+    int ret;
+    time_t now = time(NULL);
+    const char *protocols[2] = { NULL, NULL };
+
+    DEBUG(SSSDBG_TRACE_FUNC, ("Enumerating services\n"));
+
+    tmpctx = talloc_new(NULL);
+    if (!tmpctx) {
+        return ENOMEM;
+    }
+
+    svc = talloc(tmpctx, struct servent);
+    if (!svc) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    buflen = DEFAULT_BUFSIZE;
+    buffer = talloc_size(tmpctx, buflen);
+    if (!buffer) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    ret = sysdb_transaction_start(sysdb);
+    if (ret) {
+        goto done;
+    }
+    in_transaction = true;
+
+    status = ctx->ops.setservent();
+    if (status != NSS_STATUS_SUCCESS) {
+        ret = EIO;
+        goto done;
+    }
+
+again:
+    /* always zero out the svc structure */
+    memset(svc, 0, sizeof(struct servent));
+
+    /* get entry */
+    status = ctx->ops.getservent_r(svc, buffer, buflen, &ret);
+
+    switch (status) {
+    case NSS_STATUS_TRYAGAIN:
+        /* buffer too small ? */
+        if (buflen < MAX_BUF_SIZE) {
+            buflen *= 2;
+        }
+        if (buflen > MAX_BUF_SIZE) {
+            buflen = MAX_BUF_SIZE;
+        }
+        newbuf = talloc_realloc_size(tmpctx, buffer, buflen);
+        if (!newbuf) {
+            ret = ENOMEM;
+            goto done;
+        }
+        buffer = newbuf;
+        goto again;
+
+    case NSS_STATUS_NOTFOUND:
+
+        /* we are done here */
+        DEBUG(SSSDBG_TRACE_FUNC, ("Enumeration completed.\n"));
+
+        ret = sysdb_transaction_commit(sysdb);
+        in_transaction = false;
+        break;
+
+    case NSS_STATUS_SUCCESS:
+
+        DEBUG(SSSDBG_TRACE_INTERNAL,
+              ("Service found (%s, %d/%s)\n",
+               svc->s_name, svc->s_port, svc->s_proto));
+
+        protocols[0] = svc->s_proto;
+
+        const char **const_aliases;
+        ret = get_const_aliases(tmpctx, svc->s_aliases, &const_aliases);
+        if (ret != EOK) {
+            /* Do not fail completely on errors.
+             * Just report the failure to save and go on */
+            DEBUG(SSSDBG_OP_FAILURE,
+                  ("Failed to store service [%s]. Ignoring.\n",
+                   strerror(ret)));
+            goto again; /* next */
+        }
+
+        ret = sysdb_store_service(sysdb,
+                                  svc->s_name,
+                                  svc->s_port,
+                                  const_aliases,
+                                  protocols,
+                                  ctx->entry_cache_timeout,
+                                  now);
+        if (ret) {
+            /* Do not fail completely on errors.
+             * Just report the failure to save and go on */
+            DEBUG(SSSDBG_OP_FAILURE,
+                  ("Failed to store service [%s]. Ignoring.\n",
+                   strerror(ret)));
+        }
+        goto again; /* next */
+
+    case NSS_STATUS_UNAVAIL:
+        /* "remote" backend unavailable. Enter offline mode */
+        ret = ENXIO;
+        break;
+
+    default:
+        ret = EIO;
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              ("proxy -> getservent_r failed (%d)[%s]\n",
+               ret, strerror(ret)));
+        break;
+    }
+
+done:
+    talloc_zfree(tmpctx);
+    if (in_transaction) {
+        sysdb_transaction_cancel(sysdb);
+    }
+    ctx->ops.endservent();
+    return ret;
+}
-- 
1.7.7.5

