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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31#include <sys/types.h>
32#include <time.h>
33#include <signal.h>
34#include <poll.h>
35
36#include "isns_server.h"
37#include "isns_cache.h"
38#include "isns_obj.h"
39#include "isns_pdu.h"
40#include "isns_func.h"
41#include "isns_qry.h"
42#include "isns_msgq.h"
43#include "isns_log.h"
44#include "isns_sched.h"
45#include "isns_scn.h"
46#include "isns_esi.h"
47
48/*
49 * global variables.
50 */
51
52/*
53 * local variables.
54 */
55static ev_t *ev_list = NULL;
56
57static uint32_t stopwatch = 0;
58static pthread_mutex_t stw_mtx = PTHREAD_MUTEX_INITIALIZER;
59
60static int wakeup = 0;
61static pthread_mutex_t idl_mtx = PTHREAD_MUTEX_INITIALIZER;
62static pthread_cond_t idl_cond = PTHREAD_COND_INITIALIZER;
63
64/*
65 * external variables.
66 */
67extern const int UID_ATTR_INDEX[MAX_OBJ_TYPE_FOR_SIZE];
68
69extern boolean_t time_to_exit;
70
71extern msg_queue_t *sys_q;
72
73extern uint64_t esi_threshold;
74
75#ifdef DEBUG
76extern void dump_pdu1(isns_pdu_t *);
77#endif
78
79/*
80 * local functions.
81 */
82static void *esi_monitor(void *);
83
84/*
85 * ****************************************************************************
86 *
87 * new_esi_portal:
88 *	Make a new portal for ESI event.
89 *
90 * uid	- the portal object UID.
91 * ip6	- the portal IPv6 format IP address.
92 * port	- the portal port.
93 * esip	- the ESI port.
94 * return - the new ESI portal.
95 *
96 * ****************************************************************************
97 */
98static esi_portal_t *
99new_esi_portal(
100	uint32_t uid,
101	in6_addr_t *ip6,
102	uint32_t port,
103	uint32_t esip
104)
105{
106	esi_portal_t *p;
107
108	p = (esi_portal_t *)malloc(sizeof (esi_portal_t));
109	if (p != NULL) {
110		if (((int *)ip6)[0] == 0x00 &&
111		    ((int *)ip6)[1] == 0x00 &&
112		    ((uchar_t *)ip6)[8] == 0x00 &&
113		    ((uchar_t *)ip6)[9] == 0x00 &&
114		    ((uchar_t *)ip6)[10] == 0xFF &&
115		    ((uchar_t *)ip6)[11] == 0xFF) {
116			p->sz = sizeof (in_addr_t);
117			p->ip4 = ((uint32_t *)ip6)[3];
118		} else {
119			p->sz = sizeof (in6_addr_t);
120		}
121		p->ip6 = ip6;
122		p->port = port;
123		p->esip = esip;
124		p->ref = uid;
125		p->so = 0;
126		p->next = NULL;
127	}
128
129	return (p);
130}
131
132/*
133 * ****************************************************************************
134 *
135 * free_esi_portal:
136 *	Free a list of portal of one ESI event.
137 *
138 * p	- the ESI portal.
139 *
140 * ****************************************************************************
141 */
142static void
143free_esi_portal(
144	esi_portal_t *p
145)
146{
147	esi_portal_t *n;
148
149	while (p != NULL) {
150		n = p->next;
151		free(p->ip6);
152		free(p);
153		p = n;
154	}
155}
156
157/*
158 * ****************************************************************************
159 *
160 * ev_new:
161 *	Make a new ESI event.
162 *
163 * uid	- the Entity object UID.
164 * eid	- the Entity object name.
165 * len	- the length of the name.
166 * return - the ESI event.
167 *
168 * ****************************************************************************
169 */
170static ev_t *
171ev_new(
172	uint32_t uid,
173	uchar_t *eid,
174	uint32_t len
175)
176{
177	ev_t *ev;
178
179	ev = (ev_t *)malloc(sizeof (ev_t));
180	if (ev != NULL) {
181		if (pthread_mutex_init(&ev->mtx, NULL) != 0 ||
182		    (ev->eid = (uchar_t *)malloc(len)) == NULL) {
183			free(ev);
184			return (NULL);
185		}
186		ev->uid = uid;
187		(void) strcpy((char *)ev->eid, (char *)eid);
188		ev->eid_len = len;
189		/* initialization time */
190		ev->flags = EV_FLAG_INIT;
191	}
192
193	return (ev);
194}
195
196/*
197 * ****************************************************************************
198 *
199 * cb_portal_uids:
200 *	Callback function which makes a copy of the portal child object
201 *	UIDs from a Network Entity object.
202 *
203 * p1	- the Network Entity object.
204 * p2	- the lookup control data.
205 * return - the number of portal object UIDs.
206 *
207 * ****************************************************************************
208 */
209static int
210cb_portal_uids(
211	void *p1,
212	void *p2
213)
214{
215	isns_obj_t *obj = (isns_obj_t *)p1;
216	lookup_ctrl_t *lcp = (lookup_ctrl_t *)p2;
217
218	isns_attr_t *attr;
219
220	uint32_t *cuidp;
221
222	uint32_t num = 0;
223	uint32_t *p = NULL;
224
225	cuidp = get_child_t(obj, OBJ_PORTAL);
226	if (cuidp != NULL) {
227		p = (uint32_t *)malloc(*cuidp * sizeof (*p));
228		if (p != NULL) {
229			num = *cuidp ++;
230			(void) memcpy(p, cuidp, num * sizeof (*p));
231			lcp->data[1].ptr = (uchar_t *)p;
232		}
233	}
234
235	attr = &obj->attrs[ATTR_INDEX_ENTITY(ISNS_ENTITY_REG_PERIOD_ATTR_ID)];
236	if (attr->tag != 0 && attr->value.ui != 0) {
237		lcp->data[2].ui = attr->value.ui;
238	} else {
239		/* just one second before the end of the world */
240		lcp->data[2].ui = INFINITY - 1;
241	}
242
243	return (num);
244}
245
246/*
247 * ****************************************************************************
248 *
249 * cb_esi_portal:
250 *	Callback function which gets ESI port number and ESI interval
251 *	from a portal object.
252 *
253 * p1	- the Portal object.
254 * p2	- the lookup control data.
255 * return - the ESI interval.
256 *
257 * ****************************************************************************
258 */
259static int
260cb_esi_portal(
261	void *p1,
262	void *p2
263)
264{
265	uint32_t intval = 0;
266
267	isns_obj_t *obj;
268	lookup_ctrl_t *lcp;
269
270	in6_addr_t *ip;
271	uint32_t esip;
272
273	isns_attr_t *attr;
274
275	if (cb_clone_attrs(p1, p2) == 0) {
276		obj = (isns_obj_t *)p1;
277		lcp = (lookup_ctrl_t *)p2;
278		ip = lcp->data[1].ip;
279		esip = lcp->data[2].ui;
280		if (esip != 0) {
281			attr = &obj->attrs[ATTR_INDEX_PORTAL(
282			    ISNS_PORTAL_PORT_ATTR_ID)];
283			lcp->data[0].ui = attr->value.ui;
284			attr = &obj->attrs[ATTR_INDEX_PORTAL(
285			    ISNS_ESI_INTERVAL_ATTR_ID)];
286			if (attr->tag != 0 && attr->value.ui != 0) {
287				intval = attr->value.ui;
288			} else {
289				intval = DEFAULT_ESI_INTVAL;
290			}
291		} else {
292			free(ip);
293		}
294	}
295
296	return ((int)intval);
297}
298
299/*
300 * ****************************************************************************
301 *
302 * extract_esi_portal:
303 *	Extract a list of portal which have an ESI port for an Entity.
304 *
305 * uid	- the Entity object UID.
306 * intval - the ESI interval for returnning.
307 * return - the list of portals.
308 *
309 * ****************************************************************************
310 */
311static esi_portal_t *
312extract_esi_portal(
313	uint32_t uid,
314	uint32_t *intval
315)
316{
317	esi_portal_t *list = NULL;
318	esi_portal_t *p;
319
320	lookup_ctrl_t lc;
321
322	uint32_t num_of_portal;
323	uint32_t *portal_uids;
324
325	uint32_t intv;
326
327	/* prepare for looking up entity object */
328	SET_UID_LCP(&lc, OBJ_ENTITY, uid);
329	lc.data[1].ptr = NULL;
330	lc.data[2].ui = INFINITY - 1;
331
332	/* get the array of the portal uid(s) */
333	num_of_portal = (uint32_t)cache_lookup(&lc, NULL, cb_portal_uids);
334	portal_uids = (uint32_t *)lc.data[1].ptr;
335	*intval = lc.data[2].ui;
336
337	/* prepare for looking up portal object(s) */
338	SET_UID_LCP(&lc, OBJ_PORTAL, 0);
339	lc.id[1] = ISNS_PORTAL_IP_ADDR_ATTR_ID;
340	lc.id[2] = ISNS_ESI_PORT_ATTR_ID;
341	FOR_EACH_OBJS(portal_uids, num_of_portal, uid, {
342		if (uid != 0) {
343			lc.data[0].ui = uid;
344			intv = cache_lookup(&lc, NULL, cb_esi_portal);
345			if (intv != 0) {
346				p = new_esi_portal(uid,
347				    (in6_addr_t *)lc.data[1].ip,
348				    lc.data[0].ui, lc.data[2].ui);
349				if (p != NULL) {
350					p->next = list;
351					list = p;
352					if (*intval > intv) {
353						*intval = intv;
354					}
355				}
356			}
357		}
358	});
359
360	/* free up the portal uid array */
361	free(portal_uids);
362
363	return (list);
364}
365
366/*
367 * ****************************************************************************
368 *
369 * ev_add:
370 *	Add an ESI event.
371 *
372 * ev	- the ESI event.
373 * init	- 0: initialization time, otherwise not.
374 * return - error code.
375 *
376 * ****************************************************************************
377 */
378static int
379ev_add(
380	ev_t *ev,
381	int init
382)
383{
384	uint32_t intval;
385	esi_portal_t *p;
386
387	double rnd;
388	uint32_t t = 0;
389
390	/* get the portal(s) which are registered for ESI monitoring */
391	/* and the second interval for ESI or registration expiration */
392	p = extract_esi_portal(ev->uid, &intval);
393	ev->intval = intval;
394	if (p != NULL) {
395		ev->type = EV_ESI;
396		ev->portal = p;
397		/* avoid running everything at the same time */
398		if (init != 0) {
399			/* generate random number within range (0, 1] */
400			rnd = (rand() + 1) / (double)(RAND_MAX + 1);
401			t = (uint32_t)(intval * rnd);
402		}
403	} else {
404		/* no portal is registered for ESI monitoring, make */
405		/* an entry for entity registration expiration */
406		ev->type = EV_REG_EXP;
407		ev->portal = NULL;
408		if (init != 0) {
409			t = intval;
410		}
411	}
412
413	/* schedule the event */
414	return (el_add(ev, t, NULL));
415}
416
417/*
418 * global functions.
419 */
420
421/*
422 * ****************************************************************************
423 *
424 * sigalrm:
425 *	The signal handler for SIGALRM, the ESI proc uses the SIGALRM
426 *	for waking up to perform the client status inquery.
427 *
428 * sig	- the signal.
429 *
430 * ****************************************************************************
431 */
432/*ARGSUSED*/
433void
434sigalrm(
435	int sig
436)
437{
438	/* wake up the idle */
439	(void) pthread_mutex_lock(&idl_mtx);
440	wakeup = 1; /* wake up naturally */
441	(void) pthread_cond_signal(&idl_cond);
442	(void) pthread_mutex_unlock(&idl_mtx);
443}
444
445/*
446 * ****************************************************************************
447 *
448 * esi_load:
449 *	Load an ESI event from data store.
450 *
451 * uid	- the Entity object UID.
452 * eid	- the Entity object name.
453 * len	- the length of the name.
454 * return - error code.
455 *
456 * ****************************************************************************
457 */
458int
459esi_load(
460	uint32_t uid,
461	uchar_t *eid,
462	uint32_t len
463)
464{
465	int ec = 0;
466
467	/* make a new event */
468	ev_t *ev = ev_new(uid, eid, len);
469
470	/* put the new event to the list */
471	if (ev != NULL) {
472		ev->next = ev_list;
473		ev_list = ev;
474	} else {
475		ec = ISNS_RSP_INTERNAL_ERROR;
476	}
477
478	return (ec);
479}
480
481/*
482 * ****************************************************************************
483 *
484 * verify_esi_portal:
485 *	Verify ESI port and add the ESI entries after the ESI are loaded.
486 *
487 * return - error code.
488 *
489 * ****************************************************************************
490 */
491int
492verify_esi_portal(
493)
494{
495	int ec = 0;
496
497	ev_t *ev;
498
499	/* add each event from the list */
500	while (ev_list != NULL && ec == 0) {
501		ev = ev_list;
502		ev_list = ev->next;
503		ev->next = NULL;
504		ec = ev_add(ev, 1);
505	}
506
507	return (ec);
508}
509
510/*
511 * ****************************************************************************
512 *
513 * esi_add:
514 *	Add a new ESI event when a new Entity is registered.
515 *
516 * uid	- the Entity object UID.
517 * eid	- the Entity object name.
518 * len	- the length of the name.
519 * return - error code.
520 *
521 * ****************************************************************************
522 */
523int
524esi_add(
525	uint32_t uid,
526	uchar_t *eid,
527	uint32_t len
528)
529{
530	int ec = 0;
531
532	/* make a new event */
533	ev_t *ev = ev_new(uid, eid, len);
534
535	if (ev != NULL) {
536		/* interrupt idle */
537		ev->flags |= EV_FLAG_WAKEUP;
538		ec = ev_add(ev, 0);
539	} else {
540		ec = ISNS_RSP_INTERNAL_ERROR;
541	}
542
543	return (ec);
544}
545
546/*
547 * ****************************************************************************
548 *
549 * esi_remove:
550 *	Remove an ESI event immediately.
551 *
552 * uid	- the Entity object UID.
553 * return - always successful.
554 *
555 * ****************************************************************************
556 */
557int
558esi_remove(
559	uint32_t uid
560)
561{
562	(void) el_remove(uid, 0, 0);
563
564	return (0);
565}
566
567/*
568 * ****************************************************************************
569 *
570 * esi_remove_obj:
571 *	Update an ESI event when a Entity object or a Portal object is
572 *	removed from server. If the object is being removed because of
573 *	ESI failure, the ESI event will be removed with a pending time,
574 *	otherwise, the ESI will be removed immediately.
575 *
576 * obj	- the object being removed.
577 * pending - the pending flag.
578 * return - always successful.
579 *
580 * ****************************************************************************
581 */
582int
583esi_remove_obj(
584	const isns_obj_t *obj,
585	int pending
586)
587{
588	uint32_t puid, uid;
589
590	switch (obj->type) {
591	case OBJ_PORTAL:
592		puid = get_parent_uid(obj);
593		uid = get_obj_uid(obj);
594		break;
595	case OBJ_ENTITY:
596		puid = get_obj_uid(obj);
597		uid = 0;
598		break;
599	default:
600		puid = 0;
601		break;
602	}
603
604	if (puid != 0) {
605		(void) el_remove(puid, uid, pending);
606	}
607
608	return (0);
609}
610
611/*
612 * ****************************************************************************
613 *
614 * get_stopwatch:
615 *	Get the stopwatch. It might need to signal the condition to
616 *	wake up the idle so the stopwatch gets updated.
617 *
618 * flag	- wake up flag.
619 * return - the stopwatch.
620 *
621 * ****************************************************************************
622 */
623uint32_t
624get_stopwatch(
625	int flag
626)
627{
628	uint32_t t;
629
630	/* not re-schedule, wake up idle */
631	(void) pthread_mutex_lock(&idl_mtx);
632	if (flag != 0) {
633		wakeup = 2; /* wake up manually */
634		(void) pthread_cond_signal(&idl_cond);
635	} else {
636		wakeup = 0; /* clear previous interruption */
637	}
638	(void) pthread_mutex_unlock(&idl_mtx);
639
640	/* get most current time */
641	(void) pthread_mutex_lock(&stw_mtx);
642	t = stopwatch;
643	(void) pthread_mutex_unlock(&stw_mtx);
644
645	return (t);
646}
647
648/*
649 * ****************************************************************************
650 *
651 * ev_intval:
652 *	Get the time interval of an ESI event.
653 *
654 * p	- the ESI event.
655 * return - the time interval.
656 *
657 * ****************************************************************************
658 */
659uint32_t
660ev_intval(
661	void *p
662)
663{
664	return (((ev_t *)p)->intval);
665}
666
667/*
668 * ****************************************************************************
669 *
670 * ev_match:
671 *	Check the ESI event maching an Entity object.
672 *
673 * p	- the ESI event.
674 * uid	- the Entity object UID.
675 * return - 1: match, otherwise not.
676 *
677 * ****************************************************************************
678 */
679int
680ev_match(
681	void *p,
682	uint32_t uid
683)
684{
685	if (((ev_t *)p)->uid == uid) {
686		return (1);
687	} else {
688		return (0);
689	}
690}
691
692/*
693 * ****************************************************************************
694 *
695 * ev_remove:
696 *	Remove a portal or an ESI event. If all of ESI portal has been
697 *	removed, the ESI event will be marked as removal pending, which
698 *	will result in removing the Entity object after the pending time.
699 *
700 * p	- the ESI event.
701 * portal_uid	- the Portal object UID.
702 * flag	- 0: the ESI is currently in use, otherwise it is scheduled.
703 * pending	- flag for the ESI removal pending.
704 * return - 0: the ESI is physically removed, otherwise not.
705 *
706 * ****************************************************************************
707 */
708int
709ev_remove(
710	void *p,
711	uint32_t portal_uid,
712	int flag,
713	int pending
714)
715{
716	ev_t *ev = (ev_t *)p;
717	esi_portal_t **pp, *portal;
718
719	int has_portal = 0;
720	int state;
721
722	/* remove one portal only */
723	if (portal_uid != 0) {
724		pp = &ev->portal;
725		portal = *pp;
726		while (portal != NULL) {
727			/* found the match portal */
728			if (portal->ref == portal_uid) {
729				/* mark it as removed */
730				portal->ref = 0;
731				if (flag != 0) {
732					/* not in use, remove it physically */
733					*pp = portal->next;
734					portal->next = NULL;
735					free_esi_portal(portal);
736				} else {
737					pp = &portal->next;
738				}
739			} else {
740				/* one or more esi portals are available */
741				if (portal->ref != 0) {
742					has_portal = 1;
743				}
744				pp = &portal->next;
745			}
746			portal = *pp;
747		}
748	}
749
750	/* no portal available */
751	if (has_portal == 0) {
752		state = (pending << 1) | flag;
753		switch (state) {
754		case 0x0:
755			/* mark the event as removed */
756			ev->flags |= EV_FLAG_REMOVE;
757			isnslog(LOG_DEBUG, "ev_remove",
758			    "%s [%d] is marked as removed.",
759			    ev->type == EV_ESI ? "ESI" : "REG_EXP",
760			    ev->uid);
761			break;
762		case 0x1:
763			/* physically remove the event */
764			ev_free(ev);
765			break;
766		case 0x2:
767		case 0x3:
768			/* mark the event as removal pending */
769			isnslog(LOG_DEBUG, "ev_remove",
770			    "%s [%d] is marked as removal pending.",
771			    ev->type == EV_ESI ? "ESI" : "REG_EXP",
772			    ev->uid);
773			ev->flags |= EV_FLAG_REM_P1;
774			has_portal = 1;
775			break;
776		default:
777			break;
778		}
779	} else {
780		isnslog(LOG_DEBUG, "ev_remove", "%s [%d] removed portal %d.",
781		    ev->type == EV_ESI ? "ESI" : "REG_EXP",
782		    ev->uid, portal_uid);
783	}
784
785	return (has_portal);
786}
787
788/*
789 * ****************************************************************************
790 *
791 * ev_free:
792 *	Free an ESI event.
793 *
794 * p	- the ESI event.
795 *
796 * ****************************************************************************
797 */
798void
799ev_free(
800	void *p
801)
802{
803	ev_t *ev = (ev_t *)p;
804
805	/* free up all of portals */
806	free_esi_portal(ev->portal);
807
808	isnslog(LOG_DEBUG, "ev_free",
809	    "%s [%d] is physically removed.",
810	    ev->type == EV_ESI ? "ESI" : "REG_EXP",
811	    ev->uid);
812
813	free(ev->eid);
814
815	/* free the event */
816	free(ev);
817}
818
819/*
820 * ****************************************************************************
821 *
822 * evf_init:
823 *	Check the initial flag of an ESI event.
824 *
825 * p	- the ESI event.
826 * return - 0: not initial, otherwise yes.
827 *
828 * ****************************************************************************
829 */
830int
831evf_init(
832	void *p
833)
834{
835	return (((ev_t *)p)->flags & EV_FLAG_INIT);
836}
837
838/*
839 * ****************************************************************************
840 *
841 * evf_again:
842 *	Check the again flag of an ESI event.
843 *	(this flag might be eliminated and use the init flag.)
844 *
845 * p	- the ESI event.
846 * return - 0: not again, otherwise yes.
847 *
848 * ****************************************************************************
849 */
850int
851evf_again(
852	void *p
853)
854{
855	return (((ev_t *)p)->flags & EV_FLAG_AGAIN);
856}
857
858/*
859 * ****************************************************************************
860 *
861 * evf_wakeup:
862 *	Check the wakeup flag of an ESI event. The idle might need to
863 *	wake up before the event is scheduled.
864 *
865 * p	- the ESI event.
866 * return - 0: no wakeup, otherwise yes.
867 *
868 * ****************************************************************************
869 */
870int
871evf_wakeup(
872	void *p
873)
874{
875	return (((ev_t *)p)->flags & EV_FLAG_WAKEUP);
876}
877
878/*
879 * ****************************************************************************
880 *
881 * evf_rem:
882 *	Check the removal flag of an ESI event. The ESI entry might be
883 *	marked as removal.
884 *
885 * p	- the ESI event.
886 * return - 0: not removed, otherwise yes.
887 *
888 * ****************************************************************************
889 */
890int
891evf_rem(
892	void *p
893)
894{
895	return (((ev_t *)p)->flags & EV_FLAG_REMOVE);
896}
897
898/*
899 * ****************************************************************************
900 *
901 * evf_rem_pending:
902 *	Check the removal pending flag of an ESI event. The ESI entry
903 *	might be marked as removal pending. If it is, we will switch the
904 *	event type and change the time interval.
905 *
906 * p	- the ESI event.
907 * return - 0: not removal pending, otherwise yes.
908 *
909 * ****************************************************************************
910 */
911int
912evf_rem_pending(
913	void *p
914)
915{
916	ev_t *ev = (ev_t *)p;
917	if ((ev->flags & EV_FLAG_REM_P) != 0) {
918		if (ev->type != EV_REG_EXP) {
919			isnslog(LOG_DEBUG, "ev_rem_pending",
920			    "%s [%d] is changed to REG_EXP.",
921			    ev->type == EV_ESI ? "ESI" : "REG_EXP",
922			    ev->uid);
923			ev->type = EV_REG_EXP;
924			ev->intval *= 2; /* after 2 ESI interval */
925		}
926		return (1);
927	}
928
929	return (0);
930}
931
932/*
933 * ****************************************************************************
934 *
935 * evf_zero:
936 *	Reset the event flag.
937 *
938 * p	- the ESI event.
939 *
940 * ****************************************************************************
941 */
942void
943evf_zero(
944	void *p
945)
946{
947	ev_t *ev = (ev_t *)p;
948
949	/* not acutally clear it, need to set again flag */
950	/* and keep the removal pending flag */
951	ev->flags = EV_FLAG_AGAIN | (ev->flags & EV_FLAG_REM_P);
952}
953
954/*
955 * ****************************************************************************
956 *
957 * evl_append:
958 *	Append an ESI event to the list, the list contains all of
959 *	ESI events which are being processed at present.
960 *
961 * p	- the ESI event.
962 *
963 * ****************************************************************************
964 */
965void
966evl_append(
967	void *p
968)
969{
970	ev_t *ev;
971
972	ev = (ev_t *)p;
973	ev->next = ev_list;
974	ev_list = ev;
975}
976
977/*
978 * ****************************************************************************
979 *
980 * evl_strip:
981 *	Strip off an ESI event from the list after the event is being
982 *	processed, it will be scheduled in the scheduler.
983 *
984 * p	- the ESI event.
985 *
986 * ****************************************************************************
987 */
988void
989evl_strip(
990	void *p
991)
992{
993	ev_t **evp = &ev_list;
994	ev_t *ev = *evp;
995
996	while (ev != NULL) {
997		if (ev == p) {
998			*evp = ev->next;
999			break;
1000		}
1001		evp = &ev->next;
1002		ev = *evp;
1003	}
1004}
1005
1006/*
1007 * ****************************************************************************
1008 *
1009 * evl_remove:
1010 *	Remove an ESI event or a portal of an ESI event from the event list.
1011 *
1012 * id1	- the Entity object UID.
1013 * id2	- the Portal object UID.
1014 * pending - the pending flag.
1015 * return - 1: found a match event, otherwise not.
1016 *
1017 * ****************************************************************************
1018 */
1019int
1020evl_remove(
1021	uint32_t id1,
1022	uint32_t id2,
1023	int pending
1024)
1025{
1026	ev_t *ev = ev_list;
1027
1028	while (ev != NULL) {
1029		/* found it */
1030		if (ev_match(ev, id1) != 0) {
1031			/* lock the event */
1032			(void) pthread_mutex_lock(&ev->mtx);
1033			/* mark it as removed */
1034			(void) ev_remove(ev, id2, 0, pending);
1035			/* unlock the event */
1036			(void) pthread_mutex_unlock(&ev->mtx);
1037			/* tell caller removal is done */
1038			return (1);
1039		}
1040		ev = ev->next;
1041	}
1042
1043	/* not found it */
1044	return (0);
1045}
1046
1047#define	ALARM_MAX	(21427200)
1048
1049/*
1050 * ****************************************************************************
1051 *
1052 * idle:
1053 *	Idle for certain amount of time or a wakeup signal is recieved.
1054 *
1055 * t	- the idle time.
1056 * return - the time that idle left.
1057 *
1058 * ****************************************************************************
1059 */
1060static int
1061idle(
1062	uint32_t t
1063)
1064{
1065	uint32_t t1, t2, t3 = 0;
1066	int idl_int = 0;
1067
1068	/* hold the mutex for stopwatch update */
1069	(void) pthread_mutex_lock(&stw_mtx);
1070
1071	do {
1072		if (t > ALARM_MAX) {
1073			t1 = ALARM_MAX;
1074		} else {
1075			t1 = t;
1076		}
1077
1078		/* start alarm */
1079		(void) alarm(t1);
1080
1081		/* hold the mutex for idle condition */
1082		(void) pthread_mutex_lock(&idl_mtx);
1083
1084		/* wait on condition variable to wake up idle */
1085		while (wakeup == 0) {
1086			(void) pthread_cond_wait(&idl_cond, &idl_mtx);
1087		}
1088		if (wakeup == 2) {
1089			idl_int = 1;
1090		}
1091		/* clean wakeup flag */
1092		wakeup = 0;
1093
1094		/* release the mutex for idle condition */
1095		(void) pthread_mutex_unlock(&idl_mtx);
1096
1097		/* stop alarm */
1098		t2 = alarm(0);
1099
1100		/* seconds actually slept */
1101		t3 += t1 - t2;
1102		t -= t3;
1103	} while (t > 0 && idl_int == 0);
1104
1105	/* increate the stopwatch by the actually slept time */
1106	stopwatch += t3;
1107
1108	/* release the mutex after stopwatch is updated */
1109	(void) pthread_mutex_unlock(&stw_mtx);
1110
1111	/* return the amount of time which is not slept */
1112	return (t);
1113}
1114
1115/*
1116 * ****************************************************************************
1117 *
1118 * ev_ex:
1119 *	Execute an event. To inquiry the client status or
1120 *	perform registration expiration.
1121 *
1122 * ev	- the event.
1123 *
1124 * ****************************************************************************
1125 */
1126static void
1127ev_ex(
1128	ev_t *ev
1129)
1130{
1131	pthread_t tid;
1132
1133	switch (ev->type) {
1134	case EV_ESI:
1135		if (pthread_create(&tid, NULL,
1136		    esi_monitor, (void *)ev) != 0) {
1137			isnslog(LOG_DEBUG, "ev_ex", "pthread_create() failed.");
1138			/* reschedule for next occurence */
1139			(void) el_add(ev, 0, NULL);
1140		} else {
1141			/* increase the thread ref count */
1142			inc_thr_count();
1143		}
1144		break;
1145	case EV_REG_EXP:
1146		(void) queue_msg_set(sys_q, REG_EXP, (void *)ev);
1147		break;
1148	default:
1149		break;
1150	}
1151}
1152
1153/*
1154 * ****************************************************************************
1155 *
1156 * esi_proc:
1157 *	ESI thread entry, which:
1158 *	1: fetch an event from schedule,
1159 *	2: idle for some time,
1160 *	3: execute the event or re-schedule it,
1161 *	4: repeat from step 1 before server is being shutdown.
1162 *
1163 * arg	- the thread argument.
1164 *
1165 * ****************************************************************************
1166 */
1167/*ARGSUSED*/
1168void *
1169esi_proc(
1170	void *arg
1171)
1172{
1173	uint32_t t, t1, pt;
1174	ev_t *ev;
1175
1176	void *evp;
1177
1178	while (time_to_exit == B_FALSE) {
1179		ev = (ev_t *)el_first(&pt);
1180
1181		/* caculate the idle time */
1182		if (ev != NULL) {
1183			if (pt > stopwatch) {
1184				t = pt - stopwatch;
1185			} else {
1186				t = 0;
1187			}
1188		} else {
1189			t = INFINITY;
1190		}
1191
1192		do {
1193			/* block for a certain amount of time */
1194			if (t > 0) {
1195				isnslog(LOG_DEBUG, "esi_proc",
1196				    "idle for %d seconds.", t);
1197				t1 = idle(t);
1198			} else {
1199				t1 = 0;
1200			}
1201			if (t1 > 0) {
1202				isnslog(LOG_DEBUG, "esi_proc",
1203				    "idle interrupted after idle for "
1204				    "%d seconds.", t - t1);
1205			}
1206			if (time_to_exit != B_FALSE) {
1207				ev = NULL; /* force break */
1208			} else if (ev != NULL) {
1209				if (t1 > 0) {
1210					/* not naturally waken up */
1211					/* reschedule current event */
1212					evp = NULL;
1213					(void) el_add(ev, pt, &evp);
1214					ev = (ev_t *)evp;
1215					t = t1;
1216				} else {
1217					/* excute */
1218					isnslog(LOG_DEBUG, "esi_proc",
1219					    "excute the cron job[%d].",
1220					    ev->uid);
1221					ev_ex(ev);
1222					ev = NULL;
1223				}
1224			}
1225		} while (ev != NULL);
1226	}
1227
1228	return (NULL);
1229}
1230
1231/*
1232 * ****************************************************************************
1233 *
1234 * esi_ping:
1235 *	Ping the client with the ESI retry threshold for status inquiry.
1236 *
1237 * so	- the socket descriptor.
1238 * pdu	- the ESI packet.
1239 * pl	- the length of packet.
1240 * return - 1: status inquired, otherwise not.
1241 *
1242 * ****************************************************************************
1243 */
1244static int
1245esi_ping(
1246	int so,
1247	isns_pdu_t *pdu,
1248	size_t pl
1249)
1250{
1251	int try_cnt = 0;
1252	isns_pdu_t *rsp = NULL;
1253	size_t rsp_sz;
1254
1255	int alive = 0;
1256
1257	do {
1258		if (isns_send_pdu(so, pdu, pl) == 0) {
1259			if (isns_rcv_pdu(so, &rsp, &rsp_sz,
1260			    ISNS_RCV_SHORT_TIMEOUT) > 0) {
1261#ifdef DEBUG
1262				dump_pdu1(rsp);
1263#endif
1264				alive = 1;
1265				break;
1266			}
1267		} else {
1268			/* retry after 1 second */
1269			(void) sleep(1);
1270		}
1271		try_cnt ++;
1272	} while (try_cnt < esi_threshold);
1273
1274	if (rsp != NULL) {
1275		free(rsp);
1276	}
1277
1278	return (alive);
1279}
1280
1281/*
1282 * ****************************************************************************
1283 *
1284 * esi_monitor:
1285 *	Child thread for client status mornitoring.
1286 *
1287 * arg	- the ESI event.
1288 *
1289 * ****************************************************************************
1290 */
1291static void *
1292esi_monitor(
1293	void *arg
1294)
1295{
1296	ev_t *ev = (ev_t *)arg;
1297
1298	esi_portal_t *p;
1299	int so;
1300
1301	isns_pdu_t *pdu = NULL;
1302	size_t sz;
1303	size_t pl;
1304	size_t half;
1305
1306	time_t t;
1307
1308	int feedback;
1309
1310	/* lock the event for esi monitoring */
1311	(void) pthread_mutex_lock(&ev->mtx);
1312
1313	if (evf_rem(ev) != 0) {
1314		goto mon_done;
1315	} else if (evf_rem_pending(ev) != 0) {
1316		goto mon_done;
1317	}
1318
1319	/* timestamp */
1320	t = time(NULL);
1321
1322	/* allocate ESI PDU */
1323	if (pdu_reset_esi(&pdu, &pl, &sz) != 0 ||
1324	    pdu_add_tlv(&pdu, &pl, &sz,
1325	    ISNS_TIMESTAMP_ATTR_ID, 8, (void *)&t, 1) != 0 ||
1326	    pdu_add_tlv(&pdu, &pl, &sz,
1327	    ISNS_EID_ATTR_ID, ev->eid_len, (void *)ev->eid, 0) != 0) {
1328		/* no memory, will retry later */
1329		goto mon_done;
1330	}
1331
1332	/* set pdu head */
1333	pdu->version = htons((uint16_t)ISNSP_VERSION);
1334	pdu->func_id = htons((uint16_t)ISNS_ESI);
1335	pdu->xid = htons(get_server_xid());
1336
1337	/* keep the current lenght of the playload */
1338	half = pl;
1339
1340	p = ev->portal;
1341	while (p != NULL) {
1342		if (p->ref != 0 &&
1343		    /* skip IPv6 portal */
1344		    p->sz != sizeof (in6_addr_t) &&
1345		    pdu_add_tlv(&pdu, &pl, &sz,
1346		    ISNS_PORTAL_IP_ADDR_ATTR_ID,
1347		    sizeof (in6_addr_t), (void *)p->ip6, 0) == 0 &&
1348		    pdu_add_tlv(&pdu, &pl, &sz,
1349		    ISNS_PORTAL_PORT_ATTR_ID,
1350		    4, (void *)p->port, 0) == 0) {
1351			/* connect once */
1352			so = connect_to(p->sz, p->ip4, p->ip6, p->esip);
1353			if (so != -1) {
1354				feedback = esi_ping(so, pdu, pl);
1355				(void) close(so);
1356				/* p->so = so; */
1357			} else {
1358				/* cannot connect, portal is dead */
1359				feedback = 0;
1360			}
1361			if (feedback == 0) {
1362				isnslog(LOG_DEBUG, "esi_monitor",
1363				    "ESI ping failed.");
1364				(void) queue_msg_set(sys_q, DEAD_PORTAL,
1365				    (void *)p->ref);
1366			} else {
1367				goto mon_done;
1368			}
1369		}
1370		pl = half;
1371		p = p->next;
1372	}
1373
1374mon_done:
1375	/* unlock the event after esi monitoring is done */
1376	(void) pthread_mutex_unlock(&ev->mtx);
1377
1378	/* clean up pdu */
1379	if (pdu != NULL) {
1380		free(pdu);
1381	}
1382
1383	/* set reschedule flags */
1384	ev->flags |= EV_FLAG_WAKEUP;
1385
1386	/* reschedule for next occurence */
1387	(void) el_add(ev, 0, NULL);
1388
1389	/* decrease the thread ref count */
1390	dec_thr_count();
1391
1392	return (NULL);
1393}
1394
1395/*
1396 * ****************************************************************************
1397 *
1398 * portal_dies:
1399 *	Handles the dead portal that ESI detected.
1400 *
1401 * uid	- the Portal object UID.
1402 *
1403 * ****************************************************************************
1404 */
1405void
1406portal_dies(
1407	uint32_t uid
1408)
1409{
1410	int ec = 0;
1411
1412	lookup_ctrl_t lc;
1413
1414	/* prepare the lookup control for deregistration */
1415	SET_UID_LCP(&lc, OBJ_PORTAL, uid);
1416
1417	/* lock the cache for object deregistration */
1418	(void) cache_lock_write();
1419
1420	/* deregister the portal */
1421	ec = dereg_object(&lc, 1);
1422
1423	/* unlock cache and sync with data store */
1424	(void) cache_unlock_sync(ec);
1425}
1426
1427/*
1428 * ****************************************************************************
1429 *
1430 * portal_dies:
1431 *	Handles the Entity registration expiration.
1432 *
1433 * p	- the ESI event.
1434 *
1435 * ****************************************************************************
1436 */
1437void
1438reg_expiring(
1439	void *p
1440)
1441{
1442	int ec = 0;
1443	ev_t *ev = (ev_t *)p;
1444	lookup_ctrl_t lc;
1445
1446	/* prepare the lookup control for deregistration */
1447	SET_UID_LCP(&lc, OBJ_ENTITY, ev->uid);
1448
1449	/* lock the cache for object deregistration */
1450	(void) cache_lock_write();
1451
1452	if (evf_rem(ev) == 0) {
1453		/* deregister the entity */
1454		ec = dereg_object(&lc, 0);
1455
1456		/* unlock cache and sync with data store */
1457		ec = cache_unlock_sync(ec);
1458
1459		if (ec == 0) {
1460			/* successfuk, mark ev as removed */
1461			ev->flags |= EV_FLAG_REMOVE;
1462		} else {
1463			/* failed, retry after 3 mintues */
1464			ev->intval = 3 * 60;
1465			isnslog(LOG_DEBUG, "reg_expiring",
1466			    "dereg failed, retry after 3 mintues.");
1467		}
1468	} else {
1469		/* ev is marked as removed, no need to dereg */
1470		(void) cache_unlock_nosync();
1471	}
1472
1473	/* reschedule it for next occurence */
1474	(void) el_add(ev, 0, NULL);
1475}
1476