/* * Copyright (c) 2003-2006 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. */ #include "sm/generic.h" SM_RCSID("@(#)$Id: log.c,v 1.41 2007/01/14 00:44:40 ca Exp $") #include "sm/assert.h" #include "sm/error.h" #include "sm/time.h" #include "sm/magic.h" #include "sm/heap.h" #include "sm/memops.h" #include "sm/io.h" #include "sm/log.h" #include "sm/syslog.h" #include "sm/time.h" #include "sm/str.h" #if MTA_USE_PTHREADS #include "sm/pthread.h" #endif #define SM_LOG_MAGIC SM_MAGIC('S', 'L', 'O', 'G') #define MAX_LOG_ENTRIES 10000 /* can syslog() deal with these lengths? */ #ifdef MTA_LOG_LEN # define LOG_LEN MTA_LOG_LEN #else # define LOG_LEN 1022 #endif #ifdef MTA_LOG_LEN_MAX # define LOG_LEN_MAX MTA_LOG_LEN_MAX #else # define LOG_LEN_MAX 8192 #endif #if LOG_LEN > LOG_LEN_MAX ERROR _LOG_LEN > _LOG_LEN_MAX: LOG_LEN > LOG_LEN_MAX #endif /* should be dynamically allocated later on */ #define SM_LOG_MAX_CATS 32 #define SM_LOG_MAX_MODS 32 struct sm_log_ctx_S { sm_magic_T sm_magic; sm_logconfig_P lctx_cfg; uint lctx_dbg_level; #if 0 /* make some things optional: sev=, time stamp, ...?? */ uint lctx_flags; #endif int lctx_fd; sm_file_T *lctx_fp; char *lctx_filename; sm_str_P lctx_str; sm_logcategory_P lctx_cats[SM_LOG_MAX_CATS]; uint lctx_cat_n; sm_logmodule_P lctx_mods[SM_LOG_MAX_MODS]; uint lctx_mod_n; #if SM_LOG_ROTATE /* doesn't work that simple: needs filename */ time_T lctx_rotated; uint lctx_entries; #endif #if MTA_USE_PTHREADS pthread_mutex_t lctx_mutex; #endif }; #define SM_IS_LOGCTX(lctx) SM_REQUIRE_ISA((lctx), SM_LOG_MAGIC) /* move to magic.h? */ #define SM_LOGCFG_MAGIC SM_MAGIC('S', 'L', 'C', 'F') struct sm_logconfig_S { sm_magic_T sm_magic; }; #define SM_IS_LOGCFG(lcfg) SM_REQUIRE_ISA((lcfg), SM_LOGCFG_MAGIC) #if SM_LOG_SEVERITY /* ** log sev=SEVERITY () works nice here, but other stuff like ** syslog(), fprint(), don't do this... ** change all sm_log_write() calls? */ struct l2t_S { int l2t_code; char *l2t_str; }; typedef struct l2t_S l2t_T, *l2t_P; static l2t_T sm_loglev2txt[] = { { SM_LOG_EMERG, "EMERG" }, { SM_LOG_FATAL, "FATAL" }, { SM_LOG_ALERT, "ALERT" }, { SM_LOG_CRIT, "CRIT" }, { SM_LOG_ERROR, "ERROR" }, { SM_LOG_ERR, "ERROR" }, { SM_LOG_WARNING, "WARNING" }, { SM_LOG_WARN, "WARNING" }, { SM_LOG_NOTICE, "NOTICE" }, { SM_LOG_INFO, "INFO" }, { SM_LOG_DEBUG, "DEBUG" }, { -1, NULL } }; static char * sm_l2t(int loglevel) { int i; i = 0; while (sm_loglev2txt[i].l2t_code != 0 || sm_loglev2txt[i].l2t_str != NULL) { if (sm_loglev2txt[i].l2t_code == loglevel) return sm_loglev2txt[i].l2t_str; ++i; } return "Bogus_Log_Level"; } #endif /* SM_LOG_SEVERITY */ /* ** SM_LOG_CREATE -- Create a log context ** ** Parameters: ** mctx -- memory context to use [currently unused] ** plctx -- (pointer to) log context (output) ** plcfg -- (pointer to) log config (output) ** ** Returns: ** usual sm_error code; ENOMEM */ sm_ret_T sm_log_create(sm_mem_P mctx, sm_log_ctx_P *plctx, sm_logconfig_P *plcfg) { sm_ret_T ret; sm_log_ctx_P lctx; sm_logconfig_P lcfg; (void)mctx; SM_REQUIRE(plctx != NULL); lctx = (sm_log_ctx_P) sm_zalloc(sizeof(*lctx)); if (NULL == lctx) return sm_error_temp(SM_EM_LOG, ENOMEM); #if MTA_USE_PTHREADS ret = pthread_mutex_init(&lctx->lctx_mutex, SM_PTHREAD_MUTEXATTR); if (ret != 0) { /* ret = sm_error_perm(SM_EM_LOG, ret); */ ret = sm_error_perm(SM_EM_LOG, ret); goto error; } #endif /* MTA_USE_PTHREADS */ lctx->lctx_str = sm_str_new(NULL, LOG_LEN, LOG_LEN_MAX); if (NULL == lctx->lctx_str) { ret = sm_error_temp(SM_EM_LOG, ENOMEM); goto error; } lctx->lctx_fd = INVALID_FD; lctx->sm_magic = SM_LOG_MAGIC; ret = sm_logconfig_create(lctx, &lcfg); if (sm_is_err(ret)) goto error; lctx->lctx_cfg = lcfg; if (plcfg != NULL) *plcfg = lcfg; lctx->lctx_dbg_level = UINT_MAX; *plctx = lctx; return SM_SUCCESS; error: if (lctx != NULL) { SM_STR_FREE(lctx->lctx_str); lctx->sm_magic = SM_MAGIC_NULL; sm_free_size(lctx, sizeof(*lctx)); } return ret; } /* ** SM_LOG_DESTROY -- Destroy a log context ** ** Parameters: ** lctx -- log context ** ** Returns: ** SM_SUCCESS */ sm_ret_T sm_log_destroy(sm_log_ctx_P lctx) { if (NULL == lctx) return SM_SUCCESS; SM_IS_LOGCTX(lctx); if (lctx->lctx_fp != NULL) sm_io_flush(lctx->lctx_fp); sm_logconfig_destroy(lctx->lctx_cfg); #if MTA_USE_PTHREADS (void) pthread_mutex_destroy(&lctx->lctx_mutex); #endif SM_STR_FREE(lctx->lctx_str); lctx->sm_magic = SM_MAGIC_NULL; sm_free_size(lctx, sizeof(*lctx)); return SM_SUCCESS; } /* ** SM_LOGCONFIG_CREATE -- Create a log configuration ** ** Parameters: ** lctx -- log context ** plcfg -- (pointer to) log config (output) ** ** Returns: ** usual sm_error code; ENOMEM */ sm_ret_T sm_logconfig_create(sm_log_ctx_P lctx, sm_logconfig_P *plcfg) { sm_logconfig_P lcfg; SM_REQUIRE(lctx != NULL); SM_REQUIRE(plcfg != NULL); lcfg = (sm_logconfig_P) sm_zalloc(sizeof(*lcfg)); if (NULL == lcfg) return sm_error_temp(SM_EM_LOG, ENOMEM); lcfg->sm_magic = SM_LOGCFG_MAGIC; *plcfg = lcfg; return SM_SUCCESS; } /* ** SM_LOGCONFIG_USE -- Use a log configuration ** ** Parameters: ** lctx -- log context ** lcfg -- log config ** ** Returns: ** SM_SUCCESS */ sm_ret_T sm_logconfig_use(sm_log_ctx_P lctx, sm_logconfig_P lcfg) { SM_IS_LOGCTX(lctx); SM_IS_LOGCFG(lcfg); return SM_SUCCESS; } /* ** SM_LOGCONFIG_GET -- Return current log configuration ** ** Parameters: ** lctx -- log context ** ** Returns: ** log config */ sm_logconfig_P sm_logconfig_get(sm_log_ctx_P lctx) { SM_IS_LOGCTX(lctx); return lctx->lctx_cfg; } /* ** SM_LOGCONFIG_DESTROY -- Destroy a log configuration ** ** Parameters: ** lcfg -- log config ** ** Returns: ** SM_SUCCESS */ sm_ret_T sm_logconfig_destroy(sm_logconfig_P lcfg) { if (NULL == lcfg) return SM_SUCCESS; SM_IS_LOGCFG(lcfg); lcfg->sm_magic = SM_MAGIC_NULL; sm_free_size(lcfg, sizeof(*lcfg)); return SM_SUCCESS; } sm_ret_T sm_log_registercategories(sm_log_ctx_P lctx, sm_logcategory_T categories[]) { uint u; SM_IS_LOGCTX(lctx); for (u = 0; u < SM_ARRAY_SIZE(lctx->lctx_cats); u++) { if (NULL == lctx->lctx_cats[u]) { lctx->lctx_cats[u] = categories; ++lctx->lctx_cat_n; return SM_SUCCESS; } } return sm_error_perm(SM_EM_LOG, SM_E_FULL); } sm_ret_T sm_log_registermodules(sm_log_ctx_P lctx, sm_logmodule_T modules[]) { uint u; SM_IS_LOGCTX(lctx); for (u = 0; u < SM_ARRAY_SIZE(lctx->lctx_mods); u++) { if (NULL == lctx->lctx_mods[u]) { lctx->lctx_mods[u] = modules; ++lctx->lctx_mod_n; return SM_SUCCESS; } } return sm_error_perm(SM_EM_LOG, SM_E_FULL); } /* ARGSUSED1 */ sm_ret_T sm_log_createchannel(sm_logconfig_P lcfg, const char *name, uint type, int priority, uint level, const sm_logdestination_P destination, uint flags) { SM_IS_LOGCFG(lcfg); return SM_SUCCESS; } /* ARGSUSED1 */ sm_ret_T sm_log_usechannel(sm_logconfig_P lcfg, const char *name, const sm_logcategory_P category, const sm_logmodule_P module) { SM_IS_LOGCFG(lcfg); return SM_SUCCESS; } /* ** SM_LOG_SETFILE -- Set a logfile in a log context ** ** Parameters: ** lctx -- log context ** file -- file to use ** ** Returns: ** SM_SUCCESS */ sm_ret_T sm_log_setfile(sm_log_ctx_P lctx, sm_file_T *file) { SM_IS_LOGCTX(lctx); lctx->lctx_fp = file; return SM_SUCCESS; } /* ** SM_LOG_SETFD -- Set a log fd in a log context ** ** Parameters: ** lctx -- log context ** fd -- fd to use ** ** Returns: ** SM_SUCCESS */ sm_ret_T sm_log_setfd(sm_log_ctx_P lctx, int fd) { SM_IS_LOGCTX(lctx); lctx->lctx_fd = fd; return SM_SUCCESS; } /* ** SM_LOG_SETFP_FD -- Set a logfile and fd in a log context ** ** Parameters: ** lctx -- log context ** fp -- file to use ** fd -- fd to use ** ** Returns: ** SM_SUCCESS */ sm_ret_T sm_log_setfp_fd(sm_log_ctx_P lctx, sm_file_T *fp, int fd) { SM_IS_LOGCTX(lctx); lctx->lctx_fp = fp; lctx->lctx_fd = fd; return SM_SUCCESS; } /* ** SM_LOG_SETFILENAME -- Set a logfile name in a log context ** ** Parameters: ** lctx -- log context ** name -- file name to use (will NOT be copied!) ** ** Returns: ** SM_SUCCESS */ sm_ret_T sm_log_setfilename(sm_log_ctx_P lctx, char *name) { SM_IS_LOGCTX(lctx); lctx->lctx_filename = name; return SM_SUCCESS; } /* ** SM_LOG_REOPEN -- Reopen a log file ** ** Parameters: ** lctx -- log context ** ** Returns: ** usual sm_error code. */ sm_ret_T sm_log_reopen(sm_log_ctx_P lctx) { sm_ret_T ret; #if MTA_USE_PTHREADS int r; #endif SM_IS_LOGCTX(lctx); ret = SM_SUCCESS; #if MTA_USE_PTHREADS r = pthread_mutex_lock(&lctx->lctx_mutex); SM_LOCK_OK(r); if (r != 0) { /* ret = sm_error_perm(SM_EM_LOG, r); */ ret = sm_error_perm(SM_EM_LOG, r); goto error; } #endif /* MTA_USE_PTHREADS */ if (lctx->lctx_filename != NULL && *lctx->lctx_filename != '\0') { sm_io_flush(lctx->lctx_fp); sm_io_close(lctx->lctx_fp, SM_IO_CF_NONE); lctx->lctx_fp = NULL; ret = sm_io_open(SmStStdio, lctx->lctx_filename, SM_IO_WRONLY, &lctx->lctx_fp, SM_IO_WHAT_END); if (sm_is_err(ret)) goto errunl; } else if (NULL == lctx->lctx_fp || lctx->lctx_fd == INVALID_FD) /* nothing */; /* XXX Really? */ else { sm_io_flush(lctx->lctx_fp); ret = ftruncate(lctx->lctx_fd, (off_t) 0); if (ret < 0) { ret = sm_error_temp(SM_EM_LOG, errno); goto errunl; } ret = sm_io_seek(lctx->lctx_fp, 0L, SM_IO_SEEK_SET); if (sm_is_err(ret)) goto errunl; } #if SM_LOG_ROTATE lctx->lctx_entries = 0; lctx->lctx_rotated = time(NULLT); #endif #if MTA_USE_PTHREADS r = pthread_mutex_unlock(&lctx->lctx_mutex); SM_ASSERT(r == 0); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_LOG, r); #endif /* MTA_USE_PTHREADS */ return ret; errunl: #if MTA_USE_PTHREADS r = pthread_mutex_unlock(&lctx->lctx_mutex); SM_ASSERT(r == 0); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_LOG, r); error: #endif /* MTA_USE_PTHREADS */ return ret; } /* ** SM_LOG_TSTAMP -- return timestamp ** ** Parameters: ** none. ** ** Returns: ** pointer to timestamp. ** ** Locking: must be provided by caller. */ static char * sm_log_tstamp(void) { static char str[32] = "[1900-00-00/00:00:00] "; static time_t lastt = 0; struct tm *tmp; time_t currt; #if MTA_USE_PTHREADS struct tm tm; #endif #if MTA_USE_STATETHREADS currt = st_time(); #else currt = time(NULLT); #endif if (currt == lastt || currt == (time_t) -1) return str; #if MTA_USE_PTHREADS tmp = localtime_r(&currt, &tm); #else tmp = localtime(&currt); #endif sm_snprintf(str, sizeof(str), "[%d-%02d-%02d/%02d:%02d:%02d] ", 1900 + tmp->tm_year, /* HACK */ tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); lastt = currt; return str; } #if SM_LOG_ROTATE /* ** SM_LOG_ROTATE -- Rotate a log file ** ** Parameters: ** lctx -- log context ** ** Returns: ** SM_SUCCESS */ sm_ret_T sm_log_rotate(sm_log_ctx_P lctx) { SM_IS_LOGCTX(lctx); if (lctx->lctx_fp != NULL) { } lctx->lctx_entries = 0; lctx->lctx_rotated = time(NULLT); return SM_SUCCESS; } #endif /* SM_LOG_ROTATE */ /* ** SM_LOG_WRITE -- Write a logfile entry ** ** Parameters: ** lctx -- log context ** category -- category ** module -- module ** priority -- priority ** level -- log level ** format -- format ** ... -- arguments ** ** Returns: ** SM_SUCCESS except for (un)lock errors */ sm_ret_T sm_log_write(sm_log_ctx_P lctx, sm_logcategory_P category, sm_logmodule_P module, int priority, uint level, const char *format, ...) { va_list va; #if MTA_USE_PTHREADS int r; #endif sm_ret_T ret; if (lctx != NULL) { SM_IS_LOGCTX(lctx); if (!sm_log_wouldlog(lctx, category, module, level)) return SM_SUCCESS; } /* Allow NULL context, necessary for emergency logging */ if (NULL == lctx) { int r, save_errno; char *log; log = NULL; va_start(va, format); r = sm_vasprintf(&log, format, va); save_errno = errno; if (r != -1) syslog(priority, "%.*s", LOG_LEN_MAX, log); va_end(va); SM_FREE(log); return (r != -1) ? SM_SUCCESS : sm_error_perm(SM_EM_LOG, save_errno); } ret = SM_SUCCESS; #if MTA_USE_PTHREADS r = pthread_mutex_lock(&lctx->lctx_mutex); SM_LOCK_OK(r); if (r != 0) { /* ret = sm_error_perm(SM_EM_LOG, r); */ return sm_error_perm(SM_EM_LOG, r); } #endif /* MTA_USE_PTHREADS */ if (NULL == lctx->lctx_fp) { sm_str_clr(lctx->lctx_str); #if SM_LOG_SEVERITY sm_str_scat(lctx->lctx_str, sm_l2t(priority)); #endif va_start(va, format); sm_strvprintf(lctx->lctx_str, format, va); va_end(va); syslog(priority, "%.*s", (int) sm_str_getlen(lctx->lctx_str), (const char *) sm_str_data(lctx->lctx_str)); } else { sm_io_fputs(lctx->lctx_fp, (const uchar *) sm_log_tstamp()); #if SM_LOG_SEVERITY sm_io_fprintf(lctx->lctx_fp, "sev=%s, ", sm_l2t(priority)); #endif va_start(va, format); sm_io_vfprintf(lctx->lctx_fp, format, va); va_end(va); sm_putc(lctx->lctx_fp, '\n'); #if SM_LOG_ROTATE if (++lctx->lctx_entries > MAX_LOG_ENTRIES) sm_log_rotate(lctx); #endif } #if MTA_USE_PTHREADS r = pthread_mutex_unlock(&lctx->lctx_mutex); SM_ASSERT(r == 0); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_LOG, r); #endif /* MTA_USE_PTHREADS */ return ret; } /* ** SM_LOG_VWRITE -- Write a logfile entry ** ** Parameters: ** lctx -- log context ** category -- category ** module -- module ** priority -- priority ** format -- format ** args -- arguments ** ** Returns: ** SM_SUCCESS except for (un)lock errors */ sm_ret_T sm_log_vwrite(sm_log_ctx_P lctx, sm_logcategory_P category, sm_logmodule_P module, int priority, uint level, const char *format, va_list args) { #if MTA_USE_PTHREADS int r; #endif sm_ret_T ret; /* Allow NULL context, necessary for emergence logging. */ if (lctx != NULL) { SM_IS_LOGCTX(lctx); if (lctx->lctx_dbg_level < level) return SM_SUCCESS; } /* HACK: this doesn't really work, see begin of file */ if (NULL == lctx) { int r, save_errno; char *log; log = NULL; r = sm_vasprintf(&log, format, args); save_errno = errno; if (r != -1) syslog(priority, "%.*s", LOG_LEN_MAX, log); SM_FREE(log); return (r != -1) ? SM_SUCCESS : sm_error_perm(SM_EM_LOG, save_errno); } ret = SM_SUCCESS; #if MTA_USE_PTHREADS r = pthread_mutex_lock(&lctx->lctx_mutex); SM_LOCK_OK(r); if (r != 0) { /* ret = sm_error_perm(SM_EM_LOG, r); */ return sm_error_perm(SM_EM_LOG, r); } #endif /* MTA_USE_PTHREADS */ if (NULL == lctx->lctx_fp) { sm_str_clr(lctx->lctx_str); #if SM_LOG_SEVERITY sm_str_scat(lctx->lctx_str, sm_l2t(priority)); #endif sm_strvprintf(lctx->lctx_str, format, args); syslog(priority, "%.*s", (int) sm_str_getlen(lctx->lctx_str), (const char *) sm_str_data(lctx->lctx_str)); } else { sm_io_fputs(lctx->lctx_fp, (const uchar *) sm_log_tstamp()); #if SM_LOG_SEVERITY sm_io_fprintf(lctx->lctx_fp, "sev=%s, ", sm_l2t(priority)); #endif sm_io_vfprintf(lctx->lctx_fp, format, args); sm_putc(lctx->lctx_fp, '\n'); #if SM_LOG_ROTATE if (++lctx->lctx_entries > MAX_LOG_ENTRIES) sm_log_rotate(lctx); #endif } #if MTA_USE_PTHREADS r = pthread_mutex_unlock(&lctx->lctx_mutex); SM_ASSERT(r == 0); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_LOG, r); #endif /* MTA_USE_PTHREADS */ return ret; } #if 0 sm_ret_T sm_log_write1(sm_log_ctx_P lctx, sm_logcategory_P category, sm_logmodule_P module, int priority, int level, const char *format, ...) { SM_IS_LOGCTX(lctx); return SM_SUCCESS; } sm_ret_T sm_log_vwrite1(sm_log_ctx_P lctx, sm_logcategory_P category, sm_logmodule_P module, int priority, int level, const char *format, va_list args) { SM_IS_LOGCTX(lctx); return SM_SUCCESS; } #endif /* 0 */ sm_ret_T sm_log_setdebuglevel(sm_log_ctx_P lctx, uint level) { SM_IS_LOGCTX(lctx); lctx->lctx_dbg_level = level; return SM_SUCCESS; } uint sm_log_getdebuglevel(sm_log_ctx_P lctx) { SM_IS_LOGCTX(lctx); return lctx->lctx_dbg_level; } /* ** SM_LOG_WOULDLOG -- Would something be logged? ** ** Parameters: ** lctx -- log context ** category -- category ** module -- module ** level -- log level ** ** Returns: ** SM_SUCCESS except for (un)lock errors */ bool sm_log_wouldlog(sm_log_ctx_P lctx, sm_logcategory_P category, sm_logmodule_P module, uint level) { SM_IS_LOGCTX(lctx); if (NULL == category || 0 == category->smlc_log_level) if (NULL == module || 0 == module->smlm_log_level) return lctx->lctx_dbg_level >= level; else return module->smlm_log_level >= level; else if (NULL == module || 0 == module->smlm_log_level) return category->smlc_log_level >= level; else return module->smlm_log_level >= level && category->smlc_log_level >= level; } #if 0 sm_ret_T sm_log_setduplicateinterval(sm_logconfig_P lcfg, uint interval) { SM_IS_LOGCFG(lcfg); return SM_SUCCESS; } uint sm_log_getduplicateinterval(sm_logconfig_P lcfg) { SM_IS_LOGCFG(lcfg); return SM_SUCCESS; } #endif /* 0 */ /* ARGSUSED1 */ sm_ret_T sm_log_settag(sm_logconfig_P lcfg, const char *tag) { SM_IS_LOGCFG(lcfg); return SM_SUCCESS; } sm_ret_T sm_log_gettag(sm_logconfig_P lcfg) { SM_IS_LOGCFG(lcfg); return SM_SUCCESS; } sm_ret_T sm_log_opensyslog(const char *tag, int options, int facility) { openlog(tag, options, facility); return SM_SUCCESS; } sm_ret_T sm_log_closesyslog(void) { closelog(); return SM_SUCCESS; } sm_ret_T sm_log_closefilelogs(sm_log_ctx_P lctx) { SM_IS_LOGCTX(lctx); return SM_SUCCESS; } sm_ret_T sm_log_categorybyname(sm_log_ctx_P lctx, const char *name, sm_logcategory_P *pcategories) { uint ul; sm_logcategory_P categories; SM_IS_LOGCTX(lctx); SM_REQUIRE(name != NULL); SM_REQUIRE(pcategories != NULL); for (ul = 0; ul < SM_ARRAY_SIZE(lctx->lctx_cats); ul++) { categories = lctx->lctx_cats[ul]; if (NULL == categories) continue; while (categories->smlc_name != NULL) { if (strcmp(name, categories->smlc_name) == 0) { *pcategories = categories; return SM_SUCCESS; } ++categories; } } return sm_error_perm(SM_EM_LOG, SM_E_NOTFOUND); } sm_ret_T sm_log_modulebyname(sm_log_ctx_P lctx, const char *name, sm_logmodule_P *pmodules) { uint u; sm_logmodule_P modules; SM_IS_LOGCTX(lctx); SM_REQUIRE(name != NULL); SM_REQUIRE(pmodules != NULL); for (u = 0; u < SM_ARRAY_SIZE(lctx->lctx_mods); u++) { modules = lctx->lctx_mods[u]; if (NULL == modules) continue; while (modules->smlm_name != NULL) { if (strcmp(name, modules->smlm_name) == 0) { *pmodules = modules; return SM_SUCCESS; } ++modules; } } return sm_error_perm(SM_EM_LOG, SM_E_NOTFOUND); } sm_ret_T sm_log_setcontext(sm_log_ctx_P lctx) { SM_IS_LOGCTX(lctx); return SM_SUCCESS; } /* ** SM_LOG_CAT_SET_LEVEL -- Set log level per category ** ** Parameters: ** lctx -- log context ** name -- name of category ** format -- format ** ** Returns: ** usual sm_error code. ** ** Locking: none */ sm_ret_T sm_log_cat_set_level(sm_log_ctx_P lctx, const char *name, uint level) { sm_ret_T ret; sm_logcategory_P categories; SM_IS_LOGCTX(lctx); SM_REQUIRE(name != NULL); ret = sm_log_categorybyname(lctx, name, &categories); if (sm_is_err(ret)) return ret; SM_ASSERT(categories != NULL); categories->smlc_log_level = level; return SM_SUCCESS; } sm_ret_T sm_log_mod_set_level(sm_log_ctx_P lctx, const char *name, uint level) { sm_ret_T ret; sm_logmodule_P modules; SM_IS_LOGCTX(lctx); SM_REQUIRE(name != NULL); ret = sm_log_modulebyname(lctx, name, &modules); if (sm_is_err(ret)) return ret; SM_ASSERT(modules != NULL); modules->smlm_log_level = level; return SM_SUCCESS; }