nscd_log.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 <locale.h>
30#include <limits.h>
31#include <fcntl.h>
32#include <sys/stat.h>
33#include <sys/varargs.h>
34#include <synch.h>
35#include <thread.h>
36#include <string.h>
37#include <unistd.h>
38#include "nscd_log.h"
39#include "nscd_config.h"
40#include "nscd_switch.h"
41#include "cache.h"
42
43/*
44 * old nscd debug levels
45 */
46#define	DBG_OFF		0
47#define	DBG_CANT_FIND	2
48#define	DBG_NETLOOKUPS	4
49#define	DBG_ALL		6
50
51/* max. chars in a nscd log entry */
52#define	LOGBUFLEN	1024
53
54/* configuration for the nscd log component */
55int			_nscd_log_comp = 0x0;
56int			_nscd_log_level = 0x0;
57static char		logfile[PATH_MAX];
58
59/* statistics data */
60static nscd_cfg_stat_global_log_t logstats = {
61	NSCD_CFG_STAT_GROUP_INFO_GLOBAL_LOG, 0 };
62
63/* if no log file specified, log entry goes to stderr */
64int _logfd = 2;
65
66/* close old log file and open a new one */
67static nscd_rc_t
68_nscd_set_lf(
69	char	*lf)
70{
71	int	newlogfd;
72	char	*me = "_nscd_set_lf";
73
74	/*
75	 *  don't try and open the log file /dev/null
76	 */
77	if (lf == NULL || *lf == 0) {
78		/* ignore empty log file specs */
79		return (NSCD_SUCCESS);
80	} else if (strcmp(lf, "/dev/null") == 0) {
81		(void) strlcpy(logfile, lf, PATH_MAX);
82		if (_logfd >= 0)
83			(void) close(_logfd);
84		_logfd = -1;
85		return (NSCD_SUCCESS);
86	} else if (strcmp(lf, "stderr") == 0) {
87		(void) strlcpy(logfile, lf, PATH_MAX);
88		if (_logfd != -1 && _logfd != 2)
89			(void) close(_logfd);
90		_logfd = 2;
91		return (NSCD_SUCCESS);
92	} else {
93
94		/*
95		 * In order to open this file securely, we'll try a few tricks
96		 */
97
98		if ((newlogfd = open(lf, O_EXCL|O_WRONLY|O_CREAT, 0644)) < 0) {
99			/*
100			 * File already exists... now we need to get cute
101			 * since opening a file in a world-writeable directory
102			 * safely is hard = it could be a hard link or a
103			 * symbolic link to a system file.
104			 */
105			struct stat before;
106
107			if (lstat(lf, &before) < 0) {
108				_nscd_logit(me, "Cannot open new "
109				    "logfile \"%s\": %sn", lf, strerror(errno));
110				return (NSCD_CFG_FILE_OPEN_ERROR);
111			}
112
113			if (S_ISREG(before.st_mode) && /* no symbolic links */
114				(before.st_nlink == 1) && /* no hard links */
115				(before.st_uid == 0)) {   /* owned by root */
116				if ((newlogfd =
117				    open(lf, O_APPEND|O_WRONLY, 0644)) < 0) {
118					_nscd_logit(me, "Cannot open new "\
119					    "logfile \"%s\": %s\n", lf,
120					    strerror(errno));
121					return (NSCD_CFG_FILE_OPEN_ERROR);
122				}
123			} else {
124				_nscd_logit(me, "Cannot use specified "
125				    "logfile \"%s\": "\
126				    "file is/has links or isn't owned by "\
127				    "root\n", lf);
128				return (NSCD_CFG_FILE_OPEN_ERROR);
129			}
130		}
131
132		(void) close(_logfd);
133		(void) strlcpy(logfile, lf, PATH_MAX);
134		_logfd = newlogfd;
135		_nscd_logit(me, "Start of new logfile %s\n", lf);
136	}
137	return (NSCD_SUCCESS);
138}
139
140
141/* log an entry to the configured nscd log file */
142void
143_nscd_logit(
144	char		*funcname,
145	char		*format,
146	...)
147{
148	static mutex_t	loglock = DEFAULTMUTEX;
149	struct timeval	tv;
150	char		tid_buf[32];
151	char		pid_buf[32];
152	char		buffer[LOGBUFLEN];
153	int		safechars, offset;
154	va_list		ap;
155
156	if (_logfd < 0)
157		return;
158
159	va_start(ap, format);
160
161	if (gettimeofday(&tv, NULL) != 0 ||
162	    ctime_r(&tv.tv_sec, buffer, LOGBUFLEN) == NULL) {
163		(void) snprintf(buffer, LOGBUFLEN,
164		    "<time conversion failed>\t");
165	} else {
166		(void) sprintf(tid_buf, "--%d", thr_self());
167		(void) sprintf(pid_buf, "--%ld", getpid());
168		/*
169		 * ctime_r() includes some stuff we don't want;
170		 * adjust length to overwrite " YYYY\n" and
171		 * include tid string length.
172		 */
173		offset = strlen(buffer) - 6;
174		safechars = LOGBUFLEN - (offset - 1);
175		(void) snprintf(buffer + offset,
176			safechars, ".%.4ld%s%s\t%s:\n\t\t",
177			tv.tv_usec/100, tid_buf, pid_buf,
178			funcname);
179	}
180	offset = strlen(buffer);
181	safechars = LOGBUFLEN - (offset - 1);
182	/*LINTED: E_SEC_PRINTF_VAR_FMT*/
183	if (vsnprintf(buffer + offset, safechars, format, ap) >
184	    safechars) {
185		(void) strncat(buffer, "...\n", LOGBUFLEN);
186	}
187
188	(void) mutex_lock(&loglock);
189	(void) write(_logfd, buffer, strlen(buffer));
190	logstats.entries_logged++;
191	(void) mutex_unlock(&loglock);
192
193	va_end(ap);
194}
195
196
197/* ARGSUSED */
198nscd_rc_t
199_nscd_cfg_log_notify(
200	void				*data,
201	struct nscd_cfg_param_desc	*pdesc,
202	nscd_cfg_id_t			*nswdb,
203	nscd_cfg_flag_t			dflag,
204	nscd_cfg_error_t		**errorp,
205	void				*cookie)
206{
207
208	nscd_cfg_global_log_t		*logcfg;
209	int				off;
210
211	/*
212	 * At init time, the whole group of config params are received.
213	 * At update time, group or individual parameter value could
214	 * be received.
215	 */
216
217	if (_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_GROUP)) {
218
219		logcfg = (nscd_cfg_global_log_t *)data;
220
221		_nscd_log_comp = logcfg->debug_comp;
222		_nscd_log_level = logcfg->debug_level;
223
224		/*
225		 * logcfg->logfile should have been opened
226		 * by _nscd_cfg_log_verify()
227		 */
228
229		return (NSCD_SUCCESS);
230	}
231
232	/*
233	 * individual config parameter
234	 */
235	off = offsetof(nscd_cfg_global_log_t, debug_comp);
236	if (pdesc->p_offset == off) {
237		_nscd_log_comp = *(nscd_cfg_bitmap_t *)data;
238		return (NSCD_SUCCESS);
239	}
240
241	off = offsetof(nscd_cfg_global_log_t, debug_level);
242	if (pdesc->p_offset == off)
243		_nscd_log_level = *(nscd_cfg_bitmap_t *)data;
244
245	/*
246	 * logcfg->logfile should have been opened
247	 * by _nscd_cfg_log_verify()
248	 */
249
250	return (NSCD_SUCCESS);
251}
252
253/* ARGSUSED */
254nscd_rc_t
255_nscd_cfg_log_verify(
256	void				*data,
257	struct	nscd_cfg_param_desc	*pdesc,
258	nscd_cfg_id_t			*nswdb,
259	nscd_cfg_flag_t			dflag,
260	nscd_cfg_error_t		**errorp,
261	void				**cookie)
262{
263	nscd_cfg_global_log_t		*logcfg;
264	nscd_cfg_bitmap_t		bt;
265	int				off;
266
267	/*
268	 * There is no switch db specific config params
269	 * for the nscd log component. It is a bug if
270	 * the input param description is global.
271	 */
272	if (_nscd_cfg_flag_is_not_set(pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL))
273		return (NSCD_CFG_PARAM_DESC_ERROR);
274
275	/*
276	 * At init time, the whole group of config params are received.
277	 * At update time, group or individual parameter value could
278	 * be received.
279	 */
280
281	if (_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_GROUP)) {
282
283		logcfg = (nscd_cfg_global_log_t *)data;
284
285		if (_nscd_cfg_bitmap_valid(logcfg->debug_comp,
286			NSCD_LOG_ALL) == 0)
287			return (NSCD_CFG_SYNTAX_ERROR);
288
289		if (_nscd_cfg_bitmap_valid(logcfg->debug_level,
290			NSCD_LOG_LEVEL_ALL) == 0)
291			return (NSCD_CFG_SYNTAX_ERROR);
292
293		if (logcfg->logfile != NULL)
294			return (_nscd_set_lf(logcfg->logfile));
295
296		return (NSCD_SUCCESS);
297	}
298
299	/*
300	 * individual config parameter
301	 */
302
303	off = offsetof(nscd_cfg_global_log_t, debug_comp);
304	if (pdesc->p_offset == off) {
305
306		bt = *(nscd_cfg_bitmap_t *)data;
307		if (_nscd_cfg_bitmap_valid(bt, NSCD_LOG_ALL) == 0)
308			return (NSCD_CFG_SYNTAX_ERROR);
309
310		return (NSCD_SUCCESS);
311	}
312
313	off = offsetof(nscd_cfg_global_log_t, debug_level);
314	if (pdesc->p_offset == off) {
315
316		bt = *(nscd_cfg_bitmap_t *)data;
317		if (_nscd_cfg_bitmap_valid(bt, NSCD_LOG_LEVEL_ALL) == 0)
318			return (NSCD_CFG_SYNTAX_ERROR);
319
320		return (NSCD_SUCCESS);
321	}
322
323	off = offsetof(nscd_cfg_global_log_t, logfile);
324	if (pdesc->p_offset == off) {
325		if (data != NULL)
326			return (_nscd_set_lf((char *)data));
327		else
328			return (NSCD_SUCCESS);
329	}
330
331	return (NSCD_CFG_PARAM_DESC_ERROR);
332}
333
334/* ARGSUSED */
335nscd_rc_t
336_nscd_cfg_log_get_stat(
337	void				**stat,
338	struct nscd_cfg_stat_desc	*sdesc,
339	nscd_cfg_id_t			*nswdb,
340	nscd_cfg_flag_t			*dflag,
341	void				(**free_stat)(void *stat),
342	nscd_cfg_error_t		**errorp)
343{
344
345	*(nscd_cfg_stat_global_log_t **)stat = &logstats;
346
347	/* indicate the statistics are static, i.e., do not free */
348	*dflag = _nscd_cfg_flag_set(*dflag, NSCD_CFG_DFLAG_STATIC_DATA);
349
350	return (NSCD_SUCCESS);
351}
352
353/*
354 * set the name of the current log file and make it current.
355 */
356nscd_rc_t
357_nscd_set_log_file(
358	char			*name)
359{
360	nscd_rc_t		rc;
361	nscd_cfg_handle_t	*h;
362
363	rc = _nscd_cfg_get_handle("logfile", NULL, &h, NULL);
364	if (rc != NSCD_SUCCESS)
365		return (rc);
366
367	rc = _nscd_cfg_set(h, name, NULL);
368	_nscd_cfg_free_handle(h);
369	if (rc != NSCD_SUCCESS)
370		exit(rc);
371
372	return (NSCD_SUCCESS);
373}
374
375/*
376 * Map old nscd debug level to new one and make it current.
377 *   -- debug component:  NSCD_LOG_CACHE
378 *   -- debug level:
379 *      -- DBG_OFF 		--> NSCD_LOG_LEVEL_NONE
380 *      -- DBG_CANT_FIND 	--> NSCD_LOG_LEVEL_ERROR
381 *      -- DBG_DBG_NETLOOKUPS 	--> NSCD_LOG_LEVEL_ERROR
382 *      -- DBG_ALL 		--> NSCD_LOG_LEVEL_ALL
383 */
384nscd_rc_t
385_nscd_set_debug_level(
386	int			level)
387{
388	nscd_rc_t		rc;
389	nscd_cfg_handle_t	*h;
390	int			l;
391	int			c;
392
393	rc = _nscd_cfg_get_handle("debug-components", NULL, &h, NULL);
394	if (rc != NSCD_SUCCESS)
395		return (rc);
396	c = NSCD_LOG_CACHE;
397
398	if (level < 0)
399		c = -1 * level / 10000;
400	rc = _nscd_cfg_set(h, &c, NULL);
401	_nscd_cfg_free_handle(h);
402	if (rc != NSCD_SUCCESS)
403		exit(rc);
404
405	rc = _nscd_cfg_get_handle("debug-level", NULL, &h, NULL);
406	if (rc != NSCD_SUCCESS)
407		return (rc);
408
409	if (level == DBG_OFF)
410		l =  NSCD_LOG_LEVEL_NONE;
411	else if (level >= DBG_CANT_FIND)
412		l = NSCD_LOG_LEVEL_ERROR;
413	else if (level >= DBG_NETLOOKUPS)
414		l = NSCD_LOG_LEVEL_ERROR;
415	else if (level >= DBG_ALL)
416		l = NSCD_LOG_LEVEL_ALL;
417
418	if (level < 0)
419		l = -1 * level % 10000;
420
421	rc = _nscd_cfg_set(h, &l, NULL);
422	_nscd_cfg_free_handle(h);
423	if (rc != NSCD_SUCCESS)
424		exit(rc);
425
426	return (NSCD_SUCCESS);
427}
428