Merge commit 'cc6313cc697f4c90cf0fc1c5b01cb1871a075f26'
authorHarald Welte <laforge@gnumonks.org>
Fri, 26 Mar 2010 15:41:34 +0000 (23:41 +0800)
committerHarald Welte <laforge@gnumonks.org>
Fri, 26 Mar 2010 15:41:34 +0000 (23:41 +0800)
1  2 
src/shared/libosmocore/include/osmocore/Makefile.am
src/shared/libosmocore/include/osmocore/logging.h
src/shared/libosmocore/include/osmocore/write_queue.h
src/shared/libosmocore/src/Makefile.am
src/shared/libosmocore/src/logging.c
src/shared/libosmocore/src/write_queue.c

index fb4f089,0000000..1c3a33f
mode 100644,000000..100644
--- /dev/null
@@@ -1,12 -1,0 +1,12 @@@
-                  gsm48_ie.h
 +osmocore_HEADERS = signal.h linuxlist.h timer.h select.h msgb.h \
 +                 tlv.h bitvec.h comp128.h statistics.h gsm_utils.h utils.h \
 +                 gsmtap.h write_queue.h rsl.h gsm48.h rxlev_stat.h mncc.h \
++                 gsm48_ie.h logging.h
 +
 +if ENABLE_TALLOC
 +osmocore_HEADERS += talloc.h
 +endif
 +
 +osmocoredir = $(includedir)/osmocore
 +
 +SUBDIRS = protocol
