Hi Folks
A while back i suggested we re-work the /proc/net/batman/vis format. Attached are two patches to achieve this. One patch is for the kernel model sources and the second extends batctl so that it can read from the /proc file and output dot or json format as before. The patch includes documentation for the new new batctl command in the man page.
I've tested the dot output using graphviz dot. However i don't have any tools which use the json output. So that format is not tested.
Andrew
Hi Andrew,
your patch seems to work pretty well here, tested it with 9 batman-nodes in the same room. Nevertheless I found a couple of smaller bugs in there: - batctl segfaults, if "batctl vis" has no following argument - batctl now displays the help-page in certain situations where we don't want it to - batctl ommits the first TQ tupel The attached patch for your patch should fix these issues :).
And I wanted to ask, what do you think about unifying the specific help output? For instance having this "Usage: ..."-header and the alignment for the following items the same way as it is done for other batctl commands as well.
Cheers, Linus
PS: Hmm, I'm also missing a couple of link/TQ entries already in the unified /proc/../vis. The 9 nodes in the same room should be able to see each other - the originator table on those nodes is also saying so. But I think I had seen this before without your patch, the problems has to be somewhere else. I'm attaching a batctl-vis- and proc-vis-output of the setup here too, just in case someone might spot some (more) parsing mistakes.
Ah, sorry, and skip the functions.c-part please, I had submitted a seperate patch for that here already.
Cheers, Linus
On Fri, Dec 11, 2009 at 11:58:35PM +0100, Linus Lüssing wrote:
Hi Andrew,
your patch seems to work pretty well here, tested it with 9 batman-nodes in the same room. Nevertheless I found a couple of smaller bugs in there:
- batctl segfaults, if "batctl vis" has no following argument
- batctl now displays the help-page in certain situations where we don't want it to
- batctl ommits the first TQ tupel
The attached patch for your patch should fix these issues :).
And I wanted to ask, what do you think about unifying the specific help output? For instance having this "Usage: ..."-header and the alignment for the following items the same way as it is done for other batctl commands as well.
Cheers, Linus
PS: Hmm, I'm also missing a couple of link/TQ entries already in the unified /proc/../vis. The 9 nodes in the same room should be able to see each other - the originator table on those nodes is also saying so. But I think I had seen this before without your patch, the problems has to be somewhere else. I'm attaching a batctl-vis- and proc-vis-output of the setup here too, just in case someone might spot some (more) parsing mistakes.
On Fri, Dec 11, 2009 at 11:58:35PM +0100, Linus L??ssing wrote:
Hi Andrew,
your patch seems to work pretty well here, tested it with 9 batman-nodes in the same room. Nevertheless I found a couple of smaller bugs in there:
- batctl segfaults, if "batctl vis" has no following argument
- batctl now displays the help-page in certain situations where we don't want it to
- batctl ommits the first TQ tupel
The attached patch for your patch should fix these issues :).
Thanks for the review. I will try to look at the details today or tomorrow.
Andrew
And I wanted to ask, what do you think about unifying the specific help output? For instance having this "Usage: ..."-header and the alignment for the following items the same way as it is done for other batctl commands as well.
I thought about that. However the architecture allows different formats to be easily added. Different formats might need different optional arguments. It just seems easier the way it is at the moment.
Andrew
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
Hmm, everything related to vis-format can be removed from the kernel(-module) as well, can't it? This patch applying on top of your batman-adv patch should remove the vis-format-proc entry and all this dead code if I didn't miss anything.
Cheers, Linus
Hey,
A while back i suggested we re-work the /proc/net/batman/vis format. Attached are two patches to achieve this. One patch is for the kernel model sources and the second extends batctl so that it can read from the /proc file and output dot or json format as before. The patch includes documentation for the new new batctl command in the man page.
I reviewed the patches & fixed some issues: * vis raw data (kernel): I upgraded the patch to make it apply on the latest trunk and added a descriptive commit message * vis raw data (batctl): I did not change anything but I'd like to suggest we adapt the coding style to match the rest of batctl (maybe using Lindent?). Also, all other batctl modules follow the same behaviour regarding the "-h" option. To be consistent we may want to adapt that as well. * vis_format remove: This patch removed the vis_format initializations but not the corresponding free calls. I fixed that. * I added an additional patch (splitting /proc vis file into vis_server and vis_data) to follow the roadmap we layed out in Brussels. * Last but not least: the mandatory batctl vis data path change
Please give it some more testing, Marek
The batman-adv kernel module is able to output visualization data using the dot draw or JSON format. This patch transforms the output into a generic format (called vis raw). User space tool may convert the raw data to support a variety of formats without the need of modifying the kernel module.
Signed-off-by: Andrew Lunn andrew@lunn.ch Signed-off-by: Marek Lindner lindner_marek@yahoo.de --- batman-adv-kernelland/proc.c | 171 +++++++++++++++--------------------------- batman-adv-kernelland/proc.h | 9 -- 2 files changed, 61 insertions(+), 119 deletions(-)
diff --git a/batman-adv-kernelland/proc.c b/batman-adv-kernelland/proc.c index 22471ee..60eec83 100644 --- a/batman-adv-kernelland/proc.c +++ b/batman-adv-kernelland/proc.c @@ -322,166 +322,117 @@ static int proc_transt_global_open(struct inode *inode, struct file *file) return single_open(file, proc_transt_global_read, NULL); }
-/* insert interface to the list of interfaces of one originator */ +/* While scanning for vis-entries of a particular vis-originator + * this list collects its interfaces to create a subgraph/cluster + * out of them later + */ +struct if_list_entry { + uint8_t addr[ETH_ALEN]; + bool primary; + struct hlist_node list; +};
+/* insert interface to the list of interfaces of one originator, if it + * does not already exist in the list */ static void proc_vis_insert_interface(const uint8_t *interface, - struct vis_if_list **if_entry, + struct hlist_head *if_list, bool primary) { - /* Did we get an empty list? (then insert imediately) */ - if (*if_entry == NULL) { - *if_entry = kmalloc(sizeof(struct vis_if_list), GFP_KERNEL); - if (*if_entry == NULL) + struct if_list_entry *entry; + struct hlist_node *pos; + + hlist_for_each_entry(entry, pos, if_list, list) { + if (compare_orig(entry->addr, (void *)interface)) return; + }
- (*if_entry)->primary = primary; - (*if_entry)->next = NULL; - memcpy((*if_entry)->addr, interface, ETH_ALEN); - } else { - struct vis_if_list *head_if_entry = *if_entry; - /* Do we already have this interface in our list? */ - while (!compare_orig((*if_entry)->addr, (void *)interface)) { - - /* Or did we reach the end (then append the interface) */ - if ((*if_entry)->next == NULL) { - (*if_entry)->next = kmalloc(sizeof(struct vis_if_list), GFP_KERNEL); - if ((*if_entry)->next == NULL) - return; - - memcpy((*if_entry)->next->addr, interface, ETH_ALEN); - (*if_entry)->next->primary = primary; - (*if_entry)->next->next = NULL; - break; - } - *if_entry = (*if_entry)->next; + /* its a new address, add it to the list */ + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return; + memcpy(entry->addr, interface, ETH_ALEN); + entry->primary = primary; + hlist_add_head(&entry->list, if_list); +} + +static void proc_vis_read_prim_sec(struct seq_file *seq, + struct hlist_head *if_list) +{ + struct if_list_entry *entry; + struct hlist_node *pos, *n; + char tmp_addr_str[ETH_STR_LEN]; + + hlist_for_each_entry_safe(entry, pos, n, if_list, list) { + if (entry->primary) { + seq_printf(seq, "PRIMARY, "); + } else { + addr_to_string(tmp_addr_str, entry->addr); + seq_printf(seq, "SEC %s, ", tmp_addr_str); } - /* Rewind the list to its head */ - *if_entry = head_if_entry; + + hlist_del(&entry->list); + kfree(entry); } } -/* read an entry */
+/* read an entry */ static void proc_vis_read_entry(struct seq_file *seq, struct vis_info_entry *entry, - struct vis_if_list **if_entry, - uint8_t *vis_orig, - uint8_t current_format, - uint8_t first_line) + struct hlist_head *if_list, + uint8_t *vis_orig) { - char from[40]; char to[40]; - int int_part, frac_part;
addr_to_string(to, entry->dest); if (entry->quality == 0) { -#ifndef VIS_SUBCLUSTERS_DISABLED - proc_vis_insert_interface(vis_orig, if_entry, true); -#endif /* VIS_SUBCLUSTERS_DISABLED */ - addr_to_string(from, vis_orig); - if (current_format == DOT_DRAW) { - seq_printf(seq, "\t"%s" -> "%s" [label="HNA"]\n", - from, to); - } else { - seq_printf(seq, - "%s\t{ router : "%s", gateway : "%s", label : "HNA" }", - (first_line ? "" : ",\n"), from, to); - } + proc_vis_insert_interface(vis_orig, if_list, true); + seq_printf(seq, "HNA %s, ", to); } else { -#ifndef VIS_SUBCLUSTERS_DISABLED - proc_vis_insert_interface(entry->src, if_entry, compare_orig(entry->src, vis_orig)); -#endif /* VIS_SUBCLUSTERS_DISABLED */ - addr_to_string(from, entry->src); - - /* kernel has no printf-support for %f? it'd be better to return - * this in float. */ - - int_part = TQ_MAX_VALUE / entry->quality; - frac_part = 1000 * TQ_MAX_VALUE / entry->quality - int_part * 1000; - - if (current_format == DOT_DRAW) { - seq_printf(seq, - "\t"%s" -> "%s" [label="%d.%d"]\n", - from, to, int_part, frac_part); - } else { - seq_printf(seq, - "%s\t{ router : "%s", neighbor : "%s", label : %d.%d }", - (first_line ? "" : ",\n"), from, to, int_part, frac_part); - } + proc_vis_insert_interface(entry->src, if_list, + compare_orig(entry->src, vis_orig)); + seq_printf(seq, "TQ %s %d, ", to, entry->quality); } }
- static int proc_vis_read(struct seq_file *seq, void *offset) { HASHIT(hashit); struct vis_info *info; struct vis_info_entry *entries; - struct vis_if_list *if_entries = NULL; + HLIST_HEAD(vis_if_list); int i; - uint8_t current_format, first_line = 1; -#ifndef VIS_SUBCLUSTERS_DISABLED + uint8_t current_format; char tmp_addr_str[ETH_STR_LEN]; - struct vis_if_list *tmp_if_next; -#endif /* VIS_SUBCLUSTERS_DISABLED */
current_format = vis_format;
rcu_read_lock(); if (list_empty(&if_list) || (!is_vis_server())) { rcu_read_unlock(); - if (current_format == DOT_DRAW) - seq_printf(seq, "digraph {\n}\n"); goto end; }
rcu_read_unlock();
- if (current_format == DOT_DRAW) - seq_printf(seq, "digraph {\n"); - spin_lock(&vis_hash_lock); while (hash_iterate(vis_hash, &hashit)) { info = hashit.bucket->data; entries = (struct vis_info_entry *) ((char *)info + sizeof(struct vis_info)); + addr_to_string(tmp_addr_str, info->packet.vis_orig); + seq_printf(seq, "%s,", tmp_addr_str);
for (i = 0; i < info->packet.entries; i++) { - proc_vis_read_entry(seq, &entries[i], &if_entries, - info->packet.vis_orig, - current_format, first_line); - if (first_line) - first_line = 0; + proc_vis_read_entry(seq, &entries[i], &vis_if_list, + info->packet.vis_orig); }
-#ifndef VIS_SUBCLUSTERS_DISABLED - /* Generate subgraphs from the collected items */ - if (current_format == DOT_DRAW) { - - addr_to_string(tmp_addr_str, info->packet.vis_orig); - seq_printf(seq, "\tsubgraph "cluster_%s" {\n", tmp_addr_str); - while (if_entries != NULL) { - - addr_to_string(tmp_addr_str, if_entries->addr); - if (if_entries->primary) - seq_printf(seq, "\t\t"%s" [peripheries=2]\n", tmp_addr_str); - else - seq_printf(seq, "\t\t"%s"\n", tmp_addr_str); - - /* ... and empty the list while doing this */ - tmp_if_next = if_entries->next; - kfree(if_entries); - if_entries = tmp_if_next; - } - seq_printf(seq, "\t}\n"); - } -#endif /* VIS_SUBCLUSTERS_DISABLED */ + /* add primary/secondary records */ + proc_vis_read_prim_sec(seq, &vis_if_list); + seq_printf(seq, "\n"); } spin_unlock(&vis_hash_lock);
- if (current_format == DOT_DRAW) - seq_printf(seq, "}\n"); - else - seq_printf(seq, "\n"); end: return 0; } diff --git a/batman-adv-kernelland/proc.h b/batman-adv-kernelland/proc.h index 16d3efd..761c5f8 100644 --- a/batman-adv-kernelland/proc.h +++ b/batman-adv-kernelland/proc.h @@ -38,12 +38,3 @@ void cleanup_procfs(void); int setup_procfs(void);
-/* While scanning for vis-entries of a particular vis-originator - * this list collects its interfaces to create a subgraph/cluster - * out of them later - */ -struct vis_if_list { - uint8_t addr[ETH_ALEN]; - bool primary; - struct vis_if_list *next; -};
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 Signed-off-by: Linus Luessing linus.luessing@web.de --- batctl/Makefile | 4 +- batctl/functions.c | 2 +- batctl/functions.h | 1 + batctl/main.c | 10 +- batctl/man/batctl.8 | 17 +++- batctl/vis.c | 326 +++++++++++++++++++++++++++++++++++++++++++++++++++ batctl/vis.h | 21 ++++ 7 files changed, 372 insertions(+), 9 deletions(-) create mode 100644 batctl/vis.c create mode 100644 batctl/vis.h
diff --git a/batctl/Makefile b/batctl/Makefile index b5255d2..8ecdd76 100644 --- a/batctl/Makefile +++ b/batctl/Makefile @@ -39,8 +39,8 @@ SRC_FILES = "(.c)|(.h)|(Makefile)|(INSTALL)|(LIESMICH)|(README 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 diff --git a/batctl/functions.c b/batctl/functions.c index 697be1f..8d38764 100644 --- a/batctl/functions.c +++ b/batctl/functions.c @@ -99,7 +99,7 @@ char *get_name_by_macstr(char *mac_str, int read_opt) 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;
diff --git a/batctl/functions.h b/batctl/functions.h index f819754..445a619 100644 --- a/batctl/functions.h +++ b/batctl/functions.h @@ -34,6 +34,7 @@ char *get_name_by_macaddr(struct ether_addr *mac_addr, int read_opt); 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];
diff --git a/batctl/main.c b/batctl/main.c index c0f1f88..ee5ed17 100644 --- a/batctl/main.c +++ b/batctl/main.c @@ -35,6 +35,7 @@ #include "traceroute.h" #include "tcpdump.h" #include "bisect.h" +#include "vis.h"
void print_usage(void) { @@ -47,7 +48,7 @@ void print_usage(void) { 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 @@ int main(int argc, char **argv) }
/* 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 @@ int main(int argc, char **argv)
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)) {
diff --git a/batctl/man/batctl.8 b/batctl/man/batctl.8 index cdedacb..1aa68df 100644 --- a/batctl/man/batctl.8 +++ b/batctl/man/batctl.8 @@ -63,8 +63,21 @@ Once started batctl will refresh the displayed local translation table every sec .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. diff --git a/batctl/vis.c b/batctl/vis.c new file mode 100644 index 0000000..450310c --- /dev/null +++ b/batctl/vis.c @@ -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; +} diff --git a/batctl/vis.h b/batctl/vis.h new file mode 100644 index 0000000..734528e --- /dev/null +++ b/batctl/vis.h @@ -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[]); +
batman-adv used to export the vis data using different formats that were switchable via /proc/net/batman-adv/vis_format. The various formats moved to user space and rendered this configuration switch useless.
Signed-off-by: Linus Luessing linus.luessing@web.de Signed-off-by: Marek Lindner lindner_marek@yahoo.de --- batman-adv-kernelland/proc.c | 79 +----------------------------------------- batman-adv-kernelland/vis.h | 5 --- 2 files changed, 1 insertions(+), 83 deletions(-)
diff --git a/batman-adv-kernelland/proc.c b/batman-adv-kernelland/proc.c index 60eec83..d98f86a 100644 --- a/batman-adv-kernelland/proc.c +++ b/batman-adv-kernelland/proc.c @@ -29,13 +29,11 @@ #include "vis.h" #include "compat.h"
-static uint8_t vis_format = DOT_DRAW; - static struct proc_dir_entry *proc_batman_dir, *proc_interface_file; static struct proc_dir_entry *proc_orig_interval_file, *proc_originators_file; static struct proc_dir_entry *proc_transt_local_file; static struct proc_dir_entry *proc_transt_global_file; -static struct proc_dir_entry *proc_vis_file, *proc_vis_format_file; +static struct proc_dir_entry *proc_vis_file; static struct proc_dir_entry *proc_aggr_file;
static int proc_interfaces_read(struct seq_file *seq, void *offset) @@ -401,11 +399,8 @@ static int proc_vis_read(struct seq_file *seq, void *offset) struct vis_info_entry *entries; HLIST_HEAD(vis_if_list); int i; - uint8_t current_format; char tmp_addr_str[ETH_STR_LEN];
- current_format = vis_format; - rcu_read_lock(); if (list_empty(&if_list) || (!is_vis_server())) { rcu_read_unlock(); @@ -471,55 +466,6 @@ static int proc_vis_open(struct inode *inode, struct file *file) return single_open(file, proc_vis_read, NULL); }
-static int proc_vis_format_read(struct seq_file *seq, void *offset) -{ - uint8_t current_format = vis_format; - - seq_printf(seq, "[%c] %s\n", - (current_format == DOT_DRAW) ? 'x' : ' ', - VIS_FORMAT_DD_NAME); - seq_printf(seq, "[%c] %s\n", - (current_format == JSON) ? 'x' : ' ', - VIS_FORMAT_JSON_NAME); - return 0; -} - -static int proc_vis_format_open(struct inode *inode, struct file *file) -{ - return single_open(file, proc_vis_format_read, NULL); -} - -static ssize_t proc_vis_format_write(struct file *file, - const char __user *buffer, - size_t count, loff_t *ppos) -{ - char *vis_format_string; - int not_copied = 0; - - vis_format_string = kmalloc(count, GFP_KERNEL); - - if (!vis_format_string) - return -ENOMEM; - - not_copied = copy_from_user(vis_format_string, buffer, count); - vis_format_string[count - not_copied - 1] = 0; - - if (strcmp(vis_format_string, VIS_FORMAT_DD_NAME) == 0) { - printk(KERN_INFO "batman-adv:Setting VIS output format to: %s\n", - VIS_FORMAT_DD_NAME); - vis_format = DOT_DRAW; - } else if (strcmp(vis_format_string, VIS_FORMAT_JSON_NAME) == 0) { - printk(KERN_INFO "batman-adv:Setting VIS output format to: %s\n", - VIS_FORMAT_JSON_NAME); - vis_format = JSON; - } else - printk(KERN_ERR "batman-adv:Unknown VIS output format: %s\n", - vis_format_string); - - kfree(vis_format_string); - return count; -} - static int proc_aggr_read(struct seq_file *seq, void *offset) { seq_printf(seq, "%i\n", atomic_read(&aggregation_enabled)); @@ -583,15 +529,6 @@ static const struct file_operations proc_aggr_fops = { .release = single_release, };
-static const struct file_operations proc_vis_format_fops = { - .owner = THIS_MODULE, - .open = proc_vis_format_open, - .read = seq_read, - .write = proc_vis_format_write, - .llseek = seq_lseek, - .release = single_release, -}; - static const struct file_operations proc_vis_fops = { .owner = THIS_MODULE, .open = proc_vis_open, @@ -666,9 +603,6 @@ void cleanup_procfs(void) if (proc_vis_file) remove_proc_entry(PROC_FILE_VIS, proc_batman_dir);
- if (proc_vis_format_file) - remove_proc_entry(PROC_FILE_VIS_FORMAT, proc_batman_dir); - if (proc_aggr_file) remove_proc_entry(PROC_FILE_AGGR, proc_batman_dir);
@@ -755,17 +689,6 @@ int setup_procfs(void) return -EFAULT; }
- proc_vis_format_file = create_proc_entry(PROC_FILE_VIS_FORMAT, - S_IWUSR | S_IRUGO, - proc_batman_dir); - if (proc_vis_format_file) { - proc_vis_format_file->proc_fops = &proc_vis_format_fops; - } else { - printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_VIS_FORMAT); - cleanup_procfs(); - return -EFAULT; - } - proc_aggr_file = create_proc_entry(PROC_FILE_AGGR, S_IWUSR | S_IRUGO, proc_batman_dir); if (proc_aggr_file) { diff --git a/batman-adv-kernelland/vis.h b/batman-adv-kernelland/vis.h index 276faba..ff88a43 100644 --- a/batman-adv-kernelland/vis.h +++ b/batman-adv-kernelland/vis.h @@ -45,11 +45,6 @@ struct recvlist_node { uint8_t mac[ETH_ALEN]; };
-enum vis_formats { - DOT_DRAW, - JSON, -}; - extern struct hashtable_t *vis_hash; extern spinlock_t vis_hash_lock;
The /proc vis file was used to enable/disable the vis server and to output the vis data at the same time. This behaviour was confusing and lacked a proper method to display the current vis server status. This patch seperates the 2 functionalities: * use vis_server to enable/disable the vis server and to retrieve its status * use vis_data to retrieve the vis raw data (if the server is enabled)
Signed-off-by: Marek Lindner lindner_marek@yahoo.de --- batman-adv-kernelland/proc.c | 172 +++++++++++++++++------------------------ batman-adv-kernelland/proc.h | 4 +- batman-adv-kernelland/types.h | 10 +++ batman-adv-kernelland/vis.c | 62 +++++++++++++++ batman-adv-kernelland/vis.h | 6 ++ 5 files changed, 151 insertions(+), 103 deletions(-)
diff --git a/batman-adv-kernelland/proc.c b/batman-adv-kernelland/proc.c index d98f86a..9cfab8f 100644 --- a/batman-adv-kernelland/proc.c +++ b/batman-adv-kernelland/proc.c @@ -33,7 +33,7 @@ static struct proc_dir_entry *proc_batman_dir, *proc_interface_file; static struct proc_dir_entry *proc_orig_interval_file, *proc_originators_file; static struct proc_dir_entry *proc_transt_local_file; static struct proc_dir_entry *proc_transt_global_file; -static struct proc_dir_entry *proc_vis_file; +static struct proc_dir_entry *proc_vis_srv_file, *proc_vis_data_file; static struct proc_dir_entry *proc_aggr_file;
static int proc_interfaces_read(struct seq_file *seq, void *offset) @@ -320,79 +320,55 @@ static int proc_transt_global_open(struct inode *inode, struct file *file) return single_open(file, proc_transt_global_read, NULL); }
-/* While scanning for vis-entries of a particular vis-originator - * this list collects its interfaces to create a subgraph/cluster - * out of them later - */ -struct if_list_entry { - uint8_t addr[ETH_ALEN]; - bool primary; - struct hlist_node list; -}; - -/* insert interface to the list of interfaces of one originator, if it - * does not already exist in the list */ -static void proc_vis_insert_interface(const uint8_t *interface, - struct hlist_head *if_list, - bool primary) +/* setting the mode of the vis server by the user */ +static ssize_t proc_vis_srv_write(struct file *file, const char __user * buffer, + size_t count, loff_t *ppos) { - struct if_list_entry *entry; - struct hlist_node *pos; + char *vis_mode_string; + int not_copied = 0;
- hlist_for_each_entry(entry, pos, if_list, list) { - if (compare_orig(entry->addr, (void *)interface)) - return; - } + vis_mode_string = kmalloc(count, GFP_KERNEL);
- /* its a new address, add it to the list */ - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) - return; - memcpy(entry->addr, interface, ETH_ALEN); - entry->primary = primary; - hlist_add_head(&entry->list, if_list); + if (!vis_mode_string) + return -ENOMEM; + + not_copied = copy_from_user(vis_mode_string, buffer, count); + vis_mode_string[count - not_copied - 1] = 0; + + if ((strcmp(vis_mode_string, "client") == 0) || + (strcmp(vis_mode_string, "disabled") == 0)) { + printk(KERN_INFO "batman-adv:Setting VIS mode to client (disabling vis server)\n"); + vis_set_mode(VIS_TYPE_CLIENT_UPDATE); + } else if ((strcmp(vis_mode_string, "server") == 0) || + (strcmp(vis_mode_string, "enabled") == 0)) { + printk(KERN_INFO "batman-adv:Setting VIS mode to server (enabling vis server)\n"); + vis_set_mode(VIS_TYPE_SERVER_SYNC); + } else + printk(KERN_ERR "batman-adv:Unknown VIS mode: %s\n", + vis_mode_string); + + kfree(vis_mode_string); + return count; }
-static void proc_vis_read_prim_sec(struct seq_file *seq, - struct hlist_head *if_list) +static int proc_vis_srv_read(struct seq_file *seq, void *offset) { - struct if_list_entry *entry; - struct hlist_node *pos, *n; - char tmp_addr_str[ETH_STR_LEN]; + int vis_server = is_vis_server();
- hlist_for_each_entry_safe(entry, pos, n, if_list, list) { - if (entry->primary) { - seq_printf(seq, "PRIMARY, "); - } else { - addr_to_string(tmp_addr_str, entry->addr); - seq_printf(seq, "SEC %s, ", tmp_addr_str); - } + seq_printf(seq, "[%c] client mode (server disabled) \n", + (!vis_server) ? 'x' : ' '); + seq_printf(seq, "[%c] server mode (server enabled) \n", + (vis_server) ? 'x' : ' ');
- hlist_del(&entry->list); - kfree(entry); - } + return 0; }
-/* read an entry */ -static void proc_vis_read_entry(struct seq_file *seq, - struct vis_info_entry *entry, - struct hlist_head *if_list, - uint8_t *vis_orig) +static int proc_vis_srv_open(struct inode *inode, struct file *file) { - char to[40]; - - addr_to_string(to, entry->dest); - if (entry->quality == 0) { - proc_vis_insert_interface(vis_orig, if_list, true); - seq_printf(seq, "HNA %s, ", to); - } else { - proc_vis_insert_interface(entry->src, if_list, - compare_orig(entry->src, vis_orig)); - seq_printf(seq, "TQ %s %d, ", to, entry->quality); - } + return single_open(file, proc_vis_srv_read, NULL); }
-static int proc_vis_read(struct seq_file *seq, void *offset) +static int proc_vis_data_read(struct seq_file *seq, void *offset) { HASHIT(hashit); struct vis_info *info; @@ -432,38 +408,9 @@ end: return 0; }
-/* setting the mode of the vis server by the user */ -static ssize_t proc_vis_write(struct file *file, const char __user * buffer, - size_t count, loff_t *ppos) +static int proc_vis_data_open(struct inode *inode, struct file *file) { - char *vis_mode_string; - int not_copied = 0; - - vis_mode_string = kmalloc(count, GFP_KERNEL); - - if (!vis_mode_string) - return -ENOMEM; - - not_copied = copy_from_user(vis_mode_string, buffer, count); - vis_mode_string[count - not_copied - 1] = 0; - - if (strcmp(vis_mode_string, "client") == 0) { - printk(KERN_INFO "batman-adv:Setting VIS mode to client\n"); - vis_set_mode(VIS_TYPE_CLIENT_UPDATE); - } else if (strcmp(vis_mode_string, "server") == 0) { - printk(KERN_INFO "batman-adv:Setting VIS mode to server\n"); - vis_set_mode(VIS_TYPE_SERVER_SYNC); - } else - printk(KERN_ERR "batman-adv:Unknown VIS mode: %s\n", - vis_mode_string); - - kfree(vis_mode_string); - return count; -} - -static int proc_vis_open(struct inode *inode, struct file *file) -{ - return single_open(file, proc_vis_read, NULL); + return single_open(file, proc_vis_data_read, NULL); }
static int proc_aggr_read(struct seq_file *seq, void *offset) @@ -529,11 +476,20 @@ static const struct file_operations proc_aggr_fops = { .release = single_release, };
-static const struct file_operations proc_vis_fops = { +static const struct file_operations proc_vis_srv_fops = { .owner = THIS_MODULE, - .open = proc_vis_open, + .open = proc_vis_srv_open, .read = seq_read, - .write = proc_vis_write, + .write = proc_vis_srv_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct file_operations proc_vis_data_fops = { + .owner = THIS_MODULE, + .open = proc_vis_data_open, + .read = seq_read, + .write = proc_dummy_write, .llseek = seq_lseek, .release = single_release, }; @@ -600,8 +556,11 @@ void cleanup_procfs(void) if (proc_interface_file) remove_proc_entry(PROC_FILE_INTERFACES, proc_batman_dir);
- if (proc_vis_file) - remove_proc_entry(PROC_FILE_VIS, proc_batman_dir); + if (proc_vis_data_file) + remove_proc_entry(PROC_FILE_VIS_DATA, proc_batman_dir); + + if (proc_vis_srv_file) + remove_proc_entry(PROC_FILE_VIS_SRV, proc_batman_dir);
if (proc_aggr_file) remove_proc_entry(PROC_FILE_AGGR, proc_batman_dir); @@ -679,12 +638,23 @@ int setup_procfs(void) return -EFAULT; }
- proc_vis_file = create_proc_entry(PROC_FILE_VIS, S_IWUSR | S_IRUGO, + proc_vis_srv_file = create_proc_entry(PROC_FILE_VIS_SRV, + S_IWUSR | S_IRUGO, + proc_batman_dir); + if (proc_vis_srv_file) { + proc_vis_srv_file->proc_fops = &proc_vis_srv_fops; + } else { + printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_VIS_SRV); + cleanup_procfs(); + return -EFAULT; + } + + proc_vis_data_file = create_proc_entry(PROC_FILE_VIS_DATA, S_IRUGO, proc_batman_dir); - if (proc_vis_file) { - proc_vis_file->proc_fops = &proc_vis_fops; + if (proc_vis_data_file) { + proc_vis_data_file->proc_fops = &proc_vis_data_fops; } else { - printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_VIS); + printk(KERN_ERR "batman-adv: Registering the '/proc/net/%s/%s' file failed\n", PROC_ROOT_DIR, PROC_FILE_VIS_DATA); cleanup_procfs(); return -EFAULT; } diff --git a/batman-adv-kernelland/proc.h b/batman-adv-kernelland/proc.h index 761c5f8..cd690e0 100644 --- a/batman-adv-kernelland/proc.h +++ b/batman-adv-kernelland/proc.h @@ -31,8 +31,8 @@ #define PROC_FILE_LOG_LEVEL "log_level" #define PROC_FILE_TRANST_LOCAL "transtable_local" #define PROC_FILE_TRANST_GLOBAL "transtable_global" -#define PROC_FILE_VIS "vis" -#define PROC_FILE_VIS_FORMAT "vis_format" +#define PROC_FILE_VIS_SRV "vis_server" +#define PROC_FILE_VIS_DATA "vis_data" #define PROC_FILE_AGGR "aggregate_ogm"
void cleanup_procfs(void); diff --git a/batman-adv-kernelland/types.h b/batman-adv-kernelland/types.h index 9739358..d708e6f 100644 --- a/batman-adv-kernelland/types.h +++ b/batman-adv-kernelland/types.h @@ -121,4 +121,14 @@ struct forw_packet { /* structure for forw_list maintaining packet struct batman_if *if_incoming; };
+/* While scanning for vis-entries of a particular vis-originator + * this list collects its interfaces to create a subgraph/cluster + * out of them later + */ +struct if_list_entry { + uint8_t addr[ETH_ALEN]; + bool primary; + struct hlist_node list; +}; + #endif diff --git a/batman-adv-kernelland/vis.c b/batman-adv-kernelland/vis.c index ac7c7c5..daa4a49 100644 --- a/batman-adv-kernelland/vis.c +++ b/batman-adv-kernelland/vis.c @@ -114,6 +114,68 @@ static int vis_info_choose(void *data, int size) return hash % size; }
+/* insert interface to the list of interfaces of one originator, if it + * does not already exist in the list */ +static void proc_vis_insert_interface(const uint8_t *interface, + struct hlist_head *if_list, + bool primary) +{ + struct if_list_entry *entry; + struct hlist_node *pos; + + hlist_for_each_entry(entry, pos, if_list, list) { + if (compare_orig(entry->addr, (void *)interface)) + return; + } + + /* its a new address, add it to the list */ + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) + return; + memcpy(entry->addr, interface, ETH_ALEN); + entry->primary = primary; + hlist_add_head(&entry->list, if_list); +} + +void proc_vis_read_prim_sec(struct seq_file *seq, + struct hlist_head *if_list) +{ + struct if_list_entry *entry; + struct hlist_node *pos, *n; + char tmp_addr_str[ETH_STR_LEN]; + + hlist_for_each_entry_safe(entry, pos, n, if_list, list) { + if (entry->primary) { + seq_printf(seq, "PRIMARY, "); + } else { + addr_to_string(tmp_addr_str, entry->addr); + seq_printf(seq, "SEC %s, ", tmp_addr_str); + } + + hlist_del(&entry->list); + kfree(entry); + } +} + +/* read an entry */ +void proc_vis_read_entry(struct seq_file *seq, + struct vis_info_entry *entry, + struct hlist_head *if_list, + uint8_t *vis_orig) +{ + char to[40]; + + addr_to_string(to, entry->dest); + if (entry->quality == 0) { + proc_vis_insert_interface(vis_orig, if_list, true); + seq_printf(seq, "HNA %s, ", to); + } else { + proc_vis_insert_interface(entry->src, if_list, + compare_orig(entry->src, vis_orig)); + seq_printf(seq, "TQ %s %d, ", to, entry->quality); + } +} + /* tries to add one entry to the receive list. */ static void recv_list_add(struct list_head *recv_list, char *mac) { diff --git a/batman-adv-kernelland/vis.h b/batman-adv-kernelland/vis.h index ff88a43..2e24258 100644 --- a/batman-adv-kernelland/vis.h +++ b/batman-adv-kernelland/vis.h @@ -50,6 +50,12 @@ extern spinlock_t vis_hash_lock;
void vis_set_mode(int mode); int is_vis_server(void); +void proc_vis_read_entry(struct seq_file *seq, + struct vis_info_entry *entry, + struct hlist_head *if_list, + uint8_t *vis_orig); +void proc_vis_read_prim_sec(struct seq_file *seq, + struct hlist_head *if_list); void receive_server_sync_packet(struct vis_packet *vis_packet, int vis_info_len); void receive_client_update_packet(struct vis_packet *vis_packet,
The kernel module changed the vis data file path which need to be changed in batctl as well. This patch depends on the "splitting /proc vis file into vis_server and vis_data" patch.
Signed-off-by: Marek Lindner lindner_marek@yahoo.de --- batctl/vis.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/batctl/vis.c b/batctl/vis.c index 450310c..f1e95ee 100644 --- a/batctl/vis.c +++ b/batctl/vis.c @@ -185,7 +185,7 @@ open_vis(void)
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)); + strncat(full_path, "vis_data", sizeof(full_path) - strlen(full_path));
return fopen(full_path, "r"); }
b.a.t.m.a.n@lists.open-mesh.org