OpenSolaris_b135/cmd/nscd/nscd_log.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <stdlib.h>
#include <locale.h>
#include <limits.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/varargs.h>
#include <synch.h>
#include <thread.h>
#include <string.h>
#include <unistd.h>
#include "nscd_log.h"
#include "nscd_config.h"
#include "nscd_switch.h"
#include "cache.h"

/*
 * old nscd debug levels
 */
#define	DBG_OFF		0
#define	DBG_CANT_FIND	2
#define	DBG_NETLOOKUPS	4
#define	DBG_ALL		6

/* max. chars in a nscd log entry */
#define	LOGBUFLEN	1024

/* configuration for the nscd log component */
int		_nscd_log_comp = 0x0;
int		_nscd_log_level = 0x0;
static char	_nscd_logfile[PATH_MAX] = { 0 };

#define	NSCD_DEBUG_NONE		'0'
#define	NSCD_DEBUG_OPEN		'1'
#define	NSCD_DEBUG_CLOSE	'2'

static char	_nscd_debug = NSCD_DEBUG_NONE;
static char	_nscd_logfile_d[PATH_MAX] = { 0 };
static char	_nscd_logfile_s[PATH_MAX] = { 0 };

/* statistics data */
static nscd_cfg_stat_global_log_t logstats = {
	NSCD_CFG_STAT_GROUP_INFO_GLOBAL_LOG, 0 };

/* if no log file specified, log entry goes to stderr */
int _logfd = 2;


/* close old log file and open a new one */
static nscd_rc_t
_nscd_set_lf(
	char	*lf)
{
	int	newlogfd;
	char	*me = "_nscd_set_lf";

	/*
	 *  don't try and open the log file /dev/null
	 */
	if (lf == NULL || *lf == 0) {
		/* ignore empty log file specs */
		return (NSCD_SUCCESS);
	} else if (strcmp(lf, "/dev/null") == 0) {
		(void) strlcpy(_nscd_logfile, lf, PATH_MAX);
		if (_logfd >= 0)
			(void) close(_logfd);
		_logfd = -1;
		return (NSCD_SUCCESS);
	} else if (strcmp(lf, "stderr") == 0) {
		(void) strlcpy(_nscd_logfile, lf, PATH_MAX);
		if (_logfd != -1 && _logfd != 2)
			(void) close(_logfd);
		_logfd = 2;
		return (NSCD_SUCCESS);
	} else {

		/*
		 * In order to open this file securely, we'll try a few tricks
		 */

		if ((newlogfd = open(lf, O_EXCL|O_WRONLY|O_CREAT, 0644)) < 0) {
			/*
			 * File already exists... now we need to get cute
			 * since opening a file in a world-writeable directory
			 * safely is hard = it could be a hard link or a
			 * symbolic link to a system file.
			 */
			struct stat before;

			if (lstat(lf, &before) < 0) {
				if (_nscd_debug == NSCD_DEBUG_NONE)
					_nscd_logit(me, "Cannot open new "
					    "logfile \"%s\": %sn",
					    lf, strerror(errno));
				return (NSCD_CFG_FILE_OPEN_ERROR);
			}

			if (S_ISREG(before.st_mode) && /* no symbolic links */
			    (before.st_nlink == 1) && /* no hard links */
			    (before.st_uid == 0)) {   /* owned by root */
				if ((newlogfd =
				    open(lf, O_APPEND|O_WRONLY, 0644)) < 0) {
					if (_nscd_debug == NSCD_DEBUG_NONE)
						_nscd_logit(me,
						    "Cannot open new "\
						    "logfile \"%s\": %s\n", lf,
						    strerror(errno));
					return (NSCD_CFG_FILE_OPEN_ERROR);
				}
			} else {
				if (_nscd_debug == NSCD_DEBUG_NONE)
					_nscd_logit(me, "Cannot use specified "
					    "logfile \"%s\": "\
					    "file is/has links or isn't "
					    "owned by root\n", lf);
				return (NSCD_CFG_FILE_OPEN_ERROR);
			}
		}

		(void) close(_logfd);
		(void) strlcpy(_nscd_logfile, lf, PATH_MAX);
		_logfd = newlogfd;
		if (_nscd_debug == NSCD_DEBUG_NONE)
			_nscd_logit(me, "Start of new logfile %s\n", lf);
	}
	return (NSCD_SUCCESS);
}


