xref: /illumos-gate/usr/src/cmd/nscd/nscd_log.c (revision 50981ffc7e4c5048d14890df805afee6ec113991)
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 */
55 int		_nscd_log_comp = 0x0;
56 int		_nscd_log_level = 0x0;
57 static 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 
63 static char	_nscd_debug = NSCD_DEBUG_NONE;
64 static char	_nscd_logfile_d[PATH_MAX] = { 0 };
65 static char	_nscd_logfile_s[PATH_MAX] = { 0 };
66 
67 /* statistics data */
68 static 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 */
72 int _logfd = 2;
73 
74 
75 /* close old log file and open a new one */
76 static 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 */
157 void
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  */
238 static int
239 debug_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 */
256 nscd_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 */
312 nscd_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 */
393 nscd_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  */
414 nscd_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 */
434 nscd_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 
479 void
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