2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <strings.h>
2N/A#include <lber.h>
2N/A#include <ldap.h>
2N/A#include <uuid/uuid.h>
2N/A#include <nfs/fedfs.h>
2N/A#include <sasl/sasl.h>
2N/A#include <syslog.h>
2N/A#include "fedfs_impl.h"
2N/A
2N/Atypedef struct cb_param {
2N/A char *mech;
2N/A char *authid;
2N/A char *authzid;
2N/A char *passwd;
2N/A char *realm;
2N/A} cb_param_t;
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Asaslcallback(LDAP *ld, unsigned flags, void *defaults, void *in)
2N/A{
2N/A char *ret = NULL;
2N/A sasl_interact_t *interact = in;
2N/A cb_param_t *cred = (cb_param_t *)defaults;
2N/A
2N/A
2N/A while (interact->id != SASL_CB_LIST_END) {
2N/A
2N/A switch (interact->id) {
2N/A
2N/A case SASL_CB_GETREALM:
2N/A ret = cred->realm;
2N/A break;
2N/A case SASL_CB_AUTHNAME:
2N/A ret = cred->authid;
2N/A break;
2N/A case SASL_CB_PASS:
2N/A ret = cred->passwd;
2N/A break;
2N/A case SASL_CB_USER:
2N/A ret = cred->authzid;
2N/A break;
2N/A case SASL_CB_NOECHOPROMPT:
2N/A case SASL_CB_ECHOPROMPT:
2N/A default:
2N/A break;
2N/A }
2N/A
2N/A if (ret) {
2N/A /*
2N/A * No need to do strdup(ret), the data is always
2N/A * available in 'defaults' and libldap won't
2N/A * free it either. strdup(ret) causes memory
2N/A * leak.
2N/A */
2N/A interact->result = ret;
2N/A interact->len = strlen(ret);
2N/A } else {
2N/A interact->result = NULL;
2N/A interact->len = 0;
2N/A }
2N/A interact++;
2N/A }
2N/A
2N/A return (LDAP_SUCCESS);
2N/A}
2N/A
2N/ALDAP *
2N/Ansdb_connect(char *nsdb, int port, char *admin, char *password)
2N/A{
2N/A LDAP *ld = NULL;
2N/A char *host, *url = NULL, *prefix, *certpath = NULL;
2N/A char *dn, *pw, *smf_dn = NULL, *smf_pw = NULL, *digest_md5_name = NULL;
2N/A int rc, sec = FEDFS_SEC_NONE, len, do_starttls;
2N/A cb_param_t sasl_param;
2N/A nsdb_info_t *info;
2N/A int opt = LDAP_VERSION3;
2N/A
2N/A#ifdef DEBUG
2N/A (void) fprintf(stderr, "nsdb_connect args: %s/%d, %s/%s\n",
2N/A nsdb, port, admin, password);
2N/A#endif
2N/A /*
2N/A * Honor the "force_loopback" property if set
2N/A */
2N/A if (fedfs_use_loopback() == 1)
2N/A host = "localhost";
2N/A else
2N/A host = nsdb;
2N/A
2N/A dn = admin;
2N/A pw = password;
2N/A
2N/A /*
2N/A * See what info we have in SMF
2N/A */
2N/A info = fedfs_smf_lookup(host, port, 0);
2N/A if (info != NULL) {
2N/A if (info->sectype != NULL)
2N/A sec = sectype_to_int(info->sectype);
2N/A if (info->certpath != NULL)
2N/A certpath = strdup(info->certpath);
2N/A if (info->binddn != NULL)
2N/A smf_dn = strdup(info->binddn);
2N/A if (info->bindpw != NULL)
2N/A smf_pw = strdup(info->bindpw);
2N/A }
2N/A fedfs_smf_lookup_free(info);
2N/A
2N/A /*
2N/A * If we need encryption, set it up globally.
2N/A */
2N/A if (sec != FEDFS_SEC_NONE && certpath != NULL) {
2N/A rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE,
2N/A certpath);
2N/A if (rc != LDAP_SUCCESS) {
2N/A#ifdef DEBUG
2N/A (void) fprintf(stderr, "ldap_set_option(CERT) "
2N/A "failed: %s\n", ldap_err2string(rc));
2N/A#endif
2N/A syslog(LOG_ERR, "nsdb_connect: "
2N/A "ldap_set_option(CERT) failed: %s\n",
2N/A ldap_err2string(rc));
2N/A goto out;
2N/A }
2N/A#ifdef DEBUG
2N/A else {
2N/A (void) fprintf(stderr, "ldap_set_option(CERT) OK\n");
2N/A }
2N/A#endif
2N/A }
2N/A
2N/Aretry:
2N/A /*
2N/A * Authentication needs some thinking about admin user
2N/A */
2N/A if (dn != NULL) {
2N/A digest_md5_name = malloc(strlen(dn) + 5);
2N/A /* 5 = strlen("dn: ") + 1 */
2N/A if (digest_md5_name == NULL)
2N/A goto out;
2N/A (void) strcpy(digest_md5_name, "dn: ");
2N/A (void) strcat(digest_md5_name, dn);
2N/A }
2N/A else
2N/A digest_md5_name = NULL;
2N/A
2N/A /*
2N/A * Figure out what we want to do if we get to SASL bind
2N/A */
2N/A if (sec == FEDFS_SEC_NONE) {
2N/A prefix = "ldap";
2N/A do_starttls = 0;
2N/A sasl_param.mech = "SIMPLE";
2N/A sasl_param.authid = dn;
2N/A sasl_param.authzid = dn;
2N/A sasl_param.passwd = pw;
2N/A } else if (port == LDAPS_PORT) {
2N/A prefix = "ldaps";
2N/A do_starttls = 0;
2N/A sasl_param.mech = "DIGEST-MD5";
2N/A sasl_param.authid = digest_md5_name;
2N/A sasl_param.authzid = digest_md5_name;
2N/A sasl_param.passwd = pw;
2N/A } else {
2N/A prefix = "ldap";
2N/A do_starttls = 1;
2N/A sasl_param.mech = "DIGEST-MD5";
2N/A sasl_param.authid = digest_md5_name;
2N/A sasl_param.authzid = digest_md5_name;
2N/A sasl_param.passwd = pw;
2N/A }
2N/A
2N/A len = strlen(host) + 26;
2N/A url = malloc(len);
2N/A if (url == NULL)
2N/A goto out;
2N/A rc = snprintf(url, len, "%s://%s:%d", prefix, host, port);
2N/A#ifdef DEBUG
2N/A (void) fprintf(stderr, "nsdb_connect: url %s\n", url);
2N/A#endif
2N/A
2N/A /*
2N/A * Get a handle to an LDAP connection to host:port
2N/A */
2N/A rc = ldap_initialize(&ld, url);
2N/A if (rc != LDAP_SUCCESS) {
2N/A#ifdef DEBUG
2N/A (void) fprintf(stderr, "ldap_initialize failed: %s\n",
2N/A ldap_err2string(rc));
2N/A#endif
2N/A syslog(LOG_WARNING, "nsdb_connect: ldap_initialize "
2N/A "failed: %s\n", ldap_err2string(rc));
2N/A goto out;
2N/A }
2N/A#ifdef DEBUG
2N/A (void) fprintf(stderr, "nsdb_connect: past ldap_initialize\n");
2N/A#endif
2N/A
2N/A /*
2N/A * We like LDAPv3.
2N/A */
2N/A (void) ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &opt);
2N/A
2N/A /*
2N/A * Start TLS negotiation now if we want it.
2N/A */
2N/A if (do_starttls) {
2N/A rc = ldap_start_tls_s(ld, NULL, NULL);
2N/A if (rc != LDAP_SUCCESS) {
2N/A#ifdef DEBUG
2N/A (void) fprintf(stderr, "ldap_start_tls_s() "
2N/A "failed: %s\n", ldap_err2string(rc));
2N/A#endif
2N/A syslog(LOG_ERR, "nsdb_connect: ldap_start_tls_s() "
2N/A "failed: %s\n", ldap_err2string(rc));
2N/A (void) ldap_unbind(ld);
2N/A ld = NULL;
2N/A goto out;
2N/A }
2N/A#ifdef DEBUG
2N/A else {
2N/A (void) fprintf(stderr, "ldap_start_tls_s() OK\n");
2N/A }
2N/A#endif
2N/A }
2N/A
2N/A /*
2N/A * Try simple bind first, nice for anonymous access.
2N/A */
2N/A rc = ldap_simple_bind_s(ld, dn, pw);
2N/A if (rc != LDAP_SUCCESS) {
2N/A#ifdef DEBUG
2N/A (void) fprintf(stderr, "ldap_simple_bind_s() failed: %s\n",
2N/A ldap_err2string(rc));
2N/A#endif
2N/A syslog(LOG_WARNING, "nsdb_connect: ldap_simple_bind_s() "
2N/A "failed: %s\n", ldap_err2string(rc));
2N/A }
2N/A#ifdef DEBUG
2N/A else
2N/A (void) fprintf(stderr, "ldap_simple_bind_s() OK\n");
2N/A#endif
2N/A if (rc == LDAP_SUCCESS)
2N/A goto out;
2N/A
2N/A /*
2N/A * Try SASL bind next.
2N/A */
2N/A rc = ldap_sasl_interactive_bind_s(
2N/A ld, NULL, sasl_param.mech,
2N/A NULL, NULL, LDAP_SASL_QUIET,
2N/A saslcallback, &sasl_param);
2N/A if (rc != LDAP_SUCCESS) {
2N/A#ifdef DEBUG
2N/A (void) fprintf(stderr, "ldap_sasl_interactive_bind_s() "
2N/A "failed: %s\n", ldap_err2string(rc));
2N/A#endif
2N/A syslog(LOG_WARNING, "nsdb_connect: "
2N/A "ldap_sasl_interactive_bind_s() failed: %s\n",
2N/A ldap_err2string(rc));
2N/A }
2N/A#ifdef DEBUG
2N/A else
2N/A (void) fprintf(stderr, "ldap_sasl_interactive_bind_s() OK\n");
2N/A#endif
2N/A
2N/A if (rc == LDAP_SUCCESS)
2N/A goto out;
2N/A
2N/A (void) ldap_unbind(ld);
2N/A ld = NULL;
2N/A if (dn == NULL && smf_dn != NULL && smf_pw != NULL) {
2N/A dn = smf_dn;
2N/A pw = smf_pw;
2N/A free(url);
2N/A goto retry;
2N/A }
2N/A
2N/Aout:
2N/A free(certpath);
2N/A free(smf_dn);
2N/A free(smf_pw);
2N/A free(digest_md5_name);
2N/A free(url);
2N/A return (ld);
2N/A}
2N/A
2N/Avoid
2N/Ansdb_free_attrs(LDAPMod *attrs[])
2N/A{
2N/A int i;
2N/A
2N/A if (attrs == NULL)
2N/A return;
2N/A for (i = 0; attrs[i]; i++) {
2N/A free(attrs[i]->mod_type);
2N/A free(attrs[i]->mod_values[0]);
2N/A free(attrs[i]->mod_values[1]);
2N/A free(attrs[i]->mod_values);
2N/A free(attrs[i]);
2N/A }
2N/A free(attrs);
2N/A}
2N/A
2N/Astatic int
2N/Aalloc_attrs(LDAPMod **attrs, int num)
2N/A{
2N/A int i;
2N/A
2N/A if (attrs == NULL)
2N/A return (-1);
2N/A for (i = 0; i < (num - 1); i++) {
2N/A attrs[i] = (LDAPMod *)calloc(1, sizeof (LDAPMod));
2N/A if (attrs[i] == NULL) {
2N/A nsdb_free_attrs(attrs);
2N/A return (-1);
2N/A }
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/Astatic char **
2N/Achar_to_array(char *val0, char *val1, char *val2)
2N/A{
2N/A char **vals;
2N/A
2N/A if (val0 == NULL)
2N/A return (NULL);
2N/A vals = calloc(4, sizeof (char *));
2N/A if (vals == NULL)
2N/A return (NULL);
2N/A vals[0] = strdup(val0);
2N/A if (val1)
2N/A vals[1] = strdup(val1);
2N/A if (val2)
2N/A vals[2] = strdup(val2);
2N/A return (vals);
2N/A}
2N/A
2N/Astatic void
2N/Afree_annotations(char **result, int num)
2N/A{
2N/A int i;
2N/A
2N/A for (i = 0; i < num; i++)
2N/A free(result[i]);
2N/A free(result);
2N/A}
2N/A
2N/Astatic char **
2N/Asplit_annotations(char *annotations, int *num)
2N/A{
2N/A char *s, *t, **r, **result = NULL;
2N/A int len, n = 0;
2N/A
2N/A s = annotations;
2N/A do {
2N/A t = s;
2N/A s = strchr(t, ',');
2N/A if (s != NULL) {
2N/A len = s - t;
2N/A s++;
2N/A } else
2N/A len = strlen(t);
2N/A r = realloc(result, (n + 2) * sizeof (char *));
2N/A if (r == NULL) {
2N/A if (result != NULL)
2N/A free_annotations(result, n + 1);
2N/A return (NULL);
2N/A }
2N/A result = r;
2N/A result[n] = calloc(1, len + 1);
2N/A if (result[n] == NULL) {
2N/A free_annotations(result, n + 1);
2N/A return (NULL);
2N/A }
2N/A bcopy(t, result[n], len);
2N/A result[++n] = NULL;
2N/A } while (s != NULL);
2N/A *num = n;
2N/A return (result);
2N/A}
2N/A
2N/ALDAPMod **
2N/Ansdb_create_fsn(char *nsdb, char *annotations, char *fsnuuid)
2N/A{
2N/A LDAPMod **attrs;
2N/A int i = 0, n, anum = 0;
2N/A char **asplit;
2N/A
2N/A if (nsdb == NULL || fsnuuid == NULL)
2N/A return (NULL);
2N/A if (annotations != NULL)
2N/A asplit = split_annotations(annotations, &anum);
2N/A
2N/A n = 4 + (anum > 0);
2N/A attrs = calloc(n, sizeof (LDAPMod *));
2N/A if (attrs == NULL)
2N/A return (NULL);
2N/A if (alloc_attrs(attrs, n) == -1)
2N/A return (NULL);
2N/A
2N/A attrs[i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsFsnUuid");
2N/A attrs[i]->mod_values = char_to_array(fsnuuid, NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsNsdbName");
2N/A attrs[i]->mod_values = char_to_array(nsdb, NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("objectClass");
2N/A attrs[i]->mod_values = char_to_array("fedfsFsn", "top", NULL);
2N/A
2N/A if (anum > 0) {
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsAnnotation");
2N/A attrs[i]->mod_values = asplit;
2N/A }
2N/A
2N/A attrs[++i] = NULL;
2N/A
2N/A return (attrs);
2N/A}
2N/A
2N/ALDAPMod **
2N/Ansdb_create_fsl(char *nsdb, char *host, char *path, char *annotations,
2N/A char *fsnuuid, char *fsluuid)
2N/A{
2N/A LDAPMod **attrs;
2N/A struct berval **bvals;
2N/A int i = 0, n, anum = 0;
2N/A int xplen;
2N/A char *xpath;
2N/A char **asplit;
2N/A
2N/A if (nsdb == NULL || host == NULL || path == NULL ||
2N/A fsnuuid == NULL || fsluuid == NULL)
2N/A return (NULL);
2N/A if (annotations != NULL)
2N/A asplit = split_annotations(annotations, &anum);
2N/A
2N/A xplen = path2xdr(path, &xpath);
2N/A if (xplen == 0)
2N/A return (NULL);
2N/A
2N/A n = 27 + (anum > 0);
2N/A attrs = calloc(n, sizeof (LDAPMod *));
2N/A if (attrs == NULL)
2N/A return (NULL);
2N/A if (alloc_attrs(attrs, n) == -1)
2N/A return (NULL);
2N/A
2N/A attrs[i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsNsdbName");
2N/A attrs[i]->mod_values = char_to_array(nsdb, NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsFslHost");
2N/A attrs[i]->mod_values = char_to_array(host, NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES;
2N/A attrs[i]->mod_type = strdup("fedfsNfsPath");
2N/A bvals = (struct berval **)calloc(2, sizeof (struct berval *));
2N/A bvals[0] = (struct berval *)malloc(sizeof (struct berval));
2N/A bvals[0]->bv_val = xpath;
2N/A bvals[0]->bv_len = xplen;
2N/A attrs[i]->mod_bvalues = bvals;
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsFslUuid");
2N/A attrs[i]->mod_values = char_to_array(fsluuid, NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsFsnUuid");
2N/A attrs[i]->mod_values = char_to_array(fsnuuid, NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsFslTTL");
2N/A attrs[i]->mod_values = char_to_array("300", NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsNfsClassChange");
2N/A attrs[i]->mod_values = char_to_array("0", NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsNfsClassFileid");
2N/A attrs[i]->mod_values = char_to_array("1", NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsNfsClassHandle");
2N/A attrs[i]->mod_values = char_to_array("1", NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsNfsClassReaddir");
2N/A attrs[i]->mod_values = char_to_array("1", NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsNfsClassSimul");
2N/A attrs[i]->mod_values = char_to_array("1", NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsNfsClassWritever");
2N/A attrs[i]->mod_values = char_to_array("1", NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsNfsCurrency");
2N/A attrs[i]->mod_values = char_to_array("1", NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsNfsGenFlagGoing");
2N/A attrs[i]->mod_values = char_to_array("FALSE", NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsNfsGenFlagSplit");
2N/A attrs[i]->mod_values = char_to_array("FALSE", NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsNfsGenFlagWritable");
2N/A attrs[i]->mod_values = char_to_array("FALSE", NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsNfsMajorVer");
2N/A attrs[i]->mod_values = char_to_array("4", NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsNfsMinorVer");
2N/A attrs[i]->mod_values = char_to_array("0", NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsNfsReadOrder");
2N/A attrs[i]->mod_values = char_to_array("1", NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsNfsReadRank");
2N/A attrs[i]->mod_values = char_to_array("2", NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsNfsTransFlagRdma");
2N/A attrs[i]->mod_values = char_to_array("FALSE", NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsNfsValidFor");
2N/A attrs[i]->mod_values = char_to_array("2", NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsNfsVarSub");
2N/A attrs[i]->mod_values = char_to_array("TRUE", NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsNfsWriteOrder");
2N/A attrs[i]->mod_values = char_to_array("4", NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsNfsWriteRank");
2N/A attrs[i]->mod_values = char_to_array("3", NULL, NULL);
2N/A
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("objectClass");
2N/A attrs[i]->mod_values = char_to_array("fedfsFSL", "fedfsNFsFsl", "top");
2N/A
2N/A if (anum > 0) {
2N/A attrs[++i]->mod_op = LDAP_MOD_ADD;
2N/A attrs[i]->mod_type = strdup("fedfsAnnotation");
2N/A attrs[i]->mod_values = asplit;
2N/A }
2N/A
2N/A attrs[++i] = NULL;
2N/A
2N/A return (attrs);
2N/A}
2N/A
2N/Achar *
2N/Ansdb_gen_uuid()
2N/A{
2N/A uuid_t uu;
2N/A char uuid[UUID_PRINTABLE_STRING_LENGTH];
2N/A
2N/A uuid_generate_time(uu);
2N/A uuid_unparse(uu, uuid);
2N/A return (strdup(uuid));
2N/A}