xref: /titanic_44/usr/src/cmd/nscd/nscd_log.c (revision b71b9f11fdfac0fc50597b6698ed44175c702b2a)
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