#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>
#define SLP_URL_AUTH 1
#define SLP_ATTR_AUTH 3
struct reg_msg {
struct iovec *msgiov;
int msgiov_len;
struct iovec urlbytes;
struct iovec attrbytes;
int urlauth;
int attrauth;
};
struct reg_q_msg {
struct reg_msg *msg;
slp_handle_impl_t *hp;
SLPRegReport *cb;
void *cookie;
};
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;
static mutex_t start_lock = DEFAULTMUTEX;
static slp_queue_t *reg_q;
static int slp_reg_thr_running;
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(void *);
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);
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);
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;
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];
(void) gettimeofday(tp, NULL);
ts = tp->tv_sec + lifetime;
*msg = NULL;
if (!(*msg = calloc(1, sizeof (**msg)))) {
slp_err(LOG_CRIT, 0, "packSrvReg", "out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
msgLen =
slp_hdrlang_length(hp) +
5 + strlen(url) +
2 + strlen(type) +
2 + strlen(scope) +
2 + strlen(attrs);
if (!(m = calloc(msgLen, 1))) {
slp_err(LOG_CRIT, 0, "packSrvReg", "out of memory");
err = SLP_MEMORY_ALLOC_FAILED;
goto error;
}
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;
if (fresh)
slp_set_fresh(m);
len++;
if ((err = slp_add_sht(m, msgLen, lifetime, &len)) != SLP_OK)
goto error;
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;
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;
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;
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;
(*msg)->msgiov[2].iov_len = len - (*msg)->msgiov[0].iov_len;
err = slp_sign(&((*msg)->attrbytes), 1, ts,
(*msg)->msgiov, SLP_ATTR_AUTH);
if (err != SLP_OK) {
goto error;
}
msgLen += (*msg)->msgiov[SLP_URL_AUTH].iov_len;
msgLen += (*msg)->msgiov[SLP_ATTR_AUTH].iov_len;
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);
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);
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;
*msg = NULL;
if (!(*msg = calloc(1, sizeof (**msg)))) {
slp_err(LOG_CRIT, 0, "packSrvReg", "out of memory");
return (SLP_MEMORY_ALLOC_FAILED);
}
attrs = (attrs ? attrs : "");
msgLen =
slp_hdrlang_length(hp) +
2 + strlen(scopes) +
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);
}
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;
if ((err = slp_add_string(m, msgLen, scopes, &len)) != SLP_OK)
goto error;
len++;
if ((err = slp_add_sht(m, msgLen, 0, &len)) != SLP_OK)
goto error;
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;
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;
if ((err = slp_add_string(m, msgLen, attrs, &len)) != SLP_OK)
goto error;
(*msg)->msgiov[2].iov_len = len - (*msg)->msgiov[0].iov_len;
msgLen += (*msg)->msgiov[SLP_URL_AUTH].iov_len;
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);
}
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 (err != SLP_OK)
slp_end_call(hp);
return (err);
}
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));
}
static SLPError start_reg_thr() {
SLPError err = SLP_OK;
int terr;
(void) mutex_lock(&start_lock);
if (slp_reg_thr_running) {
goto start_done;
}
reg_q = slp_new_queue(&err);
if (err != SLP_OK) {
goto start_done;
}
if ((terr = thr_create(
0, 0, 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);
}
static void *
reg_thread(void *arg __unused)
{
timestruc_t timeout;
timeout.tv_nsec = 0;
for (;;) {
SLPBoolean etimed;
struct reg_q_msg *rmsg;
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) {
if (!check_reregs()) {
(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;
(void) reg_impl(rmsg->hp, rmsg->msg, rmsg->cookie, rmsg->cb);
free(rmsg);
(void) check_reregs();
}
}
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);
}
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;
free_msgiov(msg->msgiov, msg->msgiov_len);
free(msg);
if ((err = UnpackSrvAck(reply, &call_err)) != SLP_OK)
goto transaction_complete;
hp->consumer_tid = thr_self();
cb(hp, call_err, cookie);
transaction_complete:
if (reply) {
free(reply);
}
slp_end_call(hp);
return (err);
}
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);
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;
next_wake_time =
reg->wake_time < next_wake_time ?
reg->wake_time : next_wake_time;
if (!reregs) {
reregs = reg;
goto done;
}
reg->next = reregs;
reregs = reg;
done:
(void) mutex_unlock(&rereg_lock);
return (err);
}
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;
(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);
}
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) {
if (p == q) {
reregs = p->next;
} else {
q->next = p->next;
}
lifetime = p->lifetime;
free(p->url);
free(p->msg->msgiov[0].iov_base);
free(p->msg->msgiov[SLP_URL_AUTH].iov_base);
free(p->msg->msgiov[SLP_ATTR_AUTH].iov_base);
free(p->msg->msgiov);
free(p->msg);
free(p);
goto done;
}
q = p;
}
done:
(void) mutex_unlock(&rereg_lock);
return (lifetime);
}
static SLPError find_SAscopes(char **scopes) {
SLPError err;
if ((err = slp_administrative_scopes(scopes, SLP_TRUE))
!= SLP_OK) {
return (err);
}
if (strlen(*scopes) > SLP_MAX_STRINGLEN) {
if ((*scopes)[SLP_MAX_STRINGLEN - 1] == ',') {
(*scopes)[SLP_MAX_STRINGLEN - 1] = 0;
} else {
(*scopes)[SLP_MAX_STRINGLEN] = 0;
}
}
return (SLP_OK);
}
static void free_msgiov(struct iovec *msgiov, int iovlen) {
free(msgiov[0].iov_base);
free(msgiov[SLP_URL_AUTH].iov_base);
if (iovlen == 4) {
free(msgiov[SLP_ATTR_AUTH].iov_base);
}
free(msgiov);
}