index 0000000,0000000..93f18a0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,130 @@@
++#ifndef _OSMOCORE_LOGGING_H
++#define _OSMOCORE_LOGGING_H
++
++#include <stdio.h>
++#include <stdint.h>
++#include <osmocore/linuxlist.h>
++
++#define LOG_MAX_CATEGORY      32
++#define LOG_MAX_CTX           8
++#define LOG_MAX_FILTERS       8
++
++#define DEBUG
++
++#ifdef DEBUG
++#define DEBUGP(ss, fmt, args...) logp(ss, __FILE__, __LINE__, 0, fmt, ## args)
++#define DEBUGPC(ss, fmt, args...) logp(ss, __FILE__, __LINE__, 1, fmt, ## args)
++#else
++#define DEBUGP(xss, fmt, args...)
++#define DEBUGPC(ss, fmt, args...)
++#endif
++
++#define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
++
++char *hexdump(const unsigned char *buf, int len);
++void logp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 5, 6)));
++
++/* new logging interface */
++#define LOGP(ss, level, fmt, args...) \
++      logp2(ss, level, __FILE__, __LINE__, 0, fmt, ##args)
++#define LOGPC(ss, level, fmt, args...) \
++      logp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args)
++
++/* different levels */
++#define LOGL_DEBUG    1       /* debugging information */
++#define LOGL_INFO     3
++#define LOGL_NOTICE   5       /* abnormal/unexpected condition */
++#define LOGL_ERROR    7       /* error condition, requires user action */
++#define LOGL_FATAL    8       /* fatal, program aborted */
++
++#define LOG_FILTER_ALL        0x0001
++
++struct log_category {
++      uint8_t loglevel;
++      uint8_t enabled;
++};
++
++struct log_info_cat {
++      const char *name;
++      const char *color;
++      const char *description;
++      uint8_t loglevel;
++      uint8_t enabled;
++};
++
++/* log context information, passed to filter */
++struct log_context {
++      void *ctx[LOG_MAX_CTX+1];
++};
++
++struct log_target;
++
++typedef int log_filter(const struct log_context *ctx,
++                     struct log_target *target);
++
++struct log_info {
++      /* filter callback function */
++      log_filter *filter_fn;
++
++      /* per-category information */
++      const struct log_info_cat *cat;
++      unsigned int num_cat;
++};
++
++struct log_target {
++        struct llist_head entry;
++
++      int filter_map;
++      void *filter_data[LOG_MAX_FILTERS+1];
++
++      struct log_category categories[LOG_MAX_CATEGORY+1];
++      uint8_t loglevel;
++      int use_color:1;
++      int print_timestamp:1;
++
++      union {
++              struct {
++                      FILE *out;
++              } tgt_stdout;
++
++              struct {
++                      int priority;
++              } tgt_syslog;
++
++              struct {
++                      void *vty;
++              } tgt_vty;
++      };
++
++        void (*output) (struct log_target *target, const char *string);
++};
++
++/* use the above macros */
++void logp2(unsigned int subsys, unsigned int level, char *file,
++         int line, int cont, const char *format, ...)
++                              __attribute__ ((format (printf, 6, 7)));
++void log_init(const struct log_info *cat);
++
++/* context management */
++void log_reset_context(void);
++int log_set_context(uint8_t ctx, void *value);
++
++/* filter on the targets */
++void log_set_all_filter(struct log_target *target, int);
++
++void log_set_use_color(struct log_target *target, int);
++void log_set_print_timestamp(struct log_target *target, int);
++void log_set_log_level(struct log_target *target, int log_level);
++void log_parse_category_mask(struct log_target *target, const char* mask);
++int log_parse_level(const char *lvl);
++int log_parse_category(const char *category);
++void log_set_category_filter(struct log_target *target, int category,
++                             int enable, int level);
++
++/* management of the targets */
++struct log_target *log_target_create(void);
++struct log_target *log_target_create_stderr(void);
++void log_add_target(struct log_target *target);
++void log_del_target(struct log_target *target);
++
++#endif /* _OSMOCORE_LOGGING_H */
index c84000c,0000000..64d4159
mode 100644,000000..100644
--- /dev/null
@@@ -1,44 -1,0 +1,45 @@@
 +/* Generic write queue implementation */
 +/*
 + * (C) 2010 by Holger Hans Peter Freyther
 + * (C) 2010 by On-Waves
 + *
 + * All Rights Reserved
 + *
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation; either version 2 of the License, or
 + * (at your option) any later version.
 + *
 + * This program is distributed in the hope that 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
 + *
 + */
 +#ifndef write_queue_h
 +#define write_queue_h
 +
 +#include "select.h"
 +#include "msgb.h"
 +
 +struct write_queue {
 +      struct bsc_fd bfd;
 +      unsigned int max_length;
 +      unsigned int current_length;
 +
 +      struct llist_head msg_queue;
 +
 +      int (*read_cb)(struct bsc_fd *fd);
 +      int (*write_cb)(struct bsc_fd *fd, struct msgb *msg);
 +};
 +
 +void write_queue_init(struct write_queue *queue, int max_length);
++void write_queue_clear(struct write_queue *queue);
 +int write_queue_enqueue(struct write_queue *queue, struct msgb *data);
 +int write_queue_bfd_cb(struct bsc_fd *fd, unsigned int what);
 +
 +#endif
index f0effa2,0000000..1697807
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,17 @@@
-                        write_queue.c utils.c rsl.c gsm48.c gsm48_ie.c
 +# This is _NOT_ the library release version, it's an API version.
 +# Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification
 +LIBVERSION=0:0:0
 +
 +INCLUDES = $(all_includes) -I$(top_srcdir)/include
 +AM_CFLAGS = -fPIC -Wall
 +
 +lib_LTLIBRARIES = libosmocore.la
 +
 +libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c rxlev_stat.c \
 +                       tlv_parser.c bitvec.c comp128.c gsm_utils.c statistics.c \
++                       write_queue.c utils.c rsl.c gsm48.c gsm48_ie.c \
++                       logging.c
 +
 +if ENABLE_TALLOC
 +libosmocore_la_SOURCES += talloc.c
 +endif
