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