diff --git a/common/utils/T/tracer/Makefile.remote b/common/utils/T/tracer/Makefile.remote
index 08b79f2aa1645206977accfaff1920f2ad0b185d..3eadcc27daa5e3d32f192a1ae3d6d649063efe33 100644
--- a/common/utils/T/tracer/Makefile.remote
+++ b/common/utils/T/tracer/Makefile.remote
@@ -11,17 +11,20 @@ OBJS=remote_old.o plot.o database.o gui.o utils.o
 $(PROG): gui/gui.a $(OBJS)
 	$(CC) $(CFLAGS) -o $(PROG) $(OBJS) gui/gui.a $(LIBS)
 
-textlog: utils.o remote.o database.o event.o handler.o textlog.o \
-         event_selector.o view/view.a gui/gui.a
+textlog: utils.o textlog.o database.o event.o handler.o \
+         event_selector.o view/view.a gui/gui.a logger/logger.a
 	$(CC) $(CFLAGS) -o textlog $^ $(LIBS)
 
-.PHONY: gui/gui.a view/view.a
+.PHONY: gui/gui.a view/view.a logger/logger.a
+
+gui/gui.a:
+	cd gui && make
 
 view/view.a:
 	cd view && make
 
-gui/gui.a:
-	cd gui && make
+logger/logger.a:
+	cd logger && make
 
 %.o: %.c
 	$(CC) $(CFLAGS) -c -o $@ $<
@@ -30,3 +33,4 @@ clean:
 	rm -f *.o $(PROG) core textlog
 	cd gui && make clean
 	cd view && make clean
