#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"
#define DBG_OFF 0
#define DBG_CANT_FIND 2
#define DBG_NETLOOKUPS 4
#define DBG_ALL 6
#define LOGBUFLEN 1024
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 };
static nscd_cfg_stat_global_log_t logstats = {
NSCD_CFG_STAT_GROUP_INFO_GLOBAL_LOG, 0 };
int _logfd = 2;
static nscd_rc_t
_nscd_set_lf(
char *lf)
{
int newlogfd;
char *me = "_nscd_set_lf";
if (lf == NULL || *lf == 0) {
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 {
if ((newlogfd = open(lf, O_EXCL|O_WRONLY|O_CREAT, 0644)) < 0) {
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) &&
(before.st_nlink == 1) &&
(before.st_uid == 0)) {
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);
}
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());
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);
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);
}
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);
}
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;
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;
return (NSCD_SUCCESS);
}
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;
return (NSCD_SUCCESS);
}
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;
if (_nscd_cfg_flag_is_not_set(pdesc->pflag, NSCD_CFG_PFLAG_GLOBAL))
return (NSCD_CFG_PARAM_DESC_ERROR);
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);
}
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);
}
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;
*dflag = _nscd_cfg_flag_set(*dflag, NSCD_CFG_DFLAG_STATIC_DATA);
return (NSCD_SUCCESS);
}
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);
}
nscd_rc_t
_nscd_set_debug_level(
int level)
{
nscd_rc_t rc;
nscd_cfg_handle_t *h;
int l = 0;
int c = -1;
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);
}