xref: /illumos-gate/usr/src/cmd/nscd/nscd_log.c (revision 150d2c5288c645a1c1a7d2bee61199a3729406c7)
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 */
55 int			_nscd_log_comp = 0x0;
56 int			_nscd_log_level = 0x0;
57 static char		logfile[PATH_MAX];
58 
59 /* statistics data */
60 static 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 */
64 int _logfd = 2;
65 
66 /* close old log file and open a new one */
67 static 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 */
142 void
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 */
198 nscd_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 */
254 nscd_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 */
335 nscd_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  */
356 nscd_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  */
384 nscd_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