+	cd logger && make clean
diff --git a/common/utils/T/tracer/logger/Makefile b/common/utils/T/tracer/logger/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..1b4c3b1d28fcefc91744d4a73bfd37053fd69659
--- /dev/null
+++ b/common/utils/T/tracer/logger/Makefile
@@ -0,0 +1,13 @@
+CC=gcc
+CFLAGS=-Wall -g -pthread -I..
+
+OBJS=textlog.o
+
+logger.a: $(OBJS)
+	ar cr logger.a $(OBJS)
+
+%.o: %.c
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+clean:
+	rm -f *.a *.o
diff --git a/common/utils/T/tracer/logger/textlog.c b/common/utils/T/tracer/logger/textlog.c
new file mode 100644
index 0000000000000000000000000000000000000000..264068c61798d78fb260615f349f8ad60e387eaa
--- /dev/null
+++ b/common/utils/T/tracer/logger/textlog.c
@@ -0,0 +1,189 @@
+#include "textlog.h"
+#include "handler.h"
+#include "database.h"
+#include "view/view.h"
+#include "utils.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+enum format_item_type {
+  INSTRING,
+  INT, STRING, BUFFER };
+
+struct format_item {
+  enum format_item_type type;
+  union {
+    /* INSTRING */
+    char *s;
+    /* others */
+    int event_arg;
+  };
+};
+
+struct textlog {
+  char *event_name;
+  char *format;
+  void *database;
+  unsigned long handler_id;
+  /* parsed format string */
+  struct format_item *f;
+  int fsize;
+  /* list of views */
+  view **v;
+  int vsize;
+  /* local output buffer */
+  OBUF o;
+};
+
+static void _event(void *p, event e)
+{
+  struct textlog *l = p;
+  int i;
+
+  l->o.osize = 0;
+
+  for (i = 0; i < l->fsize; i++)
+  switch(l->f[i].type) {
+  case INSTRING: PUTS(&l->o, l->f[i].s); break;
+  case INT:      PUTI(&l->o, e.e[l->f[i].event_arg].i); break;
+  case STRING:   PUTS_CLEAN(&l->o, e.e[l->f[i].event_arg].s); break;
+  case BUFFER:
+    PUTS(&l->o, "{buffer size:");
+    PUTI(&l->o, e.e[l->f[i].event_arg].bsize);
+    PUTS(&l->o, "}");
+    break;
+  }
+  PUTC(&l->o, 0);
+
+  for (i = 0; i < l->vsize; i++) l->v[i]->append(l->v[i], l->o.obuf);
+}
+
+enum chunk_type { C_ERROR, C_STRING, C_ARG_NAME, C_EVENT_NAME };
+struct chunk {
+  enum chunk_type type;
+  char *s;
+  enum format_item_type it;
+  int event_arg;
+};
+
+/* TODO: speed it up? */
+static int find_argument(char *name, database_event_format f,
+    enum format_item_type *it, int *event_arg)
+{
+  int i;
+  for (i = 0; i < f.count; i++) if (!strcmp(name, f.name[i])) break;
+  if (i == f.count) return 0;
+  *event_arg = i;
+  if (!strcmp(f.type[i], "int"))         *it = INT;
+  else if (!strcmp(f.type[i], "string")) *it = STRING;
+  else if (!strcmp(f.type[i], "buffer")) *it = BUFFER;
+  else return 0;
+  return 1;
+}
+
+static struct chunk next_chunk(char **s, database_event_format f)
+{
+  char *cur = *s;
+  char *name;
+  enum format_item_type it;
+  int event_arg;
+
+  /* argument in [ ] */
+  if (*cur == '[') {
+    *cur = 0;
+    cur++;
+    name = cur;
+    /* no \ allowed there */
+    while (*cur && *cur != ']' && *cur != '\\') cur++;
+    if (*cur != ']') goto error;
+    *cur = 0;
+    cur++;
+    *s = cur;
+    if (find_argument(name, f, &it, &event_arg) == 0) goto error;
+    return (struct chunk){type:C_ARG_NAME, s:name, it:it, event_arg:event_arg};
+  }
+
+  /* { } is name of event (anything in between is smashed) */
+  if (*cur == '{') {
+    *cur = 0;
+    cur++;
+    while (*cur && *cur != '}') cur++;
+    if (*cur != '}') goto error;
+    *cur = 0;
+    cur++;
+    *s = cur;
+    return (struct chunk){type:C_EVENT_NAME};
+  }
+
+  /* anything but [ and { is raw string */
+  /* TODO: deal with \ */
+  name = cur;
+  while (*cur && *cur != '[' && *cur != '{') cur++;
+  *s = cur;
+  return (struct chunk){type:C_STRING, s:name};
+
+error:
+  return (struct chunk){type:C_ERROR};
+}
+
+textlog *new_textlog(event_handler *h, void *database,
+    char *event_name, char *format)
+{
+  struct textlog *ret;
+  int event_id;
+  database_event_format f;
+  char *cur;
+
+  ret = calloc(1, sizeof(struct textlog)); if (ret == NULL) abort();
+
+  ret->event_name = strdup(event_name); if (ret->event_name == NULL) abort();
+  ret->format = strdup(format); if (ret->format == NULL) abort();
+  ret->database = database;
+
+  event_id = event_id_from_name(database, event_name);
+
+  ret->handler_id = register_handler_function(h, event_id, _event, ret);
+
+  f = get_format(database, event_id);
+
+  /* we won't get more than strlen(format) "chunks" */
+  ret->f = malloc(sizeof(struct format_item) * strlen(format));
+  if (ret->f == NULL) abort();
+
+  cur = ret->format;
+
+  while (*cur) {
+    struct chunk c = next_chunk(&cur, f);
+    switch (c.type) {
+    case C_ERROR: goto error;
+    case C_STRING:
+      ret->f[ret->fsize].type = INSTRING;
+      ret->f[ret->fsize].s = c.s;
+      break;
+    case C_ARG_NAME:
+      ret->f[ret->fsize].type = c.it;
+      ret->f[ret->fsize].event_arg = c.event_arg;
+      break;
+    case C_EVENT_NAME:
+      ret->f[ret->fsize].type = INSTRING;
+      ret->f[ret->fsize].s = ret->event_name;
+      break;
+    }
+    ret->fsize++;
+  }
+
+  return ret;
+
+error:
+  printf("%s:%d: bad format '%s'\n", __FILE__, __LINE__, format);
+  abort();
+}
+
+void textlog_add_view(textlog *_l, view *v)
+{
+  struct textlog *l = _l;
+  l->vsize++;
+  l->v = realloc(l->v, l->vsize * sizeof(view *)); if (l->v == NULL) abort();
+  l->v[l->vsize-1] = v;
+}
diff --git a/common/utils/T/tracer/textlog.h b/common/utils/T/tracer/logger/textlog.h
similarity index 100%
rename from common/utils/T/tracer/textlog.h
rename to common/utils/T/tracer/logger/textlog.h
diff --git a/common/utils/T/tracer/remote.c b/common/utils/T/tracer/remote.c
deleted file mode 100644
index 2ef9e5bc28c518cc63ab1c23f17a56e632fe9be7..0000000000000000000000000000000000000000
--- a/common/utils/T/tracer/remote.c
+++ /dev/null
@@ -1,220 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-#include "database.h"
-#include "event.h"
-#include "handler.h"
-#include "textlog.h"
-#include "view/view.h"
-#include "gui/gui.h"
-#include "utils.h"
-#include "../T_defs.h"
-#include "event_selector.h"
-
-#define DEFAULT_REMOTE_PORT 2021
-
-int get_connection(char *addr, int port)
-{
-  struct sockaddr_in a;
-  socklen_t alen;
-  int s, t;
-
-  printf("waiting for connection on %s:%d\n", addr, port);
-
-  s = socket(AF_INET, SOCK_STREAM, 0);
-  if (s == -1) { perror("socket"); exit(1); }
-  t = 1;
-  if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &t, sizeof(int)))
-    { perror("setsockopt"); exit(1); }
-
-  a.sin_family = AF_INET;
-  a.sin_port = htons(port);
-  a.sin_addr.s_addr = inet_addr(addr);
-
-  if (bind(s, (struct sockaddr *)&a, sizeof(a))) { perror("bind"); exit(1); }
-  if (listen(s, 5)) { perror("bind"); exit(1); }
-  alen = sizeof(a);
-  t = accept(s, (struct sockaddr *)&a, &alen);
-  if (t == -1) { perror("accept"); exit(1); }
-  close(s);
-
-  printf("connected\n");
-
-  return t;
-}
-
-void usage(void)
-{
-  printf(
-"options:\n"
-"    -d <database file>        this option is mandatory\n"
-"    -on <GROUP or ID>         turn log ON for given GROUP or ID\n"
-"    -off <GROUP or ID>        turn log OFF for given GROUP or ID\n"
-"    -ON                       turn all logs ON\n"
-"    -OFF                      turn all logs OFF\n"
-"                              note: you may pass several -on/-off/-ON/-OFF,\n"
-"                                    they will be processed in order\n"
-"                                    by default, all is off\n"
-"    -p <port>                 use given port (default %d)\n"
-"    -x                        GUI output\n"
-"    -debug-gui                activate GUI debug logs\n"
-"    -no-gui                   disable GUI entirely\n",
-  DEFAULT_REMOTE_PORT
-  );
-  exit(1);
-}
-
-int fullread(int fd, void *_buf, int count)
-{
-  char *buf = _buf;
-  int ret = 0;
-  int l;
-  while (count) {
-    l = read(fd, buf, count);
-    if (l <= 0) { printf("read socket problem\n"); abort(); }
-    count -= l;
-    buf += l;
-    ret += l;
-  }
-  return ret;
-}
-
-event get_event(int s, char *v, void *d)
-{
-  int type;
-  int32_t length;
-
-  fullread(s, &length, 4);
-  fullread(s, &type, sizeof(int));
-  length -= sizeof(int);
-  fullread(s, v, length);
-
-  return new_event(type, length, v, d);
-}
-
-static void *gui_thread(void *_g)
-{
-  gui *g = _g;
-  gui_loop(g);
-  return NULL;
-}
-
-int main(int n, char **v)
-{
-  extern int volatile gui_logd;
-  char *database_filename = NULL;
-  void *database;
-  int port = DEFAULT_REMOTE_PORT;
-  char **on_off_name;
-  int *on_off_action;
-  int on_off_n = 0;
-  int *is_on;
-  int number_of_events;
-  int s;
-  int i;
-  char t;
-  int l;
-  event_handler *h;
-  textlog *textlog;
-  gui *g;
-  int gui_mode = 0;
-  view *out;
-  int gui_active = 1;
-
-  on_off_name = malloc(n * sizeof(char *)); if (on_off_name == NULL) abort();
-  on_off_action = malloc(n * sizeof(int)); if (on_off_action == NULL) abort();
-
-  for (i = 1; i < n; i++) {
-    if (!strcmp(v[i], "-h") || !strcmp(v[i], "--help")) usage();
-    if (!strcmp(v[i], "-d"))
-      { if (i > n-2) usage(); database_filename = v[++i]; continue; }
-    if (!strcmp(v[i], "-p"))
-      { if (i > n-2) usage(); port = atoi(v[++i]); continue; }
-    if (!strcmp(v[i], "-on")) { if (i > n-2) usage();
-      on_off_name[on_off_n]=v[++i]; on_off_action[on_off_n++]=1; continue; }
-    if (!strcmp(v[i], "-off")) { if (i > n-2) usage();
-      on_off_name[on_off_n]=v[++i]; on_off_action[on_off_n++]=0; continue; }
-    if (!strcmp(v[i], "-ON"))
-      { on_off_name[on_off_n]=NULL; on_off_action[on_off_n++]=1; continue; }
-    if (!strcmp(v[i], "-OFF"))
-      { on_off_name[on_off_n]=NULL; on_off_action[on_off_n++]=0; continue; }
-    if (!strcmp(v[i], "-x")) { gui_mode = 1; continue; }
-    if (!strcmp(v[i], "-debug-gui")) { gui_logd = 1; continue; }
-    if (!strcmp(v[i], "-no-gui")) { gui_active = 0; continue; }
-    usage();
-  }
-
-  if (gui_active == 0) gui_mode = 0;
-
-  if (database_filename == NULL) {
-    printf("ERROR: provide a database file (-d)\n");
-    exit(1);
-  }
-
-  database = parse_database(database_filename);
-
-  number_of_events = number_of_ids(database);
-  is_on = calloc(number_of_events, sizeof(int));
-  if (is_on == NULL) abort();
-
-  h = new_handler(database);
-
-  if (gui_active) {
-    g = gui_init();
-    new_thread(gui_thread, g);
-  }
-
-  if (gui_mode) {
-    widget *w, *win;
-//    w = new_textlist(g, 600, 20, 0);
-    w = new_textlist(g, 800, 50, BACKGROUND_COLOR);
-    win = new_toplevel_window(g, 800, 50*12, "textlog");
-    widget_add_child(g, win, w, -1);
-    out = new_view_textlist(1000, 10, g, w);
-    //tout = new_view_textlist(7, 4, g, w);
-  } else {
-    out = new_view_stdout();
-  }
-
-  for (i = 0; i < number_of_events; i++) {
-    char *name, *desc;
-    database_get_generic_description(database, i, &name, &desc);
-    textlog = new_textlog(h, database, name, desc);
-//        "ENB_UL_CHANNEL_ESTIMATE",
-//        "ev: {} eNB_id [eNB_ID] frame [frame] subframe [subframe]");
-    textlog_add_view(textlog, out);
-    free(name);
-    free(desc);
-  }
-
-  for (i = 0; i < on_off_n; i++)
-    on_off(database, on_off_name[i], is_on, on_off_action[i]);
-
-  s = get_connection("0.0.0.0", port);
-
-  /* send the first message - activate selected traces */
-  t = 0;
-  if (write(s, &t, 1) != 1) abort();
-  l = 0;
-  for (i = 0; i < number_of_events; i++) if (is_on[i]) l++;
-  if (write(s, &l, sizeof(int)) != sizeof(int)) abort();
-  for (l = 0; l < number_of_events; l++)
-    if (is_on[l])
-      if (write(s, &l, sizeof(int)) != sizeof(int)) abort();
-
-  if (gui_active)
-    setup_event_selector(g, database, s, is_on);
-
-  /* read messages */
-  while (1) {
-    char v[T_BUFFER_MAX];
-    event e;
-    e = get_event(s, v, database);
-    handle_event(h, e);
-  }
-
-  return 0;
-}
diff --git a/common/utils/T/tracer/textlog.c b/common/utils/T/tracer/textlog.c
index 264068c61798d78fb260615f349f8ad60e387eaa..f3e6f7178cf9fadd0d45c8b6055e7d4017493924 100644
--- a/common/utils/T/tracer/textlog.c
+++ b/common/utils/T/tracer/textlog.c
@@ -1,189 +1,220 @@
-#include "textlog.h"
-#include "handler.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
 #include "database.h"
