/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <synch.h>
#include <lber.h>
#include <ldap.h>
#include <nfs/fedfs.h>
#include "fedfs_impl.h"
#define NCE_CACHE_TIMEOUT 300
typedef struct nce_cache_node {
char *host;
int port;
int numnce;
nce_entry_t **nces;
time_t timestamp;
struct nce_cache_node *next;
} nce_cache_node_t;
static nce_cache_node_t *nce_cache = NULL;
static mutex_t nce_cache_lock = DEFAULTMUTEX;
static nce_entry_t **
copy_ncelist(int numnce, nce_entry_t **nces)
{
nce_entry_t **newnces;
int i;
newnces = (nce_entry_t **)calloc(sizeof (nce_entry_t **), numnce);
if (newnces == NULL)
return (NULL);
for (i = 0; i < numnce; i++) {
newnces[i] = calloc(sizeof (nce_entry_t), 1);
if (newnces[i] == NULL)
goto err;
newnces[i]->context = strdup(nces[i]->context);
if (newnces[i]->context == NULL)
goto err;
newnces[i]->prefix = strdup(nces[i]->prefix);
if (newnces[i]->prefix == NULL)
goto err;
}
return (newnces);
err:
free_nces(numnce, newnces);
return (NULL);
}
/*
* Store NCEs in the cache
*/
static void
cache_nces(char *host, int port, int numnce, nce_entry_t **nces)
{
nce_cache_node_t *n, *new, *prev = NULL;
if (numnce == 0)
return;
#ifdef DEBUG
fprintf(stderr, "cache_nces: caching %s/%d\n", host, port);
#endif
/* Create a new cache entry */
new = calloc(sizeof (nce_cache_node_t), 1);
if (new == NULL)
return;
new->host = strdup(host);
if (new->host == NULL)
goto err;
new->port = port;
new->numnce = numnce;
new->timestamp = time(NULL);
new->nces = copy_ncelist(numnce, nces);
if (new->nces == NULL)
goto err;
(void) mutex_lock(&nce_cache_lock);
/* Look for an existing entry */
for (n = nce_cache; n != NULL; n = n->next) {
if (strcmp(host, n->host) == 0 && port == n->port)
break;
prev = n;
}
if (n == NULL) {
/* Not found, add entry to tail */
if (prev == NULL)
nce_cache = new;
else
prev->next = new;
} else {
/* Found, replace the old entry */
new->next = n->next;
if (prev == NULL)
nce_cache = new;
else
prev->next = new;
#ifdef DEBUG
fprintf(stderr, "cache_nces: replacing %s/%d\n", n->host, n->port);
#endif
free_nces(n->numnce, n->nces);
free(n->host);
free(n);
}
(void) mutex_unlock(&nce_cache_lock);
return;
err:
free_nces(numnce, new->nces);
free(new->host);
free(new);
}
/*
* Look up NCEs in the cache
*/
static int
list_nces_cache(char *host, int port, nce_entry_t ***nces)
{
nce_cache_node_t *n, *prev, temp;
nce_entry_t **ncelist;
time_t t = time(NULL);
(void) mutex_lock(&nce_cache_lock);
/* Look for an existing entry */
prev = NULL;
for (n = nce_cache; n != NULL; n = n->next) {
if (t - n->timestamp >= NCE_CACHE_TIMEOUT) {
if (prev == NULL)
nce_cache = n->next;
else
prev->next = n->next;
#ifdef DEBUG
fprintf(stderr, "list_nces_cache: wiping %s/%d\n", n->host, n->port);
#endif
temp.next = n->next;
free_nces(n->numnce, n->nces);
free(n->host);
free(n);
n = &temp;
continue;
}
prev = n;
if (strcmp(host, n->host) == 0 && port == n->port)
break;
}
if (n == NULL) {
(void) mutex_unlock(&nce_cache_lock);
return (0);
}
#ifdef DEBUG
fprintf(stderr, "list_nces_cache: found %s/%d\n", n->host, n->port);
#endif
ncelist = copy_ncelist(n->numnce, n->nces);
(void) mutex_unlock(&nce_cache_lock);
if (ncelist == NULL)
return (0);
*nces = ncelist;
return (n->numnce);
}
/*
* Return a list of NCEs (NSDB container entries) for an LDAP
* server at host/port. Returns a pointer to a list which
* must be freed by the caller with free_nces() or equivalent.
* Each NCE is returned as the naming context and the prefix
* of the NCE object within the NC.
*
* An NCE is an attribute which may be present at the root of a
* naming context to indicate where FedFS entries are to be found.
* At least one NCE must be present for an LDAP server to be an
* NSDB for FedFS.
*
* Return values: the number of NCEs in the array (may be zero),
* or -1 in the case of communications errors.
*/
int
list_nces(char *host, int port, nce_entry_t ***nces)
{
LDAP *ld;
LDAPMessage *result, *e;
BerElement *ber;
char *a;
#ifdef DEBUG
char *dn;
#endif
struct berval **vals;
char *attribs[5];
char filter[80];
int i, j, numnc, numnce;
char **nclist;
nce_entry_t **ncelist;
/* Sanity */
if (host == NULL || nces == NULL)
return (-1);
/*
* Look in the cache first
*/
numnce = list_nces_cache(host, port, nces);
if (numnce > 0)
return (numnce);
/*
* Connect and bind to the directory anonymously.
*/
ld = nsdb_connect(host, port, NULL, NULL);
if (ld == NULL) {
#ifdef DEBUG
perror("nsdb_connect");
#endif
return (-1);
}
/*
* Perform search for naming contexts, equivalent to:
* ldapsearch -h $host -b "" -s base objectClass=* namingContexts
*/
attribs[0] = "namingContexts";
attribs[1] = NULL;
filter[0] = '\0';
(void) strcat(filter, "objectClass=*");
if (ldap_search_s(ld, "", LDAP_SCOPE_BASE,
filter, attribs, 0, &result) != LDAP_SUCCESS) {
#ifdef DEBUG
fprintf(stderr, "Error: Unable to list naming contexts\n");
ldap_perror(ld, "ldap_search_s");
#endif
(void) ldap_unbind(ld);
return (-1);
}
numnc = 0;
nclist = calloc(sizeof (char *), numnc);
/* for each entry print out name + all attributes and values */
for (e = ldap_first_entry(ld, result); e != NULL;
e = ldap_next_entry(ld, e)) {
#ifdef DEBUG
if ((dn = ldap_get_dn(ld, e)) != NULL)
fprintf(stderr, "distinguished name is %s\n", dn);
ldap_memfree(dn);
#endif
ber = NULL;
for (a = ldap_first_attribute(ld, e, &ber);
a != NULL; a = ldap_next_attribute(ld, e, ber)) {
vals = ldap_get_values_len(ld, e, a);
if (vals == NULL)
break;
for (j = 0; vals[j] != NULL; j++) {
#ifdef DEBUG
fprintf(stderr, "attr %s, val[%d] %s, len %d\n",
a, j, vals[j]->bv_val, vals[j]->bv_len);
#endif
nclist = realloc(nclist,
sizeof (char *) * (numnc + 1));
nclist[numnc] = strdup(vals[j]->bv_val);
numnc++;
}
ldap_value_free_len(vals);
ldap_memfree(a);
}
if (ber != NULL)
ber_free(ber, 0);
}
(void) ldap_msgfree(result);
numnce = 0;
ncelist = calloc(sizeof (char *), numnce);
/*
* Perform search for NCE in each naming context, equivalent to:
* ldapsearch -h $host -b $nc objectClass=fedfsNsdbContainerInfo \
* fedfsNcePrefix
*/
for (i = 0; i < numnc; i++) {
#ifdef DEBUG
fprintf(stderr, "list_nces(): naming context %d: %s\n",
i, nclist[i]);
#endif
attribs[0] = "fedfsNcePrefix";
attribs[1] = NULL;
filter[0] = '\0';
(void) strcat(filter, "objectClass=fedfsNsdbContainerInfo");
if (ldap_search_s(ld, nclist[i], LDAP_SCOPE_BASE,
filter, attribs, 0, &result) != LDAP_SUCCESS) {
#ifdef DEBUG
fprintf(stderr, "Warning: Unable to list NCEs\n");
ldap_perror(ld, "ldap_search_s");
#endif
continue;
}
/* for each entry print out name + all attributes and values */
for (e = ldap_first_entry(ld, result); e != NULL;
e = ldap_next_entry(ld, e)) {
#ifdef DEBUG
if ((dn = ldap_get_dn(ld, e)) != NULL)
fprintf(stderr, "distinguished name is %s\n",
dn);
ldap_memfree(dn);
#endif
ber = NULL;
for (a = ldap_first_attribute(ld, e, &ber);
a != NULL; a = ldap_next_attribute(ld, e, ber)) {
vals = ldap_get_values_len(ld, e, a);
if (vals == NULL)
break;
for (j = 0; vals[j] != NULL; j++) {
#ifdef DEBUG
fprintf(stderr,
"attr %s, val[%d] %s, len %d\n",
a, j, vals[j]->bv_val,
vals[j]->bv_len);
#endif
/*
* NCE is prefix plus the namingContext
*/
ncelist = realloc(ncelist,
sizeof (nce_entry_t *) *
(numnce + 1));
ncelist[numnce] = calloc(
sizeof (nce_entry_t), 1);
ncelist[numnce]->context =
strdup(nclist[i]);
ncelist[numnce]->prefix =
strdup(vals[j]->bv_val);
numnce++;
}
ldap_value_free_len(vals);
ldap_memfree(a);
}
if (ber != NULL)
ber_free(ber, 0);
}
(void) ldap_msgfree(result);
}
for (i = 0; i < numnc; i++)
free(nclist[i]);
free(nclist);
(void) ldap_unbind(ld);
*nces = ncelist;
#ifdef DEBUG
for (i = 0; i < numnce; i++)
fprintf(stderr, "NCE %d: %s + %s\n", i, ncelist[i]->prefix,
ncelist[i]->context);
#endif
cache_nces(host, port, numnce, ncelist);
return (numnce);
}
/*
* Free list of NCEs as returned from list_nces().
*/
void
free_nces(int n, nce_entry_t **ncelist)
{
int i;
if (n == 0 || ncelist == NULL)
return;
for (i = 0; i < n; i++) {
free(ncelist[i]->context);
free(ncelist[i]->prefix);
free(ncelist[i]);
}
free(ncelist);
}
char *
nce_to_dn(nce_entry_t *entry)
{
int size;
char *dn;
if (entry == NULL)
return (NULL);
size = strlen(entry->prefix) + strlen(entry->context) + 2;
dn = malloc(size);
if (dn == NULL)
return (NULL);
if (strlen(entry->prefix) > 0)
(void) snprintf(dn, size, "%s,%s", entry->prefix,
entry->context);
else
(void) snprintf(dn, size, "%s", entry->context);
return (dn);
}
#ifdef MAIN
int
main(int argc, char **argv)
{
char *host, *port, *uuid;
char **nce;
int i, n;
host = argv[1];
port = argv[2];
uuid = argv[3];
n = list_nces(host, atoi(port), &nce);
for (i = 0; i < n; i++)
printf("NCE %d: %s + %s\n", i, nce[i].prefix, nce[i].context);
free_nces(n, nce);
return (0);
}
#endif