/* log an entry to the configured nscd log file */
void
_nscd_logit(
	char		*funcname,
	char		*format,
	...)
{
	static mutex_t	loglock = DEFAULTMUTEX;
	struct timeval	tv;
	char		tid_buf[32];
	char		pid_buf[32];
	char		buffer[LOGBUFLEN];
	int		safechars, offset;
	va_list		ap;

	if (_logfd < 0)
		return;

	if (_nscd_debug == NSCD_DEBUG_OPEN) {
		(void) mutex_lock(&loglock);
		if (_nscd_debug == NSCD_DEBUG_OPEN &&
		    *_nscd_logfile_d != '\0' &&
		    (strcmp(_nscd_logfile, "/dev/null") == 0 ||
		    strcmp(_nscd_logfile, "stderr") == 0)) {
			(void) strlcpy(_nscd_logfile_s,
			    _nscd_logfile, PATH_MAX);
			(void) _nscd_set_lf(_nscd_logfile_d);
		}
		_nscd_debug = NSCD_DEBUG_NONE;
		(void) mutex_unlock(&loglock);
	} else if (_nscd_debug == NSCD_DEBUG_CLOSE) {
		(void) mutex_lock(&loglock);
		if (_nscd_debug == NSCD_DEBUG_CLOSE)
			(void) _nscd_set_lf(_nscd_logfile_s);
		_nscd_debug = NSCD_DEBUG_NONE;
		(void) mutex_unlock(&loglock);
	}

	va_start(ap, format);

	if (gettimeofday(&tv, NULL) != 0 ||
	    ctime_r(&tv.tv_sec, buffer, LOGBUFLEN) == NULL) {
		(void) snprintf(buffer, LOGBUFLEN,
		    "<time conversion failed>\t");
	} else {
		(void) sprintf(tid_buf, "--%d", thr_self());
		(void) sprintf(pid_buf, "--%ld", getpid());
		/*
		 * ctime_r() includes some stuff we don't want;
		 * adjust length to overwrite " YYYY\n" and
		 * include tid string length.
		 */
		offset = strlen(buffer) - 6;
		safechars = LOGBUFLEN - (offset - 1);
		(void) snprintf(buffer + offset,
		    safechars, ".%.4ld%s%s\t%s:\n\t\t",
		    tv.tv_usec/100, tid_buf, pid_buf,
		    funcname);
	}
	offset = strlen(buffer);
	safechars = LOGBUFLEN - (offset - 1);
	/*LINTED: E_SEC_PRINTF_VAR_FMT*/
	if (vsnprintf(buffer + offset, safechars, format, ap) >
	    safechars) {
		(void) strncat(buffer, "...\n", LOGBUFLEN);
	}

	(void) mutex_lock(&loglock);
	(void) write(_logfd, buffer, strlen(buffer));
	logstats.entries_logged++;
	(void) mutex_unlock(&loglock);

	va_end(ap);
}

/*
 * Map old nscd debug level (0 -10) to log level:
 *      -- >= 6: DBG_ALL 		--> NSCD_LOG_LEVEL_ALL
 *      -- >= 4: DBG_DBG_NETLOOKUPS 	--> NSCD_LOG_LEVEL_CANT_FIND
 *      -- >= 2: DBG_CANT_FIND 		--> NSCD_LOG_LEVEL_CANT_FIND
 *      -- >= 0: DBG_OFF 		--> NSCD_LOG_LEVEL_NONE
 */