+#include "event.h"
+#include "handler.h"
+#include "logger/textlog.h"
 #include "view/view.h"
+#include "gui/gui.h"
 #include "utils.h"
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
+#include "../T_defs.h"
+#include "event_selector.h"
 
-enum format_item_type {
-  INSTRING,
-  INT, STRING, BUFFER };
-
-struct format_item {
-  enum format_item_type type;
-  union {
-    /* INSTRING */
-    char *s;
-    /* others */
-    int event_arg;
-  };
-};
-
-struct textlog {
-  char *event_name;
-  char *format;
-  void *database;
-  unsigned long handler_id;
-  /* parsed format string */
-  struct format_item *f;
-  int fsize;
-  /* list of views */
-  view **v;
-  int vsize;
-  /* local output buffer */
-  OBUF o;
-};
-
-static void _event(void *p, event e)
+#define DEFAULT_REMOTE_PORT 2021
+
+int get_connection(char *addr, int port)
 {
-  struct textlog *l = p;
-  int i;
+  struct sockaddr_in a;
+  socklen_t alen;
+  int s, t;
 
-  l->o.osize = 0;
-
-  for (i = 0; i < l->fsize; i++)
-  switch(l->f[i].type) {
-  case INSTRING: PUTS(&l->o, l->f[i].s); break;
-  case INT:      PUTI(&l->o, e.e[l->f[i].event_arg].i); break;
-  case STRING:   PUTS_CLEAN(&l->o, e.e[l->f[i].event_arg].s); break;
-  case BUFFER:
-    PUTS(&l->o, "{buffer size:");
-    PUTI(&l->o, e.e[l->f[i].event_arg].bsize);
-    PUTS(&l->o, "}");
-    break;
-  }
-  PUTC(&l->o, 0);
+  printf("waiting for connection on %s:%d\n", addr, port);
+
+  s = socket(AF_INET, SOCK_STREAM, 0);
+  if (s == -1) { perror("socket"); exit(1); }
+  t = 1;
+  if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &t, sizeof(int)))
+    { perror("setsockopt"); exit(1); }
+
+  a.sin_family = AF_INET;
+  a.sin_port = htons(port);
+  a.sin_addr.s_addr = inet_addr(addr);
+
+  if (bind(s, (struct sockaddr *)&a, sizeof(a))) { perror("bind"); exit(1); }
+  if (listen(s, 5)) { perror("bind"); exit(1); }
+  alen = sizeof(a);
+  t = accept(s, (struct sockaddr *)&a, &alen);
+  if (t == -1) { perror("accept"); exit(1); }
+  close(s);
+
+  printf("connected\n");
 
