/*
* Copyright(c) 2009 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Maintained at www.Open-FCoE.org
*/
#define _GNU_SOURCE /* for GNU definition of strerror_r */
#include "fcoemon_utils.h"
#include "net_types.h"
#include "fc_types.h"
int use_syslog;
static int debug;
/*
* Size of on-stack line buffers.
* These shouldn't be to large for a kernel stack frame.
*/
#define SA_LOG_BUF_LEN 200 /* on-stack line buffer size */
void enable_syslog(int enable)
{
use_syslog = enable;
}
void enable_debug_log(int enable)
{
debug = enable;
}
/*
* log with a variable argument list.
*/
static void
sa_log_va(const char *func, const char *format, va_list arg)
{
size_t len;
size_t flen;
int add_newline;
char sa_buf[SA_LOG_BUF_LEN];
char *bp;
/*
* If the caller didn't provide a newline at the end, we will.
*/
len = strlen(format);
add_newline = 0;
if (!len || format[len - 1] != '\n')
add_newline = 1;
bp = sa_buf;
len = sizeof(sa_buf);
if (func) {
flen = snprintf(bp, len, "%s: ", func);
len -= flen;
bp += flen;
}
flen = vsnprintf(bp, len, format, arg);
if (add_newline && flen < len) {
bp += flen;
*bp++ = '\n';
*bp = '\0';
}
sa_log_output(sa_buf);
}
/*
* log
*/
void
sa_log(const char *format, ...)
{
va_list arg;
va_start(arg, format);
sa_log_va(NULL, format, arg);
va_end(arg);
}
/*
* debug log, controlled by static debug flag
*/
void
sa_log_debug(const char *format, ...)
{
va_list arg;
if (!debug)
return;
va_start(arg, format);
sa_log_va(NULL, format, arg);
va_end(arg);
}
/*
* log with error number.
*/
void
sa_log_err(int error, const char *func, const char *format, ...)
{
va_list arg;
char buf[SA_LOG_BUF_LEN];
if (func)
sa_log("%s: error %d %s", func, error,
strerror_r(error, buf, sizeof(buf)));
else
sa_log("error %d %s", error,
strerror_r(error, buf, sizeof(buf)));
va_start(arg, format);
sa_log_va(func, format, arg);
va_end(arg);
}
/*
* Size of on-stack line buffers.
* These shouldn't be to large for a kernel stack frame.
*/
#define SA_LOG_BUF_LEN 200 /* on-stack line buffer size */
/*
* Assert failures.
*/
void
assert_failed(const char *format, ...)
{
va_list arg;
char buf[SA_LOG_BUF_LEN];
va_start(arg, format);
vsnprintf(buf, sizeof(buf), format, arg);
va_end(arg);
sa_log_abort(buf);
}
/*
* Log options.
* These may be set directly by callers.
*/
u_int sa_log_flags; /* timestamp and other option flags */
int sa_log_time_delta_min = 1; /* minimum diff to print in millisec */
char *sa_log_prefix; /* string to print before any message */
/*
* Put timestamp on front of each log line, as controlled by tunables above.
*/
static void
sa_log_timestamp(void)
{
static struct timeval tlast;
char ctime_buf[30];
struct timeval t;
struct timeval diff;
gettimeofday(&t, NULL);
if (sa_log_flags & SA_LOGF_TIME) {
ctime_r(&t.tv_sec, ctime_buf);
ctime_buf[11 + 8] = '\0'; /* trim ctime after seconds */
fprintf(stderr, "%s.%3.3ld ",
ctime_buf + 11, t.tv_usec / 1000);
}
if (sa_log_flags & SA_LOGF_DELTA) {
if (tlast.tv_sec == 0)
tlast = t;
timersub(&t, &tlast, &diff);
tlast = t;
if (diff.tv_sec != 0 ||
diff.tv_usec >= sa_log_time_delta_min * 1000)
fprintf(stderr, "%4ld.%3.3ld ",
diff.tv_sec, diff.tv_usec / 1000);
else
fprintf(stderr, "%8s ", "");
}
if (sa_log_prefix)
fprintf(stderr, "%s: ", sa_log_prefix);
}
void
sa_log_output(const char *buf)
{
if (use_syslog) {
syslog(LOG_INFO, "%s", buf);
return;
}
sa_log_timestamp();
fprintf(stderr, "%s", buf);
fflush(stderr);
}
void
sa_log_abort(const char *buf)
{
sa_log_output(buf);
abort();
}
/*
* Make a printable NUL-terminated copy of the string.
* The source buffer might not be NUL-terminated.
*/
char *
sa_strncpy_safe(char *dest, size_t len, const char *src, size_t src_len)
{
char *dp = dest;
const char *sp = src;
while (len-- > 1 && src_len-- > 0 && *sp != '\0') {
*dp++ = isprint(*sp) ? *sp : (isspace(*sp) ? ' ' : '.');
sp++;
}
*dp = '\0';
/*
* Take off trailing blanks.
*/
while (--dp >= dest && isspace(*dp))
*dp = '\0';
return dest;
}
/** sa_enum_decode(buf, len, tp, val)
*
* @param buf buffer for result (may be used or not).
* @param len size of buffer (at least 32 bytes recommended).
* @param tp pointer to table of names and values, struct sa_nameval.
* @param val value to be decoded into a name.
* @returns pointer to name string and in buf. Unknown values are put into buffer in hex.
*/
const char *
sa_enum_decode(char *buf, size_t len, const struct sa_nameval *tp, u_int val)
{
for (; tp->nv_name != NULL; tp++) {
if (tp->nv_val == val) {
snprintf(buf, len, "%s", tp->nv_name);
return tp->nv_name;
}
}
snprintf(buf, len, "Unknown (code 0x%X)", val);
return buf;
}
/** sa_enum_encode(tp, name, valp)
*
* @param tp pointer to table of names and values, struct sa_nameval.
* @param name string to be encoded into a value
* @returns zero on success, non-zero if no matching string found.
*/
int
sa_enum_encode(const struct sa_nameval *tp, const char *name, u_int32_t *valp)
{
for (; tp->nv_name != NULL; tp++) {
if (strcasecmp(tp->nv_name, name) == 0) {
*valp = tp->nv_val;
return 0;
}
}
return -1;
}