static int
debug_to_log_level(
	int	level)
{
	if (level >= 0 && level <= 10) {
		if (level >= DBG_ALL)
			return (NSCD_LOG_LEVEL_ALL);
		else if (level >= DBG_NETLOOKUPS)
			return (NSCD_LOG_LEVEL_CANT_FIND);
		else if (level >= DBG_CANT_FIND)
			return (NSCD_LOG_LEVEL_CANT_FIND);
		else if (level >= DBG_OFF)
			return (NSCD_LOG_LEVEL_NONE);
	}
	return (level);
}

/* ARGSUSED */
nscd_rc_t
_nscd_cfg_log_notify(
	void				*data,
	struct nscd_cfg_param_desc	*pdesc,
	nscd_cfg_id_t			*nswdb,
	nscd_cfg_flag_t			dflag,
	nscd_cfg_error_t		**errorp,
	void				*cookie)
{

	nscd_cfg_global_log_t		*logcfg;
	int				off;

	/*
	 * At init time, the whole group of config params are received.
	 * At update time, group or individual parameter value could
	 * be received.
	 */

	if (_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_GROUP)) {

		logcfg = (nscd_cfg_global_log_t *)data;

		_nscd_log_comp = logcfg->debug_comp;
		_nscd_log_level = logcfg->debug_level;

		/*
		 * logcfg->logfile should have been opened
		 * by _nscd_cfg_log_verify()
		 */

		return (NSCD_SUCCESS);
	}

	/*
	 * individual config parameter
	 */
	off = offsetof(nscd_cfg_global_log_t, debug_comp);
	if (pdesc->p_offset == off) {
		_nscd_log_comp = *(nscd_cfg_bitmap_t *)data;
		return (NSCD_SUCCESS);
	}

	off = offsetof(nscd_cfg_global_log_t, debug_level);
	if (pdesc->p_offset == off)
		_nscd_log_level = *(nscd_cfg_bitmap_t *)data;

	/*
	 * logcfg->logfile should have been opened
	 * by _nscd_cfg_log_verify()
	 */

	return (NSCD_SUCCESS);
}

/* ARGSUSED */
nscd_rc_t
_nscd_cfg_log_verify(
	void				*data,
	struct	nscd_cfg_param_desc	*pdesc,
	nscd_cfg_id_t			*nswdb,
	nscd_cfg_flag_t			dflag,
	nscd_cfg_error_t		**errorp,
	void				**cookie)
{
	nscd_cfg_global_log_t		*logcfg;
	nscd_cfg_bitmap_t		bt;
	int				off;

	/*
	 * There is no switch db specific config params
	 * for the nscd log component. It is a bug if
	 * the input param description is global.
	 */
	if (_nscd_cfg_flag_is_not_set(pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL))
		return (NSCD_CFG_PARAM_DESC_ERROR);

	/*
	 * At init time, the whole group of config params are received.
	 * At update time, group or individual parameter value could
	 * be received.
	 */

	if (_nscd_cfg_flag_is_set(dflag, NSCD_CFG_DFLAG_GROUP)) {

		logcfg = (nscd_cfg_global_log_t *)data;

		if (_nscd_cfg_bitmap_valid(logcfg->debug_comp,
		    NSCD_LOG_ALL) == 0)
			return (NSCD_CFG_SYNTAX_ERROR);

		if (_nscd_cfg_bitmap_valid(logcfg->debug_level,
		    NSCD_LOG_LEVEL_ALL) == 0)
			return (NSCD_CFG_SYNTAX_ERROR);

		if (logcfg->logfile != NULL)
			return (_nscd_set_lf(logcfg->logfile));

		return (NSCD_SUCCESS);
	}

	/*
	 * individual config parameter
	 */

	off = offsetof(nscd_cfg_global_log_t, debug_comp);
	if (pdesc->p_offset == off) {

		bt = *(nscd_cfg_bitmap_t *)data;
		if (_nscd_cfg_bitmap_valid(bt, NSCD_LOG_ALL) == 0)
			return (NSCD_CFG_SYNTAX_ERROR);

		return (NSCD_SUCCESS);
	}

	off = offsetof(nscd_cfg_global_log_t, debug_level);
	if (pdesc->p_offset == off) {

		bt = *(nscd_cfg_bitmap_t *)data;
		if (_nscd_cfg_bitmap_valid(bt, NSCD_LOG_LEVEL_ALL) == 0)
			return (NSCD_CFG_SYNTAX_ERROR);

		return (NSCD_SUCCESS);
	}

	off = offsetof(nscd_cfg_global_log_t, logfile);
	if (pdesc->p_offset == off) {
		if (data != NULL)
			return (_nscd_set_lf((char *)data));
		else
			return (NSCD_SUCCESS);
	}

	return (NSCD_CFG_PARAM_DESC_ERROR);
}

