mirror of
https://github.com/obgm/libcoap.git
synced 2026-10-31 23:15:48 +03:00
225 lines
4.9 KiB
C
225 lines
4.9 KiB
C
/* debug.c -- debug utilities
|
|
*
|
|
* Copyright (C) 2010,2011 Olaf Bergmann <bergmann@tzi.org>
|
|
*
|
|
* This file is part of the CoAP library libcoap. Please see
|
|
* README for terms of use.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
|
|
#ifdef HAVE_ARPA_INET_H
|
|
#include <arpa/inet.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_TIME_H
|
|
#include <time.h>
|
|
#endif
|
|
|
|
#include "debug.h"
|
|
#include "net.h"
|
|
|
|
static coap_log_t maxlog = LOG_WARN; /* default maximum log level */
|
|
|
|
coap_log_t
|
|
coap_get_log_level() {
|
|
return maxlog;
|
|
}
|
|
|
|
void
|
|
coap_set_log_level(coap_log_t level) {
|
|
maxlog = level;
|
|
}
|
|
|
|
/* this array has the same order as the type log_t */
|
|
static char *loglevels[] = {
|
|
"EMRG", "ALRT", "CRIT", "WARN", "NOTE", "INFO", "DEBG"
|
|
};
|
|
|
|
#ifdef HAVE_TIME_H
|
|
|
|
static inline size_t
|
|
print_timestamp(char *s, size_t len, coap_tick_t t) {
|
|
struct tm *tmp;
|
|
time_t now = clock_offset + (t / COAP_TICKS_PER_SECOND);
|
|
tmp = localtime(&now);
|
|
return strftime(s, len, "%b %d %H:%M:%S", tmp);
|
|
}
|
|
|
|
#else /* alternative implementation: just print the timestamp */
|
|
|
|
static inline size_t
|
|
print_timestamp(char *s, size_t len, coap_tick_t t) {
|
|
return snprintf(s, len, "%u.%03u",
|
|
clock_offset + (t / COAP_TICKS_PER_SECOND),
|
|
t % COAP_TICKS_PER_SECOND);
|
|
}
|
|
|
|
#endif /* HAVE_TIME_H */
|
|
|
|
#ifndef NDEBUG
|
|
|
|
/**
|
|
* A length-safe strlen() fake.
|
|
*
|
|
* @param s The string to count characters != 0.
|
|
* @param maxlen The maximum length of @p s.
|
|
*
|
|
* @return The length of @p s.
|
|
*/
|
|
static inline size_t
|
|
strnlen(unsigned char *s, size_t maxlen) {
|
|
size_t n = 0;
|
|
while(*s++ && n < maxlen)
|
|
++n;
|
|
return n;
|
|
}
|
|
|
|
unsigned int
|
|
print_readable( const unsigned char *data, unsigned int len,
|
|
unsigned char *result, unsigned int buflen, int encode_always ) {
|
|
static const unsigned char hex[] = "0123456789ABCDEF";
|
|
unsigned int cnt = 0;
|
|
while ( len && (cnt < buflen-1) ) {
|
|
if ( !encode_always && isprint( *data ) ) {
|
|
*result++ = *data;
|
|
++cnt;
|
|
} else {
|
|
if ( cnt+4 < buflen-1 ) {
|
|
*result++ = '\\';
|
|
*result++ = 'x';
|
|
*result++ = hex[(*data & 0xf0) >> 4];
|
|
*result++ = hex[*data & 0x0f ];
|
|
cnt += 4;
|
|
} else
|
|
break;
|
|
}
|
|
|
|
++data; --len;
|
|
}
|
|
|
|
*result = '\0';
|
|
return cnt;
|
|
}
|
|
|
|
#ifndef min
|
|
#define min(a,b) ((a) < (b) ? (a) : (b))
|
|
#endif
|
|
|
|
size_t
|
|
coap_print_addr(const struct __coap_address_t *addr, unsigned char *buf, size_t len) {
|
|
#ifdef HAVE_ARPA_INET_H
|
|
const void *addrptr = NULL;
|
|
in_port_t port;
|
|
unsigned char *p = buf;
|
|
|
|
switch (addr->addr.sa.sa_family) {
|
|
case AF_INET:
|
|
addrptr = &addr->addr.sin.sin_addr;
|
|
port = ntohs(addr->addr.sin.sin_port);
|
|
break;
|
|
case AF_INET6:
|
|
if (len < 7) /* do not proceed if buffer is even too short for [::]:0 */
|
|
return 0;
|
|
|
|
*p++ = '[';
|
|
|
|
addrptr = &addr->addr.sin6.sin6_addr;
|
|
port = ntohs(addr->addr.sin6.sin6_port);
|
|
|
|
break;
|
|
default:
|
|
memcpy(buf, "(unknown address type)", min(22, len));
|
|
return min(22, len);
|
|
}
|
|
|
|
if (inet_ntop(addr->addr.sa.sa_family, addrptr, (char *)p, len) == 0) {
|
|
perror("coap_print_addr");
|
|
return 0;
|
|
}
|
|
|
|
p += strnlen(p, len);
|
|
|
|
if (addr->addr.sa.sa_family == AF_INET6) {
|
|
if (p < buf + len) {
|
|
*p++ = ']';
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
p += snprintf((char *)p, buf + len - p + 1, ":%d", port);
|
|
|
|
return buf + len - p;
|
|
#else
|
|
/* TODO: output addresses manually */
|
|
#warn "inet_ntop() not available, network addresses will "
|
|
"not be included in debug output"
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
coap_show_pdu(const coap_pdu_t *pdu) {
|
|
unsigned char buf[COAP_MAX_PDU_SIZE]; /* need some space for output creation */
|
|
|
|
fprintf(COAP_DEBUG_FD, "v:%d t:%d oc:%d c:%d id:%u",
|
|
pdu->hdr->version, pdu->hdr->type,
|
|
pdu->hdr->optcnt, pdu->hdr->code, ntohs(pdu->hdr->id));
|
|
|
|
/* show options, if any */
|
|
if (pdu->hdr->optcnt) {
|
|
coap_opt_iterator_t opt_iter;
|
|
coap_option_iterator_init((coap_pdu_t *)pdu, &opt_iter, COAP_OPT_ALL);
|
|
|
|
fprintf(COAP_DEBUG_FD, " o:");
|
|
while (coap_option_next(&opt_iter)) {
|
|
|
|
if (print_readable(COAP_OPT_VALUE(opt_iter.option),
|
|
COAP_OPT_LENGTH(opt_iter.option),
|
|
buf, sizeof(buf), 0 ))
|
|
fprintf(COAP_DEBUG_FD, " %d:%s", opt_iter.type, buf);
|
|
}
|
|
}
|
|
|
|
if (pdu->data < (unsigned char *)pdu->hdr + pdu->length) {
|
|
print_readable(pdu->data,
|
|
(unsigned char *)pdu->hdr + pdu->length - pdu->data,
|
|
buf, sizeof(buf), 0 );
|
|
fprintf(COAP_DEBUG_FD, " d:%s", buf);
|
|
}
|
|
fprintf(COAP_DEBUG_FD, "\n");
|
|
fflush(COAP_DEBUG_FD);
|
|
}
|
|
|
|
#endif /* NDEBUG */
|
|
|
|
void
|
|
coap_log_impl(coap_log_t level, char *format, ...) {
|
|
char timebuf[32];
|
|
coap_tick_t now;
|
|
va_list ap;
|
|
FILE *log_fd;
|
|
|
|
if (maxlog < level)
|
|
return;
|
|
|
|
log_fd = level <= LOG_CRIT ? COAP_ERR_FD : COAP_DEBUG_FD;
|
|
|
|
coap_ticks(&now);
|
|
if (print_timestamp(timebuf,sizeof(timebuf), now))
|
|
fprintf(log_fd, "%s ", timebuf);
|
|
|
|
if (level <= LOG_DEBUG)
|
|
fprintf(log_fd, "%s ", loglevels[level]);
|
|
|
|
va_start(ap, format);
|
|
vfprintf(log_fd, format, ap);
|
|
va_end(ap);
|
|
fflush(stdout);
|
|
}
|