public inbox for b.a.t.m.a.n@lists.open-mesh.org
 help / color / mirror / Atom feed
From: Andrew Lunn <andrew@lunn.ch>
To: The list for a Better Approach To Mobile Ad-hoc Networking
	<b.a.t.m.a.n@lists.open-mesh.net>
Cc: Marek Lindner <lindner_marek@yahoo.de>
Subject: Re: [B.A.T.M.A.N.] /proc vis rework
Date: Sun, 13 Dec 2009 17:11:51 +0100	[thread overview]
Message-ID: <20091213161151.GW29768@lunn.ch> (raw)
In-Reply-To: <20091130211445.GN4150@lunn.ch>

Subject: batctl: Parse the new /proc vis format.

Add code to batctl so that it can parse /proc/net/batman-adv/vis and
generate either graphvis dot or JSON output.

Tested and bugs found and fixed by Linus Luessing.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Index: vis.c
===================================================================
--- vis.c	(revision 0)
+++ vis.c	(revision 0)
@@ -0,0 +1,326 @@
+/* Copyright (C) 2009 B.A.T.M.A.N. contributors:
+ * Andrew Lunn <andrew@lunn.ch>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <getopt.h>
+#include <string.h>
+
+#include "main.h"
+#include "vis.h"
+#include "functions.h"
+#include "bat-hosts.h"
+#include "proc.h"
+
+#define TQ_MAX_VALUE 255
+
+typedef void (*print_tq_t)(char * orig, char * from, const long tq);
+typedef void (*print_HNA_t)(char * orig, char * from);
+typedef void (*print_1st_t)(char * orig);
+typedef void (*print_2nd_t)(char * orig, char * from);
+typedef void (*print_header_t)(void);
+typedef void (*print_footer_t)(void);
+
+struct funcs 
+{
+  print_tq_t print_tq;
+  print_HNA_t print_HNA;
+  print_1st_t print_1st;
+  print_2nd_t print_2nd;
+  print_header_t print_header;
+  print_footer_t print_footer;
+};
+
+static bool with_HNA = true;
+static bool with_2nd = true;
+static bool with_names = true;
+
+static void 
+usage(void)
+{
+  printf("batctl vis dot {--no-HNA|-h} {--no-2nd|-2} {--numbers|-n}\n");
+  printf("or\n");
+  printf("batctl vis json {--no-HNA|-h} {--no-2nd|-2} {--numbers|-n}\n");
+}
+
+static void 
+dot_print_tq(char * orig, char * from, const long tq)
+{
+  int int_part = TQ_MAX_VALUE / tq;
+  int frac_part = (1000 * TQ_MAX_VALUE / tq) - (int_part * 1000);
+        
+  printf("\t\"%s\" -> ",
+         get_name_by_macstr(orig, (with_names ? USE_BAT_HOSTS : 0)));
+  printf("\"%s\" [label=\"%d.%d\"]\n", 
+         get_name_by_macstr(from, (with_names ? USE_BAT_HOSTS : 0)),
+         int_part, frac_part);
+}
+
+static void 
+dot_print_HNA(char * orig, char * from)
+{
+        printf("\t\"%s\" -> ",
+               get_name_by_macstr(orig, (with_names ? USE_BAT_HOSTS : 0)));
+        printf("\"%s\" [label=\"HNA\"]\n", 
+               get_name_by_macstr(from, (with_names ? USE_BAT_HOSTS : 0)));
+}
+
+static void
+dot_print_1st(char * orig)
+{
+  printf("\tsubgraph \"cluster_%s\" {\n",
+         get_name_by_macstr(orig, (with_names ? USE_BAT_HOSTS : 0)));
+  printf("\t\t\"%s\" [peripheries=2]\n",
+         get_name_by_macstr(orig, (with_names ? USE_BAT_HOSTS : 0)));
+  printf("\t}\n");
+}
+
+static void
+dot_print_2nd(char * orig, char * from)
+{
+  printf("\tsubgraph \"cluster_%s\" {\n",
+         get_name_by_macstr(orig, (with_names ? USE_BAT_HOSTS : 0)));
+  printf("\t\t\"%s\" [peripheries=2]\n",
+         get_name_by_macstr(orig, (with_names ? USE_BAT_HOSTS : 0)));
+  printf("\t\t\"%s\"\n",
+         get_name_by_macstr(from, (with_names ? USE_BAT_HOSTS : 0)));
+  printf("\t}\n");
+}
+
+static void
+dot_print_header(void) 
+{
+  printf("digraph {\n");
+}
+
+static void
+dot_print_footer(void) 
+{
+  printf("}\n");
+}
+
+const struct funcs dot_funcs = 
+  { dot_print_tq,
+    dot_print_HNA,
+    dot_print_1st,
+    dot_print_2nd,
+    dot_print_header,
+    dot_print_footer
+};
+
+static void 
+json_print_tq(char * orig, char * from, const long tq)
+{
+  int int_part = TQ_MAX_VALUE / tq;
+  int frac_part = (1000 * TQ_MAX_VALUE / tq) - (int_part * 1000);
+        
+  printf("\t{ router : \"%s\", ",
+         get_name_by_macstr(orig, (with_names ? USE_BAT_HOSTS : 0)));
+  printf("neighbor : \"%s\", label : \"%d.%d\" }\n", 
+         get_name_by_macstr(from, (with_names ? USE_BAT_HOSTS : 0)),
+         int_part, frac_part);
+}
+
+static void 
+json_print_HNA(char * orig, char * from)
+{
+  printf("\t{ router : \"%s\", ",
+         get_name_by_macstr(orig, (with_names ? USE_BAT_HOSTS : 0)));
+  printf("gateway : \"%s\", label : \"HNA\" }\n", 
+         get_name_by_macstr(from, (with_names ? USE_BAT_HOSTS : 0)));
+}
+
+static void
+json_print_1st(char * orig)
+{
+  printf("\t{ primary : \"%s\" }\n",
+         get_name_by_macstr(orig, (with_names ? USE_BAT_HOSTS : 0)));
+}
+
+static void
+json_print_2nd(char * orig, char * from)
+{
+  printf("\t{ secondary : \"%s\", ",
+         get_name_by_macstr(from, (with_names ? USE_BAT_HOSTS : 0)));
+
+  printf("of : \"%s\" }\n",
+         get_name_by_macstr(orig, (with_names ? USE_BAT_HOSTS : 0)));
+}
+
+const struct funcs json_funcs = 
+  { json_print_tq,
+    json_print_HNA,
+    json_print_1st,
+    json_print_2nd,
+    NULL,
+    NULL
+};
+
+static FILE *
+open_vis(void) 
+{
+  char full_path[500];
+  
+  if (check_proc_dir("/proc") != EXIT_SUCCESS)
+    return NULL;
+
+  strncpy(full_path, PROC_ROOT_PATH, strlen(PROC_ROOT_PATH));
+  full_path[strlen(PROC_ROOT_PATH)] = '\0';
+  strncat(full_path, "vis", sizeof(full_path) - strlen(full_path));
+
+  return fopen(full_path, "r");
+}
+
+static int 
+format(const struct funcs *funcs)
+{
+  size_t len = 0;
+  ssize_t read;
+  char * line = NULL;
+  char * orig, * from;
+  char * duplet;
+  char * line_save_ptr;
+  char * duplet_save_ptr;
+  char * endptr;
+  char * value;
+  long tq;
+  char * flag;
+  
+  FILE * fp = open_vis(); 
+
+  if (!fp)
+    return EXIT_FAILURE;
+  
+  if (funcs->print_header)
+    funcs->print_header();
+  
+  while ((read = getline(&line, &len, fp)) != -1) {
+    /* First MAC address is the originator */
+    orig = strtok_r(line, ",", &line_save_ptr);
+
+    duplet_save_ptr = line_save_ptr;
+    while ((duplet = strtok_r(NULL, ",", &duplet_save_ptr)) != NULL) {
+      flag = strtok(duplet, " ");
+      if (!flag) 
+        continue;
+      if (!strcmp(flag, "TQ")) {
+        from = strtok(NULL, " ");
+        value = strtok(NULL, " ");
+        tq = strtoul(value, &endptr, 0);
+        funcs->print_tq(orig, from, tq);
+        continue;
+      }
+      if (!strcmp(flag, "HNA")) {
+        /* We have an HNA record */
+        if (!with_HNA) 
+          continue;
+        from = strtok(NULL, " ");
+        funcs->print_HNA(orig, from);
+        continue;
+      }
+      if (!strcmp(flag, "SEC") && with_2nd) {
+        /* We found a secondary interface MAC address.*/
+        from = strtok(NULL, " ");
+        funcs->print_2nd(orig, from);
+      }
+      if (!strcmp(flag, "PRIMARY") && with_2nd) {
+        /* We found a primary interface MAC address.*/
+        funcs->print_1st(orig);
+      }
+    }
+  }
+  
+  if (funcs->print_footer)
+    funcs->print_footer();
+  
+  if (line)
+    free(line);
+  return EXIT_SUCCESS;
+}
+
+int 
+vis(int argc, char *argv[])
+{
+  bool dot = false;
+  bool json = false;
+  int c;
+
+  if (argc <=1) {
+    usage();
+    return EXIT_FAILURE;
+  }
+  
+  /* Do we know the requested format? */
+  if (strcmp(argv[1], "dot") == 0)
+    dot=true;
+  if (strcmp(argv[1], "json") == 0)
+    json=true;
+  
+  if (!dot && !json) {
+    usage();
+    return EXIT_FAILURE;
+  }
+  
+  /* Move over the output format */
+  argc--;
+  argv++;
+  
+  while (1) {
+    int option_index = 0;
+    static struct option long_options[] = {
+      {"no-HNA", 0, 0, 'h'},
+      {"no-2nd", 0, 0, '2'},
+      {"numbers", 0, 0, 'n'},
+      {0, 0, 0, 0}
+    };
+    
+    c = getopt_long(argc, argv, "h2n", long_options, &option_index);
+    if (c == -1)
+      break;
+    
+    switch(c) {
+      case 'h':
+        with_HNA = false;
+        break;
+      case '2':
+        with_2nd = false;
+        break;
+      case 'n':
+        with_names = false;
+        break;
+      default:
+        usage();
+        return -1;
+    }
+  }
+  
+  if (with_names)
+	  bat_hosts_init();
+  
+  if (dot) 
+    return format(&dot_funcs);
+  
+  if (json)
+    return format(&json_funcs);
+
+  return EXIT_FAILURE;
+}
Index: vis.h
===================================================================
--- vis.h	(revision 0)
+++ vis.h	(revision 0)
@@ -0,0 +1,21 @@
+/* Copyright (C) 2009 B.A.T.M.A.N. contributors:
+ * Andrew Lunn <andrew@lunn.ch>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * 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
+ *
+ */
+
+int vis(int argc, char * argv[]);
+
Index: functions.c
===================================================================
--- functions.c	(revision 1489)
+++ functions.c	(working copy)
@@ -99,7 +99,7 @@
 	return get_name_by_macaddr(mac_addr, read_opt);
 }
 
