xref: /titanic_44/usr/src/cmd/ldapcachemgr/cachemgr_change.c (revision e1dd0a2f3a26050d1f183c1cafae42c4e3a0b57e)
1*e1dd0a2fSth160488 /*
2*e1dd0a2fSth160488  * CDDL HEADER START
3*e1dd0a2fSth160488  *
4*e1dd0a2fSth160488  * The contents of this file are subject to the terms of the
5*e1dd0a2fSth160488  * Common Development and Distribution License (the "License").
6*e1dd0a2fSth160488  * You may not use this file except in compliance with the License.
7*e1dd0a2fSth160488  *
8*e1dd0a2fSth160488  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*e1dd0a2fSth160488  * or http://www.opensolaris.org/os/licensing.
10*e1dd0a2fSth160488  * See the License for the specific language governing permissions
11*e1dd0a2fSth160488  * and limitations under the License.
12*e1dd0a2fSth160488  *
13*e1dd0a2fSth160488  * When distributing Covered Code, include this CDDL HEADER in each
14*e1dd0a2fSth160488  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*e1dd0a2fSth160488  * If applicable, add the following below this CDDL HEADER, with the
16*e1dd0a2fSth160488  * fields enclosed by brackets "[]" replaced with your own identifying
17*e1dd0a2fSth160488  * information: Portions Copyright [yyyy] [name of copyright owner]
18*e1dd0a2fSth160488  *
19*e1dd0a2fSth160488  * CDDL HEADER END
20*e1dd0a2fSth160488  */
21*e1dd0a2fSth160488 /*
22*e1dd0a2fSth160488  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23*e1dd0a2fSth160488  * Use is subject to license terms.
24*e1dd0a2fSth160488  */
25*e1dd0a2fSth160488 
26*e1dd0a2fSth160488 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*e1dd0a2fSth160488 
28*e1dd0a2fSth160488 #include <strings.h>
29*e1dd0a2fSth160488 #include <stdlib.h>
30*e1dd0a2fSth160488 #include <syslog.h>
31*e1dd0a2fSth160488 #include <errno.h>
32*e1dd0a2fSth160488 #include <libintl.h>
33*e1dd0a2fSth160488 #include <door.h>
34*e1dd0a2fSth160488 #include <sys/types.h>
35*e1dd0a2fSth160488 #include <sys/stat.h>
36*e1dd0a2fSth160488 #include <fcntl.h>
37*e1dd0a2fSth160488 #include <procfs.h>
38*e1dd0a2fSth160488 #include "cachemgr.h"
39*e1dd0a2fSth160488 
40*e1dd0a2fSth160488 extern admin_t	current_admin;
41*e1dd0a2fSth160488 
42*e1dd0a2fSth160488 #define	CLEANUP_WAIT_TIME 60
43*e1dd0a2fSth160488 
44*e1dd0a2fSth160488 typedef enum cleanup_type {
45*e1dd0a2fSth160488 	CLEANUP_ALL	= 1,
46*e1dd0a2fSth160488 	CLEANUP_BY_PID	= 2
47*e1dd0a2fSth160488 } cleanup_type_t;
48*e1dd0a2fSth160488 
49*e1dd0a2fSth160488 typedef struct cleanup_op {
50*e1dd0a2fSth160488 	pid_t		pid;
51*e1dd0a2fSth160488 	cleanup_type_t	type;
52*e1dd0a2fSth160488 } cleanup_op_t;
53*e1dd0a2fSth160488 
54*e1dd0a2fSth160488 typedef struct main_nscd_struct {
55*e1dd0a2fSth160488 	pid_t		pid;			/* main nscd pid */
56*e1dd0a2fSth160488 	thread_t	tid;			/* main nscd tid */
57*e1dd0a2fSth160488 	int		in_progress;		/* A main nscd thread is */
58*e1dd0a2fSth160488 						/* waiting for change or */
59*e1dd0a2fSth160488 						/* copying data */
60*e1dd0a2fSth160488 	int		is_waiting_cleanup;	/* A main nscd thread is */
61*e1dd0a2fSth160488 						/* waiting for another main */
62*e1dd0a2fSth160488 						/* nscd thread to be cleaned */
63*e1dd0a2fSth160488 						/* up */
64*e1dd0a2fSth160488 } main_nscd_t;
65*e1dd0a2fSth160488 
66*e1dd0a2fSth160488 static chg_info_t chg = { DEFAULTMUTEX, DEFAULTCV, 0, NULL, NULL, NULL, 0 };
67*e1dd0a2fSth160488 
68*e1dd0a2fSth160488 static main_nscd_t chg_main_nscd = {0, 0, 0, 0};
69*e1dd0a2fSth160488 static mutex_t chg_main_nscd_lock = DEFAULTMUTEX;
70*e1dd0a2fSth160488 static cond_t chg_main_nscd_cv = DEFAULTCV;
71*e1dd0a2fSth160488 
72*e1dd0a2fSth160488 /*
73*e1dd0a2fSth160488  * The cookie of the configuration and its mutex
74*e1dd0a2fSth160488  */
75*e1dd0a2fSth160488 static ldap_get_chg_cookie_t config_cookie = {0, 0};
76*e1dd0a2fSth160488 static mutex_t config_cookie_lock = DEFAULTMUTEX;
77*e1dd0a2fSth160488 
78*e1dd0a2fSth160488 static void cleanup_thread_by_pid(pid_t pid);
79*e1dd0a2fSth160488 
80*e1dd0a2fSth160488 ldap_get_chg_cookie_t
81*e1dd0a2fSth160488 chg_config_cookie_get(void)
82*e1dd0a2fSth160488 {
83*e1dd0a2fSth160488 	ldap_get_chg_cookie_t cookie;
84*e1dd0a2fSth160488 	(void) mutex_lock(&config_cookie_lock);
85*e1dd0a2fSth160488 	cookie = config_cookie;
86*e1dd0a2fSth160488 	(void) mutex_unlock(&config_cookie_lock);
87*e1dd0a2fSth160488 	return (cookie);
88*e1dd0a2fSth160488 }
89*e1dd0a2fSth160488 
90*e1dd0a2fSth160488 static void
91*e1dd0a2fSth160488 chg_config_cookie_increment_seq_num(void)
92*e1dd0a2fSth160488 {
93*e1dd0a2fSth160488 	(void) mutex_lock(&config_cookie_lock);
94*e1dd0a2fSth160488 	config_cookie.seq_num++;
95*e1dd0a2fSth160488 	(void) mutex_unlock(&config_cookie_lock);
96*e1dd0a2fSth160488 }
97*e1dd0a2fSth160488 
98*e1dd0a2fSth160488 void
99*e1dd0a2fSth160488 chg_config_cookie_set(ldap_get_chg_cookie_t *cookie)
100*e1dd0a2fSth160488 {
101*e1dd0a2fSth160488 	(void) mutex_lock(&config_cookie_lock);
102*e1dd0a2fSth160488 	config_cookie.mgr_pid = cookie->mgr_pid;
103*e1dd0a2fSth160488 	config_cookie.seq_num = cookie->seq_num;
104*e1dd0a2fSth160488 	(void) mutex_unlock(&config_cookie_lock);
105*e1dd0a2fSth160488 }
106*e1dd0a2fSth160488 static boolean_t
107*e1dd0a2fSth160488 chg_cookie_equal(ldap_get_chg_cookie_t *c1, ldap_get_chg_cookie_t *c2)
108*e1dd0a2fSth160488 {
109*e1dd0a2fSth160488 	if (c1->mgr_pid == c2->mgr_pid && c1->seq_num == c2->seq_num)
110*e1dd0a2fSth160488 		return (B_TRUE);
111*e1dd0a2fSth160488 	else
112*e1dd0a2fSth160488 		return (B_FALSE);
113*e1dd0a2fSth160488 }
114*e1dd0a2fSth160488 /*
115*e1dd0a2fSth160488  * Create a node in the list and output the node. The caller can NOT free it.
116*e1dd0a2fSth160488  */
117*e1dd0a2fSth160488 static  int
118*e1dd0a2fSth160488 waiting_list_add(chg_info_t *info, pid_t pid, thread_t tid,
119*e1dd0a2fSth160488     waiting_list_t **wlp)
120*e1dd0a2fSth160488 {
121*e1dd0a2fSth160488 
122*e1dd0a2fSth160488 	waiting_list_t	*wl;
123*e1dd0a2fSth160488 
124*e1dd0a2fSth160488 	*wlp = NULL;
125*e1dd0a2fSth160488 
126*e1dd0a2fSth160488 	if ((wl = (waiting_list_t *)calloc(1, sizeof (waiting_list_t)))
127*e1dd0a2fSth160488 	    == NULL) {
128*e1dd0a2fSth160488 		logit("waiting_list_add: No memory. pid %ld tid %d\n",
129*e1dd0a2fSth160488 		    pid, tid);
130*e1dd0a2fSth160488 		return (CHG_NO_MEMORY);
131*e1dd0a2fSth160488 	}
132*e1dd0a2fSth160488 
133*e1dd0a2fSth160488 	wl->pid = pid;
134*e1dd0a2fSth160488 	wl->tid = tid;
135*e1dd0a2fSth160488 
136*e1dd0a2fSth160488 	if (info->chg_w_first == NULL) {
137*e1dd0a2fSth160488 		info->chg_w_first = wl;
138*e1dd0a2fSth160488 		info->chg_w_last = wl;
139*e1dd0a2fSth160488 	} else {
140*e1dd0a2fSth160488 		info->chg_w_last->next = wl;
141*e1dd0a2fSth160488 		wl->prev = info->chg_w_last;
142*e1dd0a2fSth160488 		info->chg_w_last = wl;
143*e1dd0a2fSth160488 	}
144*e1dd0a2fSth160488 	*wlp = wl;
145*e1dd0a2fSth160488 	return (CHG_SUCCESS);
146*e1dd0a2fSth160488 }
147*e1dd0a2fSth160488 
148*e1dd0a2fSth160488 /*
149*e1dd0a2fSth160488  * Find a node with matching tid in the list and remove it from the list.
150*e1dd0a2fSth160488  */
151*e1dd0a2fSth160488 static int
152*e1dd0a2fSth160488 waiting_list_delete(chg_info_t *info, thread_t tid)
153*e1dd0a2fSth160488 {
154*e1dd0a2fSth160488 	waiting_list_t	*wl;
155*e1dd0a2fSth160488 
156*e1dd0a2fSth160488 	for (wl = info->chg_w_first; wl != NULL; wl = wl->next) {
157*e1dd0a2fSth160488 		if (wl->tid == tid) {
158*e1dd0a2fSth160488 			if (wl->next == NULL) {
159*e1dd0a2fSth160488 				if (wl->prev == NULL) {
160*e1dd0a2fSth160488 					info->chg_w_first = NULL;
161*e1dd0a2fSth160488 					info->chg_w_last = NULL;
162*e1dd0a2fSth160488 				} else {
163*e1dd0a2fSth160488 					wl->prev->next = NULL;
164*e1dd0a2fSth160488 					info->chg_w_last =  wl->prev;
165*e1dd0a2fSth160488 				}
166*e1dd0a2fSth160488 			} else {
167*e1dd0a2fSth160488 				if (wl->prev == NULL) {
168*e1dd0a2fSth160488 					wl->next->prev = NULL;
169*e1dd0a2fSth160488 					info->chg_w_first = wl->next;
170*e1dd0a2fSth160488 				} else {
171*e1dd0a2fSth160488 					wl->prev->next = wl->next;
172*e1dd0a2fSth160488 					wl->next->prev = wl->prev;
173*e1dd0a2fSth160488 				}
174*e1dd0a2fSth160488 			}
175*e1dd0a2fSth160488 			free(wl);
176*e1dd0a2fSth160488 			return (CHG_SUCCESS);
177*e1dd0a2fSth160488 		}
178*e1dd0a2fSth160488 	}
179*e1dd0a2fSth160488 	return (CHG_NOT_FOUND_IN_WAITING_LIST);
180*e1dd0a2fSth160488 }
181*e1dd0a2fSth160488 
182*e1dd0a2fSth160488 /*
183*e1dd0a2fSth160488  * Delete the thread from the waiting list and remove data when the list
184*e1dd0a2fSth160488  * is empty.
185*e1dd0a2fSth160488  */
186*e1dd0a2fSth160488 static void
187*e1dd0a2fSth160488 waiting_list_cleanup(chg_info_t *chg, thread_t tid)
188*e1dd0a2fSth160488 {
189*e1dd0a2fSth160488 	int	rc;
190*e1dd0a2fSth160488 
191*e1dd0a2fSth160488 	rc = waiting_list_delete(chg, tid);
192*e1dd0a2fSth160488 
193*e1dd0a2fSth160488 	if (rc == CHG_SUCCESS && chg->chg_w_first == NULL) {
194*e1dd0a2fSth160488 		free(chg->chg_data);
195*e1dd0a2fSth160488 		chg->chg_data = NULL;
196*e1dd0a2fSth160488 		chg->chg_wakeup = 0;
197*e1dd0a2fSth160488 	}
198*e1dd0a2fSth160488 }
199*e1dd0a2fSth160488 
200*e1dd0a2fSth160488 /*
201*e1dd0a2fSth160488  * Set flag by pid so it can be cleaned up.
202*e1dd0a2fSth160488  */
203*e1dd0a2fSth160488 static void
204*e1dd0a2fSth160488 waiting_list_set_cleanup(chg_info_t *info, pid_t pid)
205*e1dd0a2fSth160488 {
206*e1dd0a2fSth160488 	waiting_list_t	*wl;
207*e1dd0a2fSth160488 
208*e1dd0a2fSth160488 	for (wl = info->chg_w_first; wl != NULL; wl = wl->next) {
209*e1dd0a2fSth160488 		if (wl->pid == pid) {
210*e1dd0a2fSth160488 			wl->cleanup = 1;
211*e1dd0a2fSth160488 			break;
212*e1dd0a2fSth160488 		}
213*e1dd0a2fSth160488 	}
214*e1dd0a2fSth160488 }
215*e1dd0a2fSth160488 
216*e1dd0a2fSth160488 /*
217*e1dd0a2fSth160488  * Return: 1 - door client is dead, 0 - door client is alive
218*e1dd0a2fSth160488  */
219*e1dd0a2fSth160488 static int
220*e1dd0a2fSth160488 door_client_dead(void)
221*e1dd0a2fSth160488 {
222*e1dd0a2fSth160488 	ucred_t *uc = NULL;
223*e1dd0a2fSth160488 	int	rc;
224*e1dd0a2fSth160488 
225*e1dd0a2fSth160488 	if (door_ucred(&uc) == -1 && errno == EINVAL) {
226*e1dd0a2fSth160488 		rc = 1;
227*e1dd0a2fSth160488 	} else {
228*e1dd0a2fSth160488 		rc = 0;
229*e1dd0a2fSth160488 	}
230*e1dd0a2fSth160488 	if (uc)
231*e1dd0a2fSth160488 		ucred_free(uc);
232*e1dd0a2fSth160488 
233*e1dd0a2fSth160488 	return (rc);
234*e1dd0a2fSth160488 }
235*e1dd0a2fSth160488 
236*e1dd0a2fSth160488 /*
237*e1dd0a2fSth160488  * This function handles GETSTATUSCHANGE call from main nscd.
238*e1dd0a2fSth160488  * The call can be a START op or STOP op. A cookie is sent from main nscd too.
239*e1dd0a2fSth160488  * The static global variable main_nscd keeps record of pid, tid and some flags.
240*e1dd0a2fSth160488  * If the thread is door_return(), main_nscd.pid, main_nscd.tid are set to 0.
241*e1dd0a2fSth160488  * When the call is START op, it checks if main_nscd.pid is 0. If it is, it
242*e1dd0a2fSth160488  * proceeds to wait for the change notification. If it's not, which means
243*e1dd0a2fSth160488  * another main nscd handling thread is still around. It sends broadcast to
244*e1dd0a2fSth160488  * clean up that thread and wait until the cleanup is done then proceeds to
245*e1dd0a2fSth160488  * wait for the change notification. If same main nscd sends START op
246*e1dd0a2fSth160488  * repeatedly, it'll be rejected.
247*e1dd0a2fSth160488  * It also checks the cookie from main nscd. If it's not the same as
248*e1dd0a2fSth160488  * ldap_cachemgr's cookie, door returns config change.
249*e1dd0a2fSth160488  * If the door call is STOP op, it creates a thread to clean up main nscd START
250*e1dd0a2fSth160488  * thread so it won't be blocking.
251*e1dd0a2fSth160488  * In waiting for the change notification phase, the thread is waken up by
252*e1dd0a2fSth160488  * the notification threads or by the cleanup threads.
253*e1dd0a2fSth160488  * If it's a notification, it copies data to the stack then door return.
254*e1dd0a2fSth160488  * If it's a cleanup, door_client_dead() is called to verify it then
255*e1dd0a2fSth160488  * door return.
256*e1dd0a2fSth160488  */
257*e1dd0a2fSth160488 int
258*e1dd0a2fSth160488 chg_get_statusChange(LineBuf *info, ldap_call_t *in, pid_t nscd_pid)
259*e1dd0a2fSth160488 {
260*e1dd0a2fSth160488 	int	rc = CHG_SUCCESS, another_main_nscd_thread_alive = 0;
261*e1dd0a2fSth160488 	int	len, return_now;
262*e1dd0a2fSth160488 	thread_t this_tid = thr_self();
263*e1dd0a2fSth160488 	waiting_list_t	*wl = NULL;
264*e1dd0a2fSth160488 	ldap_get_change_out_t *cout;
265*e1dd0a2fSth160488 	ldap_get_chg_cookie_t cookie;
266*e1dd0a2fSth160488 
267*e1dd0a2fSth160488 	info->str = NULL;
268*e1dd0a2fSth160488 	info->len = 0;
269*e1dd0a2fSth160488 
270*e1dd0a2fSth160488 	if (in->ldap_u.get_change.op == NS_STATUS_CHANGE_OP_START) {
271*e1dd0a2fSth160488 
272*e1dd0a2fSth160488 		(void) mutex_lock(&chg_main_nscd_lock);
273*e1dd0a2fSth160488 		if (chg_main_nscd.pid != 0) {
274*e1dd0a2fSth160488 			if (nscd_pid != chg_main_nscd.pid) {
275*e1dd0a2fSth160488 				/*
276*e1dd0a2fSth160488 				 * This is the case that nscd doesn't shut down
277*e1dd0a2fSth160488 				 * properly(e.g. core) and STOP op is not sent,
278*e1dd0a2fSth160488 				 * the thread handling it is still around and
279*e1dd0a2fSth160488 				 * not cleaned up yet.
280*e1dd0a2fSth160488 				 * Test if the thread is still alive.
281*e1dd0a2fSth160488 				 * If it is, clean it up.
282*e1dd0a2fSth160488 				 * For thr_kill, if sig is 0, a validity check
283*e1dd0a2fSth160488 				 * is done for the existence of the target
284*e1dd0a2fSth160488 				 * thread; no signal is sent.
285*e1dd0a2fSth160488 				 */
286*e1dd0a2fSth160488 				if (thr_kill(chg_main_nscd.tid, 0) == 0) {
287*e1dd0a2fSth160488 					another_main_nscd_thread_alive = 1;
288*e1dd0a2fSth160488 					cleanup_thread_by_pid(
289*e1dd0a2fSth160488 					    chg_main_nscd.pid);
290*e1dd0a2fSth160488 				}
291*e1dd0a2fSth160488 			} else if (chg_main_nscd.in_progress ||
292*e1dd0a2fSth160488 			    chg_main_nscd.is_waiting_cleanup) {
293*e1dd0a2fSth160488 				/*
294*e1dd0a2fSth160488 				 * Same nscd pid can only send door call
295*e1dd0a2fSth160488 				 * one at a time and wait for ldap_cachemgr to
296*e1dd0a2fSth160488 				 * return change data. If it's the same pid
297*e1dd0a2fSth160488 				 * again, it's an nscd error.
298*e1dd0a2fSth160488 				 */
299*e1dd0a2fSth160488 				(void) mutex_unlock(&chg_main_nscd_lock);
300*e1dd0a2fSth160488 				return (CHG_NSCD_REPEATED_CALL);
301*e1dd0a2fSth160488 			}
302*e1dd0a2fSth160488 		}
303*e1dd0a2fSth160488 		/*
304*e1dd0a2fSth160488 		 * Wait for another thread to be cleaned up if it's alive.
305*e1dd0a2fSth160488 		 * After that this cond var is waken up.
306*e1dd0a2fSth160488 		 */
307*e1dd0a2fSth160488 		if (another_main_nscd_thread_alive) {
308*e1dd0a2fSth160488 			while (chg_main_nscd.in_progress) {
309*e1dd0a2fSth160488 				chg_main_nscd.is_waiting_cleanup = 1;
310*e1dd0a2fSth160488 				(void) cond_wait(&chg_main_nscd_cv,
311*e1dd0a2fSth160488 				    &chg_main_nscd_lock);
312*e1dd0a2fSth160488 			}
313*e1dd0a2fSth160488 		}
314*e1dd0a2fSth160488 
315*e1dd0a2fSth160488 		/*
316*e1dd0a2fSth160488 		 * Replace pid and tid and set the flag.
317*e1dd0a2fSth160488 		 */
318*e1dd0a2fSth160488 		chg_main_nscd.is_waiting_cleanup = 0;
319*e1dd0a2fSth160488 		chg_main_nscd.pid = nscd_pid;
320*e1dd0a2fSth160488 		chg_main_nscd.tid = this_tid;
321*e1dd0a2fSth160488 		chg_main_nscd.in_progress = 1;
322*e1dd0a2fSth160488 		(void) mutex_unlock(&chg_main_nscd_lock);
323*e1dd0a2fSth160488 
324*e1dd0a2fSth160488 		cookie = chg_config_cookie_get();
325*e1dd0a2fSth160488 
326*e1dd0a2fSth160488 		if (!chg_cookie_equal(&cookie, &in->ldap_u.get_change.cookie)) {
327*e1dd0a2fSth160488 			/*
328*e1dd0a2fSth160488 			 * different cookie, set new cookie and
329*e1dd0a2fSth160488 			 * return door call right away
330*e1dd0a2fSth160488 			 */
331*e1dd0a2fSth160488 			len = sizeof (ldap_get_change_out_t);
332*e1dd0a2fSth160488 			if ((cout = calloc(1, len)) == NULL) {
333*e1dd0a2fSth160488 				rc = CHG_NO_MEMORY;
334*e1dd0a2fSth160488 			} else {
335*e1dd0a2fSth160488 				cout->type = NS_STATUS_CHANGE_TYPE_CONFIG;
336*e1dd0a2fSth160488 				cout->cookie = cookie;
337*e1dd0a2fSth160488 				info->str = (char *)cout;
338*e1dd0a2fSth160488 				info->len = len;
339*e1dd0a2fSth160488 			}
340*e1dd0a2fSth160488 
341*e1dd0a2fSth160488 		} else {
342*e1dd0a2fSth160488 			(void) mutex_lock(&chg.chg_lock);
343*e1dd0a2fSth160488 
344*e1dd0a2fSth160488 			/* wait for the change notification */
345*e1dd0a2fSth160488 			rc = waiting_list_add(&chg, nscd_pid, this_tid, &wl);
346*e1dd0a2fSth160488 			if (rc == CHG_SUCCESS) {
347*e1dd0a2fSth160488 				return_now = 0;
348*e1dd0a2fSth160488 				while (!chg.chg_wakeup) {
349*e1dd0a2fSth160488 					if (wl->cleanup ||
350*e1dd0a2fSth160488 					    door_client_dead()) {
351*e1dd0a2fSth160488 						return_now = 1;
352*e1dd0a2fSth160488 						break;
353*e1dd0a2fSth160488 					}
354*e1dd0a2fSth160488 					(void) cond_wait(&chg.chg_cv,
355*e1dd0a2fSth160488 					    &chg.chg_lock);
356*e1dd0a2fSth160488 				}
357*e1dd0a2fSth160488 				/* Check if door client is still alive again */
358*e1dd0a2fSth160488 				if (!return_now && !wl->cleanup &&
359*e1dd0a2fSth160488 				    !door_client_dead()) {
360*e1dd0a2fSth160488 					/* copy data to buffer */
361*e1dd0a2fSth160488 					if ((info->str = malloc(
362*e1dd0a2fSth160488 					    chg.chg_data_size)) == NULL) {
363*e1dd0a2fSth160488 						rc = CHG_NO_MEMORY;
364*e1dd0a2fSth160488 					} else {
365*e1dd0a2fSth160488 						(void) memcpy(info->str,
366*e1dd0a2fSth160488 						    chg.chg_data,
367*e1dd0a2fSth160488 						    chg.chg_data_size);
368*e1dd0a2fSth160488 						info->len = chg.chg_data_size;
369*e1dd0a2fSth160488 					}
370*e1dd0a2fSth160488 				}
371*e1dd0a2fSth160488 				waiting_list_cleanup(&chg, this_tid);
372*e1dd0a2fSth160488 			}
373*e1dd0a2fSth160488 			(void) mutex_unlock(&chg.chg_lock);
374*e1dd0a2fSth160488 		}
375*e1dd0a2fSth160488 
376*e1dd0a2fSth160488 
377*e1dd0a2fSth160488 		/*
378*e1dd0a2fSth160488 		 * Reset pid, tid and flag, send wakeup signal.
379*e1dd0a2fSth160488 		 */
380*e1dd0a2fSth160488 		(void) mutex_lock(&chg_main_nscd_lock);
381*e1dd0a2fSth160488 		chg_main_nscd.pid = 0;
382*e1dd0a2fSth160488 		chg_main_nscd.tid = 0;
383*e1dd0a2fSth160488 		chg_main_nscd.in_progress = 0;
384*e1dd0a2fSth160488 		if (chg_main_nscd.is_waiting_cleanup)
385*e1dd0a2fSth160488 			(void) cond_broadcast(&chg_main_nscd_cv);
386*e1dd0a2fSth160488 
387*e1dd0a2fSth160488 		(void) mutex_unlock(&chg_main_nscd_lock);
388*e1dd0a2fSth160488 
389*e1dd0a2fSth160488 	} else if (in->ldap_u.get_change.op == NS_STATUS_CHANGE_OP_STOP) {
390*e1dd0a2fSth160488 
391*e1dd0a2fSth160488 		cleanup_thread_by_pid(nscd_pid);
392*e1dd0a2fSth160488 		rc = CHG_SUCCESS;
393*e1dd0a2fSth160488 
394*e1dd0a2fSth160488 	} else {
395*e1dd0a2fSth160488 		rc = CHG_INVALID_PARAM;
396*e1dd0a2fSth160488 	}
397*e1dd0a2fSth160488 	if (rc == CHG_EXCEED_MAX_THREADS)
398*e1dd0a2fSth160488 		cleanup_thread_by_pid(0);
399*e1dd0a2fSth160488 
400*e1dd0a2fSth160488 	return (rc);
401*e1dd0a2fSth160488 }
402*e1dd0a2fSth160488 
403*e1dd0a2fSth160488 /*
404*e1dd0a2fSth160488  * This function copies the header and data stream to the buffer
405*e1dd0a2fSth160488  * then send broadcast to wake up the chg_get_statusChange() threads.
406*e1dd0a2fSth160488  */
407*e1dd0a2fSth160488 int
408*e1dd0a2fSth160488 chg_notify_statusChange(char *str)
409*e1dd0a2fSth160488 {
410*e1dd0a2fSth160488 	ldap_get_change_out_t *cout = (ldap_get_change_out_t *)str;
411*e1dd0a2fSth160488 
412*e1dd0a2fSth160488 	cout->cookie = chg_config_cookie_get();
413*e1dd0a2fSth160488 
414*e1dd0a2fSth160488 	(void) mutex_lock(&chg.chg_lock);
415*e1dd0a2fSth160488 	if (chg.chg_w_first != NULL && chg.chg_wakeup == 0) {
416*e1dd0a2fSth160488 
417*e1dd0a2fSth160488 		if (chg.chg_data) {
418*e1dd0a2fSth160488 			free(chg.chg_data);
419*e1dd0a2fSth160488 			chg.chg_data = NULL;
420*e1dd0a2fSth160488 		}
421*e1dd0a2fSth160488 
422*e1dd0a2fSth160488 		chg.chg_data = str;
423*e1dd0a2fSth160488 
424*e1dd0a2fSth160488 		if (cout->type == NS_STATUS_CHANGE_TYPE_CONFIG)
425*e1dd0a2fSth160488 			chg.chg_data_size = sizeof (ldap_get_change_out_t);
426*e1dd0a2fSth160488 		else
427*e1dd0a2fSth160488 			/* NS_STATUS_CHANGE_TYPE_SERVER */
428*e1dd0a2fSth160488 			chg.chg_data_size = sizeof (ldap_get_change_out_t) -
429*e1dd0a2fSth160488 			    sizeof (int) + cout->data_size;
430*e1dd0a2fSth160488 
431*e1dd0a2fSth160488 		chg.chg_wakeup = 1;
432*e1dd0a2fSth160488 		(void) cond_broadcast(&chg.chg_cv);
433*e1dd0a2fSth160488 	}
434*e1dd0a2fSth160488 	(void) mutex_unlock(&chg.chg_lock);
435*e1dd0a2fSth160488 
436*e1dd0a2fSth160488 	return (CHG_SUCCESS);
437*e1dd0a2fSth160488 }
438*e1dd0a2fSth160488 
439*e1dd0a2fSth160488 /*
440*e1dd0a2fSth160488  * This is called when the configuration is refreshed.
441*e1dd0a2fSth160488  * The new configuration is different from the current one, a notification
442*e1dd0a2fSth160488  * is sent tochg_get_statusChange() threads.
443*e1dd0a2fSth160488  */
444*e1dd0a2fSth160488 void
445*e1dd0a2fSth160488 chg_test_config_change(ns_config_t *new, int *change_status)
446*e1dd0a2fSth160488 {
447*e1dd0a2fSth160488 	int	changed = 0;
448*e1dd0a2fSth160488 	LineBuf	new_cfg, cur_cfg;
449*e1dd0a2fSth160488 	ns_ldap_error_t *errp = NULL;
450*e1dd0a2fSth160488 	ldap_config_out_t *new_out, *cur_out;
451*e1dd0a2fSth160488 	ldap_get_change_out_t	*cout;
452*e1dd0a2fSth160488 
453*e1dd0a2fSth160488 	(void) memset(&new_cfg, 0, sizeof (LineBuf));
454*e1dd0a2fSth160488 	(void) memset(&cur_cfg, 0, sizeof (LineBuf));
455*e1dd0a2fSth160488 	/*
456*e1dd0a2fSth160488 	 * Flatten the config data of the newly downloaded config and
457*e1dd0a2fSth160488 	 * current default config and compare both.
458*e1dd0a2fSth160488 	 */
459*e1dd0a2fSth160488 	if ((errp = __ns_ldap_LoadDoorInfo(&new_cfg, NULL, new)) != NULL) {
460*e1dd0a2fSth160488 		__ns_ldap_freeError(&errp);
461*e1dd0a2fSth160488 		/* error, assume the config is changed */
462*e1dd0a2fSth160488 		changed = 1;
463*e1dd0a2fSth160488 	} else if ((errp = __ns_ldap_LoadDoorInfo(&cur_cfg, NULL, NULL))
464*e1dd0a2fSth160488 	    != NULL) {
465*e1dd0a2fSth160488 		__ns_ldap_freeError(&errp);
466*e1dd0a2fSth160488 		/* error, assume the config is changed */
467*e1dd0a2fSth160488 		changed = 1;
468*e1dd0a2fSth160488 	}
469*e1dd0a2fSth160488 	if (changed == 0) {
470*e1dd0a2fSth160488 		new_out = (ldap_config_out_t *)new_cfg.str;
471*e1dd0a2fSth160488 		cur_out = (ldap_config_out_t *)cur_cfg.str;
472*e1dd0a2fSth160488 		if (strcmp(new_out->config_str, cur_out->config_str) != 0) {
473*e1dd0a2fSth160488 			changed = 1;
474*e1dd0a2fSth160488 			if (current_admin.debug_level >= DBG_PROFILE_REFRESH) {
475*e1dd0a2fSth160488 				logit("config changed.\n");
476*e1dd0a2fSth160488 			}
477*e1dd0a2fSth160488 		}
478*e1dd0a2fSth160488 	}
479*e1dd0a2fSth160488 	if (cur_cfg.str)
480*e1dd0a2fSth160488 		free(cur_cfg.str);
481*e1dd0a2fSth160488 	if (new_cfg.str)
482*e1dd0a2fSth160488 		free(new_cfg.str);
483*e1dd0a2fSth160488 
484*e1dd0a2fSth160488 	if (changed) {
485*e1dd0a2fSth160488 
486*e1dd0a2fSth160488 		if ((cout = calloc(1, sizeof (ldap_get_change_out_t)))
487*e1dd0a2fSth160488 		    == NULL) {
488*e1dd0a2fSth160488 			logit("chg_test_config_change: No Memory\n");
489*e1dd0a2fSth160488 		} else {
490*e1dd0a2fSth160488 			/*
491*e1dd0a2fSth160488 			 * Replace the currentdefault config with the new
492*e1dd0a2fSth160488 			 * config
493*e1dd0a2fSth160488 			 */
494*e1dd0a2fSth160488 			__s_api_init_config(new);
495*e1dd0a2fSth160488 			chg_config_cookie_increment_seq_num();
496*e1dd0a2fSth160488 			cout->type = NS_STATUS_CHANGE_TYPE_CONFIG;
497*e1dd0a2fSth160488 			/*
498*e1dd0a2fSth160488 			 * cout->cookie is set by
499*e1dd0a2fSth160488 			 * chg_notify_statusChange
500*e1dd0a2fSth160488 			 */
501*e1dd0a2fSth160488 			(void) chg_notify_statusChange((char *)cout);
502*e1dd0a2fSth160488 		}
503*e1dd0a2fSth160488 	} else {
504*e1dd0a2fSth160488 		__s_api_destroy_config(new);
505*e1dd0a2fSth160488 	}
506*e1dd0a2fSth160488 
507*e1dd0a2fSth160488 	*change_status = changed;
508*e1dd0a2fSth160488 }
509*e1dd0a2fSth160488 
510*e1dd0a2fSth160488 /*
511*e1dd0a2fSth160488  * Wake up chg_get_statusChange() threads to clean up the threads
512*e1dd0a2fSth160488  * that main nscd doesn't exist on the other of door anymore or
513*e1dd0a2fSth160488  * the thread is marked as cleanup.
514*e1dd0a2fSth160488  */
515*e1dd0a2fSth160488 static void
516*e1dd0a2fSth160488 cleanup_threads(chg_info_t *chg, pid_t pid, cleanup_type_t type)
517*e1dd0a2fSth160488 {
518*e1dd0a2fSth160488 	(void) mutex_lock(&chg->chg_lock);
519*e1dd0a2fSth160488 	if (type == CLEANUP_BY_PID)
520*e1dd0a2fSth160488 		waiting_list_set_cleanup(chg, pid);
521*e1dd0a2fSth160488 	/*
522*e1dd0a2fSth160488 	 * wake up threads without setting chg.chg_wakeup.
523*e1dd0a2fSth160488 	 * It's for cleanup purpose, not for notifying changes.
524*e1dd0a2fSth160488 	 */
525*e1dd0a2fSth160488 	(void) cond_broadcast(&chg->chg_cv);
526*e1dd0a2fSth160488 	(void) mutex_unlock(&chg->chg_lock);
527*e1dd0a2fSth160488 }
528*e1dd0a2fSth160488 /*
529*e1dd0a2fSth160488  * If arg is NULL, it loops forever,
530*e1dd0a2fSth160488  * else it calls cleanup_threads once and exits.
531*e1dd0a2fSth160488  */
532*e1dd0a2fSth160488 void *
533*e1dd0a2fSth160488 chg_cleanup_waiting_threads(void *arg)
534*e1dd0a2fSth160488 {
535*e1dd0a2fSth160488 	cleanup_op_t *op = (cleanup_op_t *)arg;
536*e1dd0a2fSth160488 	cleanup_type_t type = 0;
537*e1dd0a2fSth160488 	pid_t	pid;
538*e1dd0a2fSth160488 	int	always = 1, waiting;
539*e1dd0a2fSth160488 
540*e1dd0a2fSth160488 	if (op == NULL) {
541*e1dd0a2fSth160488 		waiting = 1;
542*e1dd0a2fSth160488 		type = CLEANUP_ALL;
543*e1dd0a2fSth160488 		pid = 0;
544*e1dd0a2fSth160488 	} else {
545*e1dd0a2fSth160488 		waiting = 0;
546*e1dd0a2fSth160488 		type = op->type;
547*e1dd0a2fSth160488 		pid = op->pid;
548*e1dd0a2fSth160488 	}
549*e1dd0a2fSth160488 
550*e1dd0a2fSth160488 	while (always) {
551*e1dd0a2fSth160488 		if (waiting)
552*e1dd0a2fSth160488 			(void) sleep(CLEANUP_WAIT_TIME);
553*e1dd0a2fSth160488 		cleanup_threads(&chg, pid, type);
554*e1dd0a2fSth160488 		if (!waiting)
555*e1dd0a2fSth160488 			break;
556*e1dd0a2fSth160488 	}
557*e1dd0a2fSth160488 
558*e1dd0a2fSth160488 	if (op)
559*e1dd0a2fSth160488 		free(op);
560*e1dd0a2fSth160488 
561*e1dd0a2fSth160488 	thr_exit(NULL);
562*e1dd0a2fSth160488 	return (NULL);
563*e1dd0a2fSth160488 }
564*e1dd0a2fSth160488 /*
565*e1dd0a2fSth160488  * The door server thead which has the door client pid will be marked
566*e1dd0a2fSth160488  * as to be clean up. If pid is 0, no marking and just clean up all.
567*e1dd0a2fSth160488  */
568*e1dd0a2fSth160488 static void
569*e1dd0a2fSth160488 cleanup_thread_by_pid(pid_t pid)
570*e1dd0a2fSth160488 {
571*e1dd0a2fSth160488 	cleanup_op_t *op;
572*e1dd0a2fSth160488 
573*e1dd0a2fSth160488 	if ((op = malloc(sizeof (cleanup_op_t))) == NULL)
574*e1dd0a2fSth160488 		return;
575*e1dd0a2fSth160488 
576*e1dd0a2fSth160488 	op->pid = pid;
577*e1dd0a2fSth160488 	/* clean up all if pid is 0 */
578*e1dd0a2fSth160488 	if (pid == 0)
579*e1dd0a2fSth160488 		op->type = CLEANUP_ALL;
580*e1dd0a2fSth160488 	else
581*e1dd0a2fSth160488 		op->type = CLEANUP_BY_PID;
582*e1dd0a2fSth160488 
583*e1dd0a2fSth160488 	if (thr_create(NULL, 0, chg_cleanup_waiting_threads,
584*e1dd0a2fSth160488 	    (void *)op, THR_BOUND|THR_DETACHED, NULL) != 0) {
585*e1dd0a2fSth160488 		free(op);
586*e1dd0a2fSth160488 		logit("thr_create failed for cleanup_thread_by_pid(%ld)\n",
587*e1dd0a2fSth160488 		    pid);
588*e1dd0a2fSth160488 	}
589*e1dd0a2fSth160488 }
590*e1dd0a2fSth160488 
591*e1dd0a2fSth160488 /*
592*e1dd0a2fSth160488  * Output a psinfo of an nscd process with process id pid
593*e1dd0a2fSth160488  * Return: 0  - Can't find the process or it's not nscd
594*e1dd0a2fSth160488  *         1  - psinfo found
595*e1dd0a2fSth160488  * Note: If info is NULL, returns 0 or 1 only and no output from info.
596*e1dd0a2fSth160488  */
597*e1dd0a2fSth160488 static int
598*e1dd0a2fSth160488 get_nscd_psinfo(pid_t pid, psinfo_t *info)
599*e1dd0a2fSth160488 {
600*e1dd0a2fSth160488 	psinfo_t	pinfo;
601*e1dd0a2fSth160488 	char		fname[MAXPATHLEN];
602*e1dd0a2fSth160488 	ssize_t		ret;
603*e1dd0a2fSth160488 	int		fd;
604*e1dd0a2fSth160488 
605*e1dd0a2fSth160488 	if (snprintf(fname, MAXPATHLEN, "/proc/%d/psinfo", pid) > 0) {
606*e1dd0a2fSth160488 		if ((fd = open(fname,  O_RDONLY)) >= 0) {
607*e1dd0a2fSth160488 			ret = read(fd, &pinfo, sizeof (psinfo_t));
608*e1dd0a2fSth160488 			(void) close(fd);
609*e1dd0a2fSth160488 			if ((ret == sizeof (psinfo_t)) &&
610*e1dd0a2fSth160488 			    (strcmp(pinfo.pr_fname, "nscd") == 0)) {
611*e1dd0a2fSth160488 				if (info)
612*e1dd0a2fSth160488 					*info = pinfo;
613*e1dd0a2fSth160488 				return (1);
614*e1dd0a2fSth160488 			}
615*e1dd0a2fSth160488 		}
616*e1dd0a2fSth160488 	}
617*e1dd0a2fSth160488 	return (0);
618*e1dd0a2fSth160488 }
619*e1dd0a2fSth160488 /*
620*e1dd0a2fSth160488  * If the parent process is nscd and euid is 0, it's a peruser nscd.
621*e1dd0a2fSth160488  */
622*e1dd0a2fSth160488 static int
623*e1dd0a2fSth160488 is_peruser_nscd(pid_t pid)
624*e1dd0a2fSth160488 {
625*e1dd0a2fSth160488 	pid_t	ppid;
626*e1dd0a2fSth160488 	psinfo_t pinfo;
627*e1dd0a2fSth160488 
628*e1dd0a2fSth160488 	if (get_nscd_psinfo(pid, &pinfo)) {
629*e1dd0a2fSth160488 		ppid = pinfo.pr_ppid;
630*e1dd0a2fSth160488 		if (get_nscd_psinfo(ppid, &pinfo) && pinfo.pr_euid == 0)
631*e1dd0a2fSth160488 			/*
632*e1dd0a2fSth160488 			 * get psinfo of parent forker nscd
633*e1dd0a2fSth160488 			 */
634*e1dd0a2fSth160488 			return (1);
635*e1dd0a2fSth160488 		else
636*e1dd0a2fSth160488 			return (0);
637*e1dd0a2fSth160488 	} else {
638*e1dd0a2fSth160488 		return (0);
639*e1dd0a2fSth160488 	}
640*e1dd0a2fSth160488 }
641*e1dd0a2fSth160488 /*
642*e1dd0a2fSth160488  * Check if the door client making door call is a nscd or peruser nscd and
643*e1dd0a2fSth160488  * output door client's pid.
644*e1dd0a2fSth160488  */
645*e1dd0a2fSth160488 int
646*e1dd0a2fSth160488 chg_is_called_from_nscd_or_peruser_nscd(char *dc_str, pid_t *pidp)
647*e1dd0a2fSth160488 {
648*e1dd0a2fSth160488 	int	rc;
649*e1dd0a2fSth160488 	uid_t	euid;
650*e1dd0a2fSth160488 	pid_t	pid;
651*e1dd0a2fSth160488 	ucred_t	*uc = NULL;
652*e1dd0a2fSth160488 
653*e1dd0a2fSth160488 	if (door_ucred(&uc) != 0) {
654*e1dd0a2fSth160488 		rc = errno;
655*e1dd0a2fSth160488 		logit("door_ucred() call failed %s\n", strerror(rc));
656*e1dd0a2fSth160488 		return (0);
657*e1dd0a2fSth160488 	}
658*e1dd0a2fSth160488 	euid = ucred_geteuid(uc);
659*e1dd0a2fSth160488 	pid = *pidp = ucred_getpid(uc);
660*e1dd0a2fSth160488 
661*e1dd0a2fSth160488 	if ((euid == 0 && is_called_from_nscd(pid)) ||
662*e1dd0a2fSth160488 	    is_peruser_nscd(pid)) {
663*e1dd0a2fSth160488 		if (current_admin.debug_level >= DBG_ALL)
664*e1dd0a2fSth160488 			logit("ldap_cachemgr received %s call from pid %ld, "
665*e1dd0a2fSth160488 			    "uid %u, euid %u\n", dc_str, pid,
666*e1dd0a2fSth160488 			    ucred_getruid(uc), euid);
667*e1dd0a2fSth160488 		rc = 1;
668*e1dd0a2fSth160488 	} else {
669*e1dd0a2fSth160488 		if (current_admin.debug_level >= DBG_CANT_FIND)
670*e1dd0a2fSth160488 			logit("%s call failed(cred): caller pid %ld, uid %u, "
671*e1dd0a2fSth160488 			    "euid %u\n", dc_str, pid,
672*e1dd0a2fSth160488 			    ucred_getruid(uc), euid);
673*e1dd0a2fSth160488 
674*e1dd0a2fSth160488 		rc = 0;
675*e1dd0a2fSth160488 	}
676*e1dd0a2fSth160488 
677*e1dd0a2fSth160488 	ucred_free(uc);
678*e1dd0a2fSth160488 
679*e1dd0a2fSth160488 	return (rc);
680*e1dd0a2fSth160488 }
681