-  for (i = 0; i < l->vsize; i++) l->v[i]->append(l->v[i], l->o.obuf);
+  return t;
 }
 
-enum chunk_type { C_ERROR, C_STRING, C_ARG_NAME, C_EVENT_NAME };
-struct chunk {
-  enum chunk_type type;
-  char *s;
-  enum format_item_type it;
-  int event_arg;
-};
-
-/* TODO: speed it up? */
-static int find_argument(char *name, database_event_format f,
-    enum format_item_type *it, int *event_arg)
+void usage(void)
 {
-  int i;
-  for (i = 0; i < f.count; i++) if (!strcmp(name, f.name[i])) break;
-  if (i == f.count) return 0;
-  *event_arg = i;
-  if (!strcmp(f.type[i], "int"))         *it = INT;
-  else if (!strcmp(f.type[i], "string")) *it = STRING;
-  else if (!strcmp(f.type[i], "buffer")) *it = BUFFER;
-  else return 0;
-  return 1;
+  printf(
+"options:\n"
+"    -d <database file>        this option is mandatory\n"
+"    -on <GROUP or ID>         turn log ON for given GROUP or ID\n"
+"    -off <GROUP or ID>        turn log OFF for given GROUP or ID\n"
+"    -ON                       turn all logs ON\n"
+"    -OFF                      turn all logs OFF\n"
+"                              note: you may pass several -on/-off/-ON/-OFF,\n"
+"                                    they will be processed in order\n"
+"                                    by default, all is off\n"
+"    -p <port>                 use given port (default %d)\n"
+"    -x                        GUI output\n"
+"    -debug-gui                activate GUI debug logs\n"
+"    -no-gui                   disable GUI entirely\n",
+  DEFAULT_REMOTE_PORT
+  );
+  exit(1);
 }
 
