xref: /illumos-gate/usr/src/lib/libslp/clib/SLPReg.c (revision 628e3cbed6489fa1db545d8524a06cd6535af456)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This file contains all functions pertaining to registrations:
31  *	SLPReg
32  *	SLPDereg
33  *	SLPDelAttrs
34  *
35  * Each function talks only to the local slpd, and receives a SrvAck
36  * reply.
37  *
38  * These calls can operate in sync or async mode. Sync mode operates
39  * as follows:
40  *	format params into a char *msg
41  *	send this msg to slpd
42  *	invoke the SLPRegReport callback with the error code found in the
43  *		reply from slpd
44  *	return
45  *
46  * Async mode operates as follows:
47  *	format the params into a char *msg
48  *	there is one thread per process which handles async regs
49  *	make sure this thread is running
50  *	the reg_thread monitors the global static reg_q for messages
51  *	a queue message is represented as a struct reg_q_msg
52  *	caller thread places the reg msg on the reg_q, and returns
53  *	the reg_thread reads the message from the reg_q, and sends the
54  *		msg to slpd
55  *	the reg_thread then invokes the SLPRegReport callback with the error
56  *		code found in the reply from slpd
57  *	once started, the reg_thread manages registration refreshing.
58  *		If there are no registrations to refresh, the thread exits.
59  */
60 
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <thread.h>
64 #include <synch.h>
65 #include <syslog.h>
66 #include <slp-internal.h>
67 #include <sys/time.h>
68 #include <time.h>
69 
70 /* Indices into a reg_msg iovec for auth blocks */
71 #define	SLP_URL_AUTH	1
72 #define	SLP_ATTR_AUTH	3
73 
74 /* A registration / de-registration message */
75 struct reg_msg {
76 	struct iovec *msgiov;	/* msg contents */
77 	int msgiov_len;		/* number of iovec components in msgiov */
78 	struct iovec urlbytes;
79 	struct iovec attrbytes;
80 	int urlauth;		/* index into authiov for URL auth blocks */
81 	int attrauth;		/* index into authiov for attr auth blocks */
82 };
83 
84 /*
85  * This is the message bundle passed to the reg thread via a queue.
86  */
87 struct reg_q_msg {
88 	struct reg_msg *msg;
89 	slp_handle_impl_t *hp;
90 	SLPRegReport *cb;
91 	void *cookie;
92 };
93 
94 /*
95  * These structures and vars are used for automatic re-registering.
96  */
97 static struct rereg_entry {
98 	char *url;
99 	struct reg_msg *msg;
100 	time_t wake_time;
101 	unsigned short lifetime;
102 	struct rereg_entry *next;
103 } *reregs;
104 
105 static time_t next_wake_time;
106 static unsigned short granularity = 3600;
107 static mutex_t rereg_lock = DEFAULTMUTEX;	/* protects the rereg struct */
108 static mutex_t start_lock = DEFAULTMUTEX;	/* protects reg_thr creation */
109 
110 static slp_queue_t *reg_q;	/* the global registration queue */
111 static int slp_reg_thr_running;	/* positive if reg_thread is running */
112 
113 /* Private Utility Routines */
114 
115 static SLPBoolean check_reregs();
116 static SLPError add_rereg(const char *, struct reg_msg *, unsigned short);
117 static unsigned short dereg_rereg(const char *);
118 
119 static SLPError enqueue_reg(slp_handle_impl_t *, struct reg_msg *,
120 			    void *, SLPRegReport *);
121 static SLPError reg_impl(slp_handle_impl_t *, struct reg_msg *,
122 				void *, SLPRegReport *);
123 static void reg_thread();
124 static SLPError start_reg_thr();
125 static SLPError reg_common(slp_handle_impl_t *, struct reg_msg *,
126 				void *, SLPRegReport *);
127 static SLPError UnpackSrvAck(char *, SLPError *);
128 static SLPError packSrvReg(slp_handle_impl_t *, const char *,
129 				unsigned short, const char *, const char *,
130 				const char *, SLPBoolean, struct reg_msg **);
131 static SLPError packSrvDereg(slp_handle_impl_t *, const char *,
132 				const char *, const char *, struct reg_msg **);
133 static SLPError find_SAscopes(char **scopes);
134 static void free_msgiov(struct iovec *, int);
135 
136 /* Public API SA functionality */
137 
138 SLPError SLPReg(SLPHandle   hSLP, const char  *pcSrvURL,
139 		const unsigned short usLifetime,
140 		const char  *pcSrvType,
141 		const char  *pcAttrs, SLPBoolean  fresh,
142 		SLPRegReport callback, void *pvUser) {
143 	SLPError err;
144 	char *pcScopeList;
145 	struct reg_msg *msg;
146 
147 	if (!hSLP || !pcSrvURL || !*pcSrvURL || !pcSrvType ||
148 	    !pcAttrs || !callback) {
149 		return (SLP_PARAMETER_BAD);
150 	}
151 
152 	if ((strlen(pcSrvURL) > SLP_MAX_STRINGLEN) ||
153 	    (strlen(pcSrvType) > SLP_MAX_STRINGLEN) ||
154 	    (strlen(pcAttrs) > SLP_MAX_STRINGLEN)) {
155 	    return (SLP_PARAMETER_BAD);
156 	}
157 
158 	if ((err = find_SAscopes(&pcScopeList)) != SLP_OK) {
159 		return (err);
160 	}
161 
162 	if ((err = slp_start_call(hSLP)) != SLP_OK)
163 		return (err);
164 
165 	/* format params into msg */
166 	if ((err = packSrvReg(
167 		hSLP, pcSrvURL, usLifetime, pcSrvType,
168 		pcScopeList, pcAttrs, fresh, &msg)) != SLP_OK) {
169 		free(pcScopeList);
170 		slp_end_call(hSLP);
171 		return (err);
172 	}
173 
174 	if ((err = reg_common(hSLP, msg, pvUser, callback)) == SLP_OK &&
175 	    usLifetime == SLP_LIFETIME_MAXIMUM) {
176 		struct reg_msg *rereg_msg;
177 
178 		/* create a rereg message, with no attrs */
179 		err = packSrvReg(
180 			hSLP, pcSrvURL, usLifetime,
181 			pcSrvType, pcScopeList, "", SLP_TRUE, &rereg_msg);
182 		if (err == SLP_OK) {
183 			err = add_rereg(pcSrvURL, rereg_msg, usLifetime);
184 		}
185 	}
186 
187 	free(pcScopeList);
188 	return (err);
189 }
190 
191 static SLPError packSrvReg(slp_handle_impl_t *hp, const char *url,
192 				unsigned short lifetime, const char *type,
193 				const char *scope, const char *attrs,
194 				SLPBoolean fresh, struct reg_msg **msg) {
195 	char *m = NULL;
196 	SLPError err;
197 	size_t msgLen, tmplen, len = 0;
198 	time_t ts;
199 	struct timeval tp[1];
200 
201 	/* calculate the timestamp */
202 	(void) gettimeofday(tp, NULL);
203 	ts = tp->tv_sec + lifetime;
204 
205 	/* create the reg_msg */
206 	*msg = NULL;
207 	if (!(*msg = calloc(1, sizeof (**msg)))) {
208 		slp_err(LOG_CRIT, 0, "packSrvReg", "out of memory");
209 		return (SLP_MEMORY_ALLOC_FAILED);
210 	}
211 
212 	/* compute the total messge length */
213 	msgLen =
214 		slp_hdrlang_length(hp) +
215 		/* URL entry */
216 		5 + strlen(url) +
217 		/* srv reg msg */
218 		2 + strlen(type) +
219 		2 + strlen(scope) +
220 		2 + strlen(attrs);
221 
222 	/*
223 	 * Allocate memory for all the message except the auth blocks.
224 	 * The iovec msgiov actually contains only pointers into this
225 	 * memory.
226 	 */
227 	if (!(m = calloc(msgLen, 1))) {
228 		slp_err(LOG_CRIT, 0, "packSrvReg", "out of memory");
229 		err = SLP_MEMORY_ALLOC_FAILED;
230 		goto error;
231 	}
232 
233 	/*
234 	 * Create iovec for the msg. The iovec components are layed out thus:
235 	 *   0: header + URL
236 	 *   1: URL auth block count, URL auth block
237 	 *   2: attrs
238 	 *   3: attrs auth block count, attr auth block
239 	 */
240 	if (!((*msg)->msgiov = calloc(4, sizeof (*((*msg)->msgiov))))) {
241 		slp_err(LOG_CRIT, 0, "packSrvReg", "out of memory");
242 		err = SLP_MEMORY_ALLOC_FAILED;
243 		goto error;
244 	}
245 	(*msg)->msgiov_len = 4;
246 
247 	if ((err = slp_add_header(hp->locale, m, msgLen, SRVREG, 0, &len))
248 	    != SLP_OK)
249 		goto error;
250 	/* set fresh flag */
251 	if (fresh)
252 		slp_set_fresh(m);
253 
254 	/* URL entry */
255 	len++;	/* skip reserved byte in URL entry */
256 	if ((err = slp_add_sht(m, msgLen, lifetime, &len)) != SLP_OK)
257 		goto error;
258 
259 	/* save pointer to URL for signing */
260 	tmplen = len;
261 	(*msg)->urlbytes.iov_base = m + len;
262 
263 	if ((err = slp_add_string(m, msgLen, url, &len)) != SLP_OK)
264 		goto error;
265 
266 	(*msg)->urlbytes.iov_len = len - tmplen;
267 
268 	(*msg)->msgiov[0].iov_base = m;
269 	(*msg)->msgiov[0].iov_len = len;
270 
271 	/* add auth blocks for URL */
272 	err = slp_sign(&((*msg)->urlbytes), 1, ts,
273 			(*msg)->msgiov, SLP_URL_AUTH);
274 	if (err != SLP_OK) {
275 		goto error;
276 	}
277 
278 	(*msg)->msgiov[2].iov_base = m + len;
279 
280 	/* type, scopes, and attrs */
281 	if ((err = slp_add_string(m, msgLen, type, &len)) != SLP_OK)
282 		goto error;
283 	if ((err = slp_add_string(m, msgLen, scope, &len)) != SLP_OK)
284 		goto error;
285 
286 	/* save pointer to attr for signing */
287 	tmplen = len;
288 	(*msg)->attrbytes.iov_base = m + len;
289 
290 	if ((err = slp_add_string(m, msgLen, attrs, &len)) != SLP_OK)
291 		goto error;
292 
293 	(*msg)->attrbytes.iov_len = len - tmplen;
294 
295 	/* length of 2nd portion is len - length of 1st portion */
296 	(*msg)->msgiov[2].iov_len = len - (*msg)->msgiov[0].iov_len;
297 
298 	/* add auth blocks for attrs */
299 	err = slp_sign(&((*msg)->attrbytes), 1, ts,
300 			(*msg)->msgiov, SLP_ATTR_AUTH);
301 	if (err != SLP_OK) {
302 		goto error;
303 	}
304 
305 	/* adjust msgLen with authblocks, and set header length */
306 	msgLen += (*msg)->msgiov[SLP_URL_AUTH].iov_len;
307 	msgLen += (*msg)->msgiov[SLP_ATTR_AUTH].iov_len;
308 
309 	/* make sure msgLen is valid */
310 	if (msgLen > SLP_MAX_MSGLEN) {
311 		err = SLP_PARAMETER_BAD;
312 		goto error;
313 	}
314 	slp_set_length(m, msgLen);
315 
316 	return (SLP_OK);
317 error:
318 	if (m) free(m);
319 	if (*msg) {
320 		if ((*msg)->msgiov) free_msgiov((*msg)->msgiov, 4);
321 		free(*msg);
322 	}
323 	*msg = NULL;
324 	return (err);
325 }
326 
327 SLPError SLPDereg(SLPHandle hSLP, const char *pURL,
328 			SLPRegReport callback, void *pvUser) {
329 	char *pcScopeList;
330 	struct reg_msg *msg;
331 	SLPError err;
332 
333 	if (!hSLP || !pURL || !*pURL || !callback) {
334 		return (SLP_PARAMETER_BAD);
335 	}
336 
337 	if (strlen(pURL) > SLP_MAX_STRINGLEN) {
338 	    return (SLP_PARAMETER_BAD);
339 	}
340 
341 	if ((err = find_SAscopes(&pcScopeList))
342 	    != SLP_OK) {
343 		return (err);
344 	}
345 
346 	if ((err = slp_start_call(hSLP)) != SLP_OK)
347 		return (err);
348 
349 	/* format params into msg */
350 	if ((err = packSrvDereg(hSLP, pURL, pcScopeList, NULL, &msg))
351 	    != SLP_OK) {
352 		free(pcScopeList);
353 		slp_end_call(hSLP);
354 		return (err);
355 	}
356 
357 	if ((err = reg_common(hSLP, msg, pvUser, callback)) == SLP_OK) {
358 		(void) dereg_rereg(pURL);
359 	}
360 
361 	free(pcScopeList);
362 	return (err);
363 }
364 
365 SLPError SLPDelAttrs(SLPHandle hSLP, const char *pURL,
366 			const char *pcAttrs,
367 			SLPRegReport callback, void *pvUser) {
368 	SLPError err;
369 	char *pcScopeList;
370 	struct reg_msg *msg;
371 
372 	if (!hSLP || !pURL || !*pURL || !pcAttrs || !callback) {
373 		return (SLP_PARAMETER_BAD);
374 	}
375 
376 	if ((strlen(pURL) > SLP_MAX_STRINGLEN) ||
377 	    (strlen(pcAttrs) > SLP_MAX_STRINGLEN)) {
378 	    return (SLP_PARAMETER_BAD);
379 	}
380 
381 	if ((err = find_SAscopes(&pcScopeList))
382 	    != SLP_OK) {
383 		return (err);
384 	}
385 
386 	if ((err = slp_start_call(hSLP)) != SLP_OK)
387 		return (err);
388 
389 	/* format params into msg */
390 	if ((err = packSrvDereg(hSLP, pURL, pcScopeList, pcAttrs, &msg))
391 	    != SLP_OK) {
392 		free(pcScopeList);
393 		slp_end_call(hSLP);
394 		return (err);
395 	}
396 
397 	free(pcScopeList);
398 	return (reg_common(hSLP, msg, pvUser, callback));
399 }
400 
401 static SLPError packSrvDereg(slp_handle_impl_t *hp, const char *url,
402 				const char *scopes, const char *attrs,
403 				struct reg_msg  **msg) {
404 	char *m = NULL;
405 	SLPError err;
406 	size_t msgLen, tmplen, len = 0;
407 
408 	/* create the reg_msg */
409 	*msg = NULL;
410 	if (!(*msg = calloc(1, sizeof (**msg)))) {
411 		slp_err(LOG_CRIT, 0, "packSrvReg", "out of memory");
412 		return (SLP_MEMORY_ALLOC_FAILED);
413 	}
414 
415 	/* compute the total message length */
416 	attrs = (attrs ? attrs : "");
417 	msgLen =
418 		slp_hdrlang_length(hp) +
419 		2 + strlen(scopes) +
420 		/* URL entry */
421 		5 + strlen(url) +
422 		2 + strlen(attrs);
423 
424 	if (!(m = calloc(msgLen, 1))) {
425 		slp_err(LOG_CRIT, 0, "packSrvDereg", "out of memory");
426 		return (SLP_MEMORY_ALLOC_FAILED);
427 	}
428 
429 	/*
430 	 * Create iovec for the msg. The iovec components are layed out thus:
431 	 *   0: header + URL
432 	 *   1: URL auth block count, URL auth block
433 	 *   2: attrs
434 	 */
435 	if (!((*msg)->msgiov = calloc(3, sizeof (*((*msg)->msgiov))))) {
436 		slp_err(LOG_CRIT, 0, "packSrvDereg", "out of memory");
437 		err = SLP_MEMORY_ALLOC_FAILED;
438 		goto error;
439 	}
440 	(*msg)->msgiov_len = 3;
441 
442 	if ((err = slp_add_header(
443 		hp->locale, m, msgLen, SRVDEREG, 0, &len)) != SLP_OK)
444 		goto error;
445 
446 	/* scopes */
447 	if ((err = slp_add_string(m, msgLen, scopes, &len)) != SLP_OK)
448 		goto error;
449 
450 	/* URL Entry */
451 	len++;	/* skip reserved byte in URL entry */
452 	if ((err = slp_add_sht(m, msgLen, 0, &len)) != SLP_OK)
453 		goto error;
454 
455 	/* save pointer to URL for signing */
456 	tmplen = len;
457 	(*msg)->urlbytes.iov_base = m + len;
458 
459 	if ((err = slp_add_string(m, msgLen, url, &len)) != SLP_OK)
460 		goto error;
461 
462 	(*msg)->urlbytes.iov_len = len - tmplen;
463 
464 	(*msg)->msgiov[0].iov_base = m;
465 	(*msg)->msgiov[0].iov_len = len;
466 
467 	/* add auth blocks for URL */
468 	err = slp_sign(&((*msg)->urlbytes), 1, 0,
469 			(*msg)->msgiov, SLP_URL_AUTH);
470 	if (err != SLP_OK) {
471 		goto error;
472 	}
473 
474 	(*msg)->msgiov[2].iov_base = m + len;
475 
476 	/* tag list */
477 	if ((err = slp_add_string(m, msgLen, attrs, &len)) != SLP_OK)
478 		goto error;
479 
480 	/* length of 2nd portion is len - length of 1st portion */
481 	(*msg)->msgiov[2].iov_len = len - (*msg)->msgiov[0].iov_len;
482 
483 	/* adjust msgLen with authblocks, and set header length */
484 	msgLen += (*msg)->msgiov[SLP_URL_AUTH].iov_len;
485 
486 	/* make sure msgLen is valid */
487 	if (msgLen > SLP_MAX_MSGLEN) {
488 		err = SLP_PARAMETER_BAD;
489 		goto error;
490 	}
491 	slp_set_length(m, msgLen);
492 
493 	return (SLP_OK);
494 error:
495 	if (m) free(m);
496 	if (*msg) {
497 		if ((*msg)->msgiov) free_msgiov((*msg)->msgiov, 3);
498 		free(*msg);
499 	}
500 	*msg = NULL;
501 	return (err);
502 }
503 
504 /*
505  * Passes the packed message to the routines which talk to slpd.
506  */
507 static SLPError reg_common(slp_handle_impl_t *hp, struct reg_msg *msg,
508 				void *cookie, SLPRegReport callback) {
509 	SLPError err;
510 
511 	if (!slp_reg_thr_running)
512 		if ((err = start_reg_thr()) != SLP_OK)
513 			goto reg_done;
514 
515 	if (hp->async)
516 		err = enqueue_reg(hp, msg, cookie, callback);
517 	else
518 		err = reg_impl(hp, msg, cookie, callback);
519 
520 reg_done:
521 	/* If an error occurred, end_call() will not have happened */
522 	if (err != SLP_OK)
523 		slp_end_call(hp);
524 	return (err);
525 }
526 
527 /*
528  * Put a reg message on the queue. Assumes reg_thread is running.
529  */
530 static SLPError enqueue_reg(slp_handle_impl_t *hp, struct reg_msg *msg,
531 			    void *cookie, SLPRegReport cb) {
532 	struct reg_q_msg *rmsg;
533 
534 	if (!(rmsg = malloc(sizeof (*rmsg)))) {
535 		slp_err(LOG_CRIT, 0, "enqueue_reg", "out of memory");
536 		return (SLP_MEMORY_ALLOC_FAILED);
537 	}
538 
539 	rmsg->msg = msg;
540 	rmsg->hp = hp;
541 	rmsg->cb = cb;
542 	rmsg->cookie = cookie;
543 
544 	return (slp_enqueue(reg_q, rmsg));
545 }
546 
547 /*
548  * Create a new reg_q and start the reg thread.
549  */
550 static SLPError start_reg_thr() {
551 	SLPError err = SLP_OK;
552 	int terr;
553 
554 	(void) mutex_lock(&start_lock);
555 	/* make sure someone else hasn't already intialized the thread */
556 	if (slp_reg_thr_running) {
557 		goto start_done;
558 	}
559 
560 	/* create the reg queue */
561 	reg_q = slp_new_queue(&err);
562 	if (err != SLP_OK) {
563 		goto start_done;
564 	}
565 
566 	/* start the reg thread */
567 	if ((terr = thr_create(
568 		0, NULL, (void *(*)(void *)) reg_thread,
569 		NULL, 0, NULL)) != 0) {
570 		slp_err(LOG_CRIT, 0, "start_reg_thr",
571 			"could not start thread: %s",
572 			strerror(terr));
573 		slp_destroy_queue(reg_q);
574 		err = SLP_INTERNAL_SYSTEM_ERROR;
575 		goto start_done;
576 	}
577 	slp_reg_thr_running = 1;
578 
579 start_done:
580 	(void) mutex_unlock(&start_lock);
581 	return (err);
582 }
583 
584 /*
585  * This is what the permanent reg thread runs; it just sits in a loop
586  * monitoring the reg_q for new reg messages.
587  *
588  * To conserve resources,
589  * if there are no more registrations to refresh, it will exit.
590  */
591 static void reg_thread() {
592 	timestruc_t timeout;
593 	timeout.tv_nsec = 0;
594 
595 	for (;;) {
596 		SLPBoolean etimed;
597 		struct reg_q_msg *rmsg;
598 
599 		/* get the next message from the queue */
600 		timeout.tv_sec =
601 			next_wake_time ? next_wake_time : time(NULL) + 5;
602 		rmsg = slp_dequeue_timed(reg_q, &timeout, &etimed);
603 		if (!rmsg && etimed == SLP_TRUE) {
604 			/* timed out */
605 			if (!check_reregs()) {
606 				/* no more reregs; shut down this thread */
607 				(void) mutex_lock(&start_lock);
608 				slp_destroy_queue(reg_q);
609 				slp_reg_thr_running = 0;
610 				(void) mutex_unlock(&start_lock);
611 				thr_exit(NULL);
612 			}
613 			continue;
614 		}
615 		if (!rmsg)
616 			continue;
617 
618 		/* got a new message */
619 		(void) reg_impl(rmsg->hp, rmsg->msg, rmsg->cookie, rmsg->cb);
620 		free(rmsg);
621 		(void) check_reregs();
622 	}
623 }
624 
625 /*
626  * Unpacks a SrvAck.
627  * 'reply' should point to the beginning of the header.
628  */
629 static SLPError UnpackSrvAck(char *reply, SLPError *ans) {
630 	SLPError err;
631 	unsigned short langlen, call_err;
632 	char *p = reply + SLP_HDRLEN;
633 
634 	langlen = slp_get_langlen(reply);
635 	p += langlen;
636 	if ((err = slp_get_sht(p, 0, NULL, &call_err)) != SLP_OK)
637 		return (err);
638 
639 	*ans = slp_map_err(call_err);
640 
641 	return (SLP_OK);
642 }
643 
644 /*
645  * The dispatcher for SA messages. Sends a message to slpd, unpacks and
646  * dispatches the reply to the user callback.
647  */
648 static SLPError reg_impl(slp_handle_impl_t *hp, struct reg_msg *msg,
649 				void *cookie, SLPRegReport cb) {
650 	char *reply = NULL;
651 	SLPError err, call_err;
652 
653 	if (hp->cancel)
654 		goto transaction_complete;
655 
656 	if ((err = slp_send2slpd_iov(msg->msgiov, msg->msgiov_len, &reply))
657 	    != SLP_OK)
658 		goto transaction_complete;
659 
660 	/* through with msg, so free it now */
661 	free_msgiov(msg->msgiov, msg->msgiov_len);
662 	free(msg);
663 
664 	if ((err = UnpackSrvAck(reply, &call_err)) != SLP_OK)
665 		goto transaction_complete;
666 
667 	/* the reg thread doubles as the consumer thread for SA calls */
668 	hp->consumer_tid = thr_self();
669 
670 	cb(hp, call_err, cookie);
671 
672 transaction_complete:
673 	if (reply) {
674 		free(reply);
675 	}
676 	slp_end_call(hp);
677 	return (err);
678 }
679 
680 /*
681  * Re-registration routines
682  */
683 
684 /*
685  * Adds the registration contained in 'msg' to the refresh registration
686  * list managed by reg_thread.
687  * Only registrations which are meant to be permanent are refreshed,
688  * so we only allow reg's with lifetime == SLP_LIFETIME_PERMANENT into
689  * the rereg table.
690  */
691 static SLPError add_rereg(const char *url, struct reg_msg *msg,
692 				unsigned short lifetime) {
693 	struct rereg_entry *reg;
694 	SLPError err = SLP_OK;
695 
696 	if (lifetime != SLP_LIFETIME_MAXIMUM) {
697 		return (SLP_OK);
698 	}
699 
700 	(void) mutex_lock(&rereg_lock);
701 	/* alloc a new rereg entry */
702 	if (!(reg = malloc(sizeof (*reg)))) {
703 		slp_err(LOG_CRIT, 0, "add_rereg", "out of memory");
704 		err = SLP_MEMORY_ALLOC_FAILED;
705 		goto done;
706 	}
707 
708 	if (!(reg->url = strdup(url))) {
709 		free(reg);
710 		slp_err(LOG_CRIT, 0, "add_rereg", "out of memory");
711 		err = SLP_MEMORY_ALLOC_FAILED;
712 		goto done;
713 	}
714 
715 	reg->msg = msg;
716 	reg->lifetime = lifetime;
717 	reg->wake_time = (time(NULL) + lifetime) - 60;
718 	reg->next = NULL;
719 
720 	/* adjust the next wake time if necessary */
721 	next_wake_time =
722 		reg->wake_time < next_wake_time ?
723 		reg->wake_time : next_wake_time;
724 
725 	/* add the rereg to the list */
726 	if (!reregs) {
727 		/* first one */
728 		reregs = reg;
729 		goto done;
730 	}
731 
732 	/* else add it to the beginning of the list */
733 	reg->next = reregs;
734 	reregs = reg;
735 
736 done:
737 	(void) mutex_unlock(&rereg_lock);
738 	return (err);
739 }
740 
741 /*
742  * Walks through the rereg list and re-registers any which will expire
743  * before the reg thread wakes up and checks again.
744  * Returns true if there are more reregs on the list, false if none.
745  */
746 static SLPBoolean check_reregs() {
747 	struct rereg_entry *p;
748 	time_t now, shortest_wait;
749 	SLPBoolean more = SLP_TRUE;
750 
751 	(void) mutex_lock(&rereg_lock);
752 
753 	if (!reregs) {
754 		more = SLP_FALSE;
755 		goto done;
756 	}
757 
758 	now = time(NULL);
759 	shortest_wait = now + reregs->lifetime;
760 
761 	for (p = reregs; p; p = p->next) {
762 		if (now > (p->wake_time - granularity)) {
763 		    char *reply;
764 
765 		    /* rereg it, first recalculating signature */
766 		    (void) slp_sign(&(p->msg->urlbytes), 1, now + p->lifetime,
767 				    p->msg->msgiov, 1);
768 		    (void) slp_sign(&(p->msg->attrbytes), 1, now + p->lifetime,
769 				    p->msg->msgiov, 3);
770 
771 		    (void) slp_send2slpd_iov(
772 				p->msg->msgiov, p->msg->msgiov_len, &reply);
773 		    if (reply)
774 			    free(reply);
775 
776 		    p->wake_time = now + p->lifetime;
777 		}
778 
779 		if (p->wake_time < shortest_wait)
780 			shortest_wait = p->wake_time;
781 	}
782 	next_wake_time = shortest_wait;
783 
784 done:
785 	(void) mutex_unlock(&rereg_lock);
786 	return (more);
787 }
788 
789 /*
790  * Removes the refresh registration for 'url'.
791  */
792 static unsigned short dereg_rereg(const char *url) {
793 	struct rereg_entry *p, *q;
794 	unsigned short lifetime = 0;
795 
796 	(void) mutex_lock(&rereg_lock);
797 	for (p = q = reregs; p; p = p->next) {
798 		if (slp_strcasecmp(p->url, url) == 0) {
799 			/* found it; remove it from the list */
800 			if (p == q) {
801 				/* first one on list */
802 				reregs = p->next;
803 			} else {
804 				q->next = p->next;
805 			}
806 
807 			/* free the entry */
808 			lifetime = p->lifetime;
809 			free(p->url);
810 			/* free the message memory */
811 			free(p->msg->msgiov[0].iov_base);
812 			/* free the URL auth block */
813 			free(p->msg->msgiov[SLP_URL_AUTH].iov_base);
814 			/* free the attr auth block */
815 			free(p->msg->msgiov[SLP_ATTR_AUTH].iov_base);
816 			/* free the message iovec */
817 			free(p->msg->msgiov);
818 			/* finally, free the message structure */
819 			free(p->msg);
820 			free(p);
821 
822 			goto done;
823 		}
824 
825 		q = p;
826 	}
827 
828 done:
829 	(void) mutex_unlock(&rereg_lock);
830 	return (lifetime);
831 }
832 
833 /*
834  * Returns configured scopes in scopes. Caller should free *scopes
835  * when done. If the scope string is too long for an SLP string, the
836  * string is truncated.
837  */
838 static SLPError find_SAscopes(char **scopes) {
839 	SLPError err;
840 
841 	if ((err = slp_administrative_scopes(scopes, SLP_TRUE))
842 	    != SLP_OK) {
843 		return (err);
844 	}
845 
846 	/* Ensure string is not too long */
847 	if (strlen(*scopes) > SLP_MAX_STRINGLEN) {
848 		/* truncate the string */
849 		if ((*scopes)[SLP_MAX_STRINGLEN - 1] == ',') {
850 			/* scopes can't end with ',' */
851 			(*scopes)[SLP_MAX_STRINGLEN - 1] = 0;
852 		} else {
853 			(*scopes)[SLP_MAX_STRINGLEN] = 0;
854 		}
855 	}
856 
857 	return (SLP_OK);
858 }
859 
860 /*
861  * Does all the dirty work of freeing a msgiov.
862  */
863 static void free_msgiov(struct iovec *msgiov, int iovlen) {
864 	/* free the message memory */
865 	free(msgiov[0].iov_base);
866 	/* free the URL auth block */
867 	free(msgiov[SLP_URL_AUTH].iov_base);
868 	if (iovlen == 4) {
869 		/* free the attr auth block */
870 		free(msgiov[SLP_ATTR_AUTH].iov_base);
871 	}
872 	/* free the message iovec */
873 	free(msgiov);
874 }
875