index 0000000,0000000..2a132eb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,355 @@@
++/* Debugging/Logging support code */
++
++/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
++ * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
++ * All Rights Reserved
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
++ *
++ */
++
++#include <stdarg.h>
++#include <stdlib.h>
++#include <stdio.h>
++#include <string.h>
++#include <strings.h>
++#include <time.h>
++#include <errno.h>
++
++#include <osmocore/talloc.h>
++#include <osmocore/utils.h>
++#include <osmocore/logging.h>
++
++static const struct log_info *log_info;
++
++static struct log_context log_context;
++static void *tall_log_ctx = NULL;
++static LLIST_HEAD(target_list);
++
++static const struct value_string loglevel_strs[] = {
++      { 0,            "EVERYTHING" },
++      { LOGL_DEBUG,   "DEBUG" },
++      { LOGL_INFO,    "INFO" },
++      { LOGL_NOTICE,  "NOTICE" },
++      { LOGL_ERROR,   "ERROR" },
++      { LOGL_FATAL,   "FATAL" },
++      { 0, NULL },
++};
++
++int log_parse_level(const char *lvl)
++{
++      return get_string_value(loglevel_strs, lvl);
++}
++
++int log_parse_category(const char *category)
++{
++      int i;
++
++      for (i = 0; i < log_info->num_cat; ++i) {
++              if (!strcasecmp(log_info->cat[i].name+1, category))
++                      return i;
++      }
++
++      return -EINVAL;
++}
++
++/*
++ * Parse the category mask.
++ * The format can be this: category1:category2:category3
++ * or category1,2:category2,3:...
++ */
++void log_parse_category_mask(struct log_target* target, const char *_mask)
++{
++      int i = 0;
++      char *mask = strdup(_mask);
++      char *category_token = NULL;
++
++      /* Disable everything to enable it afterwards */
++      for (i = 0; i < ARRAY_SIZE(target->categories); ++i)
++              target->categories[i].enabled = 0;
++
++      category_token = strtok(mask, ":");
++      do {
++              for (i = 0; i < log_info->num_cat; ++i) {
++                      char* colon = strstr(category_token, ",");
++                      int length = strlen(category_token);
++
++                      if (colon)
++                          length = colon - category_token;
++
++                      if (strncasecmp(log_info->cat[i].name, category_token,
++                                      length) == 0) {
++                              int level = 0;
++
++                              if (colon)
++                                      level = atoi(colon+1);
++
++                              target->categories[i].enabled = 1;
++                              target->categories[i].loglevel = level;
++                      }
++              }
++      } while ((category_token = strtok(NULL, ":")));
++
++      free(mask);
++}
++
++static const char* color(int subsys)
++{
++      if (subsys < log_info->num_cat)
++              return log_info->cat[subsys].color;
++
++      return NULL;
++}
++
++static void _output(struct log_target *target, unsigned int subsys,
++                  char *file, int line, int cont, const char *format,
++                  va_list ap)
++{
++      char col[30];
++      char sub[30];
++      char tim[30];
++      char buf[4096];
++      char final[4096];
++
++      /* prepare the data */
++      col[0] = '\0';
++      sub[0] = '\0';
++      tim[0] = '\0';
++      buf[0] = '\0';
++
++      /* are we using color */
++      if (target->use_color) {
++              const char *c = color(subsys);
++              if (c) {
++                      snprintf(col, sizeof(col), "%s", color(subsys));
++                      col[sizeof(col)-1] = '\0';
++              }
++      }
++      vsnprintf(buf, sizeof(buf), format, ap);
++      buf[sizeof(buf)-1] = '\0';
++
++      if (!cont) {
++              if (target->print_timestamp) {
++                      char *timestr;
++                      time_t tm;
++                      tm = time(NULL);
++                      timestr = ctime(&tm);
++                      timestr[strlen(timestr)-1] = '\0';
++                      snprintf(tim, sizeof(tim), "%s ", timestr);
++                      tim[sizeof(tim)-1] = '\0';
++              }
++              snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line);
++              sub[sizeof(sub)-1] = '\0';
++      }
++
++      snprintf(final, sizeof(final), "%s%s%s%s\033[0;m", col, tim, sub, buf);
++      final[sizeof(final)-1] = '\0';
++      target->output(target, final);
++}
++
++
++static void _logp(unsigned int subsys, int level, char *file, int line,
++                int cont, const char *format, va_list ap)
++{
++      struct log_target *tar;
++
++      llist_for_each_entry(tar, &target_list, entry) {
++              struct log_category *category;
++              int output = 0;
++
++              category = &tar->categories[subsys];
++              /* subsystem is not supposed to be logged */
++              if (!category->enabled)
++                      continue;
++
++              /* Check the global log level */
++              if (tar->loglevel != 0 && level < tar->loglevel)
++                      continue;
++
++              /* Check the category log level */
++              if (tar->loglevel == 0 && category->loglevel != 0 &&
++                  level < category->loglevel)
++                      continue;
++
++              /* Apply filters here... if that becomes messy we will
++               * need to put filters in a list and each filter will
++               * say stop, continue, output */
++              if ((tar->filter_map & LOG_FILTER_ALL) != 0)
++                      output = 1;
++              else if (log_info->filter_fn)
++                      output = log_info->filter_fn(&log_context,
++                                                     tar);
++
++              if (output) {
++                      /* FIXME: copying the va_list is an ugly
++                       * workaround against a bug hidden somewhere in
++                       * _output.  If we do not copy here, the first
++                       * call to _output() will corrupt the va_list
++                       * contents, and any further _output() calls
++                       * with the same va_list will segfault */
++                      va_list bp;
++                      va_copy(bp, ap);
++                      _output(tar, subsys, file, line, cont, format, bp);
++                      va_end(bp);
++              }
++      }
++}
++
++void logp(unsigned int subsys, char *file, int line, int cont,
++        const char *format, ...)
++{
++      va_list ap;
++
++      va_start(ap, format);
++      _logp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
++      va_end(ap);
++}
++
++void logp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
++{
++      va_list ap;
++
++      va_start(ap, format);
++      _logp(subsys, level, file, line, cont, format, ap);
++      va_end(ap);
++}
++
++static char hexd_buff[4096];
++
++char *hexdump(const unsigned char *buf, int len)
++{
++      int i;
++      char *cur = hexd_buff;
++
++      hexd_buff[0] = 0;
++      for (i = 0; i < len; i++) {
++              int len_remain = sizeof(hexd_buff) - (cur - hexd_buff);
++              int rc = snprintf(cur, len_remain, "%02x ", buf[i]);
++              if (rc <= 0)
++                      break;
++              cur += rc;
++      }
++      hexd_buff[sizeof(hexd_buff)-1] = 0;
++      return hexd_buff;
++}
++
++void log_add_target(struct log_target *target)
++{
++      llist_add_tail(&target->entry, &target_list);
++}
++
++void log_del_target(struct log_target *target)
++{
++      llist_del(&target->entry);
++}
++
++void log_reset_context(void)
++{
++      memset(&log_context, 0, sizeof(log_context));
++}
++
++int log_set_context(uint8_t ctx_nr, void *value)
++{
++      if (ctx_nr > LOG_MAX_CTX)
++              return -EINVAL;
++
++      log_context.ctx[ctx_nr] = value;
++
++      return 0;
++}
++
++void log_set_all_filter(struct log_target *target, int all)
++{
++      if (all)
++              target->filter_map |= LOG_FILTER_ALL;
++      else
++              target->filter_map &= ~LOG_FILTER_ALL;
++}
++
++void log_set_use_color(struct log_target *target, int use_color)
++{
++      target->use_color = use_color;
++}
++
++void log_set_print_timestamp(struct log_target *target, int print_timestamp)
++{
++      target->print_timestamp = print_timestamp;
++}
++
++void log_set_log_level(struct log_target *target, int log_level)
++{
++      target->loglevel = log_level;
++}
++
++void log_set_category_filter(struct log_target *target, int category,
++                             int enable, int level)
++{
++      if (category >= log_info->num_cat)
++              return;
++      target->categories[category].enabled = !!enable;
++      target->categories[category].loglevel = level;
++}
++
++static void _stderr_output(struct log_target *target, const char *log)
++{
++      fprintf(target->tgt_stdout.out, "%s", log);
++      fflush(target->tgt_stdout.out);
++}
++
++struct log_target *log_target_create(void)
++{
++      struct log_target *target;
++      unsigned int i;
++
++      target = talloc_zero(tall_log_ctx, struct log_target);
++      if (!target)
++              return NULL;
++
++      INIT_LLIST_HEAD(&target->entry);
++
++      /* initialize the per-category enabled/loglevel from defaults */
++      for (i = 0; i < log_info->num_cat; i++) {
++              struct log_category *cat = &target->categories[i];
++              cat->enabled = log_info->cat[i].enabled;
++              cat->loglevel = log_info->cat[i].loglevel;
++      }
++
++      /* global settings */
++      target->use_color = 1;
++      target->print_timestamp = 0;
++
++      /* global log level */
++      target->loglevel = 0;
++      return target;
++}
++
++struct log_target *log_target_create_stderr(void)
++{
++      struct log_target *target;
++
++      target = log_target_create();
++      if (!target)
++              return NULL;
++
++      target->tgt_stdout.out = stderr;
++      target->output = _stderr_output;
++      return target;
++}
++
++void log_init(const struct log_info *cat)
++{
++      tall_log_ctx = talloc_named_const(NULL, 1, "logging");
++      log_info = cat;
++}
index 7d908b4,0000000..a0ac2d6
mode 100644,000000..100644
--- /dev/null
@@@ -1,74 -1,0 +1,85 @@@
 +/* Generic write queue implementation */
 +/*
 + * (C) 2010 by Holger Hans Peter Freyther
 + * (C) 2010 by On-Waves
 + *
 + * All Rights Reserved
 + *
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation; either version 2 of the License, or
 + * (at your option) any later version.
 + *
 + * This program is distributed in the hope that 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
 + *
 + */
 +
 +#include <osmocore/write_queue.h>
 +
 +int write_queue_bfd_cb(struct bsc_fd *fd, unsigned int what)
 +{
 +      struct write_queue *queue;
 +
 +      queue = container_of(fd, struct write_queue, bfd);
 +
 +      if (what & BSC_FD_READ)
 +              queue->read_cb(fd);
 +
 +      if (what & BSC_FD_WRITE) {
 +              struct msgb *msg;
 +
 +              fd->when &= ~BSC_FD_WRITE;
 +              msg = msgb_dequeue(&queue->msg_queue);
 +              if (!msg)
 +                      return -1;
 +
 +              --queue->current_length;
 +              queue->write_cb(fd, msg);
 +              msgb_free(msg);
 +
 +              if (!llist_empty(&queue->msg_queue))
 +                      fd->when |= BSC_FD_WRITE;
 +      }
 +
 +      return 0;
 +}
 +
 +void write_queue_init(struct write_queue *queue, int max_length)
 +{
 +      queue->max_length = max_length;
 +      queue->current_length = 0;
 +      queue->read_cb = NULL;
 +      queue->write_cb = NULL;
 +      queue->bfd.cb = write_queue_bfd_cb;
 +      INIT_LLIST_HEAD(&queue->msg_queue);
 +}
 +
 +int write_queue_enqueue(struct write_queue *queue, struct msgb *data)
 +{
 +//    if (queue->current_length + 1 >= queue->max_length)
 +//            LOGP(DMSC, LOGL_ERROR, "The queue is full. Dropping not yet implemented.\n");
 +
 +      ++queue->current_length;
 +      msgb_enqueue(&queue->msg_queue, data);
 +      queue->bfd.when |= BSC_FD_WRITE;
 +
 +      return 0;
 +}
++
++void write_queue_clear(struct write_queue *queue)
++{
++      while (!llist_empty(&queue->msg_queue)) {
++              struct msgb *msg = msgb_dequeue(&queue->msg_queue);
++              msgb_free(msg);
++      }
++
++      queue->current_length = 0;
++      queue->bfd.when &= ~BSC_FD_WRITE;
++}