-static struct chunk next_chunk(char **s, database_event_format f)
+int fullread(int fd, void *_buf, int count)
 {
-  char *cur = *s;
-  char *name;
-  enum format_item_type it;
-  int event_arg;
-
-  /* argument in [ ] */
-  if (*cur == '[') {
-    *cur = 0;
-    cur++;
-    name = cur;
-    /* no \ allowed there */
-    while (*cur && *cur != ']' && *cur != '\\') cur++;
-    if (*cur != ']') goto error;
-    *cur = 0;
-    cur++;
-    *s = cur;
-    if (find_argument(name, f, &it, &event_arg) == 0) goto error;
-    return (struct chunk){type:C_ARG_NAME, s:name, it:it, event_arg:event_arg};
+  char *buf = _buf;
+  int ret = 0;
+  int l;
+  while (count) {
+    l = read(fd, buf, count);
+    if (l <= 0) { printf("read socket problem\n"); abort(); }
+    count -= l;
+    buf += l;
+    ret += l;
   }
+  return ret;
+}
 
-  /* { } is name of event (anything in between is smashed) */
-  if (*cur == '{') {
-    *cur = 0;
-    cur++;
-    while (*cur && *cur != '}') cur++;
-    if (*cur != '}') goto error;
-    *cur = 0;
-    cur++;
-    *s = cur;
-    return (struct chunk){type:C_EVENT_NAME};
-  }
+event get_event(int s, char *v, void *d)
+{
+  int type;
+  int32_t length;
 
-  /* anything but [ and { is raw string */
-  /* TODO: deal with \ */
-  name = cur;
-  while (*cur && *cur != '[' && *cur != '{') cur++;
-  *s = cur;
-  return (struct chunk){type:C_STRING, s:name};
+  fullread(s, &length, 4);
+  fullread(s, &type, sizeof(int));
+  length -= sizeof(int);
+  fullread(s, v, length);
 
-error:
-  return (struct chunk){type:C_ERROR};
+  return new_event(type, length, v, d);
 }
 
-textlog *new_textlog(event_handler *h, void *database,
-    char *event_name, char *format)
+static void *gui_thread(void *_g)
 {
-  struct textlog *ret;
-  int event_id;
-  database_event_format f;
-  char *cur;
-
-  ret = calloc(1, sizeof(struct textlog)); if (ret == NULL) abort();
-
-  ret->event_name = strdup(event_name); if (ret->event_name == NULL) abort();
-  ret->format = strdup(format); if (ret->format == NULL) abort();
-  ret->database = database;
-
-  event_id = event_id_from_name(database, event_name);
-
-  ret->handler_id = register_handler_function(h, event_id, _event, ret);
-
-  f = get_format(database, event_id);
-
-  /* we won't get more than strlen(format) "chunks" */
-  ret->f = malloc(sizeof(struct format_item) * strlen(format));
-  if (ret->f == NULL) abort();
-
-  cur = ret->format;
-
-  while (*cur) {
-    struct chunk c = next_chunk(&cur, f);
-    switch (c.type) {
-    case C_ERROR: goto error;
-    case C_STRING:
-      ret->f[ret->fsize].type = INSTRING;
-      ret->f[ret->fsize].s = c.s;
-      break;
-    case C_ARG_NAME:
-      ret->f[ret->fsize].type = c.it;
-      ret->f[ret->fsize].event_arg = c.event_arg;
-      break;
-    case C_EVENT_NAME:
-      ret->f[ret->fsize].type = INSTRING;
-      ret->f[ret->fsize].s = ret->event_name;
-      break;
-    }
-    ret->fsize++;
+  gui *g = _g;
+  gui_loop(g);
+  return NULL;
+}
+
+int main(int n, char **v)
+{
+  extern int volatile gui_logd;
+  char *database_filename = NULL;
+  void *database;
+  int port = DEFAULT_REMOTE_PORT;
+  char **on_off_name;
+  int *on_off_action;
+  int on_off_n = 0;
+  int *is_on;
+  int number_of_events;
+  int s;
+  int i;
+  char t;
+  int l;
+  event_handler *h;
+  textlog *textlog;
+  gui *g;
+  int gui_mode = 0;
+  view *out;
+  int gui_active = 1;
+
+  on_off_name = malloc(n * sizeof(char *)); if (on_off_name == NULL) abort();
+  on_off_action = malloc(n * sizeof(int)); if (on_off_action == NULL) abort();
+
+  for (i = 1; i < n; i++) {
+    if (!strcmp(v[i], "-h") || !strcmp(v[i], "--help")) usage();
+    if (!strcmp(v[i], "-d"))
+      { if (i > n-2) usage(); database_filename = v[++i]; continue; }
+    if (!strcmp(v[i], "-p"))
+      { if (i > n-2) usage(); port = atoi(v[++i]); continue; }
+    if (!strcmp(v[i], "-on")) { if (i > n-2) usage();
+      on_off_name[on_off_n]=v[++i]; on_off_action[on_off_n++]=1; continue; }
+    if (!strcmp(v[i], "-off")) { if (i > n-2) usage();
+      on_off_name[on_off_n]=v[++i]; on_off_action[on_off_n++]=0; continue; }
+    if (!strcmp(v[i], "-ON"))
+      { on_off_name[on_off_n]=NULL; on_off_action[on_off_n++]=1; continue; }
+    if (!strcmp(v[i], "-OFF"))
+      { on_off_name[on_off_n]=NULL; on_off_action[on_off_n++]=0; continue; }
+    if (!strcmp(v[i], "-x")) { gui_mode = 1; continue; }
+    if (!strcmp(v[i], "-debug-gui")) { gui_logd = 1; continue; }
+    if (!strcmp(v[i], "-no-gui")) { gui_active = 0; continue; }
+    usage();
   }
 
-  return ret;
+  if (gui_active == 0) gui_mode = 0;
 
-error:
-  printf("%s:%d: bad format '%s'\n", __FILE__, __LINE__, format);
-  abort();
-}
+  if (database_filename == NULL) {
+    printf("ERROR: provide a database file (-d)\n");
+    exit(1);
+  }
 
-void textlog_add_view(textlog *_l, view *v)
-{
-  struct textlog *l = _l;
-  l->vsize++;
-  l->v = realloc(l->v, l->vsize * sizeof(view *)); if (l->v == NULL) abort();
-  l->v[l->vsize-1] = v;
+  database = parse_database(database_filename);
+
+  number_of_events = number_of_ids(database);
+  is_on = calloc(number_of_events, sizeof(int));
+  if (is_on == NULL) abort();
+
+  h = new_handler(database);
+
+  if (gui_active) {
+    g = gui_init();
+    new_thread(gui_thread, g);
+  }
+
+  if (gui_mode) {
+    widget *w, *win;
+//    w = new_textlist(g, 600, 20, 0);
+    w = new_textlist(g, 800, 50, BACKGROUND_COLOR);
+    win = new_toplevel_window(g, 800, 50*12, "textlog");
+    widget_add_child(g, win, w, -1);
+    out = new_view_textlist(1000, 10, g, w);
+    //tout = new_view_textlist(7, 4, g, w);
+  } else {
+    out = new_view_stdout();
+  }
+
+  for (i = 0; i < number_of_events; i++) {
+    char *name, *desc;
+    database_get_generic_description(database, i, &name, &desc);
+    textlog = new_textlog(h, database, name, desc);
+//        "ENB_UL_CHANNEL_ESTIMATE",
+//        "ev: {} eNB_id [eNB_ID] frame [frame] subframe [subframe]");
+    textlog_add_view(textlog, out);
+    free(name);
+    free(desc);
+  }
+
+  for (i = 0; i < on_off_n; i++)
+    on_off(database, on_off_name[i], is_on, on_off_action[i]);
+
+  s = get_connection("0.0.0.0", port);
+
+  /* send the first message - activate selected traces */
+  t = 0;
+  if (write(s, &t, 1) != 1) abort();
+  l = 0;
+  for (i = 0; i < number_of_events; i++) if (is_on[i]) l++;
+  if (write(s, &l, sizeof(int)) != sizeof(int)) abort();
+  for (l = 0; l < number_of_events; l++)
+    if (is_on[l])
+      if (write(s, &l, sizeof(int)) != sizeof(int)) abort();
+
+  if (gui_active)
+    setup_event_selector(g, database, s, is_on);
+
+  /* read messages */
+  while (1) {
+    char v[T_BUFFER_MAX];
+    event e;
+    e = get_event(s, v, database);
+    handle_event(h, e);
+  }
+
+  return 0;
 }