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