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 <locale.h>
30#include <string.h>
31#include "cache.h"
32#include "nscd_door.h"
33#include "nscd_log.h"
34#include "nscd_admin.h"
35
36extern nsc_ctx_t	*cache_ctx_p[];
37extern char 		*cache_name[];
38
39static nscd_admin_t	admin_c = { 0 };
40static nscd_admin_mod_t	admin_mod = { 0 };
41static mutex_t		mod_lock = DEFAULTMUTEX;
42
43/*ARGSUSED*/
44int
45_nscd_door_getadmin(void *outbuf)
46{
47	int			i;
48	int			data_size = NSCD_N2N_DOOR_BUF_SIZE(admin_c);
49	nss_pheader_t		*phdr = (nss_pheader_t *)outbuf;
50	nscd_cfg_cache_t	cfg_default = NSCD_CFG_CACHE_DEFAULTS;
51
52	/*
53	 * if size of buffer is not big enough, tell the caller to
54	 * increase it to the size returned
55	 */
56	if (phdr->pbufsiz < data_size)
57		return (sizeof (admin_c));
58
59	NSCD_SET_STATUS_SUCCESS(phdr);
60	phdr->data_off = sizeof (nss_pheader_t);
61	phdr->data_len = sizeof (admin_c);
62
63	for (i = 0; i < CACHE_CTX_COUNT; i++) {
64		if (cache_ctx_p[i] != NULL) {
65			(void) rw_rdlock(&cache_ctx_p[i]->cfg_rwlp);
66			admin_c.cache_cfg[i] = cache_ctx_p[i]->cfg;
67			(void) rw_unlock(&cache_ctx_p[i]->cfg_rwlp);
68
69			(void) mutex_lock(&cache_ctx_p[i]->stats_mutex);
70			admin_c.cache_stats[i] = cache_ctx_p[i]->stats;
71			(void) mutex_unlock(&cache_ctx_p[i]->stats_mutex);
72		} else {
73			admin_c.cache_cfg[i] = cfg_default;
74			(void) memset(&admin_c.cache_stats[i], 0,
75				sizeof (admin_c.cache_stats[0]));
76		}
77	}
78	(void) memcpy(((char *)outbuf) + phdr->data_off,
79		&admin_c, sizeof (admin_c));
80
81	return (0);
82}
83
84void
85_nscd_client_showstats()
86{
87	(void) printf("nscd configuration:\n\n");
88	(void) printf("%10d  server debug level\n", admin_c.debug_level);
89	(void) printf("\"%s\"  is server log file\n", admin_c.logfile);
90
91	(void) nsc_info(NULL, NULL, admin_c.cache_cfg, admin_c.cache_stats);
92}
93
94/*ARGSUSED*/
95nscd_rc_t
96_nscd_server_setadmin(nscd_admin_mod_t *set)
97{
98	nscd_rc_t		rc = NSCD_ADMIN_FAIL_TO_SET;
99	nscd_cfg_handle_t	*h;
100	int			i, j;
101	char			*group = "param-group-cache";
102	char			*dbname;
103	nscd_cfg_error_t	*err = NULL;
104	char			*me = "_nscd_server_setadmin";
105
106	if (set == NULL)
107		set = &admin_mod;
108
109	/* one setadmin at a time */
110	(void) mutex_lock(&mod_lock);
111
112	_NSCD_LOG_IF(NSCD_LOG_ADMIN, NSCD_LOG_LEVEL_DEBUG) {
113
114		_nscd_logit(me, "total_size = %d\n", set->total_size);
115
116		_nscd_logit(me, "debug_level_set = %d, debug_level = %d\n",
117			set->debug_level_set, set->debug_level);
118
119		_nscd_logit(me, "logfile_set = %d, logfile = %s\n",
120			set->logfile_set, *set->logfile == '\0' ?
121				"" : set->logfile);
122
123		_nscd_logit(me, "cache_cfg_num = %d\n",
124			set->cache_cfg_num);
125		_nscd_logit(me, "cache_flush_num = %d\n",
126			set->cache_flush_num);
127	}
128
129	/*
130	 *  global admin stuff
131	 */
132
133	if (set->debug_level_set == nscd_true) {
134		if (_nscd_set_debug_level(set->debug_level)
135			!= NSCD_SUCCESS) {
136
137			_NSCD_LOG(NSCD_LOG_ADMIN, NSCD_LOG_LEVEL_ERROR)
138			(me, "unable to set debug level %d\n",
139				set->debug_level);
140
141			goto err_exit;
142		}
143		admin_c.debug_level = set->debug_level;
144	}
145
146	if (set->logfile_set == nscd_true) {
147		if (_nscd_set_log_file(set->logfile) != NSCD_SUCCESS) {
148
149			_NSCD_LOG(NSCD_LOG_ADMIN, NSCD_LOG_LEVEL_ERROR)
150			(me, "unable to set log file %s\n", set->logfile);
151
152			goto err_exit;
153		}
154		(void) strlcpy(admin_c.logfile, set->logfile,
155				NSCD_LOGFILE_LEN);
156	}
157
158	/*
159	 *  For caches to be changed
160	 */
161	if (set->cache_cfg_num > CACHE_CTX_COUNT) {
162
163		_NSCD_LOG(NSCD_LOG_ADMIN, NSCD_LOG_LEVEL_ERROR)
164		(me, "number of caches (%d) to change out of bound %s\n",
165			set->cache_cfg_num);
166
167		goto err_exit;
168	}
169
170	for (i = 0; i < set->cache_cfg_num; i++) {
171
172		nscd_cfg_cache_t *new_cfg;
173
174		j = set->cache_cfg_set[i];
175		new_cfg = &set->cache_cfg[i];
176		dbname = cache_name[j];
177		if (cache_ctx_p[j] == NULL) {
178			_NSCD_LOG(NSCD_LOG_ADMIN, NSCD_LOG_LEVEL_ERROR)
179			(me, "unable to find cache context for %s\n",
180			dbname);
181		}
182
183		rc = _nscd_cfg_get_handle(group, dbname, &h, NULL);
184		if (rc != NSCD_SUCCESS) {
185			_NSCD_LOG(NSCD_LOG_ADMIN, NSCD_LOG_LEVEL_ERROR)
186			(me, "unable to get handle for < %s : %s >\n",
187			dbname, group);
188
189			goto err_exit;
190		}
191
192		rc = _nscd_cfg_set(h, new_cfg, &err);
193		if (rc != NSCD_SUCCESS) {
194			_NSCD_LOG(NSCD_LOG_ADMIN, NSCD_LOG_LEVEL_ERROR)
195			(me, "unable to set admin data for < %s : %s >\n",
196			dbname, group);
197
198			_nscd_cfg_free_handle(h);
199
200			goto err_exit;
201		}
202		_nscd_cfg_free_handle(h);
203	}
204
205	/*
206	 *  For caches to be flushed
207	 */
208	if (set->cache_flush_num > CACHE_CTX_COUNT) {
209
210		_NSCD_LOG(NSCD_LOG_ADMIN, NSCD_LOG_LEVEL_ERROR)
211		(me, "number of caches (%d) to flush out of bound %s\n",
212			set->cache_flush_num);
213
214		goto err_exit;
215	}
216
217	for (i = 0; i < set->cache_flush_num; i++) {
218		int j;
219
220		j = set->cache_flush_set[i];
221
222		if (cache_ctx_p[j] == NULL) {
223			_NSCD_LOG(NSCD_LOG_ADMIN, NSCD_LOG_LEVEL_ERROR)
224			(me, "unable to find cache context for %s\n",
225			dbname);
226		}
227		nsc_invalidate(cache_ctx_p[j], NULL, NULL);
228	}
229
230	rc = NSCD_SUCCESS;
231	err_exit:
232
233	(void) mutex_unlock(&mod_lock);
234	return (rc);
235}
236
237
238/*ARGSUSED*/
239void
240_nscd_door_setadmin(void *buf)
241{
242	nscd_rc_t	rc;
243	nss_pheader_t	*phdr = (nss_pheader_t *)buf;
244	char		*me = "_nscd_door_setadmin";
245
246	rc = _nscd_server_setadmin(NSCD_N2N_DOOR_DATA(nscd_admin_mod_t, buf));
247	if (rc != NSCD_SUCCESS) {
248		_NSCD_LOG(NSCD_LOG_ADMIN, NSCD_LOG_LEVEL_ERROR)
249		(me, "SETADMIN call failed\n");
250
251		NSCD_RETURN_N2N_STATUS(phdr, NSS_NSCD_PRIV, 0, rc);
252	} else {
253		NSCD_RETURN_STATUS_SUCCESS(phdr);
254	}
255}
256
257/*
258 * for a database 'dbname', add config value 'val' of option 'opt'
259 * to the global admin_mod structure
260 */
261int
262_nscd_add_admin_mod(char *dbname, char opt,
263	char *val, char *msg, int msglen) {
264	int			i, j;
265	nscd_cfg_cache_t	*cfg;
266	nscd_cfg_group_info_t	gi = NSCD_CFG_GROUP_INFO_CACHE;
267	char			dbn[64], *cp;
268
269	/* set initial admin_mod size; assume no cache config to set */
270	if (admin_mod.total_size == 0)
271		admin_mod.total_size = sizeof (admin_mod) -
272			sizeof (admin_mod.cache_cfg);
273
274	/* global admin stuff */
275	if (opt == 'l' || opt == 'd') {
276		if (opt == 'l') {
277			(void) strlcpy(admin_mod.logfile,
278				val, NSCD_LOGFILE_LEN);
279			admin_mod.logfile_set = nscd_true;
280		} else {
281			admin_mod.debug_level = atoi(val);
282			admin_mod.debug_level_set = nscd_true;
283		}
284		return (0);
285	}
286
287	/* options to be processed next requires cache name */
288	(void) strlcpy(dbn, dbname, sizeof (dbn));
289	if ((cp = strchr(dbn, ',')) != NULL)
290		*cp = '\0';
291	i = get_cache_idx(dbn);
292	if (i == -1) {
293		(void) snprintf(msg, msglen,
294			gettext("invalid cache name \"%s\""), dbn);
295		return (-1);
296	}
297
298	/* flush cache ? */
299	if (opt == 'i') {
300		admin_mod.cache_flush_set[admin_mod.cache_flush_num++] = i;
301		return (0);
302	}
303
304	/* options to be processed next requires a param value */
305	if (val == NULL) {
306		(void) snprintf(msg, msglen,
307			gettext("value missing after \"%s\""), dbn);
308		return (-1);
309	}
310
311	/* try to use an existing cache_cfg in admin_mod */
312	for (j = 0; j < admin_mod.cache_cfg_num; j++) {
313		if (admin_mod.cache_cfg_set[j] == i)
314			break;
315	}
316
317	/* no existing one, set up another one */
318	if (j == admin_mod.cache_cfg_num) {
319		admin_mod.cache_cfg_set[j] = i;
320		admin_mod.cache_cfg_num++;
321		admin_mod.total_size += sizeof (admin_mod.cache_cfg[0]);
322	}
323
324	cfg = &admin_mod.cache_cfg[j];
325	cfg->gi.num_param = gi.num_param;
326
327	switch (opt) {
328
329	case 'e':
330		/* enable cache */
331
332		_nscd_cfg_bitmap_set_nth(cfg->gi.bitmap, 0);
333		if (strcmp(val, "yes") == 0)
334		    cfg->enable = nscd_true;
335		else if (strcmp(val, "no") == 0)
336		    cfg->enable = nscd_false;
337		else {
338			(void) snprintf(msg, msglen,
339	gettext("\"yes\" or \"no\" not specified after \"%s\""), dbn);
340			return (-1);
341		}
342		break;
343
344	case 'c':
345		/* check files */
346
347		_nscd_cfg_bitmap_set_nth(cfg->gi.bitmap, 3);
348		if (strcmp(val, "yes") == 0)
349		    cfg->check_files = nscd_true;
350		else if (strcmp(val, "no") == 0)
351		    cfg->check_files = nscd_false;
352		else {
353			(void) snprintf(msg, msglen,
354	gettext("\"yes\" or \"no\" not specified after \"%s\""), dbn);
355			return (-1);
356		}
357		break;
358
359	case 'p':
360		/* positive time to live */
361
362		_nscd_cfg_bitmap_set_nth(cfg->gi.bitmap, 5);
363		cfg->pos_ttl = atoi(val);
364		break;
365
366	case 'n':
367		/* negative time to live */
368
369		_nscd_cfg_bitmap_set_nth(cfg->gi.bitmap, 6);
370		cfg->neg_ttl = atoi(val);
371		break;
372
373	case 'h':
374		/* keep hot count */
375
376		_nscd_cfg_bitmap_set_nth(cfg->gi.bitmap, 7);
377		cfg->keephot = atoi(val);
378		break;
379	}
380
381	return (0);
382}
383
384int
385_nscd_client_getadmin(char opt)
386{
387	int		callnum;
388	nss_pheader_t	phdr;
389
390	if (opt == 'G')
391		callnum = NSCD_GETPUADMIN;
392	else
393		callnum = NSCD_GETADMIN;
394
395	(void) _nscd_doorcall_data(callnum, NULL, sizeof (admin_c),
396		&admin_c, sizeof (admin_c), &phdr);
397
398	if (NSCD_STATUS_IS_NOT_OK(&phdr)) {
399		return (1);
400	}
401
402	return (0);
403}
404
405int
406_nscd_client_setadmin()
407{
408	return (_nscd_doorcall_data(NSCD_SETADMIN, &admin_mod,
409		sizeof (admin_mod), NULL, 0, NULL));
410}
411