nscd_getentctx.c revision 2830:5228d1267a01
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 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28#include <stdlib.h>
29#include <assert.h>
30#include <string.h>
31#include <errno.h>
32#include <fcntl.h>
33#include "nscd_db.h"
34#include "nscd_log.h"
35#include "nscd_switch.h"
36#include "nscd_door.h"
37
38extern int		_whoami;
39static mutex_t		getent_monitor_mutex = DEFAULTMUTEX;
40static int		getent_monitor_started = 0;
41
42static rwlock_t		getent_ctxDB_rwlock = DEFAULTRWLOCK;
43static nscd_db_t	*getent_ctxDB = NULL;
44
45/*
46 * internal structure representing a nscd getent context
47 */
48typedef struct nscd_getent_ctx {
49	int			to_delete; /* this ctx no longer valid */
50	nscd_getent_context_t	*ptr;
51	nscd_cookie_t		cookie;
52} nscd_getent_ctx_t;
53
54/*
55 * nscd_getent_context_t list for each nss database. Protected
56 * by the readers/writer lock nscd_getent_ctx_lock.
57 */
58nscd_getent_ctx_base_t **nscd_getent_ctx_base;
59static rwlock_t nscd_getent_ctx_base_lock = DEFAULTRWLOCK;
60
61extern nscd_db_entry_t *_nscd_walk_db(nscd_db_t *db, void **cookie);
62
63static nscd_rc_t _nscd_init_getent_ctx_monitor();
64
65/*
66 * FUNCTION: _nscd_create_getent_ctxDB
67 *
68 * Create the internal getent context database to keep track of the
69 * getent contexts currently being used.
70 */
71nscd_db_t *
72_nscd_create_getent_ctxDB()
73{
74
75	nscd_db_t	*ret;
76
77	(void) rw_wrlock(&getent_ctxDB_rwlock);
78
79	if (getent_ctxDB != NULL) {
80		(void) rw_unlock(&getent_ctxDB_rwlock);
81		return (getent_ctxDB);
82	}
83
84	ret = _nscd_alloc_db(NSCD_DB_SIZE_LARGE);
85
86	if (ret != NULL)
87		getent_ctxDB = ret;
88
89	(void) rw_unlock(&getent_ctxDB_rwlock);
90
91	return (ret);
92}
93
94/*
95 * FUNCTION: _nscd_add_getent_ctx
96 *
97 * Add a getent context to the internal context database.
98 */
99static nscd_rc_t
100_nscd_add_getent_ctx(
101	nscd_getent_context_t	*ptr,
102	nscd_cookie_t		cookie)
103{
104	int			size;
105	char			buf[2 * sizeof (cookie) + 1];
106	nscd_db_entry_t		*db_entry;
107	nscd_getent_ctx_t	*gnctx;
108
109	if (ptr == NULL)
110		return (NSCD_INVALID_ARGUMENT);
111
112	(void) snprintf(buf, sizeof (buf), "%lld", cookie);
113
114	size = sizeof (*gnctx);
115
116	db_entry = _nscd_alloc_db_entry(NSCD_DATA_CTX_ADDR,
117			(const char *)buf, size, 1, 1);
118	if (db_entry == NULL)
119		return (NSCD_NO_MEMORY);
120
121	gnctx = (nscd_getent_ctx_t *)*(db_entry->data_array);
122	gnctx->ptr = ptr;
123	gnctx->cookie = cookie;
124
125	(void) rw_wrlock(&getent_ctxDB_rwlock);
126	(void) _nscd_add_db_entry(getent_ctxDB, buf, db_entry,
127		NSCD_ADD_DB_ENTRY_FIRST);
128	(void) rw_unlock(&getent_ctxDB_rwlock);
129
130	return (NSCD_SUCCESS);
131}
132
133/*
134 * FUNCTION: _nscd_is_getent_ctx
135 *
136 * Check to see if a getent context can be found in the internal
137 * getent context database.
138 */
139nscd_getent_context_t *
140_nscd_is_getent_ctx(
141	nscd_cookie_t		cookie)
142{
143	char			ptrstr[1 + 2 * sizeof (cookie)];
144	const nscd_db_entry_t	*db_entry;
145	nscd_getent_context_t	*ret = NULL;
146
147	(void) snprintf(ptrstr, sizeof (ptrstr), "%lld", cookie);
148
149	(void) rw_rdlock(&getent_ctxDB_rwlock);
150
151	db_entry = _nscd_get_db_entry(getent_ctxDB, NSCD_DATA_CTX_ADDR,
152		(const char *)ptrstr, NSCD_GET_FIRST_DB_ENTRY, 0);
153
154	if (db_entry != NULL) {
155		nscd_getent_ctx_t *gnctx;
156
157		gnctx = (nscd_getent_ctx_t *)*(db_entry->data_array);
158
159		/*
160		 * If the ctx is not to be deleted and
161		 * the cookie numbers match, return the ctx.
162		 * Otherwise return NULL.
163		 */
164		if (gnctx->to_delete == 0 && gnctx->cookie == cookie)
165			ret = gnctx->ptr;
166	}
167
168	(void) rw_unlock(&getent_ctxDB_rwlock);
169
170	return (ret);
171}
172
173/*
174 * FUNCTION: _nscd_del_getent_ctx
175 *
176 * Delete a getent context from the internal getent context database.
177 */
178static void
179_nscd_del_getent_ctx(
180	nscd_getent_context_t	*ptr,
181	nscd_cookie_t		cookie)
182{
183	char			ptrstr[1 + 2 * sizeof (cookie)];
184	nscd_getent_ctx_t	*gnctx;
185	const nscd_db_entry_t	*db_entry;
186
187	if (ptr == NULL)
188		return;
189
190	(void) snprintf(ptrstr, sizeof (ptrstr), "%lld", cookie);
191
192	(void) rw_rdlock(&getent_ctxDB_rwlock);
193	/*
194	 * first find the db entry and make sure the
195	 * sequence number matched, then delete it from
196	 * the database.
197	 */
198	db_entry = _nscd_get_db_entry(getent_ctxDB,
199		NSCD_DATA_CTX_ADDR,
200		(const char *)ptrstr,
201		NSCD_GET_FIRST_DB_ENTRY, 0);
202	if (db_entry != NULL) {
203		gnctx = (nscd_getent_ctx_t *)*(db_entry->data_array);
204		if (gnctx->ptr == ptr && gnctx->cookie  == cookie) {
205
206			(void) rw_unlock(&getent_ctxDB_rwlock);
207			(void) rw_wrlock(&getent_ctxDB_rwlock);
208
209			(void) _nscd_delete_db_entry(getent_ctxDB,
210				NSCD_DATA_CTX_ADDR,
211				(const char *)ptrstr,
212				NSCD_DEL_FIRST_DB_ENTRY, 0);
213		}
214	}
215	(void) rw_unlock(&getent_ctxDB_rwlock);
216}
217
218static void
219_nscd_free_getent_ctx(
220	nscd_getent_context_t	*gnctx)
221{
222
223	char			*me = "_nscd_free_getent_ctx";
224
225	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
226	(me, "getent context %p\n", gnctx);
227
228	_nscd_put_nsw_state(gnctx->nsw_state);
229	_nscd_del_getent_ctx(gnctx, gnctx->cookie);
230	free(gnctx);
231}
232
233
234static void
235_nscd_free_getent_ctx_base(
236	nscd_acc_data_t		*data)
237{
238	nscd_getent_ctx_base_t	*base = (nscd_getent_ctx_base_t *)data;
239	nscd_getent_context_t	*c, *tc;
240	char			*me = "_nscd_free_getent_ctx_base";
241
242	_NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
243	(me, "getent context base %p\n", base);
244
245	if (base == NULL)
246		return;
247
248	c = base->first;
249	while (c != NULL) {
250		tc = c->next;
251		_nscd_free_getent_ctx(c);
252		c = tc;
253	}
254}
255
256void
257_nscd_free_all_getent_ctx_base()
258{
259	nscd_getent_ctx_base_t	*base;
260	int			i;
261	char			*me = "_nscd_free_all_getent_ctx_base";
262
263	_NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
264	(me, "entering ..\n");
265
266	(void) rw_wrlock(&nscd_getent_ctx_base_lock);
267
268	for (i = 0; i < NSCD_NUM_DB; i++) {
269
270		base = nscd_getent_ctx_base[i];
271		if (base == NULL)
272			continue;
273
274		nscd_getent_ctx_base[i] = (nscd_getent_ctx_base_t *)
275			_nscd_set((nscd_acc_data_t *)base, NULL);
276	}
277	(void) rw_unlock(&nscd_getent_ctx_base_lock);
278}
279
280static nscd_getent_context_t *
281_nscd_create_getent_ctx(
282	nscd_nsw_params_t	*params)
283{
284	nscd_getent_context_t	*gnctx;
285	nss_db_root_t		db_root;
286	char			*me = "_nscd_create_getent_ctx";
287
288	gnctx = calloc(1, sizeof (nscd_getent_context_t));
289	if (gnctx == NULL)
290		return (NULL);
291	else {
292		_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
293		(me, "getent context allocated %p\n", gnctx);
294	}
295
296	gnctx->dbi = params->dbi;
297	gnctx->cookie = _nscd_get_cookie();
298
299	if (_nscd_get_nsw_state(&db_root, params) != NSCD_SUCCESS) {
300		free(gnctx);
301		return (NULL);
302	}
303	gnctx->nsw_state = (nscd_nsw_state_t *)db_root.s;
304	/* this is a nsw_state used for getent processing */
305	gnctx->nsw_state->getent = 1;
306
307	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
308	(me, "got nsw_state %p\n", gnctx->nsw_state);
309
310	return (gnctx);
311}
312
313
314nscd_rc_t
315_nscd_get_getent_ctx(
316	nss_getent_t		*contextpp,
317	nscd_nsw_params_t	*params)
318{
319
320	nscd_getent_context_t	*c;
321	nscd_getent_ctx_base_t	*base, *tmp;
322	nscd_rc_t		rc;
323	char			*me = "_nscd_get_getent_ctx";
324
325	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
326	(me, "entering ...\n");
327
328	(void) rw_rdlock(&nscd_getent_ctx_base_lock);
329	base = nscd_getent_ctx_base[params->dbi];
330	(void) rw_unlock(&nscd_getent_ctx_base_lock);
331	assert(base != NULL);
332
333	/*
334	 * If the context list is not empty, return the first one
335	 * on the list. Otherwise, create and return a new one if
336	 * limit is not reached. if reacehed, wait for the 'one is
337	 * available' signal.
338	 */
339	tmp = (nscd_getent_ctx_base_t *)_nscd_mutex_lock(
340		(nscd_acc_data_t *)base);
341	assert(base == tmp);
342	if (base->first == NULL) {
343		if (base->num_getent_ctx == base->max_getent_ctx) {
344			base->num_waiter++;
345			while (base->first == NULL) {
346
347				_NSCD_LOG(NSCD_LOG_GETENT_CTX,
348					NSCD_LOG_LEVEL_DEBUG)
349				(me, "waiting for signal\n");
350
351				_nscd_cond_wait((nscd_acc_data_t *)base, NULL);
352
353				_NSCD_LOG(NSCD_LOG_GETENT_CTX,
354					NSCD_LOG_LEVEL_DEBUG)
355				(me, "woke up\n");
356			}
357			base->num_waiter--;
358		} else {
359			base->first = _nscd_create_getent_ctx(params);
360			if (base->first != NULL) {
361				base->first->base = base;
362				base->num_getent_ctx++;
363			} else {
364				/* not able to create an getent ctx */
365
366				_NSCD_LOG(NSCD_LOG_GETENT_CTX,
367					NSCD_LOG_LEVEL_ERROR)
368				(me, "create getent ctx failed\n");
369
370				_nscd_mutex_unlock((nscd_acc_data_t *)base);
371				return (NSCD_CREATE_GETENT_CTX_FAILED);
372			}
373
374			_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
375			(me, "got a new getent ctx %p\n", base->first);
376		}
377	}
378
379	assert(base->first != NULL);
380
381	c = base->first;
382	base->first = c->next;
383	c->next = NULL;
384	c->seq_num = 1;
385
386	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
387	(me, "got a getent ctx %p\n", c);
388
389	_nscd_mutex_unlock((nscd_acc_data_t *)base);
390
391	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
392	(me, "adding new ctx %p, cookie = %lld\n", c, c->cookie);
393
394	if ((rc = _nscd_add_getent_ctx(c, c->cookie)) != NSCD_SUCCESS) {
395		_nscd_put_getent_ctx(c);
396		return (rc);
397	}
398	contextpp->ctx = (struct nss_getent_context *)c;
399
400	/* start monitor and reclaim orphan getent context */
401	if (getent_monitor_started == 0) {
402		(void) mutex_lock(&getent_monitor_mutex);
403		if (getent_monitor_started == 0) {
404			getent_monitor_started = 1;
405			(void) _nscd_init_getent_ctx_monitor();
406		}
407		(void) mutex_unlock(&getent_monitor_mutex);
408	}
409
410	return (NSCD_SUCCESS);
411}
412
413void
414_nscd_put_getent_ctx(
415	nscd_getent_context_t	*gnctx)
416{
417
418	nscd_getent_ctx_base_t	*base;
419	char			*me = "_nscd_put_getent_ctx";
420
421	base = gnctx->base;
422	gnctx->seq_num = 0;
423
424	/* if context base is gone, so should this context */
425	if ((_nscd_mutex_lock((nscd_acc_data_t *)base)) == NULL) {
426		_nscd_free_getent_ctx(gnctx);
427		return;
428	}
429
430	if (base->first != NULL) {
431		gnctx->next = base->first;
432		base->first = gnctx;
433	} else
434		base->first = gnctx;
435
436	/* put back the db state */
437	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
438	(me, "putting back nsw state %p\n", gnctx->nsw_state);
439
440	/* this nsw_state is no longer used for getent processing */
441	if (gnctx->nsw_state != NULL)
442		gnctx->nsw_state->getent = 0;
443	_nscd_put_nsw_state(gnctx->nsw_state);
444	gnctx->nsw_state = NULL;
445
446	_nscd_del_getent_ctx(gnctx, gnctx->cookie);
447
448	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
449	(me, "ctx (%p seq# = %lld) removed from getent ctx DB\n",
450		gnctx, gnctx->cookie);
451
452	if (base->num_waiter > 0) {
453		_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
454		(me, "signaling (waiter = %d)\n", base->num_waiter);
455
456		_nscd_cond_signal((nscd_acc_data_t *)base);
457	}
458
459	_nscd_mutex_unlock((nscd_acc_data_t *)base);
460}
461
462nscd_rc_t
463_nscd_init_getent_ctx_base(
464	int			dbi,
465	int			lock)
466{
467	nscd_getent_ctx_base_t	*base = NULL;
468	char			*me = "_nscd_init_getent_ctx_base";
469
470	if (lock)
471		(void) rw_rdlock(&nscd_getent_ctx_base_lock);
472
473	base = (nscd_getent_ctx_base_t *)_nscd_alloc(
474		NSCD_DATA_GETENT_CTX_BASE,
475		sizeof (nscd_getent_ctx_base_t),
476		_nscd_free_getent_ctx_base,
477		NSCD_ALLOC_MUTEX | NSCD_ALLOC_COND);
478
479	if (base == NULL) {
480		if (lock)
481			(void) rw_unlock(&nscd_getent_ctx_base_lock);
482		return (NSCD_NO_MEMORY);
483	}
484	_NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
485	(me, "base %p allocated\n", base);
486
487	/*
488	 * initialize and activate the new getent_ctx base
489	 */
490	base->dbi = dbi;
491	base->max_getent_ctx = NSCD_SW_CFG(dbi).max_getent_ctx_per_db;
492	nscd_getent_ctx_base[dbi] =
493		(nscd_getent_ctx_base_t *)_nscd_set(
494		(nscd_acc_data_t *)nscd_getent_ctx_base[dbi],
495		(nscd_acc_data_t *)base);
496
497	if (lock)
498		(void) rw_unlock(&nscd_getent_ctx_base_lock);
499
500	return (NSCD_SUCCESS);
501}
502
503nscd_rc_t
504_nscd_init_all_getent_ctx_base()
505{
506	int			i;
507	nscd_rc_t		rc;
508	char			*me = "_nscd_init_all_getent_ctx_base";
509
510	(void) rw_wrlock(&nscd_getent_ctx_base_lock);
511
512	for (i = 0; i < NSCD_NUM_DB; i++) {
513
514		rc = _nscd_init_getent_ctx_base(i, 0);
515
516		if (rc != NSCD_SUCCESS) {
517			(void) rw_unlock(&nscd_getent_ctx_base_lock);
518			return (rc);
519		}
520	}
521
522	_NSCD_LOG(NSCD_LOG_GETENT_CTX | NSCD_LOG_CONFIG, NSCD_LOG_LEVEL_DEBUG)
523	(me, "all getent context base initialized\n");
524
525	(void) rw_unlock(&nscd_getent_ctx_base_lock);
526
527	return (NSCD_SUCCESS);
528}
529nscd_rc_t
530_nscd_alloc_getent_ctx_base()
531{
532
533	(void) rw_wrlock(&nscd_getent_ctx_base_lock);
534
535	nscd_getent_ctx_base = calloc(NSCD_NUM_DB,
536			sizeof (nscd_getent_ctx_base_t *));
537	if (nscd_getent_ctx_base == NULL) {
538		(void) rw_unlock(&nscd_getent_ctx_base_lock);
539		return (NSCD_NO_MEMORY);
540	}
541
542	(void) rw_unlock(&nscd_getent_ctx_base_lock);
543
544	return (NSCD_SUCCESS);
545}
546
547static int
548process_exited(pid_t pid)
549{
550	char	pname[PATH_MAX];
551	int	fd;
552
553	(void) snprintf(pname, sizeof (pname), "/proc/%d/psinfo", pid);
554	if ((fd = open(pname, O_RDONLY)) == -1)
555		return (1);
556	else {
557		(void) close(fd);
558		return (0);
559	}
560}
561
562/*
563 * FUNCTION: reclaim_getent_ctx
564 */
565/*ARGSUSED*/
566static void *
567reclaim_getent_ctx(void *arg)
568{
569	void			*cookie = NULL;
570	nscd_db_entry_t		*ep;
571	nscd_getent_ctx_t	*ctx;
572	nscd_getent_context_t	*gctx, *c;
573	nscd_getent_context_t	*first = NULL, *last = NULL;
574	char			*me = "reclaim_getent_ctx";
575
576	/*CONSTCOND*/
577	while (1) {
578
579		(void) rw_rdlock(&getent_ctxDB_rwlock);
580
581		for (ep = _nscd_walk_db(getent_ctxDB, &cookie); ep != NULL;
582				ep = _nscd_walk_db(getent_ctxDB, &cookie)) {
583
584			ctx = (nscd_getent_ctx_t *)*(ep->data_array);
585
586			gctx = ctx->ptr;
587
588			/*
589			 * if the client process, which did the setent,
590			 * exited, add the context to the orphan list
591			 */
592			if (gctx->pid != -1 && process_exited(gctx->pid)) {
593
594				_NSCD_LOG(NSCD_LOG_GETENT_CTX,
595					NSCD_LOG_LEVEL_DEBUG)
596				(me, "process  %d exited, "
597				"getent context = %p, "
598				"db index = %d, cookie = %lld, "
599				"sequence # = %lld\n",
600				gctx->pid, gctx, gctx->dbi,
601				gctx->cookie, gctx->seq_num);
602
603				if (first != NULL) {
604					last->next = gctx;
605					last = gctx;
606				} else {
607					first = gctx;
608					last = gctx;
609				}
610			}
611		}
612
613		(void) rw_unlock(&getent_ctxDB_rwlock);
614
615
616		/*
617		 * return all the orphan getent contexts to the pool
618		 */
619		for (gctx = first; gctx; ) {
620			c = gctx->next;
621			gctx->next = NULL;
622			_nscd_put_getent_ctx(gctx);
623			gctx = c;
624		}
625		first = last = NULL;
626
627		(void) sleep(60);
628	}
629	/*NOTREACHED*/
630	/*LINTED E_FUNC_HAS_NO_RETURN_STMT*/
631}
632
633static nscd_rc_t
634_nscd_init_getent_ctx_monitor() {
635
636	int	errnum;
637	char	*me = "_nscd_init_getent_ctx_monitor";
638
639	_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_DEBUG)
640	(me, "initializing the getent context monitor\n");
641
642	/*
643	 * the forker nscd does not process getent requests
644	 * so no need to monitor orphan getent contexts
645	 */
646	if (_whoami == NSCD_FORKER)
647		return (NSCD_SUCCESS);
648
649	/*
650	 * start a thread to reclaim unused getent contexts
651	 */
652	if (thr_create(NULL, NULL, reclaim_getent_ctx,
653		NULL, THR_DETACHED, NULL) != 0) {
654		errnum = errno;
655		_NSCD_LOG(NSCD_LOG_GETENT_CTX, NSCD_LOG_LEVEL_ERROR)
656		(me, "thr_create: %s\n", strerror(errnum));
657		return (NSCD_THREAD_CREATE_ERROR);
658	}
659
660	return (NSCD_SUCCESS);
661}
662