/* ARGSUSED */
nscd_rc_t
_nscd_cfg_log_get_stat(
	void				**stat,
	struct nscd_cfg_stat_desc	*sdesc,
	nscd_cfg_id_t			*nswdb,
	nscd_cfg_flag_t			*dflag,
	void				(**free_stat)(void *stat),
	nscd_cfg_error_t		**errorp)
{

	*(nscd_cfg_stat_global_log_t **)stat = &logstats;

	/* indicate the statistics are static, i.e., do not free */
	*dflag = _nscd_cfg_flag_set(*dflag, NSCD_CFG_DFLAG_STATIC_DATA);

	return (NSCD_SUCCESS);
}

/*
 * set the name of the current log file and make it current.
 */
nscd_rc_t
_nscd_set_log_file(
	char			*name)
{
	nscd_rc_t		rc;
	nscd_cfg_handle_t	*h;

	rc = _nscd_cfg_get_handle("logfile", NULL, &h, NULL);
	if (rc != NSCD_SUCCESS)
		return (rc);

	rc = _nscd_cfg_set(h, name, NULL);
	_nscd_cfg_free_handle(h);
	if (rc != NSCD_SUCCESS)
		exit(rc);

	return (NSCD_SUCCESS);
}

/* Set debug level to the new one and make it current */
nscd_rc_t
_nscd_set_debug_level(
	int			level)
{
	nscd_rc_t		rc;
	nscd_cfg_handle_t	*h;
	int			l = 0;
	int			c = -1;

	/* old nscd debug level is 1 to 10, map it to log_level and log_comp */
	if (level >= 0 && level <= 10) {
		l = debug_to_log_level(level);
		c = NSCD_LOG_CACHE;
	} else
		l = level;

	if (level < 0)
		c = -1 * level / 1000000;

	if (c != -1) {
		rc = _nscd_cfg_get_handle("debug-components", NULL, &h, NULL);
		if (rc != NSCD_SUCCESS)
			return (rc);

		rc = _nscd_cfg_set(h, &c, NULL);
		_nscd_cfg_free_handle(h);
		if (rc != NSCD_SUCCESS)
			exit(rc);
	}

	rc = _nscd_cfg_get_handle("debug-level", NULL, &h, NULL);
	if (rc != NSCD_SUCCESS)
		return (rc);

	if (level < 0)
		l = -1 * level % 1000000;

	rc = _nscd_cfg_set(h, &l, NULL);
	_nscd_cfg_free_handle(h);
	if (rc != NSCD_SUCCESS)
		exit(rc);

	return (NSCD_SUCCESS);
}

void
_nscd_get_log_info(
	char	*level,
	int	llen,
	char	*file,
	int	flen)
{
	if (_nscd_log_level != 0)
		(void) snprintf(level, llen, "%d", _nscd_log_level);
	if (*_nscd_logfile != '\0')
		(void) strlcpy(file, _nscd_logfile, flen);
}