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