-static int check_proc_dir(char *dir)
+int check_proc_dir(char *dir)
 {
 	struct stat st;
 
Index: functions.h
===================================================================
--- functions.h	(revision 1489)
+++ functions.h	(working copy)
@@ -34,6 +34,7 @@
 char *get_name_by_macstr(char *mac_str, int read_opt);
 int read_file(char *dir, char *path, int read_opt);
 int write_file(char *dir, char *path, char *value);
+int check_proc_dir(char *dir);
 
 extern char read_buff[10];
 
Index: main.c
===================================================================
--- main.c	(revision 1489)
+++ main.c	(working copy)
@@ -35,6 +35,7 @@
 #include "traceroute.h"
 #include "tcpdump.h"
 #include "bisect.h"
+#include "vis.h"
 
 
 void print_usage(void) {
@@ -47,7 +48,7 @@
 	printf(" \tlog|l                           \tread the log produced by the kernel module\n");
 	printf(" \ttranslocal|tl                   \tdisplay the local translation table\n");
 	printf(" \ttransglobal|tg                  \tdisplay the global translation table\n");
-	printf(" \tvisformat|vf  [format]          \tdisplay or modify the vis output format\n");
+	printf(" \tvis [dot|JSON]                  \tdisplay the VIS data in dot or JSON format\n");
 	printf(" \taggregation|ag   [0|1]          \tdisplay or modify the packet aggregation setting\n");
 	printf("\n");
 	printf(" \tping|p        <destination>     \tping another batman adv host via layer 2\n");
@@ -74,7 +75,8 @@
 	}
 
 	/* check if user is root */
-	if ((strcmp(argv[1], "bisect") != 0) && ((getuid()) || (getgid()))) {
+	if (((strcmp(argv[1], "bisect") != 0) && (strcmp(argv[1], "vis") != 0))
+	    && ((getuid()) || (getgid()))) {
 		fprintf(stderr, "Error - you must be root to run '%s' !\n", argv[0]);
 		exit(EXIT_FAILURE);
 	}
@@ -119,9 +121,9 @@
 
 		ret = handle_proc_setting(argc - 1, argv + 1, PROC_ORIG_INTERVAL, orig_interval_usage);
 
-	} else if ((strcmp(argv[1], "visformat") == 0) || (strcmp(argv[1], "vf") == 0)) {
+	} else if (strcmp(argv[1], "vis") == 0) {
 
-		ret = handle_proc_setting(argc - 1, argv + 1, PROC_VIS_FORMAT, vis_format_usage);
+		ret = vis(argc - 1, argv + 1);
 
 	} else if ((strcmp(argv[1], "aggregation") == 0) || (strcmp(argv[1], "ag") == 0)) {
 
Index: man/batctl.8
===================================================================
--- man/batctl.8	(revision 1489)
+++ man/batctl.8	(working copy)
@@ -63,9 +63,22 @@
 .IP "\fBtransglobal|tg\fP"
 Once started batctl will refresh the displayed global translation table every second. Use the "\-b" option to let batctl display the table only once (useful for scripts). If "\-n" was given batctl will not replace the mac addresses with bat\-host names in the output.
 .br
-.IP "\fBvisformat|vf	[format]\fP"
-If no parameter is given the current vis format settings are displayed otherwise the parameter is used to set the vis format.
+.IP "\fBvis dot\fP"
+Display the visualisation data in graphviz dot(1) format. If
+"\--numbers" or "\-n" batctl will not replace the mac addresses with
+bat-host names in the output. With "\--no-HNA" or "\-h" the HNA
+entries are not displayed, so the pure mesh topology can be seen. With
+"\--no-2nd" or "\-2" a dot cluster is not formed around primary and
+secondary addresses from the same device.
 .br
+.IP "\fBvis json\fP"
+Display the visualisation data in JSON format. If
+"\--numbers" or "\-n" batctl will not replace the mac addresses with
+bat-host names in the output. With "\--no-HNA" or "\-h" the HNA
+entries are not displayed, so the pure mesh topology can be seen. With
+"\--no-2nd" or "\-2" a dot cluster is not formed around primary and
+secondary addresses from the same device.
+.br
 .IP "\fBaggregation|ag	[0|1]\fP"
 If no parameter is given the current aggregation settings are displayed otherwise the parameter is used to enable or disable the packet aggregation.
 .br
Index: Makefile
===================================================================
--- Makefile	(revision 1489)
+++ Makefile	(working copy)
@@ -39,8 +39,8 @@
 EXTRA_MODULES_C := bisect.c
 EXTRA_MODULES_H := bisect.h
 
-SRC_C = main.c bat-hosts.c functions.c proc.c sys.c ping.c traceroute.c tcpdump.c list-batman.c hash.c $(EXTRA_MODULES_C)
-SRC_H = main.h bat-hosts.h functions.h proc.h sys.h ping.h traceroute.h tcpdump.h list-batman.h hash.h allocate.h $(EXTRA_MODULES_H)
+SRC_C = main.c bat-hosts.c functions.c proc.c sys.c ping.c traceroute.c tcpdump.c list-batman.c hash.c vis.c $(EXTRA_MODULES_C)
+SRC_H = main.h bat-hosts.h functions.h proc.h sys.h ping.h traceroute.h tcpdump.h list-batman.h hash.h allocate.h vis.h $(EXTRA_MODULES_H)
 SRC_O = $(SRC_C:.c=.o)
 
 PACKAGE_NAME = batctl

  parent reply	other threads:[~2009-12-13 16:11 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-11-30 21:14 [B.A.T.M.A.N.] /proc vis rework Andrew Lunn
2009-12-11 22:58 ` Linus Lüssing
2009-12-11 23:20   ` Linus Lüssing
2009-12-12 10:43   ` Andrew Lunn
2009-12-13 16:14   ` Andrew Lunn
2009-12-13 16:11 ` Andrew Lunn [this message]
2009-12-13 20:02   ` Linus Lüssing
2009-12-19 17:01 ` Marek Lindner
2009-12-19 17:11   ` [B.A.T.M.A.N.] [PATCH 1/5] batman-adv: moving vis output formats out of the kernel land Marek Lindner
2009-12-19 17:11     ` [B.A.T.M.A.N.] [PATCH 2/5] batctl: Parse the new /proc vis format Marek Lindner
2009-12-19 17:11       ` [B.A.T.M.A.N.] [PATCH 3/5] batman-adv: remove obsoleted vis_format /proc file Marek Lindner
2009-12-19 17:11         ` [B.A.T.M.A.N.] [PATCH 4/5] batman-adv: splitting /proc vis file into vis_server and vis_data Marek Lindner
2009-12-19 17:11           ` [B.A.T.M.A.N.] [PATCH 5/5] batctl: adjust vis data file path Marek Lindner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20091213161151.GW29768@lunn.ch \
    --to=andrew@lunn.ch \
    --cc=b.a.t.m.a.n@lists.open-mesh.net \
    --cc=lindner_marek@yahoo.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox