SLPReg.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This file contains all functions pertaining to registrations:
* SLPReg
* SLPDereg
* SLPDelAttrs
*
* Each function talks only to the local slpd, and receives a SrvAck
* reply.
*
* These calls can operate in sync or async mode. Sync mode operates
* as follows:
* format params into a char *msg
* send this msg to slpd
* invoke the SLPRegReport callback with the error code found in the
* reply from slpd
* return
*
* Async mode operates as follows:
* format the params into a char *msg
* there is one thread per process which handles async regs
* make sure this thread is running
* the reg_thread monitors the global static reg_q for messages
* a queue message is represented as a struct reg_q_msg
* caller thread places the reg msg on the reg_q, and returns
* the reg_thread reads the message from the reg_q, and sends the
* msg to slpd
* the reg_thread then invokes the SLPRegReport callback with the error
* code found in the reply from slpd
* once started, the reg_thread manages registration refreshing.
* If there are no registrations to refresh, the thread exits.
*/
#include <stdio.h>
#include <stdlib.h>
#include <thread.h>
#include <synch.h>
#include <syslog.h>
#include <slp-internal.h>
#include <sys/time.h>
#include <time.h>
/* Indices into a reg_msg iovec for auth blocks */
#define SLP_URL_AUTH 1
#define SLP_ATTR_AUTH 3
/* A registration / de-registration message */
struct reg_msg {
struct iovec *msgiov; /* msg contents */
int msgiov_len; /* number of iovec components in msgiov */
struct iovec urlbytes;
struct iovec attrbytes;
int urlauth; /* index into authiov for URL auth blocks */
int attrauth; /* index into authiov for attr auth blocks */
};
/*
* This is the message bundle passed to the reg thread via a queue.
*/
struct reg_q_msg {
struct reg_msg *msg;
slp_handle_impl_t *hp;
SLPRegReport *cb;
void *cookie;
};
/*
* These structures and vars are used for automatic re-registering.
*/
static struct rereg_entry {
char *url;
struct reg_msg *msg;
time_t wake_time;
unsigned short lifetime;
struct rereg_entry *next;
} *reregs;
static time_t next_wake_time;
static unsigned short granularity = 3600;
static mutex_t rereg_lock = DEFAULTMUTEX; /* protects the rereg struct */
static mutex_t start_lock = DEFAULTMUTEX; /* protects reg_thr creation */
static slp_queue_t *reg_q; /* the global registration queue */
static int slp_reg_thr_running; /* positive if reg_thread is running */
/* Private Utility Routines */
static SLPBoolean check_reregs();
static SLPError add_rereg(const char *, struct reg_msg *, unsigned short);
static unsigned short dereg_rereg(const char *);
static SLPError enqueue_reg(slp_handle_impl_t *, struct reg_msg *,
void *, SLPRegReport *);
static SLPError reg_impl(slp_handle_impl_t *, struct reg_msg *,
void *, SLPRegReport *);
static void reg_thread();
static SLPError start_reg_thr();
static SLPError reg_common(slp_handle_impl_t *, struct reg_msg *,
void *, SLPRegReport *);
static SLPError UnpackSrvAck(char *, SLPError *);
static SLPError packSrvReg(slp_handle_impl_t *, const char *,
unsigned short, const char *, const char *,
const char *, SLPBoolean, struct reg_msg **);
static SLPError packSrvDereg(slp_handle_impl_t *, const char *,
const char *, const char *, struct reg_msg **);
static SLPError find_SAscopes(char **scopes);
static void free_msgiov(struct iovec *, int);
/* Public API SA functionality */
SLPError SLPReg(SLPHandle hSLP, const char *pcSrvURL,
const unsigned short usLifetime,
const char *pcSrvType,
const char *pcAttrs, SLPBoolean fresh,
SLPRegReport callback, void *pvUser) {
SLPError err;
char *pcScopeList;
struct reg_msg *msg;
if (!hSLP || !pcSrvURL || !*pcSrvURL || !pcSrvType ||
!pcAttrs || !callback) {
return (SLP_PARAMETER_BAD);
}
if ((strlen(pcSrvURL) > SLP_MAX_STRINGLEN) ||
(strlen(pcSrvType) > SLP_MAX_STRINGLEN) ||
(strlen(pcAttrs) > SLP_MAX_STRINGLEN)) {
return (SLP_PARAMETER_BAD);
}
if ((err = find_SAscopes(&pcScopeList)) != SLP_OK) {
return (err);
}
if ((err = slp_start_call(hSLP)) != SLP_OK)
return (err);
/* format params into msg */
if ((err = packSrvReg(
hSLP, pcSrvURL, usLifetime, pcSrvType,
pcScopeList, pcAttrs, fresh, &msg)) != SLP_OK) {
free(pcScopeList);
slp_end_call(hSLP);
return (err);
}
if ((err = reg_common(hSLP, msg, pvUser, callback)) == SLP_OK &&
usLifetime == SLP_LIFETIME_MAXIMUM) {
struct reg_msg *rereg_msg;
/* create a rereg message, with no attrs */
err = packSrvReg(
hSLP, pcSrvURL, usLifetime,
pcSrvType, pcScopeList, "", SLP_TRUE, &rereg_msg);
if (err == SLP_OK) {
err = add_rereg(pcSrvURL, rereg_msg, usLifetime);
}
}
free(pcScopeList);
return (err);
}
static SLPError packSrvReg(slp_handle_impl_t *hp, const char *url,
unsigned short lifetime, const char *type,
const char *scope, const char *attrs,
SLPBoolean fresh, struct reg_msg **msg) {
char *m = NULL;
SLPError err;
size_t msgLen, tmplen, len = 0;
time_t ts;
struct timeval tp[1];
/* calculate the timestamp */
(void) gettimeofday(tp, NULL);
ts = tp->tv_sec + lifetime;
/* create the reg_msg */
*msg = NULL;
if (!(*msg = calloc(1, sizeof (**msg)))) {
slp_err(LOG_CRIT, 0, "packSrvReg", "out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
/* compute the total messge length */
msgLen =
slp_hdrlang_length(hp) +
/* URL entry */
5 + strlen(url) +
/* srv reg msg */
2 + strlen(type) +
2 + strlen(scope) +
2 + strlen(attrs);
/*
* Allocate memory for all the message except the auth blocks.
* The iovec msgiov actually contains only pointers into this
* memory.
*/
if (!(m = calloc(msgLen, 1))) {
slp_err(LOG_CRIT, 0, "packSrvReg", "out of memory");
err = SLP_MEMORY_ALLOC_FAILED;
goto error;
}
/*
* Create iovec for the msg. The iovec components are layed out thus:
* 0: header + URL
* 1: URL auth block count, URL auth block
* 2: attrs
* 3: attrs auth block count, attr auth block
*/
if (!((*msg)->msgiov = calloc(4, sizeof (*((*msg)->msgiov))))) {
slp_err(LOG_CRIT, 0, "packSrvReg", "out of memory");
err = SLP_MEMORY_ALLOC_FAILED;
goto error;
}
(*msg)->msgiov_len = 4;
if ((err = slp_add_header(hp->locale, m, msgLen, SRVREG, 0, &len))
!= SLP_OK)
goto error;
/* set fresh flag */
if (fresh)
slp_set_fresh(m);
/* URL entry */
len++; /* skip reserved byte in URL entry */
if ((err = slp_add_sht(m, msgLen, lifetime, &len)) != SLP_OK)
goto error;
/* save pointer to URL for signing */
tmplen = len;
(*msg)->urlbytes.iov_base = m + len;
if ((err = slp_add_string(m, msgLen, url, &len)) != SLP_OK)
goto error;
(*msg)->urlbytes.iov_len = len - tmplen;
(*msg)->msgiov[0].iov_base = m;
(*msg)->msgiov[0].iov_len = len;
/* add auth blocks for URL */
err = slp_sign(&((*msg)->urlbytes), 1, ts,
(*msg)->msgiov, SLP_URL_AUTH);
if (err != SLP_OK) {
goto error;
}
(*msg)->msgiov[2].iov_base = m + len;
/* type, scopes, and attrs */
if ((err = slp_add_string(m, msgLen, type, &len)) != SLP_OK)
goto error;
if ((err = slp_add_string(m, msgLen, scope, &len)) != SLP_OK)
goto error;
/* save pointer to attr for signing */
tmplen = len;
(*msg)->attrbytes.iov_base = m + len;
if ((err = slp_add_string(m, msgLen, attrs, &len)) != SLP_OK)
goto error;
(*msg)->attrbytes.iov_len = len - tmplen;
/* length of 2nd portion is len - length of 1st portion */
(*msg)->msgiov[2].iov_len = len - (*msg)->msgiov[0].iov_len;
/* add auth blocks for attrs */
err = slp_sign(&((*msg)->attrbytes), 1, ts,
(*msg)->msgiov, SLP_ATTR_AUTH);
if (err != SLP_OK) {
goto error;
}
/* adjust msgLen with authblocks, and set header length */
msgLen += (*msg)->msgiov[SLP_URL_AUTH].iov_len;
msgLen += (*msg)->msgiov[SLP_ATTR_AUTH].iov_len;
/* make sure msgLen is valid */
if (msgLen > SLP_MAX_MSGLEN) {
err = SLP_PARAMETER_BAD;
goto error;
}
slp_set_length(m, msgLen);
return (SLP_OK);
error:
if (m) free(m);
if (*msg) {
if ((*msg)->msgiov) free_msgiov((*msg)->msgiov, 4);
free(*msg);
}
*msg = NULL;
return (err);
}
SLPError SLPDereg(SLPHandle hSLP, const char *pURL,
SLPRegReport callback, void *pvUser) {
char *pcScopeList;
struct reg_msg *msg;
SLPError err;
if (!hSLP || !pURL || !*pURL || !callback) {
return (SLP_PARAMETER_BAD);
}
if (strlen(pURL) > SLP_MAX_STRINGLEN) {
return (SLP_PARAMETER_BAD);
}
if ((err = find_SAscopes(&pcScopeList))
!= SLP_OK) {
return (err);
}
if ((err = slp_start_call(hSLP)) != SLP_OK)
return (err);
/* format params into msg */
if ((err = packSrvDereg(hSLP, pURL, pcScopeList, NULL, &msg))
!= SLP_OK) {
free(pcScopeList);
slp_end_call(hSLP);
return (err);
}
if ((err = reg_common(hSLP, msg, pvUser, callback)) == SLP_OK) {
(void) dereg_rereg(pURL);
}
free(pcScopeList);
return (err);
}
SLPError SLPDelAttrs(SLPHandle hSLP, const char *pURL,
const char *pcAttrs,
SLPRegReport callback, void *pvUser) {
SLPError err;
char *pcScopeList;
struct reg_msg *msg;
if (!hSLP || !pURL || !*pURL || !pcAttrs || !callback) {
return (SLP_PARAMETER_BAD);
}
if ((strlen(pURL) > SLP_MAX_STRINGLEN) ||
(strlen(pcAttrs) > SLP_MAX_STRINGLEN)) {
return (SLP_PARAMETER_BAD);
}
if ((err = find_SAscopes(&pcScopeList))
!= SLP_OK) {
return (err);
}
if ((err = slp_start_call(hSLP)) != SLP_OK)
return (err);
/* format params into msg */
if ((err = packSrvDereg(hSLP, pURL, pcScopeList, pcAttrs, &msg))
!= SLP_OK) {
free(pcScopeList);
slp_end_call(hSLP);
return (err);
}
free(pcScopeList);
return (reg_common(hSLP, msg, pvUser, callback));
}
static SLPError packSrvDereg(slp_handle_impl_t *hp, const char *url,
const char *scopes, const char *attrs,
struct reg_msg **msg) {
char *m = NULL;
SLPError err;
size_t msgLen, tmplen, len = 0;
/* create the reg_msg */
*msg = NULL;
if (!(*msg = calloc(1, sizeof (**msg)))) {
slp_err(LOG_CRIT, 0, "packSrvReg", "out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
/* compute the total message length */
attrs = (attrs ? attrs : "");
msgLen =
slp_hdrlang_length(hp) +
2 + strlen(scopes) +
/* URL entry */
5 + strlen(url) +
2 + strlen(attrs);
if (!(m = calloc(msgLen, 1))) {
slp_err(LOG_CRIT, 0, "packSrvDereg", "out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
/*
* Create iovec for the msg. The iovec components are layed out thus:
* 0: header + URL
* 1: URL auth block count, URL auth block
* 2: attrs
*/
if (!((*msg)->msgiov = calloc(3, sizeof (*((*msg)->msgiov))))) {
slp_err(LOG_CRIT, 0, "packSrvDereg", "out of memory");
err = SLP_MEMORY_ALLOC_FAILED;
goto error;
}
(*msg)->msgiov_len = 3;
if ((err = slp_add_header(
hp->locale, m, msgLen, SRVDEREG, 0, &len)) != SLP_OK)
goto error;
/* scopes */
if ((err = slp_add_string(m, msgLen, scopes, &len)) != SLP_OK)
goto error;
/* URL Entry */
len++; /* skip reserved byte in URL entry */
if ((err = slp_add_sht(m, msgLen, 0, &len)) != SLP_OK)
goto error;
/* save pointer to URL for signing */
tmplen = len;
(*msg)->urlbytes.iov_base = m + len;
if ((err = slp_add_string(m, msgLen, url, &len)) != SLP_OK)
goto error;
(*msg)->urlbytes.iov_len = len - tmplen;
(*msg)->msgiov[0].iov_base = m;
(*msg)->msgiov[0].iov_len = len;
/* add auth blocks for URL */
err = slp_sign(&((*msg)->urlbytes), 1, 0,
(*msg)->msgiov, SLP_URL_AUTH);
if (err != SLP_OK) {
goto error;
}
(*msg)->msgiov[2].iov_base = m + len;
/* tag list */
if ((err = slp_add_string(m, msgLen, attrs, &len)) != SLP_OK)
goto error;
/* length of 2nd portion is len - length of 1st portion */
(*msg)->msgiov[2].iov_len = len - (*msg)->msgiov[0].iov_len;
/* adjust msgLen with authblocks, and set header length */
msgLen += (*msg)->msgiov[SLP_URL_AUTH].iov_len;
/* make sure msgLen is valid */
if (msgLen > SLP_MAX_MSGLEN) {
err = SLP_PARAMETER_BAD;
goto error;
}
slp_set_length(m, msgLen);
return (SLP_OK);
error:
if (m) free(m);
if (*msg) {
if ((*msg)->msgiov) free_msgiov((*msg)->msgiov, 3);
free(*msg);
}
*msg = NULL;
return (err);
}
/*
* Passes the packed message to the routines which talk to slpd.
*/
static SLPError reg_common(slp_handle_impl_t *hp, struct reg_msg *msg,
void *cookie, SLPRegReport callback) {
SLPError err;
if (!slp_reg_thr_running)
if ((err = start_reg_thr()) != SLP_OK)
goto reg_done;
if (hp->async)
err = enqueue_reg(hp, msg, cookie, callback);
else
err = reg_impl(hp, msg, cookie, callback);
reg_done:
/* If an error occurred, end_call() will not have happened */
if (err != SLP_OK)
slp_end_call(hp);
return (err);
}
/*
* Put a reg message on the queue. Assumes reg_thread is running.
*/
static SLPError enqueue_reg(slp_handle_impl_t *hp, struct reg_msg *msg,
void *cookie, SLPRegReport cb) {
struct reg_q_msg *rmsg;
if (!(rmsg = malloc(sizeof (*rmsg)))) {
slp_err(LOG_CRIT, 0, "enqueue_reg", "out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
rmsg->msg = msg;
rmsg->hp = hp;
rmsg->cb = cb;
rmsg->cookie = cookie;
return (slp_enqueue(reg_q, rmsg));
}
/*
* Create a new reg_q and start the reg thread.
*/
static SLPError start_reg_thr() {
SLPError err = SLP_OK;
int terr;
(void) mutex_lock(&start_lock);
/* make sure someone else hasn't already intialized the thread */
if (slp_reg_thr_running) {
goto start_done;
}
/* create the reg queue */
reg_q = slp_new_queue(&err);
if (err != SLP_OK) {
goto start_done;
}
/* start the reg thread */
if ((terr = thr_create(
0, NULL, (void *(*)(void *)) reg_thread,
NULL, 0, NULL)) != 0) {
slp_err(LOG_CRIT, 0, "start_reg_thr",
"could not start thread: %s",
strerror(terr));
slp_destroy_queue(reg_q);
err = SLP_INTERNAL_SYSTEM_ERROR;
goto start_done;
}
slp_reg_thr_running = 1;
start_done:
(void) mutex_unlock(&start_lock);
return (err);
}
/*
* This is what the permanent reg thread runs; it just sits in a loop
* monitoring the reg_q for new reg messages.
*
* To conserve resources,
* if there are no more registrations to refresh, it will exit.
*/
static void reg_thread() {
timestruc_t timeout;
timeout.tv_nsec = 0;
for (;;) {
SLPBoolean etimed;
struct reg_q_msg *rmsg;
/* get the next message from the queue */
timeout.tv_sec =
next_wake_time ? next_wake_time : time(NULL) + 5;
rmsg = slp_dequeue_timed(reg_q, &timeout, &etimed);
if (!rmsg && etimed == SLP_TRUE) {
/* timed out */
if (!check_reregs()) {
/* no more reregs; shut down this thread */
(void) mutex_lock(&start_lock);
slp_destroy_queue(reg_q);
slp_reg_thr_running = 0;
(void) mutex_unlock(&start_lock);
thr_exit(NULL);
}
continue;
}
if (!rmsg)
continue;
/* got a new message */
(void) reg_impl(rmsg->hp, rmsg->msg, rmsg->cookie, rmsg->cb);
free(rmsg);
(void) check_reregs();
}
}
/*
* Unpacks a SrvAck.
* 'reply' should point to the beginning of the header.
*/
static SLPError UnpackSrvAck(char *reply, SLPError *ans) {
SLPError err;
unsigned short langlen, call_err;
char *p = reply + SLP_HDRLEN;
langlen = slp_get_langlen(reply);
p += langlen;
if ((err = slp_get_sht(p, 0, NULL, &call_err)) != SLP_OK)
return (err);
*ans = slp_map_err(call_err);
return (SLP_OK);
}
/*
* The dispatcher for SA messages. Sends a message to slpd, unpacks and
* dispatches the reply to the user callback.
*/
static SLPError reg_impl(slp_handle_impl_t *hp, struct reg_msg *msg,
void *cookie, SLPRegReport cb) {
char *reply = NULL;
SLPError err, call_err;
if (hp->cancel)
goto transaction_complete;
if ((err = slp_send2slpd_iov(msg->msgiov, msg->msgiov_len, &reply))
!= SLP_OK)
goto transaction_complete;
/* through with msg, so free it now */
free_msgiov(msg->msgiov, msg->msgiov_len);
free(msg);
if ((err = UnpackSrvAck(reply, &call_err)) != SLP_OK)
goto transaction_complete;
/* the reg thread doubles as the consumer thread for SA calls */
hp->consumer_tid = thr_self();
cb(hp, call_err, cookie);
transaction_complete:
if (reply) {
free(reply);
}
slp_end_call(hp);
return (err);
}
/*
* Re-registration routines
*/
/*
* Adds the registration contained in 'msg' to the refresh registration
* list managed by reg_thread.
* Only registrations which are meant to be permanent are refreshed,
* so we only allow reg's with lifetime == SLP_LIFETIME_PERMANENT into
* the rereg table.
*/
static SLPError add_rereg(const char *url, struct reg_msg *msg,
unsigned short lifetime) {
struct rereg_entry *reg;
SLPError err = SLP_OK;
if (lifetime != SLP_LIFETIME_MAXIMUM) {
return (SLP_OK);
}
(void) mutex_lock(&rereg_lock);
/* alloc a new rereg entry */
if (!(reg = malloc(sizeof (*reg)))) {
slp_err(LOG_CRIT, 0, "add_rereg", "out of memory");
err = SLP_MEMORY_ALLOC_FAILED;
goto done;
}
if (!(reg->url = strdup(url))) {
free(reg);
slp_err(LOG_CRIT, 0, "add_rereg", "out of memory");
err = SLP_MEMORY_ALLOC_FAILED;
goto done;
}
reg->msg = msg;
reg->lifetime = lifetime;
reg->wake_time = (time(NULL) + lifetime) - 60;
reg->next = NULL;
/* adjust the next wake time if necessary */
next_wake_time =
reg->wake_time < next_wake_time ?
reg->wake_time : next_wake_time;
/* add the rereg to the list */
if (!reregs) {
/* first one */
reregs = reg;
goto done;
}
/* else add it to the beginning of the list */
reg->next = reregs;
reregs = reg;
done:
(void) mutex_unlock(&rereg_lock);
return (err);
}
/*
* Walks through the rereg list and re-registers any which will expire
* before the reg thread wakes up and checks again.
* Returns true if there are more reregs on the list, false if none.
*/
static SLPBoolean check_reregs() {
struct rereg_entry *p;
time_t now, shortest_wait;
SLPBoolean more = SLP_TRUE;
(void) mutex_lock(&rereg_lock);
if (!reregs) {
more = SLP_FALSE;
goto done;
}
now = time(NULL);
shortest_wait = now + reregs->lifetime;
for (p = reregs; p; p = p->next) {
if (now > (p->wake_time - granularity)) {
char *reply;
/* rereg it, first recalculating signature */
(void) slp_sign(&(p->msg->urlbytes), 1, now + p->lifetime,
p->msg->msgiov, 1);
(void) slp_sign(&(p->msg->attrbytes), 1, now + p->lifetime,
p->msg->msgiov, 3);
(void) slp_send2slpd_iov(
p->msg->msgiov, p->msg->msgiov_len, &reply);
if (reply)
free(reply);
p->wake_time = now + p->lifetime;
}
if (p->wake_time < shortest_wait)
shortest_wait = p->wake_time;
}
next_wake_time = shortest_wait;
done:
(void) mutex_unlock(&rereg_lock);
return (more);
}
/*
* Removes the refresh registration for 'url'.
*/
static unsigned short dereg_rereg(const char *url) {
struct rereg_entry *p, *q;
unsigned short lifetime = 0;
(void) mutex_lock(&rereg_lock);
for (p = q = reregs; p; p = p->next) {
if (slp_strcasecmp(p->url, url) == 0) {
/* found it; remove it from the list */
if (p == q) {
/* first one on list */
reregs = p->next;
} else {
q->next = p->next;
}
/* free the entry */
lifetime = p->lifetime;
free(p->url);
/* free the message memory */
free(p->msg->msgiov[0].iov_base);
/* free the URL auth block */
free(p->msg->msgiov[SLP_URL_AUTH].iov_base);
/* free the attr auth block */
free(p->msg->msgiov[SLP_ATTR_AUTH].iov_base);
/* free the message iovec */
free(p->msg->msgiov);
/* finally, free the message structure */
free(p->msg);
free(p);
goto done;
}
q = p;
}
done:
(void) mutex_unlock(&rereg_lock);
return (lifetime);
}
/*
* Returns configured scopes in scopes. Caller should free *scopes
* when done. If the scope string is too long for an SLP string, the
* string is truncated.
*/
static SLPError find_SAscopes(char **scopes) {
SLPError err;
if ((err = slp_administrative_scopes(scopes, SLP_TRUE))
!= SLP_OK) {
return (err);
}
/* Ensure string is not too long */
if (strlen(*scopes) > SLP_MAX_STRINGLEN) {
/* truncate the string */
if ((*scopes)[SLP_MAX_STRINGLEN - 1] == ',') {
/* scopes can't end with ',' */
(*scopes)[SLP_MAX_STRINGLEN - 1] = 0;
} else {
(*scopes)[SLP_MAX_STRINGLEN] = 0;
}
}
return (SLP_OK);
}
/*
* Does all the dirty work of freeing a msgiov.
*/
static void free_msgiov(struct iovec *msgiov, int iovlen) {
/* free the message memory */
free(msgiov[0].iov_base);
/* free the URL auth block */
free(msgiov[SLP_URL_AUTH].iov_base);
if (iovlen == 4) {
/* free the attr auth block */
free(msgiov[SLP_ATTR_AUTH].iov_base);
}
/* free the message iovec */
free(msgiov);
}