aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AUTHORS.txt1
-rw-r--r--LICENSE.txt15
-rw-r--r--README.txt30
-rw-r--r--doc/ARCHITECTURE.txt48
-rw-r--r--doc/DEFINITIONS.txt4
-rw-r--r--doc/ROADMAP.txt76
-rw-r--r--doc/TODO.txt72
-rw-r--r--doc/architecture.xojbin0 -> 18500 bytes
-rw-r--r--doc/gg_map.xojbin0 -> 72970 bytes
-rw-r--r--doc/gg_sniff.xojbin0 -> 17526 bytes
-rw-r--r--doc/sendring.xojbin0 -> 42759 bytes
-rw-r--r--egraph/AUTHORS (renamed from AUTHORS)0
-rw-r--r--egraph/COPYING15
-rw-r--r--egraph/Egraph.h233
-rw-r--r--egraph/Makefile38
-rw-r--r--egraph/README47
-rw-r--r--egraph/data/blob.pngbin0 -> 3649 bytes
-rw-r--r--egraph/data/edge.pngbin0 -> 372 bytes
-rw-r--r--egraph/data/edge.xcfbin0 -> 8278 bytes
-rw-r--r--egraph/data/vertice.pngbin0 -> 4488 bytes
-rw-r--r--egraph/doc/TODO.txt118
-rw-r--r--egraph/doc/bug_lockup_evas.txt646
-rw-r--r--egraph/doc/egraph.xojbin0 -> 105411 bytes
-rw-r--r--egraph/doc/internals.txt89
-rw-r--r--egraph/egraph.c1275
-rw-r--r--egraph/egraph.edc250
-rw-r--r--egraph/examples/Makefile12
-rw-r--r--egraph/examples/demoapp.c371
-rw-r--r--egraph/examples/simplegraph.c59
-rwxr-xr-xegraph/retest.sh9
-rw-r--r--egraph/tests/Makefile32
-rw-r--r--egraph/tests/README.txt4
-rw-r--r--egraph/tests/creategraph.c37
-rw-r--r--gg_elife/.gitignore (renamed from .gitignore)0
-rw-r--r--gg_elife/AUTHORS1
-rw-r--r--gg_elife/COPYING (renamed from COPYING)0
-rw-r--r--gg_elife/ChangeLog (renamed from ChangeLog)0
-rw-r--r--gg_elife/Makefile.am (renamed from Makefile.am)0
-rw-r--r--gg_elife/NEWS (renamed from NEWS)0
-rw-r--r--gg_elife/README (renamed from README)0
-rwxr-xr-xgg_elife/autogen.sh (renamed from autogen.sh)0
-rw-r--r--gg_elife/configure.ac (renamed from configure.ac)0
-rw-r--r--gg_elife/data/Makefile.am (renamed from data/Makefile.am)0
-rw-r--r--gg_elife/data/elife.edc (renamed from data/elife.edc)0
-rw-r--r--gg_elife/in_separate_git.txt6
-rw-r--r--gg_elife/m4/efl_binary.m4 (renamed from m4/efl_binary.m4)0
-rw-r--r--gg_elife/src/Makefile.am (renamed from src/Makefile.am)0
-rw-r--r--gg_elife/src/elife.c (renamed from src/elife.c)0
-rw-r--r--gg_elife/src/elife_edje_external.c (renamed from src/elife_edje_external.c)0
-rw-r--r--gg_elife/src/elife_evas_smart.c (renamed from src/elife_evas_smart.c)0
-rw-r--r--gg_elife/src/elife_evas_smart.h (renamed from src/elife_evas_smart.h)0
-rw-r--r--gg_map/BUGS.txt506
-rw-r--r--gg_map/Makefile21
-rw-r--r--gg_map/README.txt30
-rw-r--r--gg_map/doc_research/20121205_irc_efr.fr270
-rw-r--r--gg_map/doc_research/20121205_irc_enesim.txt28
-rw-r--r--gg_map/doc_research/20121205_ntamas.txt29
-rw-r--r--gg_map/doc_research/igraph.txt27
-rw-r--r--gg_map/gg_map.c454
-rw-r--r--gg_sniff/Makefile34
-rw-r--r--gg_sniff/README.txt45
-rw-r--r--gg_sniff/gg_sniff.c146
-rw-r--r--gg_sniff/gg_sniff.h5
-rw-r--r--gg_sniff/pcap.c662
-rw-r--r--gg_trackproc/Makefile29
-rw-r--r--gg_trackproc/README.txt9
-rw-r--r--gg_trackproc/gg_trackproc.c346
-rw-r--r--glougloud/Makefile33
-rw-r--r--glougloud/README.txt39
-rw-r--r--glougloud/glougloud.c130
-rw-r--r--libglouglou/Makefile26
-rw-r--r--libglouglou/README.txt20
-rw-r--r--libglouglou/examples/Makefile10
-rw-r--r--libglouglou/examples/dnsreverse.c53
-rw-r--r--libglouglou/libggnet.c496
-rw-r--r--libglouglou/libggnet.h132
-rw-r--r--libglouglou/libggnet_dns.c116
-rw-r--r--libglouglou/libggnet_dns.h34
-rw-r--r--libglouglou/libglouglou.c721
-rw-r--r--libglouglou/libglouglou.h202
-rw-r--r--libglouglou/sendbuf.c142
-rw-r--r--libglouglou/sendbuf.h24
-rw-r--r--libglouglou/tests/Makefile48
-rw-r--r--libglouglou/tests/README.txt4
-rw-r--r--libglouglou/tests/connect.c46
-rw-r--r--libglouglou/tests/sendrecv.c67
-rw-r--r--libglouglou/utils.c200
-rw-r--r--old/glouglou_ruby/README.txt3
-rwxr-xr-xold/glouglou_ruby/aa_test.rb304
-rwxr-xr-xold/glouglou_ruby/glouglou_ruby.rb304
-rw-r--r--old/glouglou_ruby/vars2
-rw-r--r--old/glougloud/BUGS.txt10
-rw-r--r--old/glougloud/Makefile12
-rw-r--r--old/glougloud/README.txt51
-rw-r--r--old/glougloud/external/README.txt8
-rw-r--r--old/glougloud/external/imsg-buffer.c305
-rw-r--r--old/glougloud/external/imsg.c301
-rw-r--r--old/glougloud/external/imsg.h118
-rw-r--r--old/glougloud/external/imsgev.c170
-rw-r--r--old/glougloud/external/imsgev.h49
-rw-r--r--old/glougloud/external/queue.h568
-rw-r--r--old/glougloud/glougloud.c487
-rw-r--r--old/glougloud/glougloud.h79
-rw-r--r--old/glougloud/server.c238
-rw-r--r--old/glougloud/user.c806
-rw-r--r--old/glougloud/util.c192
-rw-r--r--spike/Makefile9
-rw-r--r--spike/efl_ephysics_heavy.c76
-rw-r--r--spike/efl_ephysics_heavy.txt7
-rw-r--r--spike/evdns.c112
-rw-r--r--spike/evdns_chrooted.c146
-rw-r--r--spike/igraph_edges.c106
-rw-r--r--spike/igraph_evas_broken.c233
-rw-r--r--spike/igraph_graph.c96
114 files changed, 13464 insertions, 0 deletions
diff --git a/AUTHORS.txt b/AUTHORS.txt
new file mode 100644
index 0000000..b3e4756
--- /dev/null
+++ b/AUTHORS.txt
@@ -0,0 +1 @@
+2012, 2013 Laurent Ghigonis <laurent@p1sec.com>
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..95c53b2
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2012, 2013 Laurent Ghigonis <laurent@p1sec.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..64460e7
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,30 @@
+glouglou - machine activity visualisation in real time
+
+The solution is constitued of multiple probes, multiple analyser and a relay
+daemon that provides visualisation of network and process activity on local or
+remote machines in real time.
+
+List of programs
+================
+
+The library:
+libglouglou - undelaying library for glougloud and glouglou clients
+
+The daemon:
+glougloud - relay glouglou packets between probes and visualisation clients
+
+The probes:
+gg_trackproc - glouglou probe client in C that track process activity
+gg_sniff (INPROGRESS) - glouglou probe client of network activity
+
+The analysers / visualisation clients:
+gg_elife - glouglou visualisation client of process activity in e17 background
+gg_map (INPROGRESS) - glouglou visualisation client of network activity on a map
+
+Order of installation
+=====================
+
+1. libglouglou
+2. glougloud daemon
+3. probes
+4. analysers/visualisation clients
diff --git a/doc/ARCHITECTURE.txt b/doc/ARCHITECTURE.txt
new file mode 100644
index 0000000..721b094
--- /dev/null
+++ b/doc/ARCHITECTURE.txt
@@ -0,0 +1,48 @@
+===============================================================================
+2012-11-06_00-19
+
+refer to architecture.xoj for big picture
+* libglouglou
+* viz clients
+* probes (trackproc, netsniff, fwsniff)
+
+===============================================================================
+libglouglou/includes
+* protocol version
+* packet structure
+* common constants
+
+===============================================================================
+libglouglou/lib
+* client and server code
+* tools
+
+/*
+ * start a server
+ * totaly unblocking, using libevent
+ */
+struct gg_server *
+gg_server_start(char *ip, int port,
+ int (*handle_conn)(struct gg_server *s, int client_id, struct sockaddr_in *addr),
+ int (*handle_packet)(struct gg_server *s, struct gg_packet *p));
+
+int
+gg_server_send(struct gg_server *s, int client_id, struct gg_packet *p);
+
+void
+gg_server_stop(struct gg_server *s);
+
+/*
+ * connect to a server
+ * totaly unblocking, using libevent
+ */
+struct gg_client *
+gg_client_connect(char *ip, int port,
+ int (*handle_conn)(struct gg_client *c, int status),
+ int (*handle_packet)(struct gg_client *c, struct gg_packet *p));
+
+int
+gg_client_send(struct gg_client *c, struct gg_packet *p);
+
+void
+gg_client_disconnect(struct gg_client *c);
diff --git a/doc/DEFINITIONS.txt b/doc/DEFINITIONS.txt
new file mode 100644
index 0000000..0795725
--- /dev/null
+++ b/doc/DEFINITIONS.txt
@@ -0,0 +1,4 @@
+server: the glougloud daemon
+clients: the glouglou_* clients
+nodes: hosts exchanging network traffic
+server node: node where the glougloud daemon is hosted and captures traffic
diff --git a/doc/ROADMAP.txt b/doc/ROADMAP.txt
new file mode 100644
index 0000000..9af67f3
--- /dev/null
+++ b/doc/ROADMAP.txt
@@ -0,0 +1,76 @@
+=== TODO ===
+* look at flexible Netflow
+ * IPFX
+
+=== IN PROGRESS ===
+
+0.4 - minimal version of gg_trackproc
+
+=== PLANNED ===
+
+0.5 - minimal version of gg_efl
+
+=== LATER ===
+
+* packet chuncking
+
+* interactive display:
+when users click on a node, only this node and the server appears, zoomed in
+
+* SS7 SCCP PC/GT plugin, creating nodes with SCCP addressing
+
+* change daemon-clients architecture to sniffer-daemon-clients
+sniffers send traffic to daemon where client connects
+
+=== DONE ===
+
+0.1 - basic daemon
+* glougloud: capture traffic and send summary to clients
+
+0.2 - minimal version of libglouglou with unit tests
+* libglouglou: calls that are in ARCHITECTURE.txt
+* unit tests for all calls
+
+0.3 - minimal version of gg_server
+
+===============================================================================
+OLD
+===============================================================================
+
+0.2 - display elements
+* gg_efl: display server node on the left (10min)
+* gg_efl: display other nodes of the right (45min != 60min, dev 1.2)
+* gg_efl: find a good elementary layout, or use directly evas on the bg
+* libglouglou: rename all id to connid, and update ggd gg_elf
+* gg_efl: display packets, moving between nodes (45min)
+* glougloud: send IP type for a connection (10min)
+* gg_efl: different colors for packets depending of IP type (20min)
+* glougloud: send ports for a connection (10min)
+* gg_efl: different connections per port (20min)
+* gg_efl: timer for nodes and conns
+
+
+=== PLANNED ===
+
+0.3 - bug killing
+* glougloud: see XXX in code and BUGS.txt
+* gg_efl: see XXX in code and BUGS.txt
+* glougloud: handle 2 ways of a connection in 1 connection
+
+0.4 - nicer display
+* gg_efl: nodes are in a circle around the server node
+* gg_efl: nodes intensity reflects their usage, proportionaly to other nodes
+* gg_efl: packets go quick when leaving the sender node and slowing down before ariving to destination node
+* gg_efl: packets are fading out when ariving to destination node
+* gg_efl: packets between 2 nodes have a different ellipse path for each connection / IP type
+
+0.5 - daemon plugins
+* glougloud: load plugins capability, with hooks on parsing traffic
+* glougloud: http plugin that parses basic html elements
+
+0.6 - deeper traffic display
+* glougloud: parse and send keywords to clients
+* gg_efl: display keywords
+* glougloud: read, reduce and send captured images to clients
+* gg_efl: display images
+
diff --git a/doc/TODO.txt b/doc/TODO.txt
new file mode 100644
index 0000000..a6c02b1
--- /dev/null
+++ b/doc/TODO.txt
@@ -0,0 +1,72 @@
+TODO
+====
+
+* gg_map BUG
+we seem to sometime create 2 egraph vertice with the same name.
+happends for 127.0.0.1 when sniffing on all interfaces with gg_sniff
+
+* email libevent doc mistake ?
+===============================================================================
+To: nickm@alum.mit.edu Cc: provos@citi.umich.edu
+
+http://www.wangafu.net/~nickm/libevent-book/Ref9_dns.html
+The addresses argument to the callback is NULL in the event of an error. For a PTR record, it’s a NUL-terminated string. For IPv4 records, it is an array of four-byte values in network order. For IPv6 records, it is an array of 16-byte records in network order. (Note that the number of addresses can be 0 even if there was no error. This can happen when the name exists, but it has no records of the requested type.)
+=>
+The addresses argument to the callback is NULL in the event of an error. For a PTR record, it’s pointer to a NUL-terminated string. For IPv4 records, it is an array of four-byte values in network order. For IPv6 records, it is an array of 16-byte records in network order. (Note that the number of addresses can be 0 even if there was no error. This can happen when the name exists, but it has no records of the requested type.)
+
+http://www.wangafu.net/~nickm/libevent-2.0/doxygen/html/dns_8h.html
+addresses needs to be cast according to type. It will be an array of 4-byte sequences for ipv4, or an array of 16-byte sequences for ipv6, or a nul-terminated string for PTR.
+=>
+addresses needs to be cast according to type. It will be an array of 4-byte sequences for ipv4, or an array of 16-byte sequences for ipv6, or a pointer to a nul-terminated string for PTR.
+===============================================================================
+
+
+
+gg_map: minimal version
+using evas, efx and elementary
+and igraph - http://igraph.sourceforge.net/index.html
+
+gg_sniff traceroute
+using jtrace code http://monkey.org/~jose/software/jtrace/
+use libdnet for encoding / decoding
+
+Connectivity improvements:
+libglouglou: connect and disconnect packets
+libglouglou: keepalive and timeout
+glougloud: when no viz cli, report to probes
+probes: when ggd tells no viz cli, stop sending traffic
+
+gg_elife: forced_colors of gg_packet disapear to black with time
+
+libglouglou: doxygen documentation
+
+libglouglou: ability to log to file + logging from utils and sendbuf
+
+glougloud + gg_trackproc + gg_sniff: use gg_log
+
+libglouglou: function handlers per packet: _encode, _decode, _getsize
+
+general glouglou Makefile that builds the right stuff
+
+TO LOOK AT
+==========
+
+skyrails
+iron main
+
+TODO later: network
+===================
+
+gg_jsmap
+using http://networkx.lanl.gov/
+make graph without displaying data packets
+without even receving them (register packet type to glougloud, that could
+tell the probe that all clients have registered only specific events so the
+probe do not send other type of events)
+
+include wireshark dissectors to give habitility to builds maps based on known
+dissectors.
+
+netexpect-0.20/src/packets/
+wireshark/epan/
+wireshark/epan/dissectors
diff --git a/doc/architecture.xoj b/doc/architecture.xoj
new file mode 100644
index 0000000..ce6d8d8
--- /dev/null
+++ b/doc/architecture.xoj
Binary files differ
diff --git a/doc/gg_map.xoj b/doc/gg_map.xoj
new file mode 100644
index 0000000..b6c7389
--- /dev/null
+++ b/doc/gg_map.xoj
Binary files differ
diff --git a/doc/gg_sniff.xoj b/doc/gg_sniff.xoj
new file mode 100644
index 0000000..5182a06
--- /dev/null
+++ b/doc/gg_sniff.xoj
Binary files differ
diff --git a/doc/sendring.xoj b/doc/sendring.xoj
new file mode 100644
index 0000000..f3b2959
--- /dev/null
+++ b/doc/sendring.xoj
Binary files differ
diff --git a/AUTHORS b/egraph/AUTHORS
index 4e42cdc..4e42cdc 100644
--- a/AUTHORS
+++ b/egraph/AUTHORS
diff --git a/egraph/COPYING b/egraph/COPYING
new file mode 100644
index 0000000..bae178d
--- /dev/null
+++ b/egraph/COPYING
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 2012 Laurent Ghigonis <laurent@p1sec.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
diff --git a/egraph/Egraph.h b/egraph/Egraph.h
new file mode 100644
index 0000000..553d5cd
--- /dev/null
+++ b/egraph/Egraph.h
@@ -0,0 +1,233 @@
+#include <Eina.h>
+#include <Evas.h>
+#include <Ecore.h>
+#include <igraph/igraph.h>
+
+#define EGRAPH_VERTICES_MAX 16384 /* cannot be more that u_int32_t */
+#define EGRAPH_VERTICE_NAME_MAXLEN 60
+
+typedef struct Egraph Egraph;
+typedef struct Egraph_Edge Egraph_Edge;
+typedef struct Egraph_Vertice Egraph_Vertice;
+
+typedef enum {
+ EGRAPH_LAYOUT_GRAPHOPT = 0,
+ EGRAPH_LAYOUT_KAMADAKAWAI = 1,
+ EGRAPH_LAYOUT_FRUCHTERMANREINGOLD = 2,
+} Egraph_Layout;
+#define EGRAPH_LAYOUT_DEFAULT EGRAPH_LAYOUT_GRAPHOPT
+#define EGRAPH_LAYOUTING_IMPROVEMENTS 5
+
+struct Egraph {
+ Evas_Object_Smart_Clipped_Data __clipped_data;
+ Evas_Object *obj;
+ Evas_Object *split_vertice_edge;
+ Evas *evas;
+ int graph_directed;
+ int display_vertices;
+ int display_names;
+ int display_edges;
+ int use_animations;
+ int do_improvements;
+ char *theme_path;
+ int theme_edges;
+ Egraph_Layout layout;
+ struct {
+ Ecore_Thread *thread;
+ int running;
+ int todo;
+ int changes_diff;
+ int improvement; /* special pass to improve the graph layout */
+ } layouting;
+ Eina_Hash *vertices;
+ int vertices_count;
+ u_int32_t vertices_freeids[EGRAPH_VERTICES_MAX];
+ int vertice_max_w;
+ int vertice_max_h;
+ Eina_List *edges;
+ igraph_t graph;
+ int graph_vcount;
+ igraph_matrix_t coords;
+ igraph_t graph2; // XXX remove use of graph here, see _repos()
+ int graph2_vcount;
+ igraph_vector_t graph2_wmin, graph2_wmax, graph2_hmin, graph2_hmax;
+ int graph_wmin, graph_wmax, graph_hmin, graph_hmax;
+ igraph_matrix_t coords2;
+};
+
+struct Egraph_Vertice {
+ u_int32_t id;
+ char *name;
+ char *type;
+ u_int status : 1;
+ Evas_Object *o;
+ Eina_List *edges;
+ Eina_List *blobs_incoming;
+ void *data;
+ u_int is_group : 1;
+ Eina_List *group_vertices; /* used if the vertice is a group */
+ u_int new : 1;
+ u_int v2_new : 1;
+ u_int v2_del : 1;
+ u_int v3_new : 1;
+ u_int v3_del : 1;
+};
+
+struct Egraph_Edge {
+ Egraph_Vertice *a;
+ Egraph_Vertice *b;
+ char *type;
+ Evas_Object *o;
+ u_int o_usetheme : 1;
+ void *data;
+ u_int new : 1;
+ u_int v2_new : 1;
+ u_int v2_del : 1;
+ u_int v3_new : 1;
+ u_int v3_del : 1;
+};
+
+/**
+ * Creates Egraph Evas_Object
+ */
+Evas_Object *egraph_new(Evas *evas, int directed);
+
+/**
+ * Remove all nodes and edges
+ */
+void egraph_clear(Evas_Object *obj);
+
+/**
+ * Configure egraph to use an edje theme
+ */
+void egraph_theme_file_set(Evas_Object *obj, char *path);
+
+/**
+ * Sets if egraph should theme the edges
+ */
+void egraph_theme_edges_set(Evas_Object *obj, int set);
+
+/**
+ * Sets the layout of Egraph
+ */
+void egraph_layout_set(Evas_Object *obj, Egraph_Layout layout);
+
+/**
+ * Configure if Egraph should display vertices
+ */
+void egraph_display_vertices_set(Evas_Object *obj, int set);
+
+/**
+ * Configure if Egraph should display vertices names
+ */
+void egraph_display_names_set(Evas_Object *obj, int set);
+
+/**
+ * Configure if Egraph should display edges
+ */
+void egraph_display_edges_set(Evas_Object *obj, int set);
+
+/**
+ * Configure if Egraph should use animations
+ */
+void egraph_use_animations_set(Evas_Object *obj, int set);
+
+/**
+ * Configure if Egraph should do improvements after a graph change
+ */
+void egraph_do_improvements_set(Evas_Object *obj, int set);
+
+/**
+ * Adds an edge between existing vertices
+ *
+ * @param obj The Egraph object
+ * @param a First vertice
+ * @param b Second vertice
+ * @param data The pointer to attach
+ * @return The new Egraph_Egde object
+ */
+Egraph_Edge *egraph_edge_add(Evas_Object *obj,
+ Egraph_Vertice *a, Egraph_Vertice *b, void *data);
+
+/**
+ * Delete an edge
+ *
+ * Hint: This does not delete vertices
+ */
+void egraph_edge_del(Evas_Object *obj, Egraph_Edge *e);
+
+/**
+ * Sets the type of an edge, to make it appear differently in the graph,
+ * depending on theme
+ */
+void egraph_edge_type_set(Evas_Object *obj,
+ Egraph_Edge *e, const char *type);
+/**
+ * Finds if an edge exists between 2 vertices
+ */
+Egraph_Edge *egraph_edge_find(Evas_Object *obj,
+ Egraph_Vertice *a, Egraph_Vertice *b);
+
+/**
+ * Add a vertice to the graph
+ *
+ * @param obj The Egraph object
+ * @param name The name of the vertice to be displayed. If NULL, no name is
+ * displayed
+ * @param data The pointer to attach
+ */
+Egraph_Vertice *egraph_vertice_add(Evas_Object *obj,
+ const char *name, void *data);
+
+/**
+ * Delete a vertice
+ *
+ * Hint: Also deletes all the edges attached to it
+ *
+ * @param obj The Egraph object
+ * @todo add user callback where edges are deleted
+ */
+void egraph_vertice_del(Evas_Object *obj, Egraph_Vertice *v);
+
+/**
+ * Update the name of a vertice
+ */
+void egraph_vertice_rename(Evas_Object *obj, Egraph_Vertice *v,
+ const char *name);
+
+/**
+ * Sets the type of a vertice, to make it appear differently in the graph,
+ * depending on theme
+ */
+void
+egraph_vertice_type_set(Evas_Object *obj, Egraph_Vertice *v, const char *type);
+
+ /**
+ * Send a blob from vertice to vertice
+ *
+ * A blob is a visual object that will move quickly from the first node to the
+ * second node.
+ */
+void egraph_vertice_send_blob(Evas_Object *obj,
+ Egraph_Vertice *a, Egraph_Vertice *b,
+ int size, u_int32_t color);
+
+/**
+ * Add a group of vertices, for later attaching nodes to it.
+ *
+ * The group is represented by an Egraph_Vertice with special properties.
+ */
+Egraph_Vertice *egraph_group_add(Evas_Object *obj,
+ const char *name, void *data);
+
+/**
+ * Attach a vertice to a group
+ */
+int egraph_group_vertice_attach(Evas_Object *obj,
+ Egraph_Vertice *group, Egraph_Vertice *v);
+
+/**
+ * Detach a vertice from a group
+ */
+void egraph_group_vertice_detach(Evas_Object *obj,
+ Egraph_Vertice *group, Egraph_Vertice *v);
diff --git a/egraph/Makefile b/egraph/Makefile
new file mode 100644
index 0000000..3aa6042
--- /dev/null
+++ b/egraph/Makefile
@@ -0,0 +1,38 @@
+# CFLAGS += -Werror -Wall -O2 -fPIC -shared -g
+CFLAGS += -Werror -Wall -fPIC -shared -g
+CFLAGS += $(shell pkg-config --libs --cflags edje)
+CFLAGS += $(shell pkg-config --libs --cflags efx)
+CFLAGS += $(shell pkg-config --libs --cflags igraph)
+
+PREFIX=/usr/local
+INCLUDEDIR=$(PREFIX)/include
+SHAREDIR=$(PREFIX)/share/egraph
+LIBDIR=$(PREFIX)/lib
+
+SOURCES = egraph.c
+HEADERS = Egraph.h
+EDC = egraph.edc
+EDJ = $(EDC:.edc=.edj)
+OBJECTS = $(SOURCES:.c=.o)
+LIBNAME = libegraph
+TARGET = ${LIBNAME}.so
+
+all: $(TARGET) $(EDJ)
+
+$(TARGET): $(OBJECTS)
+ $(CC) $(CFLAGS) -o $(TARGET) $(OBJECTS)
+
+$(EDJ):
+ edje_cc $(EDC) $(EDJ)
+
+install:
+ @echo "installation of $(LIBNAME)"
+ mkdir -p $(LIBDIR)
+ mkdir -p $(INCLUDEDIR)
+ mkdir -p $(SHAREDIR)
+ install -m 0644 $(TARGET) $(LIBDIR)
+ install -m 0644 $(HEADERS) $(INCLUDEDIR)
+ install -m 0644 $(EDJ) $(SHAREDIR)
+
+clean:
+ rm -f $(TARGET) $(OBJECTS) $(EDJ)
diff --git a/egraph/README b/egraph/README
new file mode 100644
index 0000000..dc4be18
--- /dev/null
+++ b/egraph/README
@@ -0,0 +1,47 @@
+egraph - library for rendring dynamic graphs in evas
+
+Requirements
+============
+
+* Enlightenment Foundation Libraries
+http://www.enlightenment.org
+ * eina
+ * evas
+ * ecore
+ * efx
+
+* igraph v0.6
+http://igraph.sourceforge.net/index.html
+ * for Ubuntu: v0.6 is *not* available in ubuntu packages
+You can download it here :
+http://sourceforge.net/projects/igraph/files/C%20library/0.6/igraph-0.6.tar.gz/download
+ * for Archlinux: yaourt -S igraph
+
+Installation
+============
+
+make
+sudo make install
+
+You can run the demoapp in examples :
+make -C examples
+./examples/demoapp
+
+You can use OpenGL rendering :
+ELM_ENGINE=opengl_x11 ./examples/demoapp
+
+Thanks
+======
+
+Thanks to ntamas (http://sixdegrees.hu/) for her advices on igraph.
+Thanks to cedric for tech advices on evas / edje.
+Thanks to vtorri for tech advices on evas / edje.
+Thanks to zmike for tech advices on efx.
+Thanks to raster for tech advices on evas / elementary.
+Thanks to ludivina for graphical advices.
+
+History
+=======
+
+Egraph was developped for the Glouglou Network Map (gg_map), a tool for
+live computer network visualisation based on the Glouglou framework.
diff --git a/egraph/data/blob.png b/egraph/data/blob.png
new file mode 100644
index 0000000..96e4aff
--- /dev/null
+++ b/egraph/data/blob.png
Binary files differ
diff --git a/egraph/data/edge.png b/egraph/data/edge.png
new file mode 100644
index 0000000..f6bbfc4
--- /dev/null
+++ b/egraph/data/edge.png
Binary files differ
diff --git a/egraph/data/edge.xcf b/egraph/data/edge.xcf
new file mode 100644
index 0000000..9481336
--- /dev/null
+++ b/egraph/data/edge.xcf
Binary files differ
diff --git a/egraph/data/vertice.png b/egraph/data/vertice.png
new file mode 100644
index 0000000..54c556b
--- /dev/null
+++ b/egraph/data/vertice.png
Binary files differ
diff --git a/egraph/doc/TODO.txt b/egraph/doc/TODO.txt
new file mode 100644
index 0000000..beed508
--- /dev/null
+++ b/egraph/doc/TODO.txt
@@ -0,0 +1,118 @@
+CURRENT
+=======
+
+vertice / vertice_text visibility will be broken
+need to implement it in the edje object and send signals
+
+* BUG: show nodes labels does not show old nodes labels
+reproduce: do not show labels, add edges, show label, add edges
+
+* BUG: layer stacking is wrong, edges end up on top of nodes, despite
+eg->split_vertice_edge
+
+* BUG: vertices / vertices text fight for being on top. use evas layers ?
+add 500 edges to reproduce, and watch the nodes
+
+TODO
+====
+
+* missing function declaration for static _color_int_to_rgb()
+* reorder static funcs
+
+* BUG: sometimes when you add nodes during layouting, the graph is broken
+a new edge attaches to the wrong new vertice
+it seems to be always the same node numbers that gets this wrong edge
+
+* BUG: EGRAPH_LAYOUT_FRUCHTERMANREINGOLD is broken when adding many edges
+
+* zoom
+10:16 < glouglou> i want to implement zooming with mouse scroll, do you have some ideas about that ?
+10:16 < zmike> that should be trivial
+10:16 < zmike> just hook mouse wheel and use efx_zoom
+10:18 < glouglou> does efx_zoom resize the objects ?
+10:18 < glouglou> all the graph is one evas smart object
+10:18 < zmike> it does not resize, it just maps
+10:18 < glouglou> that clips everything
+10:18 < zmike> it should work, you can see the elm test is working, and that uses smart objects
+10:18 < glouglou> yeah i want to update the coordinates i guess, not really "zoom"
+10:19 < zmike> hm there's no efx_scale yet, but you could use efx_resize I would think
+10:19 < glouglou> i would prefer to resize / update the coords
+10:19 < glouglou> scale yes, that would be it
+10:19 < glouglou> ok
+10:19 < zmike> just calculate the size you want and use efx_resize
+10:20 < zmike> it will animate the resizing
+
+* blobs
+05:17 < glouglou> cedric: t'as vu dans ma demo les packets qui vont d'une node a l'autre quand les nodes bougent et ben les paquets arrivent a l'ancien endroit
+05:17 < glouglou> j'ai regarde un peu dans efx mais j'ai pas trouve de bon moyen
+05:18 < glouglou> il faudrait que les paquets ne puissent pas sortir de l'edge (le trait) entre les 2 nodes
+05:18 < glouglou> si tu as une idee ...
+05:22 -!- Munto [~frugal@2a01:e35:139d:91e0:221:85ff:fee1:5c3c] has quit [Read error: Connection reset by peer]
+05:33 -!- sharinganex [~sharingan@233.208.85.79.rev.sfr.net] has joined #e.fr
+06:22 <@cedric> glouglou: oui, swallow un smart objet
+06:22 <@cedric> qui n'a qu'une seule tache faire bouger des paquets de haut en bas et de bas en haut
+06:22 <@cedric> parcontre ca a un probleme, la taille des objets de tes paquets est limite par la taille de ton objet edje parent
+06:40 < glouglou> cedric: mais un smart object dans un edje object qui est ensuite mappe, juste pour faire un trait, ca va etre un peu lourd non ?
+06:40 < glouglou> et complexe
+06:40 < glouglou> mais c'est vrai que ca resous mon probleme
+06:41 <@cedric> bah, de toute facon, tu vas avoir une surface cree pour mettre ton edje dans ta map
+06:41 <@cedric> dc ca reviendra au meme voir tu fairas l'economie d'une surface
+06:49 < glouglou> cedric: donc quand tu swallow un evas smart object dans un edje object, les coordonnes pour le smart object sont relatifs a la part swallow ?
+06:50 < glouglou> genre si je fais un geometry_get dans mon smart obj, le 0x0 sera en fait en haut a gauche de ma part swallow edje ?
+06:52 <@cedric> oui
+06:52 < glouglou> mais c'est magnifique
+06:52 < glouglou> et splendide a fois
+06:53 < glouglou> merci :D
+
+TODO LATER
+==========
+
+* find a better storage type for vertices
+quick access O(1) -> table
+possibility to foreach and remove at the same time
+
+* possible future API: no edges no vertices for the user
+
+struct Egraph_Vertice {
+ const char *name;
+ int vertice_id;
+ Evas_Object *o;
+ void *data;
+}
+egraph_vertice_add(Egraph *eg, const char *name, void *data);
+egraph_edge_add(Egraph *eg,
+ const char *a, const char *b, void *data);
+
+* speed: draw directly to a surface and map to evas via invisible polygons
+see elementary/src/bin/test_gesture_layer3.c
+04:45 < glouglou> i would like to draw directly in a surface and then give it to evas, but i want in the future to have user interaction with the nodes of my
+ graph, like click click reaction, so i guess i cannot escape from creating one evas object for each node and edge
+04:46 <@raster> trick:
+04:47 <@raster> u can just create invisible rects
+04:47 < glouglou> (i can imagine crazy mapping of the clicks on my surface that triggers evas callbacks, but i'm not that crazy)
+04:47 <@raster> and overlay them on your image
+04:47 <@raster> use them for event stuff
+04:47 < glouglou> lol
+04:47 <@raster> or polygons tyoo
+04:47 <@raster> thats about the only use of polygons
+04:47 <@raster> as u can do exact inside/outside poly checks for events
+04:48 < glouglou> i think you are talking about the crazy things i don't want to do :p
+04:48 <@raster> one of the elm demos does this
+04:48 <@raster> for soming/rotating with little photos
+04:48 <@raster> multitouch test stuff
+
+* speed: from Thanatermesis :
+the Core library (https://github.com/acaudwell/Core)
+renders to OpenGL directly
+used by :
+http://code.google.com/p/logstalgia/
+https://github.com/acaudwell/Gource (http://code.google.com/p/gource/)
+
+OLD
+===
+
+* speed: evas_object_rectangles
+04:40 <@raster> they add clipoouts
+04:41 <@raster> make them just a bit translucent
+04:41 <@raster> and it'll be faster
+
diff --git a/egraph/doc/bug_lockup_evas.txt b/egraph/doc/bug_lockup_evas.txt
new file mode 100644
index 0000000..4f194c5
--- /dev/null
+++ b/egraph/doc/bug_lockup_evas.txt
@@ -0,0 +1,646 @@
+===============================================================================
+This bug is a lockup of evas during rendering in my efl app.
+App freeze and takes 100% CPU. Below are backtraces when gdb was running and
+I interrupt it with Ctrl-C.
+
+At the time of crash, around 300objects total :
+elementary interface + ~100 edje + ~100 evas_object_line
+
+This crash is not predictible from what I see, sometimes it goes fine for a long
+time.
+It seem to crash more when using more edje objects.
+
+svn info
+Revision: 79909
+
+uname -a
+Linux balboa 3.6.7-1-ARCH #1 SMP PREEMPT Sun Nov 18 10:11:22 CET 2012 x86_64 GNU/Linux
+
+===============================================================================
+2012-12-09_18-10
+
+ELM_ENGINE=opengl_x11 gdb ./examples/demoapp
+run
+
+// use the app, and it locks up during an animation (app takes 100% CPU)
+// Ctrl-C
+
+(gdb) bt
+#0 0x00007ffff7233fe6 in _calc_intra_outer_rect_area (outer=<synthetic pointer>, intra=<synthetic pointer>, a=..., b=...) at lib/evas/common/evas_tiler.c:468
+#1 rect_list_add_split_fuzzy (node=0x1967160, rects=0xdf91a8, accepted_error=<optimized out>) at lib/evas/common/evas_tiler.c:652
+#2 rect_list_add_split_fuzzy_and_merge (node=0x1967160, rects=0xdf91a8, split_accepted_error=<optimized out>, merge_accepted_error=<optimized out>)
+ at lib/evas/common/evas_tiler.c:849
+#3 _add_redraw (h=16, w=24, y=200, x=882, rects=0xdf91a8) at lib/evas/common/evas_tiler.c:956
+#4 evas_common_tilebuf_add_redraw (tb=0xdf9170, x=882, y=200, w=24, h=16) at lib/evas/common/evas_tiler.c:986
+#5 0x00007ffff71b2af2 in evas_object_render_pre_effect_updates (rects=0x68ad48, eo_obj=<optimized out>, is_v=1, was_v=<optimized out>)
+ at lib/evas/canvas/evas_object_main.c:343
+#6 0x00007ffff71f1bfc in _evas_render_phase1_direct (render_objects=0x1498d30, active_objects=0x68ab50, e=<optimized out>, restack_objects=<optimized out>,
+ delete_objects=<optimized out>) at lib/evas/canvas/evas_render.c:231
+#7 evas_render_updates_internal (eo_e=0x68aac0, make_updates=make_updates@entry=1 '\001', do_draw=do_draw@entry=1 '\001') at lib/evas/canvas/evas_render.c:1357
+#8 0x00007ffff71f3cd7 in _canvas_render_updates (eo_e=<optimized out>, _pd=<optimized out>, list=<optimized out>) at lib/evas/canvas/evas_render.c:1795
+#9 0x00007ffff6acba35 in _eo_op_internal (obj=0x68aac0, op_type=EO_OP_TYPE_REGULAR, op=402, p_list=0x7fffffffe1b8) at lib/eo/eo.c:363
+#10 0x00007ffff6acd6cd in _eo_dov_internal (p_list=0x7fffffffe1b8, op_type=EO_OP_TYPE_REGULAR, obj=0x68aac0) at lib/eo/eo.c:403
+#11 eo_do_internal (obj=0x68aac0, op_type=op_type@entry=EO_OP_TYPE_REGULAR) at lib/eo/eo.c:434
+#12 0x00007ffff71f3c5f in evas_render_updates (eo_e=<optimized out>) at lib/evas/canvas/evas_render.c:1779
+#13 0x00007fffe73da43a in _ecore_evas_x_render (ee=0x6892f0) at modules/ecore_evas/engines/x/ecore_evas_x.c:447
+#14 0x00007ffff68ba7f1 in _ecore_evas_idle_enter (data=<optimized out>) at lib/ecore_evas/ecore_evas.c:59
+#15 0x00007ffff6f3c019 in _ecore_call_task_cb (data=<optimized out>, func=<optimized out>) at lib/ecore/ecore_private.h:300
+#16 _ecore_idle_enterer_call () at lib/ecore/ecore_idle_enterer.c:235
+#17 0x00007ffff6f3e47b in _ecore_main_loop_iterate_internal (once_only=once_only@entry=0) at lib/ecore/ecore_main.c:1866
+#18 0x00007ffff6f3eb27 in ecore_main_loop_begin () at lib/ecore/ecore_main.c:964
+#19 0x00000000004027bf in elm_main (argc=1, argv=0x7fffffffe568) at demoapp.c:248
+#20 0x0000000000402804 in main (argc=1, argv=0x7fffffffe568) at demoapp.c:255
+
+===============================================================================
+
+===============================================================================
+2012-12-10_15-48
+
+ELM_ENGINE=opengl_x11 gdb ./examples/demoapp
+run
+
+// use the app, and it locks up during an animation (app takes 100% CPU)
+// Ctrl-C
+
+^C
+Program received signal SIGINT, Interrupt.
+0x00007ffff7233fd3 in rect_list_add_split_fuzzy (node=0x1ce6300, rects=0x9c49b8, accepted_error=<optimized out>) at lib/evas/common/evas_tiler.c:650
+650 current = ((rect_node_t *)cur_node)->rect;
+(gdb) bt
+#0 0x00007ffff7233fd3 in rect_list_add_split_fuzzy (node=0x1ce6300, rects=0x9c49b8, accepted_error=<optimized out>) at lib/evas/common/evas_tiler.c:650
+#1 rect_list_add_split_fuzzy_and_merge (node=0x1ce6300, rects=0x9c49b8, split_accepted_error=<optimized out>, merge_accepted_error=<optimized out>)
+ at lib/evas/common/evas_tiler.c:849
+#2 _add_redraw (h=7, w=34, y=254, x=698, rects=0x9c49b8) at lib/evas/common/evas_tiler.c:956
+#3 evas_common_tilebuf_add_redraw (tb=0x9c4980, x=698, y=254, w=34, h=7) at lib/evas/common/evas_tiler.c:986
+#4 0x00007ffff71b2af2 in evas_object_render_pre_effect_updates (rects=0x68bed8, eo_obj=<optimized out>, is_v=1, was_v=<optimized out>)
+ at lib/evas/canvas/evas_object_main.c:343
+#5 0x00007ffff71f1bfc in _evas_render_phase1_direct (render_objects=0x139e3a0, active_objects=0x68bce0, e=<optimized out>, restack_objects=<optimized out>,
+ delete_objects=<optimized out>) at lib/evas/canvas/evas_render.c:231
+#6 evas_render_updates_internal (eo_e=0x68bc50, make_updates=make_updates@entry=1 '\001', do_draw=do_draw@entry=1 '\001') at lib/evas/canvas/evas_render.c:1357
+#7 0x00007ffff71f3cd7 in _canvas_render_updates (eo_e=<optimized out>, _pd=<optimized out>, list=<optimized out>) at lib/evas/canvas/evas_render.c:1795
+#8 0x00007ffff6acba35 in _eo_op_internal (obj=0x68bc50, op_type=EO_OP_TYPE_REGULAR, op=402, p_list=0x7fffffffe158) at lib/eo/eo.c:363
+#9 0x00007ffff6acd6cd in _eo_dov_internal (p_list=0x7fffffffe158, op_type=EO_OP_TYPE_REGULAR, obj=0x68bc50) at lib/eo/eo.c:403
+#10 eo_do_internal (obj=0x68bc50, op_type=op_type@entry=EO_OP_TYPE_REGULAR) at lib/eo/eo.c:434
+#11 0x00007ffff71f3c5f in evas_render_updates (eo_e=<optimized out>) at lib/evas/canvas/evas_render.c:1779
+#12 0x00007fffe73d943a in _ecore_evas_x_render (ee=0x68a480) at modules/ecore_evas/engines/x/ecore_evas_x.c:447
+#13 0x00007ffff68ba7f1 in _ecore_evas_idle_enter (data=<optimized out>) at lib/ecore_evas/ecore_evas.c:59
+#14 0x00007ffff6f3c019 in _ecore_call_task_cb (data=<optimized out>, func=<optimized out>) at lib/ecore/ecore_private.h:300
+#15 _ecore_idle_enterer_call () at lib/ecore/ecore_idle_enterer.c:235
+#16 0x00007ffff6f3e47b in _ecore_main_loop_iterate_internal (once_only=once_only@entry=0) at lib/ecore/ecore_main.c:1866
+#17 0x00007ffff6f3eb27 in ecore_main_loop_begin () at lib/ecore/ecore_main.c:964
+#18 0x00000000004028de in elm_main (argc=1, argv=0x7fffffffe508) at demoapp.c:284
+#19 0x0000000000402923 in main (argc=1, argv=0x7fffffffe508) at demoapp.c:291
+
+===============================================================================
+2012-12-10_23-49
+
+Lockup without opengl !
+I was using software rendering.
+
+gdb ./examples/demoapp
+run
+
+// use the app, and it locks up during an animation (app takes 100% CPU)
+// Ctrl-C
+
+^C
+Program received signal SIGINT, Interrupt.
+_calc_intra_outer_rect_area (outer=<synthetic pointer>, intra=<synthetic pointer>, a=..., b=...) at lib/evas/common/evas_tiler.c:524
+524 intra->area = intra->width * intra->height;
+(gdb) bt
+#0 _calc_intra_outer_rect_area (outer=<synthetic pointer>, intra=<synthetic pointer>, a=..., b=...) at lib/evas/common/evas_tiler.c:524
+#1 rect_list_add_split_fuzzy (node=0xb54050, rects=0x6927f8, accepted_error=<optimized out>) at lib/evas/common/evas_tiler.c:652
+#2 rect_list_add_split_fuzzy_and_merge (node=0xb54050, rects=0x6927f8, split_accepted_error=<optimized out>, merge_accepted_error=<optimized out>)
+ at lib/evas/common/evas_tiler.c:849
+#3 _add_redraw (h=72, w=348, y=429, x=296, rects=0x6927f8) at lib/evas/common/evas_tiler.c:956
+#4 evas_common_tilebuf_add_redraw (tb=0x6927c0, x=296, y=429, w=348, h=72) at lib/evas/common/evas_tiler.c:986
+#5 0x00007ffff71b2af2 in evas_object_render_pre_effect_updates (rects=rects@entry=0x689ac8, eo_obj=eo_obj@entry=0x8d2360, is_v=is_v@entry=1, was_v=was_v@entry=
+ 1) at lib/evas/canvas/evas_object_main.c:343
+#6 0x00007ffff71ab60d in evas_object_image_render_pre (eo_obj=0x8d2360, obj=0x8d23f0) at lib/evas/canvas/evas_object_image.c:3768
+#7 0x00007ffff71f1668 in evas_render_updates_internal (eo_e=0x689840, make_updates=make_updates@entry=1 '\001', do_draw=do_draw@entry=1 '\001')
+ at lib/evas/canvas/evas_render.c:1365
+#8 0x00007ffff71f3cd7 in _canvas_render_updates (eo_e=<optimized out>, _pd=<optimized out>, list=<optimized out>) at lib/evas/canvas/evas_render.c:1795
+#9 0x00007ffff6acba35 in _eo_op_internal (obj=0x689840, op_type=EO_OP_TYPE_REGULAR, op=402, p_list=0x7fffffffe168) at lib/eo/eo.c:363
+#10 0x00007ffff6acd6cd in _eo_dov_internal (p_list=0x7fffffffe168, op_type=EO_OP_TYPE_REGULAR, obj=0x689840) at lib/eo/eo.c:403
+#11 eo_do_internal (obj=0x689840, op_type=op_type@entry=EO_OP_TYPE_REGULAR) at lib/eo/eo.c:434
+#12 0x00007ffff71f3c5f in evas_render_updates (eo_e=<optimized out>) at lib/evas/canvas/evas_render.c:1779
+#13 0x00007fffe73d943a in _ecore_evas_x_render (ee=0x6880c0) at modules/ecore_evas/engines/x/ecore_evas_x.c:447
+#14 0x00007ffff68ba7f1 in _ecore_evas_idle_enter (data=<optimized out>) at lib/ecore_evas/ecore_evas.c:59
+#15 0x00007ffff6f3c019 in _ecore_call_task_cb (data=<optimized out>, func=<optimized out>) at lib/ecore/ecore_private.h:300
+#16 _ecore_idle_enterer_call () at lib/ecore/ecore_idle_enterer.c:235
+#17 0x00007ffff6f3e47b in _ecore_main_loop_iterate_internal (once_only=once_only@entry=0) at lib/ecore/ecore_main.c:1866
+#18 0x00007ffff6f3eb27 in ecore_main_loop_begin () at lib/ecore/ecore_main.c:964
+#19 0x00000000004028de in elm_main (argc=1, argv=0x7fffffffe518) at demoapp.c:284
+#20 0x0000000000402923 in main (argc=1, argv=0x7fffffffe518) at demoapp.c:291
+
+===============================================================================
+2012-12-10_23-58
+
+// Same scenario
+
+^C
+Program received signal SIGINT, Interrupt.
+rect_list_add_split_fuzzy (node=0xfe0150, rects=0x6927f8, accepted_error=<optimized out>) at lib/evas/common/evas_tiler.c:653
+653 area = current.area + r.area - intra.area;
+(gdb) bt
+#0 rect_list_add_split_fuzzy (node=0xfe0150, rects=0x6927f8, accepted_error=<optimized out>) at lib/evas/common/evas_tiler.c:653
+#1 rect_list_add_split_fuzzy_and_merge (node=0xfe0150, rects=0x6927f8, split_accepted_error=<optimized out>, merge_accepted_error=<optimized out>)
+ at lib/evas/common/evas_tiler.c:849
+#2 _add_redraw (h=63, w=85, y=314, x=493, rects=0x6927f8) at lib/evas/common/evas_tiler.c:956
+#3 evas_common_tilebuf_add_redraw (tb=0x6927c0, x=493, y=314, w=85, h=63) at lib/evas/common/evas_tiler.c:986
+#4 0x00007ffff71b2b9d in evas_object_render_pre_effect_updates (rects=0x689ac8, eo_obj=<optimized out>, is_v=1, was_v=<optimized out>)
+ at lib/evas/canvas/evas_object_main.c:356
+#5 0x00007ffff71f1bfc in _evas_render_phase1_direct (render_objects=0x1b79f50, active_objects=0x6898d0, e=<optimized out>, restack_objects=<optimized out>,
+ delete_objects=<optimized out>) at lib/evas/canvas/evas_render.c:231
+#6 evas_render_updates_internal (eo_e=0x689840, make_updates=make_updates@entry=1 '\001', do_draw=do_draw@entry=1 '\001') at lib/evas/canvas/evas_render.c:1357
+#7 0x00007ffff71f3cd7 in _canvas_render_updates (eo_e=<optimized out>, _pd=<optimized out>, list=<optimized out>) at lib/evas/canvas/evas_render.c:1795
+#8 0x00007ffff6acba35 in _eo_op_internal (obj=0x689840, op_type=EO_OP_TYPE_REGULAR, op=402, p_list=0x7fffffffe168) at lib/eo/eo.c:363
+#9 0x00007ffff6acd6cd in _eo_dov_internal (p_list=0x7fffffffe168, op_type=EO_OP_TYPE_REGULAR, obj=0x689840) at lib/eo/eo.c:403
+#10 eo_do_internal (obj=0x689840, op_type=op_type@entry=EO_OP_TYPE_REGULAR) at lib/eo/eo.c:434
+#11 0x00007ffff71f3c5f in evas_render_updates (eo_e=<optimized out>) at lib/evas/canvas/evas_render.c:1779
+#12 0x00007fffe73d943a in _ecore_evas_x_render (ee=0x6880c0) at modules/ecore_evas/engines/x/ecore_evas_x.c:447
+#13 0x00007ffff68ba7f1 in _ecore_evas_idle_enter (data=<optimized out>) at lib/ecore_evas/ecore_evas.c:59
+#14 0x00007ffff6f3c019 in _ecore_call_task_cb (data=<optimized out>, func=<optimized out>) at lib/ecore/ecore_private.h:300
+#15 _ecore_idle_enterer_call () at lib/ecore/ecore_idle_enterer.c:235
+#16 0x00007ffff6f3e47b in _ecore_main_loop_iterate_internal (once_only=once_only@entry=0) at lib/ecore/ecore_main.c:1866
+#17 0x00007ffff6f3eb27 in ecore_main_loop_begin () at lib/ecore/ecore_main.c:964
+#18 0x00000000004028de in elm_main (argc=1, argv=0x7fffffffe518) at demoapp.c:284
+#19 0x0000000000402923 in main (argc=1, argv=0x7fffffffe518) at demoapp.c:291
+
+===============================================================================
+2012-12-11_00-34
+
+// Same scenario
+
+^C
+Program received signal SIGINT, Interrupt.
+rect_list_add_split_fuzzy (node=0xb54370, rects=0x6927f8, accepted_error=<optimized out>) at lib/evas/common/evas_tiler.c:702
+702 else if (intra.area <= accepted_error)
+(gdb) bt
+#0 rect_list_add_split_fuzzy (node=0xb54370, rects=0x6927f8, accepted_error=<optimized out>) at lib/evas/common/evas_tiler.c:702
+#1 rect_list_add_split_fuzzy_and_merge (node=0xb54370, rects=0x6927f8, split_accepted_error=<optimized out>, merge_accepted_error=<optimized out>)
+ at lib/evas/common/evas_tiler.c:849
+#2 _add_redraw (h=39, w=49, y=156, x=486, rects=0x6927f8) at lib/evas/common/evas_tiler.c:956
+#3 evas_common_tilebuf_add_redraw (tb=0x6927c0, x=486, y=156, w=49, h=39) at lib/evas/common/evas_tiler.c:986
+#4 0x00007ffff71b2af2 in evas_object_render_pre_effect_updates (rects=0x689ac8, eo_obj=<optimized out>, is_v=1, was_v=<optimized out>)
+ at lib/evas/canvas/evas_object_main.c:343
+#5 0x00007ffff71f1bfc in _evas_render_phase1_direct (render_objects=0x1671880, active_objects=0x6898d0, e=<optimized out>, restack_objects=<optimized out>,
+ delete_objects=<optimized out>) at lib/evas/canvas/evas_render.c:231
+#6 evas_render_updates_internal (eo_e=0x689840, make_updates=make_updates@entry=1 '\001', do_draw=do_draw@entry=1 '\001') at lib/evas/canvas/evas_render.c:1357
+#7 0x00007ffff71f3cd7 in _canvas_render_updates (eo_e=<optimized out>, _pd=<optimized out>, list=<optimized out>) at lib/evas/canvas/evas_render.c:1795
+#8 0x00007ffff6acba35 in _eo_op_internal (obj=0x689840, op_type=EO_OP_TYPE_REGULAR, op=402, p_list=0x7fffffffe168) at lib/eo/eo.c:363
+#9 0x00007ffff6acd6cd in _eo_dov_internal (p_list=0x7fffffffe168, op_type=EO_OP_TYPE_REGULAR, obj=0x689840) at lib/eo/eo.c:403
+#10 eo_do_internal (obj=0x689840, op_type=op_type@entry=EO_OP_TYPE_REGULAR) at lib/eo/eo.c:434
+#11 0x00007ffff71f3c5f in evas_render_updates (eo_e=<optimized out>) at lib/evas/canvas/evas_render.c:1779
+#12 0x00007fffe73d943a in _ecore_evas_x_render (ee=0x6880c0) at modules/ecore_evas/engines/x/ecore_evas_x.c:447
+#13 0x00007ffff68ba7f1 in _ecore_evas_idle_enter (data=<optimized out>) at lib/ecore_evas/ecore_evas.c:59
+#14 0x00007ffff6f3c019 in _ecore_call_task_cb (data=<optimized out>, func=<optimized out>) at lib/ecore/ecore_private.h:300
+#15 _ecore_idle_enterer_call () at lib/ecore/ecore_idle_enterer.c:235
+#16 0x00007ffff6f3e47b in _ecore_main_loop_iterate_internal (once_only=once_only@entry=0) at lib/ecore/ecore_main.c:1866
+#17 0x00007ffff6f3eb27 in ecore_main_loop_begin () at lib/ecore/ecore_main.c:964
+#18 0x00000000004028de in elm_main (argc=1, argv=0x7fffffffe518) at demoapp.c:284
+#19 0x0000000000402923 in main (argc=1, argv=0x7fffffffe518) at demoapp.c:291
+
+===============================================================================
+2012-12-11_05-39
+
+Without any evas_object_line
+
+^C
+Program received signal SIGINT, Interrupt.
+rect_list_add_split_fuzzy (node=0xcf4c20, rects=0x692788, accepted_error=<optimized out>) at lib/evas/common/evas_tiler.c:650
+650 current = ((rect_node_t *)cur_node)->rect;
+(gdb) bt
+#0 rect_list_add_split_fuzzy (node=0xcf4c20, rects=0x692788, accepted_error=<optimized out>) at lib/evas/common/evas_tiler.c:650
+#1 rect_list_add_split_fuzzy_and_merge (node=0xcf4c20, rects=0x692788, split_accepted_error=<optimized out>, merge_accepted_error=<optimized out>)
+ at lib/evas/common/evas_tiler.c:849
+#2 _add_redraw (h=16, w=1047, y=240, x=263, rects=0x692788) at lib/evas/common/evas_tiler.c:956
+#3 evas_common_tilebuf_add_redraw (tb=0x692750, x=263, y=240, w=1047, h=16) at lib/evas/common/evas_tiler.c:986
+#4 0x00007ffff71b2af2 in evas_object_render_pre_effect_updates (rects=rects@entry=0x689a58, eo_obj=eo_obj@entry=0x1100690, is_v=is_v@entry=1,
+ was_v=was_v@entry=1) at lib/evas/canvas/evas_object_main.c:343
+#5 0x00007ffff71ab60d in evas_object_image_render_pre (eo_obj=0x1100690, obj=0x1100720) at lib/evas/canvas/evas_object_image.c:3768
+#6 0x00007ffff71f1bfc in _evas_render_phase1_direct (render_objects=0x1100690, active_objects=0x689860, e=<optimized out>, restack_objects=<optimized out>,
+ delete_objects=<optimized out>) at lib/evas/canvas/evas_render.c:231
+#7 evas_render_updates_internal (eo_e=0x6897d0, make_updates=make_updates@entry=1 '\001', do_draw=do_draw@entry=1 '\001') at lib/evas/canvas/evas_render.c:1357
+#8 0x00007ffff71f3cd7 in _canvas_render_updates (eo_e=<optimized out>, _pd=<optimized out>, list=<optimized out>) at lib/evas/canvas/evas_render.c:1795
+#9 0x00007ffff6acba35 in _eo_op_internal (obj=0x6897d0, op_type=EO_OP_TYPE_REGULAR, op=402, p_list=0x7fffffffe1d8) at lib/eo/eo.c:363
+#10 0x00007ffff6acd6cd in _eo_dov_internal (p_list=0x7fffffffe1d8, op_type=EO_OP_TYPE_REGULAR, obj=0x6897d0) at lib/eo/eo.c:403
+#11 eo_do_internal (obj=0x6897d0, op_type=op_type@entry=EO_OP_TYPE_REGULAR) at lib/eo/eo.c:434
+#12 0x00007ffff71f3c5f in evas_render_updates (eo_e=<optimized out>) at lib/evas/canvas/evas_render.c:1779
+#13 0x00007fffe73d743a in _ecore_evas_x_render (ee=0x688050) at modules/ecore_evas/engines/x/ecore_evas_x.c:447
+#14 0x00007ffff68ba7f1 in _ecore_evas_idle_enter (data=<optimized out>) at lib/ecore_evas/ecore_evas.c:59
+#15 0x00007ffff6f3c019 in _ecore_call_task_cb (data=<optimized out>, func=<optimized out>) at lib/ecore/ecore_private.h:300
+#16 _ecore_idle_enterer_call () at lib/ecore/ecore_idle_enterer.c:235
+#17 0x00007ffff6f3e47b in _ecore_main_loop_iterate_internal (once_only=once_only@entry=0) at lib/ecore/ecore_main.c:1866
+#18 0x00007ffff6f3eb27 in ecore_main_loop_begin () at lib/ecore/ecore_main.c:964
+#19 0x00000000004028ce in elm_main (argc=1, argv=0x7fffffffe588) at demoapp.c:284
+#20 0x0000000000402913 in main (argc=1, argv=0x7fffffffe588) at demoapp.c:291
+
+===============================================================================
+2012-12-11_05-42
+
+with 500 nodes = 1500 objects, no evas_object_line, but 500 evas_map
+
+^C
+Program received signal SIGINT, Interrupt.
+_calc_intra_outer_rect_area (outer=<synthetic pointer>, intra=<synthetic pointer>, a=..., b=...) at lib/evas/common/evas_tiler.c:498
+498 if (a.top < b.top)
+(gdb) bt
+#0 _calc_intra_outer_rect_area (outer=<synthetic pointer>, intra=<synthetic pointer>, a=..., b=...) at lib/evas/common/evas_tiler.c:498
+#1 rect_list_add_split_fuzzy (node=0x1acbb00, rects=0x93f238, accepted_error=<optimized out>) at lib/evas/common/evas_tiler.c:652
+#2 rect_list_add_split_fuzzy_and_merge (node=0x1acbb00, rects=0x93f238, split_accepted_error=<optimized out>, merge_accepted_error=<optimized out>)
+ at lib/evas/common/evas_tiler.c:849
+#3 _add_redraw (h=52, w=371, y=437, x=826, rects=0x93f238) at lib/evas/common/evas_tiler.c:956
+#4 evas_common_tilebuf_add_redraw (tb=0x93f200, x=826, y=437, w=371, h=52) at lib/evas/common/evas_tiler.c:986
+#5 0x00007ffff71ed67f in _evas_render_prev_cur_clip_cache_add (obj=obj@entry=0x1c752b0, e=0x68bc90, e=0x68bc90) at lib/evas/canvas/evas_render.c:158
+#6 0x00007ffff71ee940 in _evas_render_phase1_object_process (e=e@entry=0x68bc90, eo_obj=0x1c75220, active_objects=active_objects@entry=0x68bda8,
+ restack_objects=restack_objects@entry=0x68bdc8, delete_objects=delete_objects@entry=0x68bd88, render_objects=render_objects@entry=0x68bde8, restack=0,
+ redraw_all=redraw_all@entry=0x7fffffffe0ec, mapped_parent=mapped_parent@entry=0 '\000') at lib/evas/canvas/evas_render.c:355
+#7 0x00007ffff71eeaf6 in _evas_render_phase1_object_process (e=e@entry=0x68bc90, eo_obj=<optimized out>, active_objects=active_objects@entry=0x68bda8,
+ restack_objects=restack_objects@entry=0x68bdc8, delete_objects=delete_objects@entry=0x68bd88, render_objects=render_objects@entry=0x68bde8, restack=0,
+ redraw_all=redraw_all@entry=0x7fffffffe0ec, mapped_parent=mapped_parent@entry=0 '\000') at lib/evas/canvas/evas_render.c:413
+#8 0x00007ffff71eeaf6 in _evas_render_phase1_object_process (e=e@entry=0x68bc90, eo_obj=<optimized out>, active_objects=active_objects@entry=0x68bda8,
+ restack_objects=restack_objects@entry=0x68bdc8, delete_objects=delete_objects@entry=0x68bd88, render_objects=render_objects@entry=0x68bde8, restack=0,
+ redraw_all=redraw_all@entry=0x7fffffffe0ec, mapped_parent=mapped_parent@entry=0 '\000') at lib/evas/canvas/evas_render.c:413
+#9 0x00007ffff71eeaf6 in _evas_render_phase1_object_process (e=e@entry=0x68bc90, eo_obj=<optimized out>, active_objects=active_objects@entry=0x68bda8,
+ restack_objects=restack_objects@entry=0x68bdc8, delete_objects=delete_objects@entry=0x68bd88, render_objects=render_objects@entry=0x68bde8, restack=0,
+ redraw_all=redraw_all@entry=0x7fffffffe0ec, mapped_parent=mapped_parent@entry=0 '\000') at lib/evas/canvas/evas_render.c:413
+#10 0x00007ffff71eeaf6 in _evas_render_phase1_object_process (e=e@entry=0x68bc90, eo_obj=<optimized out>, active_objects=active_objects@entry=0x68bda8,
+ restack_objects=restack_objects@entry=0x68bdc8, delete_objects=delete_objects@entry=0x68bd88, render_objects=render_objects@entry=0x68bde8, restack=0,
+ redraw_all=redraw_all@entry=0x7fffffffe0ec, mapped_parent=mapped_parent@entry=0 '\000') at lib/evas/canvas/evas_render.c:413
+#11 0x00007ffff71eeaf6 in _evas_render_phase1_object_process (e=e@entry=0x68bc90, eo_obj=<optimized out>, active_objects=active_objects@entry=0x68bda8,
+ restack_objects=restack_objects@entry=0x68bdc8, delete_objects=delete_objects@entry=0x68bd88, render_objects=render_objects@entry=0x68bde8,
+ restack=restack@entry=0, redraw_all=redraw_all@entry=0x7fffffffe0ec, mapped_parent=mapped_parent@entry=0 '\000') at lib/evas/canvas/evas_render.c:413
+#12 0x00007ffff71f0f29 in _evas_render_phase1_process (redraw_all=0x7fffffffe0dc, render_objects=0x68bde8, delete_objects=0x68bd88, restack_objects=0x68bdc8,
+ active_objects=0x68bda8, e=0x68bc90) at lib/evas/canvas/evas_render.c:557
+#13 evas_render_updates_internal (eo_e=0x68bc00, make_updates=make_updates@entry=1 '\001', do_draw=do_draw@entry=1 '\001') at lib/evas/canvas/evas_render.c:1337
+#14 0x00007ffff71f3cd7 in _canvas_render_updates (eo_e=<optimized out>, _pd=<optimized out>, list=<optimized out>) at lib/evas/canvas/evas_render.c:1795
+#15 0x00007ffff6acba35 in _eo_op_internal (obj=0x68bc00, op_type=EO_OP_TYPE_REGULAR, op=402, p_list=0x7fffffffe1b8) at lib/eo/eo.c:363
+#16 0x00007ffff6acd6cd in _eo_dov_internal (p_list=0x7fffffffe1b8, op_type=EO_OP_TYPE_REGULAR, obj=0x68bc00) at lib/eo/eo.c:403
+#17 eo_do_internal (obj=0x68bc00, op_type=op_type@entry=EO_OP_TYPE_REGULAR) at lib/eo/eo.c:434
+#18 0x00007ffff71f3c5f in evas_render_updates (eo_e=<optimized out>) at lib/evas/canvas/evas_render.c:1779
+#19 0x00007fffe73d743a in _ecore_evas_x_render (ee=0x68a430) at modules/ecore_evas/engines/x/ecore_evas_x.c:447
+#20 0x00007ffff68ba7f1 in _ecore_evas_idle_enter (data=<optimized out>) at lib/ecore_evas/ecore_evas.c:59
+#21 0x00007ffff6f3c019 in _ecore_call_task_cb (data=<optimized out>, func=<optimized out>) at lib/ecore/ecore_private.h:300
+#22 _ecore_idle_enterer_call () at lib/ecore/ecore_idle_enterer.c:235
+#23 0x00007ffff6f3e47b in _ecore_main_loop_iterate_internal (once_only=once_only@entry=0) at lib/ecore/ecore_main.c:1866
+#24 0x00007ffff6f3eb27 in ecore_main_loop_begin () at lib/ecore/ecore_main.c:964
+#25 0x00000000004028ce in elm_main (argc=1, argv=0x7fffffffe568) at demoapp.c:284
+#26 0x0000000000402913 in main (argc=1, argv=0x7fffffffe568) at demoapp.c:291
+
+
+===============================================================================
+2012-12-13_06-32
+
+
+(gdb) bt
+#0 0x00007ffff53ddff7 in _calc_intra_outer_rect_area (
+ outer=<synthetic pointer>, intra=<synthetic pointer>, a=..., b=...)
+ at lib/evas/common/evas_tiler.c:479
+#1 rect_list_add_split_fuzzy (node=0x8ed9c0, rects=0x691668,
+ accepted_error=<optimized out>) at lib/evas/common/evas_tiler.c:652
+#2 rect_list_add_split_fuzzy_and_merge (node=0x8ed9c0, rects=0x691668,
+ split_accepted_error=<optimized out>, merge_accepted_error=<optimized out>)
+ at lib/evas/common/evas_tiler.c:849
+#3 _add_redraw (h=115, w=327, y=203, x=504, rects=0x691668)
+ at lib/evas/common/evas_tiler.c:956
+#4 evas_common_tilebuf_add_redraw (tb=0x691630, x=504, y=203, w=327, h=115)
+ at lib/evas/common/evas_tiler.c:986
+#5 0x00007ffff535cb9d in evas_object_render_pre_effect_updates (rects=
+ 0x688938, eo_obj=<optimized out>, is_v=1, was_v=<optimized out>)
+ at lib/evas/canvas/evas_object_main.c:356
+#6 0x00007ffff539bbfc in _evas_render_phase1_direct (render_objects=0x9baa60,
+ active_objects=0x688740, e=<optimized out>,
+ restack_objects=<optimized out>, delete_objects=<optimized out>)
+ at lib/evas/canvas/evas_render.c:231
+#7 evas_render_updates_internal (eo_e=0x6886b0,
+ make_updates=make_updates@entry=1 '\001', do_draw=do_draw@entry=1 '\001')
+ at lib/evas/canvas/evas_render.c:1357
+#8 0x00007ffff539dcd7 in _canvas_render_updates (eo_e=<optimized out>,
+ _pd=<optimized out>, list=<optimized out>)
+ at lib/evas/canvas/evas_render.c:1795
+#9 0x00007ffff706fa35 in _eo_op_internal (obj=0x6886b0, op_type=
+ EO_OP_TYPE_REGULAR, op=402, p_list=0x7fffffffe228) at lib/eo/eo.c:363
+#10 0x00007ffff70716cd in _eo_dov_internal (p_list=0x7fffffffe228, op_type=
+ EO_OP_TYPE_REGULAR, obj=0x6886b0) at lib/eo/eo.c:403
+#11 eo_do_internal (obj=0x6886b0, op_type=op_type@entry=EO_OP_TYPE_REGULAR)
+ at lib/eo/eo.c:434
+#12 0x00007ffff539dc5f in evas_render_updates (eo_e=<optimized out>)
+ at lib/evas/canvas/evas_render.c:1779
+#13 0x00007fffe6f7a43a in _ecore_evas_x_render (ee=0x686f30)
+ at modules/ecore_evas/engines/x/ecore_evas_x.c:447
+#14 0x00007ffff6e5e7f1 in _ecore_evas_idle_enter (data=<optimized out>)
+ at lib/ecore_evas/ecore_evas.c:59
+#15 0x00007ffff50e6019 in _ecore_call_task_cb (data=<optimized out>,
+ func=<optimized out>) at lib/ecore/ecore_private.h:300
+#16 _ecore_idle_enterer_call () at lib/ecore/ecore_idle_enterer.c:235
+#17 0x00007ffff50e862d in _ecore_main_loop_iterate_internal (
+ once_only=once_only@entry=0) at lib/ecore/ecore_main.c:1841
+#18 0x00007ffff50e8b27 in ecore_main_loop_begin () at lib/ecore/ecore_main.c:964
+#19 0x000000000040230e in elm_main (argc=1, argv=0x7fffffffe5b8) at gg_map.c:269
+#20 0x0000000000402359 in main (argc=1, argv=0x7fffffffe5b8) at gg_map.c:276
+
+
+===============================================================================
+2012-12-13_06-42
+
+^C
+Program received signal SIGINT, Interrupt.
+rect_list_add_split_fuzzy (node=0x884d70, rects=0x691668,
+ accepted_error=<optimized out>) at lib/evas/common/evas_tiler.c:653
+653 area = current.area + r.area - intra.area;
+
+(gdb) bt
+#0 rect_list_add_split_fuzzy (node=0x884d70, rects=0x691668,
+ accepted_error=<optimized out>) at lib/evas/common/evas_tiler.c:653
+#1 rect_list_add_split_fuzzy_and_merge (node=0x884d70, rects=0x691668,
+ split_accepted_error=<optimized out>, merge_accepted_error=<optimized out>)
+ at lib/evas/common/evas_tiler.c:849
+#2 _add_redraw (h=12, w=542, y=356, x=118, rects=0x691668)
+ at lib/evas/common/evas_tiler.c:956
+#3 evas_common_tilebuf_add_redraw (tb=tb@entry=0x691630, x=118, y=356, w=542,
+ h=12) at lib/evas/common/evas_tiler.c:986
+#4 0x00007fffe6d672ac in _merge_rects (tb=0x691630, r1=<optimized out>, r2=
+ 0x0, r3=0x0) at modules/evas/engines/software_x11/evas_engine.c:713
+#5 0x00007fffe6d6759b in eng_output_redraws_next_update_get (data=0x691c00, x=
+ 0x7fffffffe13c, y=0x7fffffffe140, w=0x7fffffffe144, h=0x7fffffffe148,
+ cx=<optimized out>, cy=0x7fffffffe150, cw=0x7fffffffe154, ch=
+ 0x7fffffffe158) at modules/evas/engines/software_x11/evas_engine.c:821
+#6 0x00007ffff539bf4f in evas_render_updates_internal (eo_e=0x6886b0,
+ make_updates=make_updates@entry=1 '\001', do_draw=do_draw@entry=1 '\001')
+ at lib/evas/canvas/evas_render.c:1533
+#7 0x00007ffff539dcd7 in _canvas_render_updates (eo_e=<optimized out>,
+ _pd=<optimized out>, list=<optimized out>)
+ at lib/evas/canvas/evas_render.c:1795
+#8 0x00007ffff706fa35 in _eo_op_internal (obj=0x6886b0, op_type=
+ EO_OP_TYPE_REGULAR, op=402, p_list=0x7fffffffe228) at lib/eo/eo.c:363
+#9 0x00007ffff70716cd in _eo_dov_internal (p_list=0x7fffffffe228, op_type=
+ EO_OP_TYPE_REGULAR, obj=0x6886b0) at lib/eo/eo.c:403
+#10 eo_do_internal (obj=0x6886b0, op_type=op_type@entry=EO_OP_TYPE_REGULAR)
+ at lib/eo/eo.c:434
+#11 0x00007ffff539dc5f in evas_render_updates (eo_e=<optimized out>)
+ at lib/evas/canvas/evas_render.c:1779
+#12 0x00007fffe6f7a43a in _ecore_evas_x_render (ee=0x686f30)
+ at modules/ecore_evas/engines/x/ecore_evas_x.c:447
+#13 0x00007ffff6e5e7f1 in _ecore_evas_idle_enter (data=<optimized out>)
+ at lib/ecore_evas/ecore_evas.c:59
+#14 0x00007ffff50e6019 in _ecore_call_task_cb (data=<optimized out>,
+ func=<optimized out>) at lib/ecore/ecore_private.h:300
+#15 _ecore_idle_enterer_call () at lib/ecore/ecore_idle_enterer.c:235
+#16 0x00007ffff50e847b in _ecore_main_loop_iterate_internal (
+ once_only=once_only@entry=0) at lib/ecore/ecore_main.c:1866
+#17 0x00007ffff50e8b27 in ecore_main_loop_begin ()
+ at lib/ecore/ecore_main.c:964
+#18 0x000000000040230e in elm_main (argc=1, argv=0x7fffffffe5b8)
+ at gg_map.c:269
+#19 0x0000000000402359 in main (argc=1, argv=0x7fffffffe5b8) at gg_map.c:276
+
+===============================================================================
+2012-12-13_15-32
+
+^C 0x7fffe6d61700 (LWP 21696)]
+[Thread 0x7fffe6d61700 (LWP 21696) exited]
+[New Thread 0x7fffe6d61700 (LWP 21697)]
+[Thread 0x7fffe6d61700 (LWP 21697) exited]
+[New Thread 0x7fffe6d61700 (LWP 21711)]
+[Thread 0x7fffe6d61700 (LWP 21711) exited]
+[New Thread 0x7fffe6d61700 (LWP 21712)]
+[Thread 0x7fffe6d61700 (LWP 21712) exited]
+
+Program received signal SIGINT, Interrupt.
+rect_list_add_split_fuzzy (node=0xa977b0, rects=0x691668,
+ accepted_error=<optimized out>) at lib/evas/common/evas_tiler.c:680
+680 else if ((outer.area - area) <= accepted_error)
+
+
+(gdb) bt
+#0 rect_list_add_split_fuzzy (node=0xa977b0, rects=0x691668,
+ accepted_error=<optimized out>) at lib/evas/common/evas_tiler.c:680
+#1 rect_list_add_split_fuzzy_and_merge (node=0xa977b0, rects=0x691668,
+ split_accepted_error=<optimized out>, merge_accepted_error=<optimized out>)
+ at lib/evas/common/evas_tiler.c:849
+#2 _add_redraw (h=6, w=126, y=110, x=463, rects=0x691668)
+ at lib/evas/common/evas_tiler.c:956
+#3 evas_common_tilebuf_add_redraw (tb=0x691630, x=463, y=110, w=126, h=6)
+ at lib/evas/common/evas_tiler.c:986
+#4 0x00007ffff535cb9d in evas_object_render_pre_effect_updates (
+ rects=rects@entry=0x688938, eo_obj=eo_obj@entry=0xa83500, is_v=is_v@entry=
+ 1, was_v=was_v@entry=1) at lib/evas/canvas/evas_object_main.c:356
+#5 0x00007ffff535560d in evas_object_image_render_pre (eo_obj=0xa83500, obj=
+ 0xa83590) at lib/evas/canvas/evas_object_image.c:3768
+#6 0x00007ffff539bbfc in _evas_render_phase1_direct (render_objects=0xa83500,
+ active_objects=0x688740, e=<optimized out>,
+ restack_objects=<optimized out>, delete_objects=<optimized out>)
+ at lib/evas/canvas/evas_render.c:231
+#7 evas_render_updates_internal (eo_e=0x6886b0,
+ make_updates=make_updates@entry=1 '\001', do_draw=do_draw@entry=1 '\001')
+ at lib/evas/canvas/evas_render.c:1357
+#8 0x00007ffff539dcd7 in _canvas_render_updates (eo_e=<optimized out>,
+ _pd=<optimized out>, list=<optimized out>)
+ at lib/evas/canvas/evas_render.c:1795
+#9 0x00007ffff706fa35 in _eo_op_internal (obj=0x6886b0, op_type=
+ EO_OP_TYPE_REGULAR, op=402, p_list=0x7fffffffe228) at lib/eo/eo.c:363
+#10 0x00007ffff70716cd in _eo_dov_internal (p_list=0x7fffffffe228, op_type=
+ EO_OP_TYPE_REGULAR, obj=0x6886b0) at lib/eo/eo.c:403
+#11 eo_do_internal (obj=0x6886b0, op_type=op_type@entry=EO_OP_TYPE_REGULAR)
+ at lib/eo/eo.c:434
+#12 0x00007ffff539dc5f in evas_render_updates (eo_e=<optimized out>)
+ at lib/evas/canvas/evas_render.c:1779
+#13 0x00007fffe6f7943a in _ecore_evas_x_render (ee=0x686f30)
+ at modules/ecore_evas/engines/x/ecore_evas_x.c:447
+#14 0x00007ffff6e5e7f1 in _ecore_evas_idle_enter (data=<optimized out>)
+ at lib/ecore_evas/ecore_evas.c:59
+#15 0x00007ffff50e6019 in _ecore_call_task_cb (data=<optimized out>,
+ func=<optimized out>) at lib/ecore/ecore_private.h:300
+#16 _ecore_idle_enterer_call () at lib/ecore/ecore_idle_enterer.c:235
+#17 0x00007ffff50e847b in _ecore_main_loop_iterate_internal (
+ once_only=once_only@entry=0) at lib/ecore/ecore_main.c:1866
+#18 0x00007ffff50e8b27 in ecore_main_loop_begin ()
+ at lib/ecore/ecore_main.c:964
+#19 0x00000000004022f1 in elm_main (argc=1, argv=0x7fffffffe5b8)
+ at gg_map.c:273
+#20 0x000000000040233c in main (argc=1, argv=0x7fffffffe5b8) at gg_map.c:280
+(gdb)
+
+
+===============================================================================
+2012-12-13_17-19
+
+
+rect_list_add_split_fuzzy (node=0xb07d60, rects=0x692668, [0/1873]
+ accepted_error=<optimized out>) at lib/evas/common/evas_tiler.c:653
+653 area = current.area + r.area - intra.area;
+(gdb) bt
+#0 rect_list_add_split_fuzzy (node=0xb07d60, rects=0x692668,
+ accepted_error=<optimized out>) at lib/evas/common/evas_tiler.c:653
+#1 rect_list_add_split_fuzzy_and_merge (node=0xb07d60, rects=0x692668,
+ split_accepted_error=<optimized out>, merge_accepted_error=<optimized out>)
+ at lib/evas/common/evas_tiler.c:849
+#2 _add_redraw (h=30, w=79, y=539, x=665, rects=0x692668)
+ at lib/evas/common/evas_tiler.c:956
+#3 evas_common_tilebuf_add_redraw (tb=0x692630, x=665, y=539, w=79, h=30)
+ at lib/evas/common/evas_tiler.c:986
+#4 0x00007ffff539bd1c in _evas_render_phase1_direct (render_objects=0xb61c40,
+ active_objects=0x689740, e=<optimized out>,
+ restack_objects=<optimized out>, delete_objects=<optimized out>)
+ at lib/evas/canvas/evas_render.c:257
+#5 evas_render_updates_internal (eo_e=0x6896b0,
+ make_updates=make_updates@entry=1 '\001', do_draw=do_draw@entry=1 '\001')
+ at lib/evas/canvas/evas_render.c:1357
+#6 0x00007ffff539dcd7 in _canvas_render_updates (eo_e=<optimized out>,
+ _pd=<optimized out>, list=<optimized out>)
+ at lib/evas/canvas/evas_render.c:1795
+#7 0x00007ffff706fa35 in _eo_op_internal (obj=0x6896b0, op_type=
+ EO_OP_TYPE_REGULAR, op=402, p_list=0x7fffffffe228) at lib/eo/eo.c:363
+#8 0x00007ffff70716cd in _eo_dov_internal (p_list=0x7fffffffe228, op_type=
+ EO_OP_TYPE_REGULAR, obj=0x6896b0) at lib/eo/eo.c:403
+#9 eo_do_internal (obj=0x6896b0, op_type=op_type@entry=EO_OP_TYPE_REGULAR)
+ at lib/eo/eo.c:434
+#10 0x00007ffff539dc5f in evas_render_updates (eo_e=<optimized out>)
+ at lib/evas/canvas/evas_render.c:1779
+#11 0x00007fffe6f7943a in _ecore_evas_x_render (ee=0x687f30)
+ at modules/ecore_evas/engines/x/ecore_evas_x.c:447
+#12 0x00007ffff6e5e7f1 in _ecore_evas_idle_enter (data=<optimized out>)
+ at lib/ecore_evas/ecore_evas.c:59
+#13 0x00007ffff50e6019 in _ecore_call_task_cb (data=<optimized out>,
+ func=<optimized out>) at lib/ecore/ecore_private.h:300
+#14 _ecore_idle_enterer_call () at lib/ecore/ecore_idle_enterer.c:235
+#15 0x00007ffff50e847b in _ecore_main_loop_iterate_internal (
+ once_only=once_only@entry=0) at lib/ecore/ecore_main.c:1866
+#16 0x00007ffff50e8b27 in ecore_main_loop_begin () at lib/ecore/ecore_main.c:964
+#17 0x00000000004025d9 in elm_main (argc=1, argv=0x7fffffffe5b8) at gg_map.c:324
+#18 0x0000000000402624 in main (argc=1, argv=0x7fffffffe5b8) at gg_map.c:331
+
+===============================================================================
+2012-12-16_04-22
+
+This bug is a lockup of evas during rendering in my efl app.
+App freeze and takes 100% CPU. Below are backtraces when gdb was running and
+I interrupt it with Ctrl-C.
+
+At the time of crash, around 300objects total :
+elementary interface + ~100 edje + 100 edje mapped with evas_map + ~100 evas_object_text
+
+This crash is not predictible from what I see, sometimes it goes fine for a long
+time.
+It seem to crash more when using more edje objects.
+
+happened AFTER raster's fix
+------------------------------------------------------------------------
+r81039 | raster | 2012-12-16 03:01:11 +0100 (Sun, 16 Dec 2012) | 5 lines
+small change - dont let update rect list for image object become a
+runaway endless list if evas doenst come around and render (pick it
+up) any time soon - limit to 512 update rects.
+
+
+^C
+Program received signal SIGINT, Interrupt.
+0x00007ffff53ddc54 in _calc_intra_outer_rect_area (outer=<synthetic pointer>,
+ intra=<synthetic pointer>, a=..., b=...) at lib/evas/common/evas_tiler.c:530
+530 outer->height = max_bottom - min_top;
+(gdb) bt
+#0 0x00007ffff53ddc54 in _calc_intra_outer_rect_area (
+ outer=<synthetic pointer>, intra=<synthetic pointer>, a=..., b=...)
+ at lib/evas/common/evas_tiler.c:530
+#1 rect_list_add_split_fuzzy (node=0x9d27c0, rects=0x6942b8,
+ accepted_error=<optimized out>) at lib/evas/common/evas_tiler.c:652
+#2 rect_list_add_split_fuzzy_and_merge (node=0x9d27c0, rects=0x6942b8,
+ split_accepted_error=<optimized out>, merge_accepted_error=<optimized out>)
+ at lib/evas/common/evas_tiler.c:849
+#3 _add_redraw (h=76, w=276, y=453, x=990, rects=0x6942b8)
+ at lib/evas/common/evas_tiler.c:956
+#4 evas_common_tilebuf_add_redraw (tb=0x694280, x=990, y=453, w=276, h=76)
+ at lib/evas/common/evas_tiler.c:986
+#5 0x00007ffff539736f in _evas_render_prev_cur_clip_cache_add (obj=obj@entry=
+ 0xbee890, e=0x68b390, e=0x68b390) at lib/evas/canvas/evas_render.c:158
+#6 0x00007ffff539ba0c in _evas_render_phase1_direct (render_objects=0xbee800,
+ active_objects=0x68b390, e=<optimized out>,
+ restack_objects=<optimized out>, delete_objects=<optimized out>)
+ at lib/evas/canvas/evas_render.c:257
+#7 evas_render_updates_internal (eo_e=0x68b300,
+ make_updates=make_updates@entry=1 '\001', do_draw=do_draw@entry=1 '\001')
+ at lib/evas/canvas/evas_render.c:1357
+#8 0x00007ffff539d9c7 in _canvas_render_updates (eo_e=<optimized out>,
+ _pd=<optimized out>, list=<optimized out>)
+ at lib/evas/canvas/evas_render.c:1795
+#9 0x00007ffff706fa45 in _eo_op_internal (obj=0x68b300, op_type=
+ EO_OP_TYPE_REGULAR, op=402, p_list=0x7fffffffe1f8) at lib/eo/eo.c:363
+#10 0x00007ffff70716dd in _eo_dov_internal (p_list=0x7fffffffe1f8, op_type=
+ EO_OP_TYPE_REGULAR, obj=0x68b300) at lib/eo/eo.c:403
+#11 eo_do_internal (obj=0x68b300, op_type=op_type@entry=EO_OP_TYPE_REGULAR)
+ at lib/eo/eo.c:434
+#12 0x00007ffff539d94f in evas_render_updates (eo_e=<optimized out>)
+ at lib/evas/canvas/evas_render.c:1779
+#13 0x00007fffe6f7743a in _ecore_evas_x_render (ee=0x689b80)
+ at modules/ecore_evas/engines/x/ecore_evas_x.c:447
+#14 0x00007ffff6e5e841 in _ecore_evas_idle_enter (data=<optimized out>)
+ at lib/ecore_evas/ecore_evas.c:59
+#15 0x00007ffff50e5059 in _ecore_call_task_cb (data=<optimized out>,
+ func=<optimized out>) at lib/ecore/ecore_private.h:300
+#16 _ecore_idle_enterer_call () at lib/ecore/ecore_idle_enterer.c:235
+#17 0x00007ffff50e74bb in _ecore_main_loop_iterate_internal (
+ once_only=once_only@entry=0) at lib/ecore/ecore_main.c:1866
+#18 0x00007ffff50e7b67 in ecore_main_loop_begin () at lib/ecore/ecore_main.c:964
+#19 0x00000000004035b0 in elm_main (argc=1, argv=0x7fffffffe5b8) at gg_map.c:461
+---Type <return> to continue, or q <return> to quit---
+#20 0x00000000004035fb in main (argc=1, argv=0x7fffffffe5b8) at gg_map.c:468
+
+===============================================================================
+2012-12-16_18-16
+
+
+^C
+Program received signal SIGINT, Interrupt.
+0x00007ffff53ddd20 in _calc_intra_outer_rect_area (outer=<synthetic pointer>,
+ intra=<synthetic pointer>, a=..., b=...)
+ at lib/evas/common/evas_tiler.c:509
+509 if (a.bottom < b.bottom)
+(gdb) bt
+#0 0x00007ffff53ddd20 in _calc_intra_outer_rect_area (
+ outer=<synthetic pointer>, intra=<synthetic pointer>, a=..., b=...)
+ at lib/evas/common/evas_tiler.c:509
+#1 rect_list_add_split_fuzzy (node=0x16b03d0, rects=0x692668,
+ accepted_error=<optimized out>) at lib/evas/common/evas_tiler.c:652
+#2 rect_list_add_split_fuzzy_and_merge (node=0x16b03d0, rects=0x692668,
+ split_accepted_error=<optimized out>, merge_accepted_error=<optimized out>)
+ at lib/evas/common/evas_tiler.c:849
+#3 _add_redraw (h=31, w=323, y=264, x=696, rects=0x692668)
+ at lib/evas/common/evas_tiler.c:956
+#4 evas_common_tilebuf_add_redraw (tb=0x692630, x=696, y=264, w=323, h=31)
+ at lib/evas/common/evas_tiler.c:986
+#5 0x00007ffff539736f in _evas_render_prev_cur_clip_cache_add (obj=obj@entry=
+ 0x36cef40, e=0x689740, e=0x689740) at lib/evas/canvas/evas_render.c:158
+#6 0x00007ffff539ba0c in _evas_render_phase1_direct (render_objects=
+ 0x36ceeb0, active_objects=0x689740, e=<optimized out>,
+ restack_objects=<optimized out>, delete_objects=<optimized out>)
+ at lib/evas/canvas/evas_render.c:257
+#7 evas_render_updates_internal (eo_e=0x6896b0,
+ make_updates=make_updates@entry=1 '\001', do_draw=do_draw@entry=1 '\001')
+ at lib/evas/canvas/evas_render.c:1357
+#8 0x00007ffff539d9c7 in _canvas_render_updates (eo_e=<optimized out>,
+ _pd=<optimized out>, list=<optimized out>)
+ at lib/evas/canvas/evas_render.c:1795
+#9 0x00007ffff706fa45 in _eo_op_internal (obj=0x6896b0, op_type=
+ EO_OP_TYPE_REGULAR, op=402, p_list=0x7fffffffe1f8) at lib/eo/eo.c:363
+#10 0x00007ffff70716dd in _eo_dov_internal (p_list=0x7fffffffe1f8, op_type=
+ EO_OP_TYPE_REGULAR, obj=0x6896b0) at lib/eo/eo.c:403
+#11 eo_do_internal (obj=0x6896b0, op_type=op_type@entry=EO_OP_TYPE_REGULAR)
+ at lib/eo/eo.c:434
+#12 0x00007ffff539d94f in evas_render_updates (eo_e=<optimized out>)
+ at lib/evas/canvas/evas_render.c:1779
+#13 0x00007fffe6f7743a in _ecore_evas_x_render (ee=0x687f30)
+ at modules/ecore_evas/engines/x/ecore_evas_x.c:447
+#14 0x00007ffff6e5e841 in _ecore_evas_idle_enter (data=<optimized out>)
+ at lib/ecore_evas/ecore_evas.c:59
+#15 0x00007ffff50e5059 in _ecore_call_task_cb (data=<optimized out>,
+ func=<optimized out>) at lib/ecore/ecore_private.h:300
+#16 _ecore_idle_enterer_call () at lib/ecore/ecore_idle_enterer.c:235
+#17 0x00007ffff50e766d in _ecore_main_loop_iterate_internal (
+ once_only=once_only@entry=0) at lib/ecore/ecore_main.c:1841
+#18 0x00007ffff50e7b67 in ecore_main_loop_begin ()
+ at lib/ecore/ecore_main.c:964
+#19 0x00000000004031c1 in elm_main (argc=1, argv=0x7fffffffe5b8)
+ at gg_map.c:447
+#20 0x000000000040320c in main (argc=1, argv=0x7fffffffe5b8) at gg_map.c:454
+
+
diff --git a/egraph/doc/egraph.xoj b/egraph/doc/egraph.xoj
new file mode 100644
index 0000000..59ca038
--- /dev/null
+++ b/egraph/doc/egraph.xoj
Binary files differ
diff --git a/egraph/doc/internals.txt b/egraph/doc/internals.txt
new file mode 100644
index 0000000..7a8b34d
--- /dev/null
+++ b/egraph/doc/internals.txt
@@ -0,0 +1,89 @@
+Internals
+=========
+
+=== algo to handle threaded graph layouting correctly ===
+
+###### GENERAL ALGO
+_layouting_start(graph-2, coords-2);
+
+_add/del(structs-3);
+_repositionning(graph, structs, coords);
+
+_layouting_end(coords-2);
+
+_v2_updtate(structs-2, graph-2, coords-2);
+ _v2_structs/graph_update(structs-2, graph);
+ -> structs, graph // real add/del
+ _coords_copy(coords-2);
+ -> coords
+
+_repositionning(graph, structs, coords);
+
+_v3_update(structs-3);
+ _v3_structs/graph_update(structs-3, graph-2);
+ -> structs-2, graph-2
+
+###### TIMELINE VERTICE ADD
+egraph_vertice_add();
+ id = freeids[vertices_count];
+ v = vertices[id];
+ v->id = id;
+ vertices_count++;
+ v->v3-new = 1;
+
+_v3_update();
+ v->v2-new = v->v3-new;
+ v->v3-new = 0;
+ _v2_add()
+ v->graph2_vid = graph2_vcount;
+ graph2_vcount++;
+ igraph_add_vertices(graph2);
+
+_layouting_start();
+ // end up in coords2
+
+_layouting_end();
+_coords_copy();
+ // end up in coords
+
+_v2_update();
+ _add()
+ v->v2-new = 0;
+ v->graph_vid = graph_vcount;
+ graph_vcount++;
+ igraph_add_vertices(graph);
+ evas_object
+
+// in structs, graph, coords
+
+###### TIMELINE VERTICE DEL
+egraph_vertice_del();
+ v->v3-del = 1;
+
+_v3_update();
+ v->v2-del = v->v3-del;
+ v->v3-del = 0;
+ _v2_del()
+ igraph_delete_vertices(v->graph2_vid);
+ graph2_vcount--;
+
+_layouting_start();
+ // not in coords2
+
+_layouting_end();
+_coords_copy();
+ // not in coords
+
+_v2_update();
+ _del();
+ igraph_delete_vertices(v->graph_vid);
+ graph_vcount--;
+ freeids[vertices_count] = v->id;
+ vertices_count--;
+ free(v);
+
+// removed from structs, graph, coords
+
+###### TIMELINE VERTICE DEL
+
+
diff --git a/egraph/egraph.c b/egraph/egraph.c
new file mode 100644
index 0000000..bffcebf
--- /dev/null
+++ b/egraph/egraph.c
@@ -0,0 +1,1275 @@
+#include <err.h>
+#include <Efx.h>
+#include <Edje.h>
+
+#include "Egraph.h"
+
+#define DEBUG 0
+
+#define TYPE_MAXLEN 30
+
+static void _smart_add(Evas_Object *obj);
+static void _smart_del(Evas_Object *obj);
+static void _smart_move(Evas_Object *obj, Evas_Coord x, Evas_Coord y);
+static void _smart_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h);
+static void _edge_v2_add(Egraph *eg, Egraph_Edge *e);
+static void _edge_add(Egraph *eg, Egraph_Edge *e);
+static void _edge_signal(Egraph *eg, Egraph_Edge *e, const char *signal);
+static void _edge_v2_del(Egraph *eg, Egraph_Edge *e);
+static void _edge_del(Egraph *eg, Egraph_Edge *e);
+static void _vertice_v2_add(Egraph *eg, Egraph_Vertice *v);
+static void _vertice_add(Egraph *eg, Egraph_Vertice *v);
+static void _vertice_v2_del(Egraph *eg, Egraph_Vertice *v);
+static void _vertice_del(Egraph *eg, Egraph_Vertice *v);
+static void _vertice_update_status(Egraph *eg, Egraph_Vertice *v);
+static void _vertice_signal(Egraph *eg, Egraph_Vertice *v, const char *signal);
+static void _vertice_geometry_update(Egraph *eg, Egraph_Vertice *v);
+static void _v2_update(Egraph *eg);
+static void _v3_update(Egraph *eg);
+static void _coords2_copy(Egraph *eg);
+static void _cb_blob_arrived(void *data, Efx_Map_Data *e, Evas_Object *obj);
+static void _blob_send(Evas_Object *blob,
+ Egraph_Vertice *v, Evas_Coord x, Evas_Coord y);
+static void _layouting_schedule(Egraph *eg);
+static Eina_Bool _cb_layouting_start(void *data);
+static void _cb_layouting_run(void *data, Ecore_Thread *thread);
+static void _cb_layouting_end(void *data, Ecore_Thread *thread);
+static void _cb_layouting_cancel(void *data, Ecore_Thread *thread);
+static int _igraph_query_vid(igraph_t *g, int g_len, int id);
+static void _cb_vertice_move(void *data,
+ Evas *evas, Evas_Object *obj, void *event_info);
+static void _reposition(Egraph *eg, int no_animation);
+static void _matrix_minmax_2v(const igraph_matrix_t *m, int row_count,
+ float *c1min, float *c1max,
+ float *c2min, float *c2max);
+static Evas_Object *_edje_obj_new(Egraph *eg, const char *group);
+static Evas_Object *_edje_obj_set(Egraph *eg, Evas_Object *o, const char *group);
+
+static const Evas_Smart_Cb_Description _smart_callbacks[] = {{NULL, NULL}};
+#define EGRAPH_DATA_GET(o, ptr) \
+ Egraph * ptr = evas_object_smart_data_get(o)
+/* defines _egraph_parent_sc and _egraph_smart_class_new */
+EVAS_SMART_SUBCLASS_NEW("Egraph", _egraph,
+ Evas_Smart_Class, Evas_Smart_Class,
+ evas_object_smart_clipped_class_get, _smart_callbacks);
+
+static void
+_egraph_smart_set_user(Evas_Smart_Class *sc)
+{
+ /* specializing these two */
+ sc->add = _smart_add;
+ sc->del = _smart_del;
+ sc->move = _smart_move;
+ /* clipped smart object has no hook on resizes or calculations */
+ sc->resize = _smart_resize;
+}
+
+static void
+_smart_add(Evas_Object *obj)
+{
+ EVAS_SMART_DATA_ALLOC(obj, Egraph);
+
+ priv->evas = evas_object_evas_get(obj);
+
+ _egraph_parent_sc->add(obj);
+}
+
+static void
+_smart_del(Evas_Object *obj)
+{
+ EGRAPH_DATA_GET(obj, eg);
+
+ igraph_destroy(&eg->graph);
+ if (eg->layouting.running == 2)
+ ecore_thread_cancel(eg->layouting.thread);
+
+ _egraph_parent_sc->del(obj);
+}
+
+static void
+_smart_move(Evas_Object *obj, Evas_Coord x, Evas_Coord y)
+{
+ EGRAPH_DATA_GET(obj, eg);
+
+ _reposition(eg, 1);
+
+ _egraph_parent_sc->move(obj, x, y);
+}
+
+static void
+_smart_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h)
+{
+ EGRAPH_DATA_GET(obj, eg);
+
+ _reposition(eg, 1);
+}
+
+Evas_Object *
+egraph_new(Evas *evas, int directed)
+{
+ Evas_Object *obj = NULL;
+ Evas_Object *rect;
+ Egraph *eg;
+ int i;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(evas, NULL);
+
+ efx_init();
+ eina_log_domain_level_set("efx", EINA_LOG_LEVEL_WARN);
+
+ obj = evas_object_smart_add(evas, _egraph_smart_class_new());
+ eg = evas_object_smart_data_get(obj);
+ if (!eg) goto err;
+ eg->obj = obj;
+ rect = evas_object_rectangle_add(eg->evas);
+ evas_object_color_set(rect, 0, 0, 0, 0);
+ evas_object_smart_member_add(rect, obj);
+ eg->split_vertice_edge = rect;
+
+ eg->graph_directed = directed;
+ eg->display_vertices = 1;
+ eg->display_names = 1;
+ eg->display_edges = 1;
+ eg->use_animations = 1;
+ eg->do_improvements = 1;
+ egraph_theme_file_set(obj, NULL);
+ eg->theme_edges = 1;
+ eg->layout = EGRAPH_LAYOUT_DEFAULT;
+
+ /* needed for igraph attribute handling */
+ igraph_i_set_attribute_table(&igraph_cattribute_table);
+
+ eg->vertices = eina_hash_int32_new(NULL);
+ for (i=0; i<EGRAPH_VERTICES_MAX-1; i++)
+ eg->vertices_freeids[i] = i;
+ if (igraph_empty(&eg->graph, 0, directed))
+ goto err;
+ if (igraph_empty(&eg->graph2, 0, directed))
+ goto err;
+ igraph_matrix_init(&eg->coords, 0, 2);
+ igraph_matrix_init(&eg->coords2, 0, 2);
+ eg->graph_wmin = 0;
+ eg->graph_wmax = 200;
+ eg->graph_hmin = 0;
+ eg->graph_hmax = 200;
+ eg->vertice_max_w = 0;
+ eg->vertice_max_h = 0;
+
+ return obj;
+
+err:
+ if (obj)
+ evas_object_del(obj);
+ return NULL;
+}
+
+void
+egraph_clear(Evas_Object *obj)
+{
+ EGRAPH_DATA_GET(obj, eg);
+ Eina_Iterator *it;
+ Egraph_Vertice *v;
+ void *data;
+
+ it = eina_hash_iterator_tuple_new(eg->vertices);
+ while (eina_iterator_next(it, &data)) {
+ Eina_Hash_Tuple *t = data;
+ v = t->data;
+ egraph_vertice_del(obj, v);
+ }
+}
+
+void
+egraph_theme_file_set(Evas_Object *obj, char *path)
+{
+ EGRAPH_DATA_GET(obj, eg);
+
+ if (eg->theme_path)
+ free(eg->theme_path);
+
+ if (!path) {
+ char buf[256];
+
+ snprintf(buf, sizeof(buf),
+ "%s/egraph.edj",
+ "/usr/local/share/egraph"); /* XXX use eina_prefix */
+ path = buf;
+ }
+ eg->theme_path = strndup(path, 256);
+}
+
+void
+egraph_theme_edges_set(Evas_Object *obj, int set)
+{
+ EGRAPH_DATA_GET(obj, eg);
+
+ eg->theme_edges = set;
+}
+
+void
+egraph_layout_set(Evas_Object *obj, Egraph_Layout layout)
+{
+ EGRAPH_DATA_GET(obj, eg);
+
+ eg->layout = layout;
+ _layouting_schedule(eg);
+}
+
+void
+egraph_display_vertices_set(Evas_Object *obj, int set)
+{
+ EGRAPH_DATA_GET(obj, eg);
+ Eina_Iterator *it;
+ Egraph_Vertice *v;
+ void *data;
+
+ eg->display_vertices = set;
+
+ it = eina_hash_iterator_tuple_new(eg->vertices);
+ while (eina_iterator_next(it, &data)) {
+ Eina_Hash_Tuple *t = data;
+ v = t->data;
+ if (v->o) {
+ if (set == 0)
+ evas_object_hide(v->o);
+ else
+ evas_object_show(v->o);
+ }
+ }
+
+ _reposition(eg, 0);
+}
+
+void
+egraph_display_names_set(Evas_Object *obj, int set)
+{
+ EGRAPH_DATA_GET(obj, eg);
+ Eina_Iterator *it;
+ Egraph_Vertice *v;
+ void *data;
+
+ eg->display_names = set;
+
+ it = eina_hash_iterator_tuple_new(eg->vertices);
+ while (eina_iterator_next(it, &data)) {
+ Eina_Hash_Tuple *t = data;
+ v = t->data;
+ egraph_vertice_rename(obj, v, set ? v->name : NULL);
+ }
+}
+
+void
+egraph_display_edges_set(Evas_Object *obj, int set)
+{
+ EGRAPH_DATA_GET(obj, eg);
+ Eina_List *l;
+ Egraph_Edge *e;
+
+ eg->display_edges = set;
+
+ EINA_LIST_FOREACH(eg->edges, l, e) {
+ if (e->o) {
+ if (set == 0)
+ evas_object_hide(e->o);
+ else
+ evas_object_show(e->o);
+ }
+ }
+}
+
+void
+egraph_use_animations_set(Evas_Object *obj, int set)
+{
+ EGRAPH_DATA_GET(obj, eg);
+
+ eg->use_animations = set;
+}
+
+void
+egraph_do_improvements_set(Evas_Object *obj, int set)
+{
+ EGRAPH_DATA_GET(obj, eg);
+
+ eg->do_improvements = set;
+}
+
+Egraph_Edge *
+egraph_edge_add(Evas_Object *obj, Egraph_Vertice *a, Egraph_Vertice *b,
+ void *data)
+{
+ EGRAPH_DATA_GET(obj, eg);
+ Egraph_Edge *e;
+
+ e = calloc(1, sizeof(Egraph_Edge));
+ if (!e)
+ err(1, "egraph_edge_add: cannot calloc");
+ e->v3_new = 1;
+ e->a = a;
+ e->b = b;
+ e->data = data;
+ eg->edges = eina_list_append(eg->edges, e);
+ if (DEBUG)
+ printf("egraph_edge_add %d %d\n", e->a->id, e->b->id);
+
+ egraph_edge_type_set(obj, e, "edge_normal");
+
+ _layouting_schedule(eg);
+ return e;
+}
+
+static void
+_edge_v2_add(Egraph *eg, Egraph_Edge *e)
+{
+ int a_pos, b_pos;
+
+ if (DEBUG)
+ printf("_edge_v2_add %d %d\n", e->a->id, e->b->id);
+
+ e->v2_new = 1;
+ e->v3_new = 0;
+
+ a_pos = _igraph_query_vid(&eg->graph2, eg->graph2_vcount, e->a->id);
+ b_pos = _igraph_query_vid(&eg->graph2, eg->graph2_vcount, e->b->id);
+ igraph_add_edge(&eg->graph2, a_pos, b_pos);
+}
+
+static void
+_edge_add(Egraph *eg, Egraph_Edge *e)
+{
+ Evas_Object *eobj = NULL;
+ int a_pos, b_pos;
+
+ e->new = 1;
+ e->v2_new = 0;
+
+ a_pos = _igraph_query_vid(&eg->graph, eg->graph_vcount, e->a->id);
+ b_pos = _igraph_query_vid(&eg->graph, eg->graph_vcount, e->b->id);
+ igraph_add_edge(&eg->graph, a_pos, b_pos);
+
+ if (eg->theme_edges) {
+ eobj = _edje_obj_new(eg, e->type);
+ if (eobj) {
+ e->o_usetheme = 1;
+ evas_object_resize(eobj, eg->vertice_max_w, eg->vertice_max_h); // XXX rm
+ }
+ }
+ if (!eobj) {
+ eobj = evas_object_line_add(eg->evas);
+ evas_object_color_set(eobj, 255, 0, 0, 255);
+ }
+ evas_object_smart_member_add(eobj, eg->obj);
+ evas_object_stack_below(eobj, eg->split_vertice_edge);
+ e->o = eobj;
+ _edge_signal(eg, e, "become_active");
+
+ e->a->edges = eina_list_append(e->a->edges, e);
+ e->b->edges = eina_list_append(e->b->edges, e);
+ _vertice_update_status(eg, e->a);
+ _vertice_update_status(eg, e->b);
+}
+
+void
+egraph_edge_type_set(Evas_Object *obj, Egraph_Edge *e, const char *type)
+{
+ EGRAPH_DATA_GET(obj, eg);
+
+ if (e->type)
+ free(e->type);
+
+ e->type = strndup(type, TYPE_MAXLEN);
+ if (eg->theme_edges && e->o) {
+ _edje_obj_set(eg, e->o, type);
+ }
+}
+
+static void
+_edge_signal(Egraph *eg, Egraph_Edge *e, const char *signal)
+{
+ if (eg->theme_edges && e->o);
+ edje_object_signal_emit(e->o, signal, "");
+}
+
+void
+egraph_edge_del(Evas_Object *obj, Egraph_Edge *e)
+{
+ EGRAPH_DATA_GET(obj, eg);
+
+ // XXX DEBUG find mem corrupt, del already del edge
+ if (!egraph_edge_find(obj, e->a, e->b))
+ printf("XXX DEBUG: egraph_edge_del on unknown edge !!!\n");
+ if (DEBUG)
+ printf("egraph_edge_del: %d %d\n", e->a->id, e->b->id);
+
+ e->v3_del = 1;
+ _layouting_schedule(eg);
+}
+
+static void
+_edge_v2_del(Egraph *eg, Egraph_Edge *e)
+{
+ igraph_es_t es;
+ int a_pos, b_pos;
+
+ if (e->v2_del == 1)
+ return;
+
+ if (DEBUG)
+ printf("_edge_v2_del %d %d\n", e->a->id, e->b->id);
+
+ e->v2_del = 1;
+ e->v3_del = 0;
+
+ if (!e->v3_new) {
+ a_pos = _igraph_query_vid(&eg->graph2, eg->graph2_vcount, e->a->id);
+ b_pos = _igraph_query_vid(&eg->graph2, eg->graph2_vcount, e->b->id);
+ igraph_es_pairs_small(&es, eg->graph_directed, a_pos, b_pos, -1);
+ igraph_delete_edges(&eg->graph2, es);
+ }
+}
+
+static void
+_edge_del(Egraph *eg, Egraph_Edge *e)
+{
+ igraph_es_t es;
+ int a_pos, b_pos;
+
+ if (!e->v3_new && !e->v2_new) {
+ a_pos = _igraph_query_vid(&eg->graph, eg->graph_vcount, e->a->id);
+ b_pos = _igraph_query_vid(&eg->graph, eg->graph_vcount, e->b->id);
+ igraph_es_pairs_small(&es, eg->graph_directed, a_pos, b_pos, -1);
+ igraph_delete_edges(&eg->graph, es);
+
+ e->a->edges = eina_list_remove(e->a->edges, e);
+ e->b->edges = eina_list_remove(e->b->edges, e);
+ _vertice_update_status(eg, e->a);
+ _vertice_update_status(eg, e->b);
+
+ evas_object_del(e->o);
+ }
+ eg->edges = eina_list_remove(eg->edges, e);
+ free(e);
+}
+
+Egraph_Edge *
+egraph_edge_find(Evas_Object *obj, Egraph_Vertice *a, Egraph_Vertice *b)
+{
+ EGRAPH_DATA_GET(obj, eg);
+ Egraph_Edge *e;
+ Eina_List *l;
+
+ EINA_LIST_FOREACH(eg->edges, l, e)
+ if (!(e->v3_del || e->v2_del) &&
+ ((e->a == a && e->b == b) ||
+ (e->a == b && e->b == a)))
+ return e;
+ return NULL;
+}
+
+Egraph_Vertice *
+egraph_vertice_add(Evas_Object *obj, const char *name, void *data)
+{
+ EGRAPH_DATA_GET(obj, eg);
+ Egraph_Vertice *v;
+ u_int32_t id;
+
+ if (eg->vertices_count == EGRAPH_VERTICES_MAX) {
+ printf("egraph error: maximum number of vertices reached !\n");
+ return NULL;
+ }
+ v = calloc(1, sizeof(Egraph_Vertice));
+ if (!v)
+ err(1, "egraph_vertice_add: cannot calloc");
+ id = eg->vertices_freeids[eg->vertices_count];
+ v->id = id;
+ eina_hash_add(eg->vertices, &id, v);
+ eg->vertices_count++;
+ v->v3_new = 1;
+ if (DEBUG)
+ printf("egraph_vertice_add %d\n", id);
+
+ if (name)
+ v->name = strndup(name, EGRAPH_VERTICE_NAME_MAXLEN);
+ egraph_vertice_type_set(obj, v, "vertice_normal");
+
+ v->data = data;
+
+ _layouting_schedule(eg);
+ return v;
+}
+
+static void
+_vertice_v2_add(Egraph *eg, Egraph_Vertice *v)
+{
+ v->v2_new = 1;
+ v->v3_new = 0;
+
+ igraph_add_vertices(&eg->graph2, 1, 0);
+ SETVAN(&eg->graph2, "id", eg->graph2_vcount, v->id);
+ eg->graph2_vcount++;
+}
+
+static void
+_vertice_add(Egraph *eg, Egraph_Vertice *v)
+{
+ Evas_Object *vobj = NULL;
+
+ v->new = 1;
+ v->v2_new = 0;
+
+ vobj = _edje_obj_new(eg, v->type);
+ if (!vobj) {
+ printf("egraph: could not load theme for vertice !");
+ v->v2_new = 1;
+ v->new = 0;
+ _vertice_v2_del(eg, v);
+ return;
+ }
+ evas_object_smart_member_add(vobj, eg->obj);
+ evas_object_stack_above(vobj, eg->split_vertice_edge);
+ evas_object_event_callback_add(vobj, EVAS_CALLBACK_MOVE, _cb_vertice_move, v);
+ v->o = vobj;
+ _vertice_geometry_update(eg, v);
+
+ igraph_add_vertices(&eg->graph, 1, 0);
+ SETVAN(&eg->graph, "id", eg->graph_vcount, v->id);
+ eg->graph_vcount++;
+
+ if (v->name)
+ egraph_vertice_rename(eg->obj, v, v->name);
+}
+
+void
+egraph_vertice_del(Evas_Object *obj, Egraph_Vertice *v)
+{
+ EGRAPH_DATA_GET(obj, eg);
+ Egraph_Edge *e;
+ Eina_List *l;
+
+ v->v3_del = 1;
+
+ EINA_LIST_FOREACH(eg->edges, l, e)
+ if (e->a == v || e->b == v)
+ egraph_edge_del(obj, e);
+
+ _layouting_schedule(eg);
+}
+
+static void
+_vertice_v2_del(Egraph *eg, Egraph_Vertice *v)
+{
+ Egraph_Edge *e;
+ Eina_List *l;
+ int pos;
+
+ if (v->v2_del == 1)
+ return;
+
+ v->v2_del = 1;
+ v->v3_del = 0;
+
+ if (DEBUG)
+ printf("_vertice_v2_del %d\n", v->id);
+ EINA_LIST_FOREACH(eg->edges, l, e)
+ if (e->a == v || e->b == v)
+ _edge_v2_del(eg, e);
+
+ if (!v->v3_new) {
+ pos = _igraph_query_vid(&eg->graph2, eg->graph2_vcount, v->id);
+ igraph_delete_vertices(&eg->graph2, igraph_vss_1(pos));
+ eg->graph2_vcount--;
+ }
+}
+
+static void
+_vertice_del(Egraph *eg, Egraph_Vertice *v)
+{
+ Egraph_Edge *e;
+ Eina_List *l, *lprev;
+ Evas_Object *blob;
+ int pos;
+
+ if (!v->v3_new && !v->v2_new) {
+ EINA_LIST_FOREACH_SAFE(eg->edges, l, lprev, e)
+ if (e->a == v || e->b == v)
+ _edge_del(eg, e);
+ eina_list_free(v->edges);
+
+ pos = _igraph_query_vid(&eg->graph, eg->graph_vcount, v->id);
+ igraph_delete_vertices(&eg->graph, igraph_vss_1(pos));
+ eg->graph_vcount--;
+
+ EINA_LIST_FOREACH(v->blobs_incoming, l, blob)
+ evas_object_del(blob);
+ eina_list_free(v->blobs_incoming);
+
+ evas_object_del(v->o);
+ }
+
+ eina_hash_del(eg->vertices, &v->id, NULL);
+ eg->vertices_count--;
+ eg->vertices_freeids[eg->vertices_count] = v->id;
+
+ free(v);
+}
+
+void
+egraph_vertice_type_set(Evas_Object *obj, Egraph_Vertice *v, const char *type)
+{
+ EGRAPH_DATA_GET(obj, eg);
+
+ if (v->type)
+ free(v->type);
+
+ v->type = strndup(type, TYPE_MAXLEN);
+ if (v->o)
+ _edje_obj_set(eg, v->o, type);
+}
+
+static void
+_vertice_update_status(Egraph *eg, Egraph_Vertice *v)
+{
+ Eina_List *l;
+ Egraph_Edge *e;
+ int status = 0;
+
+ if (v->is_group && (eina_list_count(v->edges) >= 1)) {
+ status = 1; /* we are a group with childs */
+ } else {
+ EINA_LIST_FOREACH(v->edges, l, e) {
+ if ((v == e->a && !e->b->is_group) ||
+ (v == e->b && !e->a->is_group)) {
+ status = 1; /* we are connected to at least on other non group node */
+ break;
+ }
+ }
+ }
+
+ if (status != v->status) {
+ if (status)
+ _vertice_signal(eg, v, "become_active");
+ else
+ _vertice_signal(eg, v, "become_inactive");
+ v->status = status;
+ }
+}
+
+static void
+_vertice_signal(Egraph *eg, Egraph_Vertice *v, const char *signal)
+{
+ if (v->o)
+ edje_object_signal_emit(v->o, signal, "");
+}
+
+void
+egraph_vertice_rename(Evas_Object *obj, Egraph_Vertice *v, const char *name)
+{
+ EGRAPH_DATA_GET(obj, eg);
+
+ if (eg->display_names == 0)
+ return;
+
+ if (name) {
+ if (v->name != name) {
+ if (v->name)
+ free(v->name);
+ v->name = strndup(name, EGRAPH_VERTICE_NAME_MAXLEN);
+ }
+ }
+ if (v->o) {
+ if (v->name)
+ edje_object_part_text_set(v->o, "label", v->name);
+ else
+ edje_object_part_text_set(v->o, "label", "");
+ _vertice_geometry_update(eg, v);
+ }
+}
+
+static void
+_vertice_geometry_update(Egraph *eg, Egraph_Vertice *v)
+{
+ int w, h;
+
+ edje_object_size_min_calc(v->o, &w, &h);
+ if (DEBUG)
+ printf("_vertice_geometry_update: %d %d\n", w, h);
+ evas_object_resize(v->o, w, h);
+ if (w > eg->vertice_max_w)
+ eg->vertice_max_w = w;
+ if (h > eg->vertice_max_h)
+ eg->vertice_max_h = h;
+ if (DEBUG)
+ printf("_vertice_geometry_update: end %d %d\n",
+ eg->vertice_max_w, eg->vertice_max_h);
+}
+
+void
+_color_int_to_rgb(u_int32_t color, int *r, int *g , int *b)
+{
+ *r = (color & 0xFF000000) >> 24;
+ *g = (color & 0x00FF0000) >> 16;
+ *b = (color & 0x0000FF00) >> 8;
+}
+
+void
+egraph_vertice_send_blob(Evas_Object *obj,
+ Egraph_Vertice *a, Egraph_Vertice *b,
+ int size, u_int32_t color)
+{
+ EGRAPH_DATA_GET(obj, eg);
+ Evas_Object *blob;
+ int ax, ay, aw, ah, bx, by, bw, bh;
+ int cr, cg, cb;
+
+ if (!a->o || !b->o)
+ return;
+ evas_object_geometry_get(a->o, &ax, &ay, &aw, &ah);
+ evas_object_geometry_get(b->o, &bx, &by, &bw, &bh);
+ _color_int_to_rgb(color, &cr, &cg, &cb);
+
+ blob = _edje_obj_new(eg, "blob");
+ if (!blob)
+ blob = evas_object_rectangle_add(eg->evas);
+ evas_object_color_set(blob, cr, cg, cb, 255);
+ evas_object_smart_member_add(blob, eg->obj);
+ evas_object_stack_above(blob, eg->split_vertice_edge);
+ evas_object_move(blob, ax, ay);
+ evas_object_resize(blob, size, size);
+ evas_object_show(blob);
+ b->blobs_incoming = eina_list_append(b->blobs_incoming, blob);
+ _blob_send(blob, b, bx, by);
+}
+
+static void
+_cb_blob_arrived(void *data, Efx_Map_Data *e, Evas_Object *obj)
+{
+ Egraph_Vertice *v;
+
+ v = data;
+ v->blobs_incoming = eina_list_remove(v->blobs_incoming, obj);
+ evas_object_del(obj);
+}
+
+static void
+_blob_send(Evas_Object *blob, Egraph_Vertice *v, Evas_Coord x, Evas_Coord y)
+{
+ if (DEBUG)
+ printf("blob_send %d %d\n", x, y);
+ efx_move(blob, EFX_EFFECT_SPEED_SINUSOIDAL,
+ &(Evas_Point){ x, y }, 0.6, _cb_blob_arrived, v);
+}
+
+static void
+_layouting_schedule(Egraph *eg)
+{
+ if (eg->layouting.running > 0) {
+ eg->layouting.todo = 1;
+ return;
+ }
+
+ eg->layouting.running = 1;
+ ecore_timer_add(0.0, _cb_layouting_start, eg); /* delayed start */
+}
+
+static void
+_v2_update(Egraph *eg)
+{
+ Eina_List *l, *lprev, *todel, *toadd;
+ Eina_Iterator *it;
+ Egraph_Vertice *v;
+ Egraph_Edge *e;
+ void *data;
+
+ /* update graph and structs based on v2_add / v2_del */
+ it = eina_hash_iterator_tuple_new(eg->vertices);
+ todel = NULL; toadd = NULL;
+ while (eina_iterator_next(it, &data)) {
+ Eina_Hash_Tuple *t = data;
+ v = t->data;
+ if (v->v2_del)
+ todel = eina_list_append(todel, v);
+ else if (v->v2_new)
+ toadd = eina_list_append(toadd, v);
+ }
+ eina_iterator_free(it);
+ EINA_LIST_FOREACH(todel, l, v)
+ _vertice_del(eg, v);
+ EINA_LIST_FOREACH(toadd, l, v)
+ _vertice_add(eg, v);
+ todel = eina_list_free(todel);
+ toadd = eina_list_free(toadd);
+ EINA_LIST_FOREACH_SAFE(eg->edges, l, lprev, e) {
+ if (e->v2_del)
+ _edge_del(eg, e);
+ else if (e->v2_new)
+ _edge_add(eg, e);
+ }
+}
+
+static void
+_v3_update(Egraph *eg)
+{
+ Eina_List *l, *lprev;
+ Eina_Iterator *it;
+ Egraph_Vertice *v;
+ Egraph_Edge *e;
+ void *data;
+ float ranx, rany;
+ int changes_count = 0;
+ int changes_diff = 0;
+ int i, id;
+
+ if (DEBUG)
+ printf("_v3_update\n");
+ /* update graph2 and structs based on v3_add / v3_del */
+ it = eina_hash_iterator_tuple_new(eg->vertices);
+ while (eina_iterator_next(it, &data)) {
+ Eina_Hash_Tuple *t = data;
+ v = t->data;
+ if (v->v3_del) {
+ _vertice_v2_del(eg, v); changes_diff--; changes_count++;
+ } else if (v->v3_new) {
+ _vertice_v2_add(eg, v); changes_diff++; changes_count++;
+ }
+ }
+ eina_iterator_free(it);
+ if (DEBUG)
+ printf("_v3_update edges\n");
+ EINA_LIST_FOREACH_SAFE(eg->edges, l, lprev, e) {
+ if (e->v3_del) {
+ _edge_v2_del(eg, e); changes_count++;
+ } else if (e->v3_new) {
+ _edge_v2_add(eg, e); changes_count++;
+ }
+ }
+
+ /* set correct coords2 size */
+ if (changes_diff > 0) {
+ for (i=0; i<changes_diff; i++) {
+ id = (eg->graph2_vcount - changes_diff) + i;
+ igraph_matrix_add_rows(&eg->coords2, 1);
+ ranx = eg->graph_wmin + fmod((float)random() / 1000, (float)((eg->graph_wmax + 1) - eg->graph_wmin));
+ rany = eg->graph_hmin + fmod((float)random() / 1000, (float)((eg->graph_hmax + 1) - eg->graph_hmin));
+ if (DEBUG)
+ printf("ranx %6.3f rany %6.3f\n", ranx, rany);
+ igraph_matrix_set(&eg->coords2, id, 0, ranx);
+ igraph_matrix_set(&eg->coords2, id, 1, rany);
+ }
+ } else if (changes_diff < 0) {
+ changes_diff = -changes_diff;
+ for (i=0; i<changes_diff; i++) {
+ id = (eg->graph2_vcount + changes_diff) - i;
+ igraph_matrix_remove_row(&eg->coords2, 0);
+ }
+ }
+
+ if (eg->layout == EGRAPH_LAYOUT_FRUCHTERMANREINGOLD) {
+ /* set minimum and maximum for each node */
+ /* XXX do that in layouting thread ? */
+ if (DEBUG)
+ printf("g wmin %d wmax %d hmin %d hmax %d\n",
+ eg->graph_wmin, eg->graph_wmax, eg->graph_hmin, eg->graph_hmax);
+ igraph_vector_init(&eg->graph2_wmin, eg->graph2_vcount);
+ igraph_vector_init(&eg->graph2_wmax, eg->graph2_vcount);
+ igraph_vector_init(&eg->graph2_hmin, eg->graph2_vcount);
+ igraph_vector_init(&eg->graph2_hmax, eg->graph2_vcount);
+ igraph_vector_fill(&eg->graph2_wmin, eg->graph_wmin);
+ igraph_vector_fill(&eg->graph2_wmax, eg->graph_wmax);
+ igraph_vector_fill(&eg->graph2_hmin, eg->graph_hmin);
+ igraph_vector_fill(&eg->graph2_hmax, eg->graph_hmax);
+ }
+
+ eg->layouting.changes_diff = changes_diff;
+}
+
+static Eina_Bool
+_cb_layouting_start(void *data)
+{
+ Egraph *eg;
+
+ eg = data;
+
+ eg->layouting.todo = 0;
+
+ _v3_update(eg);
+
+ eg->layouting.running = 2;
+ eg->layouting.thread = ecore_thread_run(_cb_layouting_run, _cb_layouting_end,
+ _cb_layouting_cancel, eg);
+
+ return EINA_FALSE; /* no repeat */
+}
+
+void
+_cb_layouting_run(void *data, Ecore_Thread *thread)
+{
+ Egraph *eg;
+ int niter;
+
+ eg = data;
+
+ if (DEBUG)
+ printf("[-] _cb_layouting_run begin (%d)\n", eg->layout);
+
+ switch(eg->layout) {
+ case EGRAPH_LAYOUT_KAMADAKAWAI:
+ if (eg->layouting.improvement) {
+ niter = 300;
+ } else {
+ if (eg->layouting.changes_diff == 0)
+ niter = 1000;
+ else
+ niter = 1000 * eg->layouting.changes_diff;
+ if (niter > 2000)
+ niter = 2000;
+ }
+ /* http://igraph.sourceforge.net/doc/html/ch18s01.html#igraph_layout_kamada_kawai
+ * int igraph_layout_kamada_kawai(const igraph_t *g, igraph_matrix_t *res,
+ * igraph_integer_t niter, igraph_real_t sigma,
+ * igraph_real_t initemp, igraph_real_t coolexp,
+ * igraph_real_t kkconst, igraph_bool_t use_seed,
+ * const igraph_vector_t *minx,
+ * const igraph_vector_t *maxx,
+ * const igraph_vector_t *miny,
+ * const igraph_vector_t *maxy);
+ * Defaults :
+ * igraph_layout_kamada_kawai(&eg->graph2, &eg->coords2,
+ * 1000, eg->vertices_count / 4, 10, 0.99,
+ * eg->vertices_count ^ 2, 1,
+ * NULL, NULL, NULL, NULL);
+ */
+ igraph_layout_kamada_kawai(&eg->graph2, &eg->coords2,
+ niter, eg->graph2_vcount, 10, 0.99,
+ eg->vertices_count ^ 2, 1,
+ NULL, NULL, NULL, NULL);
+ break;
+
+ case EGRAPH_LAYOUT_GRAPHOPT:
+ if (eg->layouting.improvement) {
+ niter = 300;
+ } else {
+ if (eg->layouting.changes_diff == 0)
+ niter = 100;
+ else
+ niter = 50 * eg->layouting.changes_diff;
+ if (niter > 500)
+ niter = 500;
+ }
+ /* http://igraph.sourceforge.net/doc/html/ch18s01.html#igraph_layout_graphopt
+ * int igraph_layout_graphopt(const igraph_t *g, igraph_matrix_t *res,
+ * igraph_integer_t niter,
+ * igraph_real_t node_charge, igraph_real_t node_mass,
+ * igraph_real_t spring_length,
+ * igraph_real_t spring_constant,
+ * igraph_real_t max_sa_movement,
+ * igraph_bool_t use_seed);
+ * Defaults :
+ * igraph_layout_graphopt(&eg->graph2, &eg->coords2,
+ * 1000, 0.001, 30, 0, 1, 5, 0);
+ */
+ igraph_layout_graphopt(&eg->graph2, &eg->coords2,
+ niter, 0.003, 10, 10, 1, 5, 1);
+ break;
+
+ case EGRAPH_LAYOUT_FRUCHTERMANREINGOLD:
+ niter = 1000;
+ /* http://igraph.sourceforge.net/doc/html/ch18s01.html#igraph_layout_fruchterman_reingold
+ * int igraph_layout_fruchterman_reingold(const igraph_t *graph, igraph_matrix_t *res,
+ * igraph_integer_t niter, igraph_real_t maxdelta,
+ * igraph_real_t area, igraph_real_t coolexp,
+ * igraph_real_t repulserad, igraph_bool_t use_seed,
+ * const igraph_vector_t *weight,
+ * const igraph_vector_t *minx,
+ * const igraph_vector_t *maxx,
+ * const igraph_vector_t *miny,
+ * const igraph_vector_t *maxy);
+ * Defaults:
+ * igraph_layout_fruchterman_reingold(&eg->graph2, &eg->coords2,
+ * 500, eg->graph2_vcount,
+ * sqrt(eg->graph2_vcount) * sqrt(eg->graph2_vcount), 1.5,
+ * eg->graph2_vcount, 0,
+ * NULL, NULL, NULL, NULL, NULL);
+ */
+ igraph_layout_fruchterman_reingold(&eg->graph2, &eg->coords2,
+ niter, 0.05,
+ sqrt(eg->graph2_vcount), 1.5,
+ sqrt(eg->graph2_vcount) * eg->graph2_vcount, 1,
+ NULL, NULL, NULL, NULL, NULL);
+ //&eg->graph2_wmin, &eg->graph2_wmax,
+ //&eg->graph2_hmin, &eg->graph2_hmax);
+ break;
+ }
+ if (DEBUG)
+ printf("[-] _cb_layouting_run end\n");
+}
+
+static void
+_coords2_copy(Egraph *eg)
+{
+ igraph_matrix_copy(&eg->coords, &eg->coords2);
+}
+
+void
+_cb_layouting_end(void *data, Ecore_Thread *thread)
+{
+ Egraph *eg;
+
+ eg = data;
+ _v2_update(eg);
+ _coords2_copy(eg);
+ _reposition(eg, 0);
+ eg->layouting.running = 0;
+ if (eg->layouting.todo) {
+ eg->layouting.todo = 0;
+ eg->layouting.improvement = 0;
+ _layouting_schedule(eg);
+ } else {
+ if (eg->do_improvements) {
+ if (eg->layouting.improvement < EGRAPH_LAYOUTING_IMPROVEMENTS) {
+ eg->layouting.improvement++;
+ _layouting_schedule(eg);
+ } else {
+ eg->layouting.improvement = 0;
+ }
+ }
+ }
+}
+
+void
+_cb_layouting_cancel(void *data, Ecore_Thread *thread)
+{
+ Egraph *eg;
+
+ eg = data;
+ eg->layouting.running = 0;
+ eg->layouting.todo = 0;
+ /* we are not in a clean state now, but it happends on exit only */
+}
+
+/* XXX slow ! possible to do edge/vertice delete on repositionning ? */
+static int
+_igraph_query_vid(igraph_t *g, int g_len, int id)
+{
+ int i;
+
+ for (i=0; i<g_len; i++) {
+ if (VAN(g, "id", i) == id)
+ return i;
+ }
+ printf("egraph: WARNING: _igraph_query_id %d not found !\n", id);
+ return -1;
+}
+
+/* Apply vertice move to it's edges */
+static void
+_cb_vertice_move(void *data, Evas *evas, Evas_Object *obj, void *event_info)
+{
+ EGRAPH_DATA_GET(evas_object_smart_parent_get(obj), eg);
+ Egraph_Vertice *v;
+ Egraph_Edge *e;
+ Evas_Map *m;
+ Eina_List *l;
+ int x, y, w, h;
+ int ax, ay, bx, by, aw, ah, bw, bh;
+ int p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y;
+ float a;
+
+ v = data;
+ evas_object_geometry_get(v->o, &x, &y, &w, &h);
+
+ EINA_LIST_FOREACH(v->edges, l, e) {
+ if (e->new) {
+ if (eg->display_edges)
+ evas_object_show(e->o);
+ e->new = 0;
+ }
+ if (e->o_usetheme) {
+ /* XXX we map the edges once per node = 2 times ... */
+ evas_object_move(e->o, x, y);
+ m = evas_map_new(4);
+ evas_map_smooth_set(m, 1);
+ evas_map_util_points_populate_from_object(m, e->o);
+ evas_object_geometry_get(e->a->o, &ax, &ay, &aw, &ah);
+ evas_object_geometry_get(e->b->o, &bx, &by, &bw, &bh);
+ ax = ax + aw / 2; ay = ay + ah / 2;
+ bx = bx + bw / 2; by = by + bh / 2;
+ aw = aw / 2; ah = ah / 2;
+ bw = bw / 2; bh = bh / 2;
+ /* rotate edge endpoints */
+ a = atan2(by - ay, bx - ax);
+#define ROTX(x, h, a) (-sin(a)*h + x)
+#define ROTY(y, h, a) (cos(a)*h + y)
+ p0x = ROTX(ax, ah, a); p0y = ROTY(ay, ah, a);
+ p1x = ROTX(ax, -ah, a); p1y = ROTY(ay, -ah, a);
+ p2x = ROTX(bx, bh, a); p2y = ROTY(by, bh, a);
+ p3x = ROTX(bx, -bh, a); p3y = ROTY(by, -bh, a);
+ /* set edge endpoints */
+ evas_map_point_coord_set(m, 0, p2x, p2y, 0);
+ evas_map_point_coord_set(m, 1, p0x, p0y, 0);
+ evas_map_point_coord_set(m, 2, p1x, p1y, 0);
+ evas_map_point_coord_set(m, 3, p3x, p3y, 0);
+ evas_object_map_set(e->o, m);
+ evas_object_map_enable_set(e->o, EINA_TRUE); // XXX do at init
+ evas_map_free(m);
+ } else {
+ evas_object_line_xy_get(e->o, &ax, &ay, &bx, &by);
+ if (e->a == v) {
+ ax = x + w/2;
+ ay = y + h/2;
+ } else {
+ bx = x + w/2;
+ by = y + h/2;
+ }
+ evas_object_line_xy_set(e->o, ax, ay, bx, by);
+ }
+ }
+}
+
+static void
+_reposition(Egraph *eg, int no_animation)
+{
+ Egraph_Vertice *v;
+ u_int32_t id;
+ float gw_min, gw_max, gh_min, gh_max, factor_w, factor_h;
+ float x, y;
+ int obj_x, obj_y, obj_w, obj_h;
+ int vcur;
+
+ if (DEBUG)
+ printf("[-] _reposition\n");
+ if (eg->graph_vcount == 0)
+ return;
+
+ evas_object_geometry_get(eg->obj, &obj_x, &obj_y, &obj_w, &obj_h);
+ _matrix_minmax_2v(&eg->coords, eg->graph_vcount,
+ &gw_min, &gw_max, &gh_min, &gh_max);
+ eg->graph_wmin = gw_min;
+ eg->graph_wmax = gw_max;
+ eg->graph_hmin = gh_min;
+ eg->graph_hmax = gh_max;
+
+ if (DEBUG)
+ printf("gw_min %6.3f gw_max %6.3f gh_min %6.3f gh_max %6.3f\n",
+ gw_min, gw_max, gh_min, gh_max);
+ if (gw_max == gw_min)
+ factor_w = 1;
+ else
+ factor_w = (obj_w - eg->vertice_max_w) / (gw_max - gw_min);
+ if (gh_max == gh_min)
+ factor_h = 1;
+ else
+ factor_h = (obj_h - eg->vertice_max_h) / (gh_max - gh_min);
+ if (DEBUG)
+ printf("factor_w %6.3f factor_h %6.3f\n", factor_w, factor_h);
+
+ for (vcur=0; vcur<eg->graph_vcount; vcur++) {
+ id = VAN(&eg->graph, "id", vcur);
+
+ x = MATRIX(eg->coords, vcur, 0);
+ y = MATRIX(eg->coords, vcur, 1);
+ if (DEBUG)
+ printf("%d: %6.3f %6.3f id %d\n", vcur, x, y, id);
+ x = obj_x + ((x - gw_min) * factor_w);
+ y = obj_y + ((y - gh_min) * factor_h);
+ if (DEBUG)
+ printf(" inobj: %6.3f %6.3f\n", x, y);
+
+ v = eina_hash_find(eg->vertices, &id);
+ if (eg->use_animations && !no_animation) {
+ efx_move(v->o, EFX_EFFECT_SPEED_DECELERATE,
+ &(Evas_Point){ x, y }, 0.5, NULL, NULL);
+ }
+ else
+ evas_object_move(v->o, x, y);
+ /* XXX fix blob repositionning
+ Evas_Object *blob;
+ Eina_List *l;
+ EINA_LIST_FOREACH(v->blobs_incoming, l, blob)
+ _blob_send(blob, v, x, y);
+ */
+ if (v->new) {
+ evas_object_show(v->o);
+ v->new = 0;
+ }
+ }
+}
+
+/**
+ * Compute minimum and maximum of a matrices of 2 colum vectors
+ *
+ * @note igraph is crazy in the coconut
+ */
+static void
+_matrix_minmax_2v(const igraph_matrix_t *m, int row_count,
+ float *c1min, float *c1max, float *c2min, float *c2max)
+{
+ float val;
+ int row;
+
+ *c1min=MATRIX(*m, 0, 0); *c1max=MATRIX(*m, 0, 0);
+ *c2min=MATRIX(*m, 0, 1); *c2max=MATRIX(*m, 0, 1);
+
+ for (row=1; row<row_count; row++) {
+ val = MATRIX(*m, row, 0);
+ if (val < *c1min) *c1min = val;
+ else if (val > *c1max) *c1max = val;
+ val = MATRIX(*m, row, 1);
+ if (val < *c2min) *c2min = val;
+ else if (val > *c2max) *c2max = val;
+ }
+}
+
+static Evas_Object *
+_edje_obj_new(Egraph *eg, const char *group)
+{
+ Evas_Object *obj;
+
+ obj = edje_object_add(eg->evas);
+ return _edje_obj_set(eg, obj, group);
+}
+
+static Evas_Object *
+_edje_obj_set(Egraph *eg, Evas_Object *obj, const char *group)
+{
+ if (!obj || !edje_object_file_set(obj, eg->theme_path, group)) {
+ int err = edje_object_load_error_get(obj);
+ const char *errmsg = edje_load_error_str(err);
+ if (DEBUG)
+ fprintf(stderr, "Could not load the edje file - reason:%s\n", errmsg);
+ return NULL;
+ }
+ return obj;
+}
+
+Egraph_Vertice *
+egraph_group_add(Evas_Object *obj, const char *name, void *data)
+{
+ Egraph_Vertice *g;
+
+ g = egraph_vertice_add(obj, name, data);
+ g->is_group = 1;
+ egraph_vertice_type_set(obj, g, "vertice_group");
+ return g;
+}
+
+int
+egraph_group_vertice_attach(Evas_Object *obj,
+ Egraph_Vertice *group, Egraph_Vertice *v)
+{
+ Egraph_Edge *e;
+
+ group->group_vertices = eina_list_append(group->group_vertices, v);
+ e = egraph_edge_add(obj, group, v, NULL);
+ egraph_edge_type_set(obj, e, "edge_group");
+ return 1;
+}
+
+void
+egraph_group_vertice_detach(Evas_Object *obj,
+ Egraph_Vertice *group, Egraph_Vertice *v)
+{
+ Egraph_Edge *e;
+
+ group->group_vertices = eina_list_remove(group->group_vertices, v);
+ e = egraph_edge_find(obj, group, v);
+ egraph_edge_del(obj, e);
+}
diff --git a/egraph/egraph.edc b/egraph/egraph.edc
new file mode 100644
index 0000000..399442a
--- /dev/null
+++ b/egraph/egraph.edc
@@ -0,0 +1,250 @@
+collections {
+ group {
+ name: "vertice_normal";
+ max: 700 700;
+ min: 700 700;
+ images {
+ image: "data/vertice.png" COMP;
+ }
+ parts {
+ part {
+ name: "image";
+ type: IMAGE;
+ description {
+ state: "default" 0.0;
+ max: 15 15;
+ min: 15 15;
+ image{
+ normal: "data/vertice.png";
+ }
+ rel1.relative: 0.0 0.0;
+ rel2.relative: 1.0 1.0;
+ color: 255 255 255 100;
+ }
+ description {
+ state: "active" 1.0;
+ inherit: "default" 0.0;
+ color: 255 255 255 255;
+ }
+ }
+ part {
+ name: "label";
+ type: TEXT;
+ description {
+ state: "default" 0.0;
+ max: 50 8;
+ fixed: 1 1;
+ color: 200 255 0 50; /* yellow low */
+ text {
+ font: "vera";
+ size: 10;
+ min: 1 1;
+ }
+ rel1.to: "image";
+ rel1.relative: 0.5 1.0;
+ rel2.to: "image";
+ rel2.relative: 0.5 1.0;
+ rel2.offset: 0 8;
+ }
+ description {
+ state: "active" 1.0;
+ inherit: "default" 0.0;
+ color: 200 255 0 255; /* yellow visible */
+ }
+ }
+ }
+ programs {
+ program {
+ name: "state_active";
+ signal: "become_active";
+ source: "";
+ action: STATE_SET "active" 0.0;
+ target: "image";
+ target: "label";
+ transition: DECELERATE 0.5;
+ }
+ program {
+ name: "state_inactive";
+ signal: "become_inactive";
+ source: "";
+ action: STATE_SET "default" 0.0;
+ target: "image";
+ target: "label";
+ transition: DECELERATE 0.5;
+ }
+ }
+ }
+ group {
+ name: "vertice_group";
+ max: 700 700;
+ min: 700 700;
+ images {
+ image: "data/vertice.png" COMP;
+ }
+ parts {
+ part {
+ name: "image";
+ type: IMAGE;
+ description {
+ state: "default" 0.0;
+ max: 15 15;
+ min: 15 15;
+ image{
+ normal: "data/vertice.png";
+ }
+ rel1.relative: 0.0 0.0;
+ rel2.relative: 1.0 1.0;
+ color: 160 120 40 70;
+ }
+ description {
+ state: "active" 1.0;
+ inherit: "default" 0.0;
+ color: 160 120 40 170;
+ }
+ }
+ part {
+ name: "label";
+ type: TEXT;
+ description {
+ state: "default" 0.0;
+ max: 50 8;
+ fixed: 1 1;
+ color: 160 120 40 70; /* yellow low */
+ text {
+ font: "vera";
+ size: 10;
+ min: 1 1;
+ }
+ rel1.to: "image";
+ rel1.relative: 0.5 1.0;
+ rel2.to: "image";
+ rel2.relative: 0.5 1.0;
+ rel2.offset: 0 8;
+ }
+ description {
+ state: "active" 1.0;
+ inherit: "default" 0.0;
+ color: 160 120 40 170; /* yellow visible */
+ }
+ }
+ }
+ programs {
+ program {
+ name: "state_active";
+ signal: "become_active";
+ action: STATE_SET "active" 0.0;
+ target: "image";
+ transition: DECELERATE 0.5;
+ }
+ program {
+ name: "state_inactive";
+ signal: "become_inactive";
+ action: STATE_SET "default" 0.0;
+ target: "image";
+ transition: DECELERATE 0.5;
+ }
+ }
+ }
+ group {
+ name: "edge_normal";
+ max: 700 700;
+ min: 700 700;
+ images {
+ image: "data/edge.png" COMP;
+ }
+ parts {
+ part {
+ name: "image";
+ type: IMAGE;
+ description {
+ state: "default" 0.0;
+ max: 50 10;
+ min: 50 10;
+ image{
+ normal: "data/edge.png";
+ }
+ rel1.relative: 0.0 0.0;
+ rel2.relative: 1.0 1.0;
+ color: 255 255 255 50;
+ }
+ description {
+ state: "active" 1.0;
+ inherit: "default" 0.0;
+ color: 255 255 255 255;
+ }
+ }
+ }
+ programs {
+ program {
+ name: "state_active";
+ signal: "become_active";
+ action: STATE_SET "active" 0.0;
+ target: "image";
+ transition: DECELERATE 0.5;
+ }
+ }
+ }
+ group {
+ name: "edge_group";
+ max: 700 700;
+ min: 700 700;
+ images {
+ image: "data/edge.png" COMP;
+ }
+ parts {
+ part {
+ name: "image";
+ type: IMAGE;
+ description {
+ state: "default" 0.0;
+ max: 50 10;
+ min: 50 10;
+ image{
+ normal: "data/edge.png";
+ }
+ rel1.relative: 0.0 0.0;
+ rel2.relative: 1.0 1.0;
+ color: 255 120 40 70;
+ }
+ description {
+ state: "active" 1.0;
+ inherit: "default" 0.0;
+ color: 255 120 40 170;
+ }
+ }
+ }
+ programs {
+ program {
+ name: "state_active";
+ signal: "become_active";
+ action: STATE_SET "active" 0.0;
+ target: "image";
+ transition: DECELERATE 0.5;
+ }
+ }
+ }
+ group {
+ name: "blob";
+ max: 700 700;
+ min: 700 700;
+ images {
+ image: "data/blob.png" COMP;
+ }
+ parts {
+ part {
+ name: "image";
+ type: IMAGE;
+ description {
+ state: "default" 0.0;
+ max: 50 50;
+ min: 3 3;
+ image{
+ normal: "data/blob.png";
+ }
+ rel1.relative: 0.0 0.0;
+ rel2.relative: 1.0 1.0;
+ }
+ }
+ }
+ }
+}
diff --git a/egraph/examples/Makefile b/egraph/examples/Makefile
new file mode 100644
index 0000000..9ac573e
--- /dev/null
+++ b/egraph/examples/Makefile
@@ -0,0 +1,12 @@
+CFLAGS += -Wall -g
+CFLAGS += $(shell pkg-config --libs --cflags elementary)
+CFLAGS += -L../ -legraph -lecore_evas
+
+SOURCES = $(shell echo *.c)
+OBJECTS = $(SOURCES:.c=.o)
+TARGETS = $(SOURCES:.c=)
+
+all: $(TARGETS)
+
+clean:
+ rm -f $(TARGETS) $(OBJECTS)
diff --git a/egraph/examples/demoapp.c b/egraph/examples/demoapp.c
new file mode 100644
index 0000000..4e64363
--- /dev/null
+++ b/egraph/examples/demoapp.c
@@ -0,0 +1,371 @@
+#include <err.h>
+
+#include <Elementary.h>
+#include <Egraph.h>
+
+Evas_Object *_mainwin;
+Evas_Object *_egraph;
+Eina_List *_vertice_list = NULL;
+Eina_List *_group_list = NULL;
+
+static void
+_cb_add_edge(void *data, Evas_Object *obj, void *event_info)
+{
+ Egraph_Vertice *a, *b;
+ int ran, count;
+ char buf[64];
+
+ if (!_egraph)
+ return;
+
+ count = eina_list_count(_vertice_list);
+ if (count == 0) {
+ a = egraph_vertice_add(_egraph, "first", NULL);
+ _vertice_list = eina_list_append(_vertice_list, a);
+ } else {
+ ran = random() % count;
+ a = eina_list_nth(_vertice_list, ran);
+ }
+ b = egraph_vertice_add(_egraph, NULL, NULL);
+ snprintf(buf, sizeof(buf), "%d", b->id);
+ egraph_vertice_rename(_egraph, b, buf);
+ _vertice_list = eina_list_append(_vertice_list, b);
+
+ egraph_edge_add(_egraph, a, b, NULL);
+}
+
+static void
+_cb_add_50edges(void *data, Evas_Object *obj, void *event_info)
+{
+ int i;
+
+ if (!_egraph)
+ return;
+
+ for (i=0; i<50; i++)
+ _cb_add_edge(data, obj, event_info);
+}
+
+static void
+_cb_add_500edges(void *data, Evas_Object *obj, void *event_info)
+{
+ int i;
+
+ if (!_egraph)
+ return;
+
+ for (i=0; i<500; i++)
+ _cb_add_edge(data, obj, event_info);
+}
+
+static void
+_cb_del_edges(void *data, Evas_Object *obj, void *event_info)
+{
+ Eina_List *l;
+ Egraph_Vertice *v;
+
+ if (!_egraph)
+ return;
+
+ egraph_clear(_egraph);
+ _vertice_list = eina_list_free(_vertice_list);
+ _group_list = eina_list_free(_group_list);
+}
+
+static void
+_cb_add_node(void *data, Evas_Object *obj, void *event_info)
+{
+ Egraph_Vertice *v;
+ char buf[64];
+
+ if (!_egraph)
+ return;
+
+ v = egraph_vertice_add(_egraph, NULL, NULL);
+ snprintf(buf, sizeof(buf), "%d", v->id);
+ egraph_vertice_rename(_egraph, v, buf);
+ _vertice_list = eina_list_append(_vertice_list, v);
+}
+
+static void
+_cb_send_100blobs(void *data, Evas_Object *obj, void *event_info)
+{
+ Egraph_Vertice *a, *b;
+ int count, ran, i;
+
+ count = eina_list_count(_vertice_list);
+ if (!count)
+ return;
+
+ for (i=0; i<100; i++) {
+ ran = random() % count;
+ a = eina_list_nth(_vertice_list, ran);
+ ran = random() % count;
+ b = eina_list_nth(_vertice_list, ran);
+ egraph_vertice_send_blob(_egraph, a, b, 5, 0xFF000000);
+ }
+}
+
+static void
+_cb_add_10n_group(void *data, Evas_Object *obj, void *event_info)
+{
+ Egraph_Vertice *group, *v;
+ char buf[64];
+ int i;
+
+ if (!_egraph)
+ return;
+
+ group = egraph_group_add(_egraph, NULL, NULL);
+ snprintf(buf, sizeof(buf), "%d-group", group->id);
+ egraph_vertice_rename(_egraph, group, buf);
+ _group_list = eina_list_append(_group_list, group);
+ for (i=0; i<10; i++) {
+ v = egraph_vertice_add(_egraph, NULL, NULL);
+ snprintf(buf, sizeof(buf), "%d-child(%d)", v->id, group->id);
+ egraph_vertice_rename(_egraph, v, buf);
+ egraph_group_vertice_attach(_egraph, group, v);
+ _vertice_list = eina_list_append(_vertice_list, v);
+ }
+}
+
+
+static void
+_cb_show_nodes(void *data, Evas_Object *obj, void *event_info)
+{
+ egraph_display_vertices_set(_egraph, elm_check_state_get(obj));
+}
+
+static void
+_cb_show_labels(void *data, Evas_Object *obj, void *event_info)
+{
+ if (!_egraph)
+ return;
+
+ egraph_display_names_set(_egraph, elm_check_state_get(obj));
+}
+
+static void
+_cb_show_edges(void *data, Evas_Object *obj, void *event_info)
+{
+ if (!_egraph)
+ return;
+
+ egraph_display_edges_set(_egraph, elm_check_state_get(obj));
+}
+
+static void
+_cb_use_animations(void *data, Evas_Object *obj, void *event_info)
+{
+ if (!_egraph)
+ return;
+
+ egraph_use_animations_set(_egraph, elm_check_state_get(obj));
+}
+
+static void
+_cb_do_improvements(void *data, Evas_Object *obj, void *event_info)
+{
+ if (!_egraph)
+ return;
+
+ egraph_do_improvements_set(_egraph, elm_check_state_get(obj));
+}
+
+static void
+_cb_use_theme_e(void *data, Evas_Object *obj, void *event_info)
+{
+ if (!_egraph)
+ return;
+
+ egraph_theme_edges_set(_egraph, elm_check_state_get(obj));
+}
+
+static void
+_cb_layout_changed(void *data, Evas_Object *obj, void *event_info)
+{
+ Elm_Object_Item *it;
+ char *selected;
+ int layout;
+
+ if (!_egraph)
+ return;
+
+ it = event_info;
+ selected = elm_object_item_text_get(it);
+ layout = EGRAPH_LAYOUT_DEFAULT;
+ if (!strcmp(selected, "Kamada K."))
+ layout = EGRAPH_LAYOUT_KAMADAKAWAI;
+ else if (!strcmp(selected, "GraphOpt"))
+ layout = EGRAPH_LAYOUT_GRAPHOPT;
+ else if (!strcmp(selected, "Fruchterman R."))
+ layout = EGRAPH_LAYOUT_FRUCHTERMANREINGOLD;
+
+ egraph_layout_set(_egraph, layout);
+}
+
+static void
+_cb_on_done(void *data, Evas_Object *obj, void *event_info)
+{
+ elm_exit();
+}
+
+EAPI_MAIN int
+elm_main(int argc, char **argv)
+{
+ Evas_Object *win, *bg, *egraph, *panes;
+ Evas_Object *bx, *tb, *tb_it, *ck, *sc, *seg_it, *lb;
+ Evas *evas;
+ int retval = -1;
+
+ win = elm_win_add(NULL, "panes", ELM_WIN_BASIC);
+ evas = evas_object_evas_get(win);
+ elm_win_title_set(win, "Egraph demo app");
+ evas_object_smart_callback_add(win, "delete,request", _cb_on_done, NULL);
+
+ bg = elm_bg_add(win);
+ elm_win_resize_object_add(win, bg);
+ evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_color_set(bg, 0, 0, 0, 255);
+ evas_object_show(bg);
+
+ bx = elm_box_add(win);
+ elm_box_horizontal_set(bx, EINA_TRUE);
+ evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ elm_win_resize_object_add(win, bx);
+ evas_object_show(bx);
+
+ tb = elm_toolbar_add(win);
+ elm_toolbar_homogeneous_set(tb, EINA_FALSE);
+ elm_toolbar_horizontal_set(tb, EINA_FALSE);
+ elm_toolbar_shrink_mode_set(tb, ELM_TOOLBAR_SHRINK_EXPAND);
+ elm_toolbar_select_mode_set(tb, ELM_OBJECT_SELECT_MODE_ALWAYS);
+ elm_toolbar_transverse_expanded_set(tb, EINA_TRUE);
+ elm_object_style_set(tb, "item_centered");
+ evas_object_size_hint_weight_set(tb, 0.0, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(tb, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ elm_box_pack_end(bx, tb);
+ evas_object_show(tb);
+ elm_toolbar_item_append(tb, "object-rotate-right", "Add 1 edge",
+ _cb_add_edge, NULL);
+ elm_toolbar_item_append(tb, "object-rotate-right", "Add 50 edges",
+ _cb_add_50edges, NULL);
+ elm_toolbar_item_append(tb, "object-rotate-right", "Add 500 edges",
+ _cb_add_500edges, NULL);
+ elm_toolbar_item_append(tb, "object-rotate-right", "Add 1 node alone",
+ _cb_add_node, NULL);
+ elm_toolbar_item_append(tb, "edit-delete", "Delete all",
+ _cb_del_edges, NULL);
+ elm_toolbar_item_append(tb, "object-rotate-right", "Send 100 blobs",
+ _cb_send_100blobs, NULL);
+ elm_toolbar_item_append(tb, "object-rotate-right", "Add a 10n group",
+ _cb_add_10n_group, NULL);
+ elm_toolbar_item_separator_set(elm_toolbar_item_append(tb, NULL, NULL, NULL, NULL), EINA_FALSE);
+ lb = elm_label_add(win);
+ elm_object_text_set(lb, "<b>Layout</b>");
+ evas_object_size_hint_weight_set(lb, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(lb, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ tb_it = elm_toolbar_item_append(tb, NULL, NULL, NULL, NULL);
+ elm_object_item_part_content_set(tb_it, "object", lb);
+ sc = elm_segment_control_add(win);
+ //evas_object_size_hint_weight_set(sc, em->weight.w, em->weight.h);
+ //evas_object_size_hint_align_set(sc, em->align.x, em->align.y);
+ seg_it = elm_segment_control_item_add(sc, NULL, "GraphOpt");
+ elm_segment_control_item_selected_set(seg_it, EINA_TRUE);
+ seg_it = elm_segment_control_item_add(sc, NULL, "Kamada K.");
+ elm_segment_control_item_selected_set(seg_it, EINA_FALSE);
+ seg_it = elm_segment_control_item_add(sc, NULL, "Fruchterman R.");
+ elm_segment_control_item_selected_set(seg_it, EINA_FALSE);
+ evas_object_smart_callback_add(sc, "changed", _cb_layout_changed, NULL);
+ evas_object_show(sc);
+ tb_it = elm_toolbar_item_append(tb, NULL, NULL, NULL, NULL);
+ elm_object_item_part_content_set(tb_it, "object", sc);
+ //elm_toolbar_item_separator_set(elm_toolbar_item_append(tb, NULL, NULL, NULL, NULL), EINA_FALSE);
+ ck = elm_check_add(win);
+ elm_object_text_set(ck, "Show Node");
+ elm_check_state_set(ck, EINA_TRUE);
+ evas_object_show(ck);
+ evas_object_smart_callback_add(ck, "changed", _cb_show_nodes, NULL);
+ tb_it = elm_toolbar_item_append(tb, NULL, NULL, NULL, NULL);
+ elm_object_item_part_content_set(tb_it, "object", ck);
+ ck = elm_check_add(win);
+ elm_object_text_set(ck, "Show Label");
+ elm_check_state_set(ck, EINA_TRUE);
+ evas_object_show(ck);
+ evas_object_smart_callback_add(ck, "changed", _cb_show_labels, NULL);
+ tb_it = elm_toolbar_item_append(tb, NULL, NULL, NULL, NULL);
+ elm_object_item_part_content_set(tb_it, "object", ck);
+ ck = elm_check_add(win);
+ elm_object_text_set(ck, "Show Edges");
+ elm_check_state_set(ck, EINA_TRUE);
+ evas_object_show(ck);
+ evas_object_smart_callback_add(ck, "changed", _cb_show_edges, NULL);
+ tb_it = elm_toolbar_item_append(tb, NULL, NULL, NULL, NULL);
+ elm_object_item_part_content_set(tb_it, "object", ck);
+ ck = elm_check_add(win);
+ elm_object_text_set(ck, "Use animations");
+ elm_check_state_set(ck, EINA_TRUE);
+ evas_object_show(ck);
+ evas_object_smart_callback_add(ck, "changed", _cb_use_animations, NULL);
+ tb_it = elm_toolbar_item_append(tb, NULL, NULL, NULL, NULL);
+ elm_object_item_part_content_set(tb_it, "object", ck);
+ ck = elm_check_add(win);
+ elm_object_text_set(ck, "Do improvements");
+ elm_check_state_set(ck, EINA_TRUE);
+ evas_object_show(ck);
+ evas_object_smart_callback_add(ck, "changed", _cb_do_improvements, NULL);
+ tb_it = elm_toolbar_item_append(tb, NULL, NULL, NULL, NULL);
+ elm_object_item_part_content_set(tb_it, "object", ck);
+ ck = elm_check_add(win);
+ elm_object_text_set(ck, "Use theme for edges");
+ elm_check_state_set(ck, EINA_TRUE);
+ evas_object_show(ck);
+ evas_object_smart_callback_add(ck, "changed", _cb_use_theme_e, NULL);
+ tb_it = elm_toolbar_item_append(tb, NULL, NULL, NULL, NULL);
+ elm_object_item_part_content_set(tb_it, "object", ck);
+ //elm_toolbar_item_separator_set(elm_toolbar_item_append(tb, NULL, NULL, NULL, NULL), EINA_FALSE);
+ elm_toolbar_item_append(tb, "exit", "Quit",
+ _cb_on_done, NULL);
+
+ panes = elm_panes_add(win);
+ evas_object_size_hint_weight_set(panes, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(panes, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ elm_panes_content_right_size_set(panes, 0.0);
+ elm_box_pack_end(bx, panes);
+ evas_object_show(panes);
+
+ egraph = egraph_new(evas, 1);
+ if (!egraph)
+ goto quit;
+ evas_object_size_hint_weight_set(egraph, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(egraph, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_show(egraph);
+ elm_object_part_content_set(panes, "left", egraph);
+
+ lb = elm_label_add(win);
+ elm_object_style_set(lb, "marker");
+ evas_object_color_set(lb, 255, 255, 255, 255);
+ elm_object_text_set(lb,
+ "Demo application for Egraph<br/>"
+ "<br/>"
+ "Enjoy !<br/>");
+ evas_object_show(lb);
+ elm_object_part_content_set(panes, "right", lb);
+
+ evas_object_resize(win, 150, 150); // XXX workaround elm sizing issue
+ evas_object_show(win);
+ evas_object_resize(win, 950, 715);
+ evas_object_show(win);
+ _egraph = egraph;
+ _mainwin = win;
+
+ _cb_add_50edges(NULL, NULL, NULL);
+
+ elm_run();
+ retval = 0;
+
+quit:
+ elm_shutdown();
+ return retval;
+}
+ELM_MAIN()
diff --git a/egraph/examples/simplegraph.c b/egraph/examples/simplegraph.c
new file mode 100644
index 0000000..43e6d59
--- /dev/null
+++ b/egraph/examples/simplegraph.c
@@ -0,0 +1,59 @@
+#include <Ecore.h>
+#include <Ecore_Evas.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "../Egraph.h"
+
+Evas_Object *_egraph_obj = NULL;
+
+static void
+_on_destroy(Ecore_Evas *ee)
+{
+ ecore_main_loop_quit();
+}
+
+static void
+_canvas_resize_cb(Ecore_Evas *ee)
+{
+ int w, h;
+
+ printf("_canvas_resize_cb\n");
+ ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
+ evas_object_resize(_egraph_obj, w, h);
+}
+
+int
+main(void)
+{
+ Ecore_Evas *ee;
+ Evas *evas;
+ Egraph_Vertice *a, *b;
+ Egraph_Edge *e;
+
+ if (!ecore_evas_init())
+ return EXIT_FAILURE;
+ ee = ecore_evas_new(NULL, 10, 10, 100, 100, NULL);
+ if (!ee)
+ return -1;
+ ecore_evas_callback_destroy_set(ee, _on_destroy);
+ ecore_evas_callback_resize_set(ee, _canvas_resize_cb);
+ ecore_evas_show(ee);
+ evas = ecore_evas_get(ee);
+
+ _egraph_obj = egraph_new(evas, 1);
+ evas_object_resize(_egraph_obj, 100, 100);
+ evas_object_show(_egraph_obj);
+
+ a = egraph_vertice_add(_egraph_obj, "a", NULL);
+ b = egraph_vertice_add(_egraph_obj, "b", NULL);
+ e = egraph_edge_add(_egraph_obj, a, b, NULL);
+
+ ecore_main_loop_begin();
+
+ evas_object_del(_egraph_obj);
+ ecore_evas_free(ee);
+ ecore_evas_shutdown();
+
+ return (!_egraph_obj);
+}
diff --git a/egraph/retest.sh b/egraph/retest.sh
new file mode 100755
index 0000000..c778fa1
--- /dev/null
+++ b/egraph/retest.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+make clean && \
+ make && \
+ sudo make install && \
+ make -C tests/ clean && \
+ make -C tests/ run && \
+ make -C examples clean && \
+ make -C examples
diff --git a/egraph/tests/Makefile b/egraph/tests/Makefile
new file mode 100644
index 0000000..8b70170
--- /dev/null
+++ b/egraph/tests/Makefile
@@ -0,0 +1,32 @@
+CFLAGS += -Wall -g
+CFLAGS += $(shell pkg-config --libs --cflags igraph)
+CFLAGS += $(shell pkg-config --libs --cflags efx)
+CFLAGS += -L../ -legraph -lecore_evas
+
+SOURCES = $(shell echo *.c)
+OBJECTS = $(SOURCES:.c=.o)
+TARGETS = $(SOURCES:.c=)
+
+all: $(TARGETS)
+
+run: $(TARGETS)
+ @count=0 ;\
+ errors=0 ;\
+ for test in $(TARGETS); do \
+ echo =============================================================================== ;\
+ echo $$test ;\
+ LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:../ ./$$test ;\
+ if [ $$? -eq 0 ]; then \
+ echo OK ;\
+ else \
+ echo FAILED ;\
+ errors=$$(($$errors + 1)) ;\
+ fi ;\
+ count=$$(($$count + 1)) ;\
+ done ;\
+ echo =============================================================================== ;\
+ echo "$$count tests executed, $$errors errors" ;\
+ exit $$errors
+
+clean:
+ rm -f $(TARGETS) $(OBJECTS)
diff --git a/egraph/tests/README.txt b/egraph/tests/README.txt
new file mode 100644
index 0000000..9a25111
--- /dev/null
+++ b/egraph/tests/README.txt
@@ -0,0 +1,4 @@
+egraph library unit tests
+
+do "make run" to execute them
+returns the number of tests that failed
diff --git a/egraph/tests/creategraph.c b/egraph/tests/creategraph.c
new file mode 100644
index 0000000..d27e3d5
--- /dev/null
+++ b/egraph/tests/creategraph.c
@@ -0,0 +1,37 @@
+#include <Ecore.h>
+#include <Ecore_Evas.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "../Egraph.h"
+
+int
+main(void)
+{
+ Ecore_Evas *ee;
+ Evas *evas;
+ Evas_Object *obj = NULL;
+ Egraph_Vertice *a, *b;
+ Egraph_Edge *e;
+
+ if (!ecore_evas_init())
+ return EXIT_FAILURE;
+ ee = ecore_evas_new(NULL, 10, 10, 100, 100, NULL);
+ if (!ee)
+ return -1;
+ ecore_evas_show(ee);
+ evas = ecore_evas_get(ee);
+
+ obj = egraph_new(evas, 1);
+
+ a = egraph_vertice_add(obj, "a", NULL);
+ b = egraph_vertice_add(obj, NULL, NULL);
+ e = egraph_edge_add(obj, a, b, NULL);
+ egraph_vertice_del(obj, a);
+
+ evas_object_del(obj);
+ ecore_evas_free(ee);
+ ecore_evas_shutdown();
+
+ return (!obj);
+}
diff --git a/.gitignore b/gg_elife/.gitignore
index a8e7ece..a8e7ece 100644
--- a/.gitignore
+++ b/gg_elife/.gitignore
diff --git a/gg_elife/AUTHORS b/gg_elife/AUTHORS
new file mode 100644
index 0000000..4e42cdc
--- /dev/null
+++ b/gg_elife/AUTHORS
@@ -0,0 +1 @@
+Laurent Ghigonis <laurent@p1sec.com>
diff --git a/COPYING b/gg_elife/COPYING
index 8b13789..8b13789 100644
--- a/COPYING
+++ b/gg_elife/COPYING
diff --git a/ChangeLog b/gg_elife/ChangeLog
index 8b13789..8b13789 100644
--- a/ChangeLog
+++ b/gg_elife/ChangeLog
diff --git a/Makefile.am b/gg_elife/Makefile.am
index 215bf9f..215bf9f 100644
--- a/Makefile.am
+++ b/gg_elife/Makefile.am
diff --git a/NEWS b/gg_elife/NEWS
index 5c5261f..5c5261f 100644
--- a/NEWS
+++ b/gg_elife/NEWS
diff --git a/README b/gg_elife/README
index 1906131..1906131 100644
--- a/README
+++ b/gg_elife/README
diff --git a/autogen.sh b/gg_elife/autogen.sh
index ae01364..ae01364 100755
--- a/autogen.sh
+++ b/gg_elife/autogen.sh
diff --git a/configure.ac b/gg_elife/configure.ac
index 60d1703..60d1703 100644
--- a/configure.ac
+++ b/gg_elife/configure.ac
diff --git a/data/Makefile.am b/gg_elife/data/Makefile.am
index 8664660..8664660 100644
--- a/data/Makefile.am
+++ b/gg_elife/data/Makefile.am
diff --git a/data/elife.edc b/gg_elife/data/elife.edc
index 69df75c..69df75c 100644
--- a/data/elife.edc
+++ b/gg_elife/data/elife.edc
diff --git a/gg_elife/in_separate_git.txt b/gg_elife/in_separate_git.txt
new file mode 100644
index 0000000..6bdc618
--- /dev/null
+++ b/gg_elife/in_separate_git.txt
@@ -0,0 +1,6 @@
+git clone git@gouloum.fr:elife
+
+cd elife
+./configure --enable-glouglou
+make
+sudo make install
diff --git a/m4/efl_binary.m4 b/gg_elife/m4/efl_binary.m4
index c774688..c774688 100644
--- a/m4/efl_binary.m4
+++ b/gg_elife/m4/efl_binary.m4
diff --git a/src/Makefile.am b/gg_elife/src/Makefile.am
index f8135a0..f8135a0 100644
--- a/src/Makefile.am
+++ b/gg_elife/src/Makefile.am
diff --git a/src/elife.c b/gg_elife/src/elife.c
index 8b97cb8..8b97cb8 100644
--- a/src/elife.c
+++ b/gg_elife/src/elife.c
diff --git a/src/elife_edje_external.c b/gg_elife/src/elife_edje_external.c
index 14035d2..14035d2 100644
--- a/src/elife_edje_external.c
+++ b/gg_elife/src/elife_edje_external.c
diff --git a/src/elife_evas_smart.c b/gg_elife/src/elife_evas_smart.c
index acbd9a2..acbd9a2 100644
--- a/src/elife_evas_smart.c
+++ b/gg_elife/src/elife_evas_smart.c
diff --git a/src/elife_evas_smart.h b/gg_elife/src/elife_evas_smart.h
index b5a5b85..b5a5b85 100644
--- a/src/elife_evas_smart.h
+++ b/gg_elife/src/elife_evas_smart.h
diff --git a/gg_map/BUGS.txt b/gg_map/BUGS.txt
new file mode 100644
index 0000000..353f3cb
--- /dev/null
+++ b/gg_map/BUGS.txt
@@ -0,0 +1,506 @@
+===============================================================================
+#00: <title>
+DATE: 20121231-0549
+DESCRIPTION: <description>
+REPRODUCE: <reproduce>
+STATUS: <status>
+======================
+<details (bt ...)>
+======================
+======================
+======================
+======================
+
+
+===============================================================================
+===============================================================================
+TODO
+===============================================================================
+===============================================================================
+
+
+===============================================================================
+#01: del_edge after node group implementation
+DATE: 20121213-0555
+DESCRIPTION: igraph check fails
+REPRODUCE: use gg_map and browse the web, load many pages
+STATUS: should be fixed, added checks for v2/v3_new in v2_del and _del
+
+======================
+not last one
+ type PACKET_DELCONN
+ delconn_id 10
+not last one
+ type PACKET_DELCONN
+ delconn_id 14
+not last one
+ type PACKET_DELCONN
+ delconn_id 75
+not last one
+ type PACKET_DELCONN
+ delconn_id 16
+not last one
+ type PACKET_DELCONN
+ delconn_id 20
+not last one
+ type PACKET_DELCONN
+ delconn_id 23
+not last one
+ type PACKET_DELCONN
+ delconn_id 6
+not last one
+ type PACKET_DELCONN
+ delconn_id 24
+ type PACKET_DELCONN
+ delconn_id 17
+not last one
+ type PACKET_DELCONN
+ delconn_id 49
+not last one
+blob_send 624 460
+ type PACKET_DELCONN
+ delconn_id 42
+blob_send 449 367
+blob_send 452 338
+blob_send 449 367
+ type PACKET_DELCONN
+ delconn_id 18
+blob_send 420 338
+Error at type_indexededgelist.c:1158 :Cannot get edge id, no such edge, Invalid value
+
+Program received signal SIGABRT, Aborted.
+0x00007ffff44f2fd5 in raise () from /usr/lib/libc.so.6
+
+======================
+with egraph DEBUG=1
+
+ delconn_id 21
+not last one
+ type PACKET_DELCONN
+ delconn_id 26
+not last one
+ type PACKET_DELCONN
+ delconn_id 112
+not last one
+ type PACKET_DELCONN
+ delconn_id 115
+not last one
+ type PACKET_DELCONN
+ delconn_id 40
+ type PACKET_DELCONN
+ delconn_id 105
+ type PACKET_DELCONN
+ delconn_id 44
+not last one
+ type PACKET_DELCONN
+ delconn_id 36
+not last one
+ type PACKET_DELCONN
+ delconn_id 79
+not last one
+ type PACKET_DELCONN
+ delconn_id 76
+ type PACKET_DELCONN
+ delconn_id 116
+ type PACKET_DELCONN
+ delconn_id 31
+_v3_update
+_v3_update edges
+_edge_v2_del 2 51
+_edge_v2_del 2 23
+_edge_v2_del 2 37
+_edge_v2_del 2 41
+_edge_v2_del 2 40
+_edge_v2_del 2 18
+_edge_v2_del 2 12
+_edge_v2_del 2 3
+_edge_v2_del 2 11
+Error at type_indexededgelist.c:1158 :Cannot get edge id, no such edge, Invalid value
+
+Program received signal SIGABRT, Aborted.
+0x00007ffff44f2fd5 in raise () from /usr/lib/libc.so.6
+
+
+(gdb) bt
+#0 0x00007ffff44f2fd5 in raise () from /usr/lib/libc.so.6
+#1 0x00007ffff44f4458 in abort () from /usr/lib/libc.so.6
+#2 0x00007fffec9ad932 in igraph_error_handler_abort ()
+ from /usr/lib/libigraph.so.0
+#3 0x00007fffec9ad9ad in igraph_error () from /usr/lib/libigraph.so.0
+#4 0x00007fffec9aa041 in igraph_get_eid () from /usr/lib/libigraph.so.0
+#5 0x00007fffec97e96f in igraph_i_eit_pairs () from /usr/lib/libigraph.so.0
+#6 0x00007fffec97f212 in igraph_eit_create () from /usr/lib/libigraph.so.0
+#7 0x00007fffec9a8766 in igraph_delete_edges () from /usr/lib/libigraph.so.0
+#8 0x00007ffff4a84ac5 in _edge_v2_del (eg=0x7b1350, e=0x9ace10) at egraph.c:413
+#9 0x00007ffff4a85cfb in _v3_update (eg=0x7b1350) at egraph.c:782
+#10 0x00007ffff4a860e8 in _cb_layouting_start (data=0x7b1350) at egraph.c:836
+#11 0x00007ffff50ec339 in _ecore_call_task_cb (data=<optimized out>,
+ func=<optimized out>) at lib/ecore/ecore_private.h:300
+#12 _ecore_timer_expired_call (when=when@entry=87262.772331019005)
+ at lib/ecore/ecore_timer.c:911
+#13 0x00007ffff50ec45b in _ecore_timer_expired_timers_call (when=
+ 87262.772331019005) at lib/ecore/ecore_timer.c:865
+#14 0x00007ffff50e8451 in _ecore_main_loop_iterate_internal (
+ once_only=once_only@entry=0) at lib/ecore/ecore_main.c:1831
+#15 0x00007ffff50e8b27 in ecore_main_loop_begin () at lib/ecore/ecore_main.c:964
+#16 0x00000000004022ad in elm_main (argc=1, argv=0x7fffffffe5b8) at gg_map.c:269
+#17 0x00000000004022f8 in main (argc=1, argv=0x7fffffffe5b8) at gg_map.c:276
+
+(gdb) p e
+$1 = (Egraph_Edge *) 0x9ace10
+(gdb) p *e
+$2 = {a = 0x82a200, b = 0x856fd0, type = 0x0, o = 0x0, o_usetheme = 0, data =
+ 0x88bbb0, new = 0, v2_new = 0, v2_del = 1, v3_new = 1, v3_del = 0}
+======================
+
+======================
+======================
+======================
+
+===============================================================================
+#01: sigsegv in ecore timer
+DATE: 20121213-0620
+DESCRIPTION: segv. should be caused by calling efx_move on blobs on every
+vertice move. anyway this is bad solution.
+REPRODUCE: gg_map usage
+STATUS: should be fixed, removed efx_move calls on vertice move.
+======================
+
+blob_send 450 305
+blob_send 404 331
+blob_send 404 331
+blob_send 450 305
+blob_send 404 331
+[New Thread 0x7fffe6561700 (LWP 32312)]
+
+Program received signal SIGSEGV, Segmentation fault.
+_ecore_call_task_cb (data=0x3fe3333333333333, func=0x40f5b9be3ba41dac)
+ at lib/ecore/ecore_private.h:300
+300 r = func(data);
+(gdb) bt
+#0 _ecore_call_task_cb (data=0x3fe3333333333333, func=0x40f5b9be3ba41dac)
+ at lib/ecore/ecore_private.h:300
+#1 _do_tick () at lib/ecore/ecore_anim.c:125
+#2 0x00007ffff50e14a0 in _ecore_animator (data=<optimized out>)
+ at lib/ecore/ecore_anim.c:595
+#3 0x00007ffff50ec339 in _ecore_call_task_cb (data=<optimized out>,
+ func=<optimized out>) at lib/ecore/ecore_private.h:300
+#4 _ecore_timer_expired_call (when=when@entry=88988.202157983003)
+ at lib/ecore/ecore_timer.c:911
+#5 0x00007ffff50ec45b in _ecore_timer_expired_timers_call (when=
+ 88988.202157983003) at lib/ecore/ecore_timer.c:865
+#6 0x00007ffff50e8451 in _ecore_main_loop_iterate_internal (
+ once_only=once_only@entry=0) at lib/ecore/ecore_main.c:1831
+#7 0x00007ffff50e8b27 in ecore_main_loop_begin () at lib/ecore/ecore_main.c:964
+#8 0x00000000004022ad in elm_main (argc=1, argv=0x7fffffffe5b8) at gg_map.c:269
+#9 0x00000000004022f8 in main (argc=1, argv=0x7fffffffe5b8) at gg_map.c:276
+======================
+======================
+======================
+======================
+
+===============================================================================
+#00: efx_move eina_list segv
+DATE: 20121213-0705
+DESCRIPTION: bug in my egraph blob or in efx ?
+REPRODUCE: gg_map
+STATUS: new
+======================
+gg_map with opengl rendering
+
+ delconn_id 23 [44/1812]
+not last one
+ type PACKET_DELCONN
+ delconn_id 15
+not last one
+ type PACKET_DELCONN
+ delconn_id 13
+not last one
+ type PACKET_DELCONN
+ delconn_id 12
+ type PACKET_DELCONN
+ delconn_id 11
+not last one
+ type PACKET_DELCONN
+ delconn_id 10
+blob_send 791 313
+blob_send 773 210
+blob_send 791 313
+blob_send 773 210
+blob_send 791 313
+blob_send 773 210
+blob_send 791 313
+blob_send 773 210
+blob_send 791 313
+blob_send 773 210
+blob_send 791 313
+blob_send 773 210
+blob_send 791 313
+blob_send 773 210
+blob_send 791 313
+blob_send 773 210
+
+Program received signal SIGSEGV, Segmentation fault.
+eina_list_data_get (list=<optimized out>)
+ at /usr/local/include/eina-1/eina/eina_inline_list.x:47
+47 return list->data;
+(gdb) bt
+#0 eina_list_data_get (list=<optimized out>)
+ at /usr/local/include/eina-1/eina/eina_inline_list.x:47
+#1 _move_cb (emd=0x14371a0, pos=0.24678331501490902) at efx_move.c:84
+#2 0x00007ffff50e1b62 in _ecore_animator_run (data=<optimized out>)
+ at lib/ecore/ecore_anim.c:585
+#3 0x00007ffff50e13c3 in _ecore_call_task_cb (data=<optimized out>,
+ func=<optimized out>) at lib/ecore/ecore_private.h:300
+#4 _do_tick () at lib/ecore/ecore_anim.c:125
+#5 0x00007ffff50e14a0 in _ecore_animator (data=<optimized out>)
+#4 _do_tick () at lib/ecore/ecore_anim.c:125 [0/1812]
+#5 0x00007ffff50e14a0 in _ecore_animator (data=<optimized out>)
+ at lib/ecore/ecore_anim.c:595
+#6 0x00007ffff50ec339 in _ecore_call_task_cb (data=<optimized out>,
+ func=<optimized out>) at lib/ecore/ecore_private.h:300
+#7 _ecore_timer_expired_call (when=when@entry=91912.774344784004)
+ at lib/ecore/ecore_timer.c:911
+#8 0x00007ffff50ec45b in _ecore_timer_expired_timers_call (when=
+ 91912.774344784004) at lib/ecore/ecore_timer.c:865
+#9 0x00007ffff50e8451 in _ecore_main_loop_iterate_internal (
+ once_only=once_only@entry=0) at lib/ecore/ecore_main.c:1831
+#10 0x00007ffff50e8b27 in ecore_main_loop_begin ()
+ at lib/ecore/ecore_main.c:964
+#11 0x000000000040230e in elm_main (argc=1, argv=0x7fffffffe5a8)
+ at gg_map.c:269
+#12 0x0000000000402359 in main (argc=1, argv=0x7fffffffe5a8) at gg_map.c:276
+(gdb) f 1
+#1 _move_cb (emd=0x14371a0, pos=0.24678331501490902) at efx_move.c:84
+84 EINA_LIST_FOREACH(emd->e->followers, l, e)
+(gdb) p emd
+$1 = (Efx_Move_Data *) 0x14371a0
+(gdb) p emd->
+A syntax error in expression, near `'.
+(gdb) p emd->e
+$2 = (EFX *) 0x14cac50
+(gdb) p *emd->e
+$3 = {obj = 0x14701c0, owner = 0x0, spin_data = 0x0, rotate_data = 0x0,
+ zoom_data = 0x0, move_data = 0x14371a0, bumpmap_data = 0x0, pan_data = 0x0,
+ fade_data = 0x0, resize_data = 0x0, map_data = {rotation = 0,
+ rotate_center = 0x0, zoom = 0, zoom_center = 0x0, move_center = 0x0, pan =
+ {x = 0, y = 0}}, followers = 0x10, queue = 0x0}
+(gdb) f 1
+#1 _move_cb (emd=0x14371a0, pos=0.24678331501490902) at efx_move.c:84
+84 EINA_LIST_FOREACH(emd->e->followers, l, e)
+(gdb) l
+79 pct = ecore_animator_pos_map(pos, emd->speed, 0, 0);
+80 x = lround(pct * (double)emd->change.x) - emd->current.x;
+81 y = lround(pct * (double)emd->change.y) - emd->current.y;
+82 _move(emd->e->obj, x, y);
+83 efx_maps_apply(emd->e, emd->e->obj, NULL, EFX_MAPS_APPLY_ALL);
+84 EINA_LIST_FOREACH(emd->e->followers, l, e)
+85 {
+86 _move(e->obj, x, y);
+87 efx_maps_apply(e, e->obj, NULL, EFX_MAPS_APPLY_ALL);
+88 }
+
+
+(gdb) p emd->e->obj
+$4 = (Evas_Object *) 0x14701c0
+(gdb) p *emd->e->obj
+$5 = {__magic = 2709961778, __in_list = {next = 0x1358d48, prev = 0x1547ce8,
+ last = 0x0}, parent = 0x68ab10, children = 0x0, klass = 0x99ba30,
+ refcount = 1, composite_objects = 0x0, mro_itr = {kls = 0x0}, do_error =
+ 0 '\000', condtor_done = 1 '\001', composite = 0 '\000', del = 0 '\000',
+ manual_free = 1 '\001'}
+(gdb) p *emd->move_data
+There is no member named move_data.
+(gdb) p *emd->e->move_data
+Attempt to dereference a generic pointer.
+(gdb) p e
+$6 = <optimized out>
+
+
+
+===============================================================================
+#03: malloc sigsegv
+DATE: 20121213
+DESCRIPTION: we must be facing memory corruption, probably during group handling
+because it's something i implemented lastly
+REPRODUCE: ggmap with opengl rendering
+STATUS: new
+
+======================
+ type PACKET_DELCONN
+ delconn_id 187
+not last one
+ type PACKET_DELCONN
+ delconn_id 184
+not last one
+ type PACKET_DELCONN
+ delconn_id 183
+
+Program received signal SIGSEGV, Segmentation fault.
+0x00007ffff45379e3 in malloc_consolidate () from /usr/lib/libc.so.6
+(gdb) bt
+#0 0x00007ffff45379e3 in malloc_consolidate () from /usr/lib/libc.so.6
+#1 0x00007ffff4538ba7 in _int_malloc () from /usr/lib/libc.so.6
+#2 0x00007ffff453b996 in calloc () from /usr/lib/libc.so.6
+#3 0x00007fffec9df2a1 in igraph_vector_copy () from /usr/lib/libigraph.so.0
+#4 0x00007ffff4a862b7 in _coords2_copy (eg=0x11434c0) at egraph.c:956
+#5 0x00007ffff4a862e9 in _cb_layouting_end (data=0x11434c0, thread=0x93bfd0)
+ at egraph.c:966
+#6 0x00007ffff50ec573 in _ecore_thread_kill (work=0x93bfd0)
+ at lib/ecore/ecore_thread.c:247
+#7 0x00007ffff50e0114 in _ecore_main_call_flush () at lib/ecore/ecore.c:853
+#8 0x00007ffff50e944e in _ecore_pipe_handler_call (p=0x98761237, buf=
+ 0x520 <Address 0x520 out of bounds>, len=65) at lib/ecore/ecore_pipe.c:551
+#9 0x00007ffff50e9b44 in _ecore_pipe_read (data=0x620d10,
+ fd_handler=<optimized out>) at lib/ecore/ecore_pipe.c:693
+#10 0x00007ffff50e86b1 in _ecore_call_fd_cb (data=<optimized out>,
+ func=<optimized out>, fd_handler=0x621280) at lib/ecore/ecore_private.h:378
+#11 _ecore_main_fd_handlers_call () at lib/ecore/ecore_main.c:1688
+#12 _ecore_main_loop_iterate_internal (once_only=once_only@entry=0)
+ at lib/ecore/ecore_main.c:1935
+#13 0x00007ffff50e8b27 in ecore_main_loop_begin ()
+ at lib/ecore/ecore_main.c:964
+#14 0x000000000040230e in elm_main (argc=1, argv=0x7fffffffe5a8)
+ at gg_map.c:269
+#15 0x0000000000402359 in main (argc=1, argv=0x7fffffffe5a8) at gg_map.c:276
+(gdb) f 4
+#4 0x00007ffff4a862b7 in _coords2_copy (eg=0x11434c0) at egraph.c:956
+956 igraph_matrix_copy(&eg->coords, &eg->coords2);
+(gdb) p eg->coords
+$7 = {data = {stor_begin = 0x13eaf90, stor_end = 0x13eb4b0, end = 0x13eb4b0},
+ nrow = 82, ncol = 2}
+(gdb) p eg->coords2
+$8 = {data = {stor_begin = 0x155b580, stor_end = 0x155baa0, end = 0x155baa0},
+
+======================
+======================
+======================
+======================
+
+
+===============================================================================
+#00: <title>
+DATE: 20121231-0549
+DESCRIPTION: <description>
+REPRODUCE: <reproduce>
+STATUS: <status>
+======================
+
+
+not last one
+ type PACKET_DELCONN
+ delconn_id 85
+ type PACKET_DELCONN
+ delconn_id 193
+ type PACKET_DELCONN
+ delconn_id 14
+not last one
+ type PACKET_DELCONN
+ delconn_id 13
+not last one
+ type PACKET_DELCONN
+ delconn_id 110
+not last one
+ type PACKET_DELCONN
+ delconn_id 109
+not last one
+ type PACKET_DELCONN
+ delconn_id 119
+not last one
+ type PACKET_DELCONN
+ delconn_id 34
+ type PACKET_DELCONN
+ delconn_id 108
+not last one
+ type PACKET_DELCONN
+ delconn_id 103
+not last one
+ type PACKET_DELCONN
+ delconn_id 101
+not last one
+ type PACKET_DELCONN
+ delconn_id 113
+not last one
+ type PACKET_DELCONN
+ delconn_id 100
+ type PACKET_DELCONN
+ delconn_id 99
+[Thread 0x7fffe6560700 (LWP 10889) exited]
+[New Thread 0x7fffe6560700 (LWP 10890)]
+
+Program received signal SIGSEGV, Segmentation fault.
+_eo_dov_internal (p_list=0x7fffffffe258, op_type=EO_OP_TYPE_REGULAR, obj=
+ 0xdf6740) at lib/eo/eo.c:402
+402 _eo_kls_itr_init(obj->klass, &obj->mro_itr, &prev_state);
+
+
+(gdb) bt
+#0 _eo_dov_internal (p_list=0x7fffffffe258, op_type=EO_OP_TYPE_REGULAR, obj=
+ 0xdf6740) at lib/eo/eo.c:402
+#1 eo_do_internal (obj=obj@entry=0xdf6740, op_type=op_type@entry=
+ EO_OP_TYPE_REGULAR) at lib/eo/eo.c:434
+#2 0x00007ffff707121b in _eo_del_internal (obj=0xdf6740) at lib/eo/eo.c:1254
+#3 _eo_unref (obj=0xdf6740) at lib/eo/eo.c:1309
+#4 eo_parent_set (obj=0xdf6740, parent=parent@entry=0x0) at lib/eo/eo.c:1109
+#5 0x00007ffff50e1438 in _do_tick () at lib/ecore/ecore_anim.c:146
+#6 0x00007ffff50e14a0 in _ecore_animator (data=<optimized out>)
+ at lib/ecore/ecore_anim.c:595
+#7 0x00007ffff50ec339 in _ecore_call_task_cb (data=<optimized out>,
+ func=<optimized out>) at lib/ecore/ecore_private.h:300
+#8 _ecore_timer_expired_call (when=when@entry=97571.372429319003)
+ at lib/ecore/ecore_timer.c:911
+#9 0x00007ffff50ec45b in _ecore_timer_expired_timers_call (when=
+ 97571.372429319003) at lib/ecore/ecore_timer.c:865
+#10 0x00007ffff50e8451 in _ecore_main_loop_iterate_internal (
+ once_only=once_only@entry=0) at lib/ecore/ecore_main.c:1831
+#11 0x00007ffff50e8b27 in ecore_main_loop_begin ()
+ at lib/ecore/ecore_main.c:964
+#12 0x000000000040230e in elm_main (argc=1, argv=0x7fffffffe5b8)
+ at gg_map.c:269
+#13 0x0000000000402359 in main (argc=1, argv=0x7fffffffe5b8) at gg_map.c:276
+
+
+
+(gdb) f 7
+#7 0x00007ffff50ec339 in _ecore_call_task_cb (data=<optimized out>,
+ func=<optimized out>) at lib/ecore/ecore_private.h:300
+300 r = func(data);
+(gdb) l
+295 void *data)
+296 {
+297 Eina_Bool r;
+298
+299 _ecore_unlock();
+300 r = func(data);
+301 _ecore_lock();
+302
+303 return r;
+304 }
+(gdb) f 6
+#6 0x00007ffff50e14a0 in _ecore_animator (data=<optimized out>)
+ at lib/ecore/ecore_anim.c:595
+595 r = _do_tick();
+(gdb) l
+590 static Eina_Bool
+591 _ecore_animator(void *data EINA_UNUSED)
+592 {
+593 Eina_Bool r;
+594 _ecore_lock();
+595 r = _do_tick();
+596 _ecore_unlock();
+597 return r;
+598 }
+599
+======================
+======================
+======================
+======================
+
+
+===============================================================================
+===============================================================================
+DONE
+===============================================================================
+===============================================================================
+
+
diff --git a/gg_map/Makefile b/gg_map/Makefile
new file mode 100644
index 0000000..17040af
--- /dev/null
+++ b/gg_map/Makefile
@@ -0,0 +1,21 @@
+CFLAGS += $(shell pkg-config --cflags elementary evas ecore)
+LIBS += $(shell pkg-config --libs elementary evas ecore) -levent -lglouglou -legraph
+#CFLAGS += -Wall -O2
+CFLAGS += -Wall -g
+
+BINARY=gg_map
+
+PREFIX=/usr/local
+BINDIR=$(PREFIX)/bin
+
+$(BINARY): $(BINARY).o
+ $(CC) $(CFLAGS) -o $@ $< $(LIBS)
+
+install: $(BINARY)
+ @echo "installation of $(BINARY)"
+ mkdir -p $(BINDIR)
+ install -m 0755 $(BINARY) $(BINDIR)
+
+clean:
+ rm -f $(BINARY) $(BINARY).o
+
diff --git a/gg_map/README.txt b/gg_map/README.txt
new file mode 100644
index 0000000..5b5146e
--- /dev/null
+++ b/gg_map/README.txt
@@ -0,0 +1,30 @@
+gg_map - glouglou visualisation client of network traffic in real time
+
+WARNING: Work in progress, don't expect this to work !
+
+=== Requirements ===
+
+* libglouglou
+
+* egraph
+
+* Enlightenment Foundation Libraries
+http://www.enlightenment.org
+ * evas
+ * elementary
+
+TODO
+====
+
+dot file export
+
+DEBUG
+=====
+
+make clean && make && valgrind --leak-check=yes --log-fd=1 ./gg_map |tee valgring.txt
+
+Research
+========
+
+maybe one day using enesim - http://code.google.com/p/enesim/
+see doc_research/
diff --git a/gg_map/doc_research/20121205_irc_efr.fr b/gg_map/doc_research/20121205_irc_efr.fr
new file mode 100644
index 0000000..eed9160
--- /dev/null
+++ b/gg_map/doc_research/20121205_irc_efr.fr
@@ -0,0 +1,270 @@
+22:35 -!- glouglou [~laurent@gouloum.fr] has joined #e.fr
+22:35 -!- Topic for #e.fr: Le canal de discussion pour les mordus d'Enlightenment et des EFLs || http://enlightenment.fr || http://omicron.homeip.net/projects/easy_e17/easy_e17.sh || grenouillère et slip kangourou : http://enlightenmentfr.spreadshirt.fr/ || EFL 1.7.2 & 1.2.1 & 1.1.1 & 1.0.2
+22:35 -!- Topic set by vtorri [~torri@enlightenment/developer/vtorri] [Sun Nov 25 00:06:20 2012]
+22:35 [Users #e.fr]
+22:35 [@beber_ ] [ aissen ] [ DeusP` ] [ JackDanielZ ] [ nicolasboulais] [ Vaur` ]
+22:35 [@cedric ] [ ayeuu ] [ dgarcin ] [ jcEcaSinters] [ rookmoot ] [ vdehors]
+22:35 [@ChanServ ] [ billiob ] [ donch ] [ KaKaRoTo ] [ Sachiel ] [ wolog ]
+22:35 [@vtorri ] [ black_rez ] [ e-bawt ] [ kleph ] [ sebastienb ] [ youx ]
+22:35 [+Lutin ] [ captainigloo ] [ esby ] [ Kuri ] [ Sp4rKy ]
+22:35 [+yoz ] [ chidambar ] [ glouglou] [ lemagoup_ ] [ stefzekiller ]
+22:35 [ _billiob_ ] [ chouchoune ] [ GNU\Jack] [ mathieui ] [ TaPiOn ]
+22:35 [ adrien ] [ clem-vangelis] [ Haze` ] [ Munto ] [ TAsn ]
+22:35 [ adrien_oww] [ cthuun ] [ illogict] [ nemunaire ] [ Thanatermesis ]
+22:35 -!- Irssi: #e.fr: Total of 49 nicks [4 ops, 0 halfops, 2 voices, 43 normal]
+22:35 -!- Channel #e.fr created Sun Nov 26 07:42:59 2006
+22:35 -!- Irssi: Join to #e.fr was synced in 0 secs
+22:35 -!- Home page for #e.fr: http://enlightenment.fr
+22:36 < glouglou> salut
+22:36 < glouglou> vtorri: ping
+22:56 <@vtorri> glouglou: pong
+23:07 < glouglou> salut :)
+23:08 < glouglou> voila je suis entrain de coder un outil pour visualiser un reseau informatique en temps reel, et la partie graphique se ferait avec les EFLs
+23:09 < glouglou> j'ai besoin d'afficher un graph, qui pourrait etre compose de plusieurs centaines d'objets
+23:09 < glouglou> ca va bouger en temps reel
+23:10 < glouglou> je regardais dans le trunk de e pour voir quelles libs au dessus de evas je pourrais utiliser, je sais que evas ne fait pas de vectoriel mais c'est pour ca que je veux l'utiliser, les libs vectorielles seront a mon avis trop lentes pour ce que je veux
+23:11 < glouglou> vtorri: je suis tombe sur efx, et j'ai vu que tu etais dans les AUTHORS, alors je me suis dit que peut etre tu aurais un conseil :)
+23:12 < glouglou> apres ptetr que je ferais mieux d'utiliser cairo je sais pas
+23:14 < glouglou> (ca serait trop facile :p
+23:15 < glouglou> et ce moment je cherche une lib qui calculerais les coordonnes de mon graph de nodes (machines sur le reseau), que je donnerais ensuite a evas
+23:15 -!- Marty_Stoopid [~Marty_Sto@90.36.12.93.rev.sfr.net] has joined #e.fr
+23:16 <@vtorri> et bien, il existe une lib vectorielle qui pourrait t'interesser : enesim
+23:16 <@vtorri> on l'utilise pour faire du svg
+23:17 <@vtorri> glouglou: de plus, il faut savoir qu'evas a une limite au nombre d'objets
+23:18 <@vtorri> evas a ete concu pour avoir au max 3000 objets
+23:18 <@vtorri> apres il devient trop lent
+23:18 <@vtorri> si tu en a une centaine, ca devrait aller
+23:18 < captainigloo> une lib de graph avec enesim serait juste magnifique
+23:18 <@vtorri> apres, tu veux animer le bouzin,
+23:19 <@vtorri> il faudrait que je sache quel type d'animation
+23:19 -!- clem-vangelis [~vangelis@35.194.31.93.rev.sfr.net] has quit [Remote host closed the connection]
+23:19 <@vtorri> captainigloo: genre ca : https://developers.google.com/chart/interactive/docs/gallery ?
+23:19 < glouglou> merci !
+23:19 <@vtorri> captainigloo: ou bien autre chose (donne un lien si possible)
+23:20 <@vtorri> glouglou: quel genre d'animation tu veux faire ?
+23:21 < glouglou> l'introduction de nouvelle nodes par exemple, qui ne soit pas brutale sinon le graph va bouger d'un coup et on ne va rien comprendre
+23:22 < glouglou> ensuite, l'inspection du traffic peut descendre au niveau packet, donc afficher le flux entre les nodes, mais ca avec efx / evas je vois a peu pres comment faire
+23:22 <@vtorri> donc si tu introduis un nouveau noeud, tu sais comment doit etre le nouveau graphe
+23:22 <@vtorri> donc tu veuxx interpoler entre la position initiale et celle avec les nouveau noeuds
+23:22 < glouglou> oui
+23:22 <@vtorri> glouglou: tu sais parler anglais ?
+23:23 < glouglou> oui
+23:23 <@vtorri> viens sur #enesim
+23:24 < glouglou> vtorri: tu vois qqchose de plus adapte a mes besoins que les EFLs ?
+23:25 <@vtorri> non :p
+23:25 < glouglou> hehe :p
+23:25 <@vtorri> ce qui me gene avec evas est que l'objet evas 'ligne' n'est pas optimise, il me semble
+23:26 <@vtorri> donc tu as des noeuds
+23:26 <@vtorri> autrement dit des disques
+23:26 <@vtorri> reliés avec des lignes
+23:26 <@vtorri> c'est ca ?
+23:27 < glouglou> oui
+23:28 <@vtorri> il y aurait donc combien d'objets (en comptant les lignes et le disques) ?
+23:28 < glouglou> je veux que la taille de la ligne represente la quantite de traffic ... je pourrais peut etre emuler ca avec evas polygon
+23:29 < glouglou> c'est sur que la limite des 3000 sera peut etre problematique
+23:29 < glouglou> mais a ce moment la, je vois pas quelles autres choix j'ai
+23:31 <@vtorri> faire directement le rendering dans un evas_image
+23:32 <@vtorri> faire le truc soit-meme
+23:32 <@vtorri> i.e. tu aurais un objet evas
+23:32 <@vtorri> enesim ets bien en ce sens qu'il ne depend pas de truc bizarre comme cairo
+23:33 <@vtorri> cairo depend de xlib, xcb, d'extensions X , zlib, expat, etc...
+23:34 <@vtorri> fais un ldd sur ton libcairo.so.***
+23:34 <@vtorri> et tu comprendras
+23:34 < glouglou> mhmh oui j'ai deja du faire le rendering directement dans une image a cause de ce probleme de nombre d'objet en fait ... http://gouloum.fr/projects/elife/
+23:34 <@vtorri> bon, turan se fait desirer :/
+23:35 < glouglou> je viens de tomber sur fog-framework tiens, interessant
+23:35 < captainigloo> vtorri: oui par exemple
+23:35 < captainigloo> il y en a des plus jolis encore
+23:35 <@vtorri> fog est une autre lib de rendu vectoriel
+23:35 < captainigloo> mais c'est lid?e
+23:35 <@vtorri> captainigloo: liens ?
+23:36 <@vtorri> glouglou: enesim est multithreade, et peux aussi utiliser opengl, au cas ou ca t'interesse
+23:36 < glouglou> interessant oui oui :)
+23:36 < captainigloo> http://www.splashnology.com/article/15-awesome-free-javascript-charts/325/
+23:37 <@vtorri> arg, fog est en c++ :/
+23:37 < glouglou> ouaip
+23:37 < glouglou> mais bon j'etais pret a faire le pas
+23:37 < captainigloo> vtorri: l'id?e c'est de se servir d'enesim pour dessiner dans un evas object image ?
+23:37 <@vtorri> fog : NOTE 1: Multi-threaded paint engine and JIT compiler were temporarily removed due to refactorization and feature additions. These concepts will be reimplemented after the internal API will be complete and stable.
+23:38 <@vtorri> captainigloo: par exemple
+23:38 <@vtorri> captainigloo: c'est ce que je fais
+23:38 < captainigloo> pour esvg ?
+23:38 <@vtorri> glouglou: enesim !
+23:38 < captainigloo> glouglou: tu veux faire quel genre de graphes ?
+23:38 < glouglou> vtorri: je lis le code
+23:38 <@vtorri> captainigloo: non, esvg est un objet enesim, plutot une surface
+23:39 < glouglou> captainigloo: graph de reseau : des machines, des liens de communication. et ca bouge en temps reel
+23:39 <@vtorri> captainigloo: et apres je prends les donnees de la surface et je les mets dans un evas object image
+23:40 <@vtorri> glouglou: http://code.google.com/p/fog/source/list
+23:40 < captainigloo> glouglou: ok
+23:40 <@vtorri> glouglou: le code n'est plus mis a jour depuis le 17 avril
+23:40 < glouglou> je lis le code de enesim ;{
+23:40 < glouglou> ;)
+23:43 < captainigloo> c'est vraiment dommage de pas avoir de primitives vectorielles dans evas
+23:44 < glouglou> si elles sont dans enesim ca me derange pas :p
+23:44 <@vtorri> http://code.google.com/p/enesim/source/browse/trunk/utils/enesim_examples
+23:44 < captainigloo> oui mais tu pers la possibilti? de faire un theme avec edje
+23:44 <@vtorri> certe
+23:44 < glouglou> mhmh
+23:44 <@vtorri> glouglou: idee d'enesim (proche de cairo) :
+23:45 < captainigloo> dans ton cas ce qu'il te manque ce soont de path pour relier des objets
+23:45 <@vtorri> tu crees des objets, tu les mets dans une surface
+23:45 <@vtorri> tu 'render' la surface
+23:45 <@vtorri> apres, tu peux la mettre dans une image evas
+23:45 < captainigloo> oui, c'est ce que fait raoul dans eskiss
+23:45 <@vtorri> truc classique
+23:45 -!- stefzekiller [~stefzekil@109.89.246.160] has quit [Quit: Quitte]
+23:48 < glouglou> ok donc je n'ai plus la meme limitation du nombre d'objets
+23:48 < glouglou> vu que je n'utilise plus evas
+23:48 <@vtorri> non
+23:48 <@vtorri> par contre, tu peux utiliser elementary pour la GUI
+23:48 < glouglou> yep
+23:49 <@vtorri> tu utilises evas pour aute chose, en fait
+23:50 < glouglou> ?
+23:50 <@vtorri> tu utilises evas indirectement avec elementary
+23:50 < glouglou> oui
+23:50 <@vtorri> mais pas pour ton reseau
+23:51 <@vtorri> franchement, avec enesim, tu peux faire un truc vriament sympa
+23:51 <@vtorri> et regarde etch
+23:51 <@vtorri> dans le repo d'enesim
+23:51 < glouglou> le probleme c'est que je ne peux plus utiliser les fonctionnalitees d'evas sur ma carte de reseau
+23:51 <@vtorri> c'est une lib d'animation
+23:51 <@vtorri> pour l'interpolation, ca va t'aider
+23:51 < glouglou> oui justement j'etais etrain de lire Etch.h
+23:52 < glouglou> par exemple j'aurais bien voulu afficher des images a certains endroits
+23:52 <@vtorri> quelle fonctionalite d'evas ?
+23:52 < glouglou> integrees dans la carte de reseau
+23:52 <@vtorri> emage
+23:52 <@vtorri> texte : etex
+23:52 < glouglou> qui se deplacent d'une node a l'autre
+23:53 < glouglou> mais c'est la magie dites moi
+23:53 <@vtorri> http://code.google.com/p/enesim/source/browse/trunk/enesim/emage/include/Emage.h
+23:53 < glouglou> ouais je vois
+23:53 < glouglou> c'est un boulot de ouf Enesim
+23:54 < glouglou> nice
+23:54 <@vtorri> http://code.google.com/p/enesim/source/browse/trunk/enesim/etex/include/Etex.h
+23:54 <@vtorri> c'est adapte a une surface enesim
+23:55 <@vtorri> turran est espagnol et vit a cote de madrid, il me semble
+23:55 <@vtorri> donc il a la meme timezone que nous
+23:55 <@vtorri> s'il ne repond pas ce soir, essaie demain a partir de 10h
+23:55 <@vtorri> en general, il est la
+23:56 < glouglou> nikel
+23:56 <@vtorri> je sais qu'en ce moment, il a pas mal de boulot
+23:57 <@vtorri> http://code.google.com/p/enesim/source/browse/trunk/marketing/conf/2012_barcelona_linuxcon/Beyond%20enesim%20and%20esvg.pdf
+23:57 < glouglou> je me rappelle vaguement de son nom de quand j'etais plus sur les efls il y a genre 7 ans
+23:57 <@vtorri> ca te donnera une idee
+23:57 < glouglou> ben je vais tester tout ca (enesim) et on va voir :)
+23:57 < glouglou> ok
+23:57 <@vtorri> cool
+23:57 < glouglou> merci milles fois
+23:57 <@vtorri> n'hesite pas a me demander
+23:57 <@vtorri> je suis un contributeur sur enesim
+23:57 < glouglou> cool :)
+23:58 <@vtorri> par contre, en journee, je ne peux pas, je bosse
+23:58 <@vtorri> donc le soir
+23:58 <@vtorri> captainigloo: yoz a commence un echart :)
+23:59 < glouglou> toujours prof ?
+23:59 < glouglou> de maths
+23:59 <@vtorri> oui :)
+00:04 <@vtorri> c'est chouette de revenir sur les EFL :)
+00:05 < glouglou> hiboux
+00:05 < glouglou> donc d'apres le comparatif que je vois slide 11, enesim est 2 fois plus lent que cairo ? :)
+00:05 <@vtorri> oui
+00:05 <@vtorri> mais
+00:06 <@vtorri> le rendering a ete optimise la semaine derniere
+00:06 <@vtorri> on doit etre au meme niveau
+00:06 -!- beber_ [~beber@2a01:e35:2422:da50:941:20:1:136] has quit [Quit: Kenavo les poteaux]
+00:06 <@vtorri> on a une grosse marge de manoeuvre par rapport a cairo
+00:06 <@vtorri> pas d'asm
+00:06 <@vtorri> ou quasiment pas
+00:06 <@vtorri> cairo est optimise a mort, avec plein de devs
+00:07 <@vtorri> il y a juste 2 gros devs et un contributeur (moi)
+00:07 < glouglou> tu serais capable de relancer le meme test que dans le slide 11 pour voir si tu as raison ? :p
+00:08 < glouglou> non pas que ca va influer sur mon choix, car dans tous les cas je vais tester enesim, mais juste pour voir ;)
+00:08 <@vtorri> la, non
+00:09 <@vtorri> peux pas
+00:09 <@vtorri> le code est chez turran
+00:09 <@vtorri> j'attends qu'il le committe
+00:11 <@vtorri> glouglou: bon, turran a repondu :)
+00:11 < glouglou> il peut pas commiter il regarde la tele :p
+00:11 <@vtorri> hehe
+00:11 <@vtorri> envoie un mail (cf pm)
+00:13 < glouglou> btw mon nom de dev sur le svn de e c'etait kiwi
+00:13 <@vtorri> hooo
+00:13 <@vtorri> kiwi
+00:13 <@vtorri> ca fait un bail, en effet
+00:13 < glouglou> clair
+00:15 <@vtorri> tu recodera sur les efl / e ?
+00:16 < glouglou> non pas trop juste http://gouloum.fr/projects/elife/
+00:17 < glouglou> j'ai reswitche a e17 recement car spectrwm aka scrotwm (https://opensource.conformal.com/wiki/spectrwm) ne convenait plus a mes besoins
+00:18 < glouglou> j'utilise un peu trop d'applications merdiques ;p
+00:18 <@vtorri> :)
+00:18 <@vtorri> bon, je vais aller faire dodo :)
+00:18 <@vtorri> bn
+00:18 < glouglou> et meme avec e17 j'ai des pb defois de focus avec des applis java ...
+00:18 < glouglou> ok
+00:19 < glouglou> *merci encore !!!*
+00:22 < glouglou> il n'y avais pas un chat irc special dev en anglais sur e ?
+00:22 < glouglou> chan*
+00:40 < captainigloo> edevelop
+00:40 < captainigloo> et e
+00:40 < captainigloo> kiwi, c'est vrai que ca fait un bail !
+00:40 < captainigloo> moi aussi dodo
+00:40 < captainigloo> a+
+00:48 < glouglou> ah oui merci :p
+00:48 < glouglou> bounnui
+00:52 -!- Marty_Stoopid [~Marty_Sto@90.36.12.93.rev.sfr.net] has quit [Quit: bye]
+02:50 < glouglou> http://igraph.sourceforge.net/index.html
+02:50 < glouglou> igraph is my friend
+02:51 < glouglou> + enesim for rendering :)
+03:04 <@cedric> ca va t'occuper tiens
+03:05 < glouglou> tu l'as dis
+03:08 < glouglou> c'est des oufs les mecs de igraph, tu regarde certains de leurs code ca fait peur (http://igraph.sourceforge.net/doc/html/ch12s02.html)
+03:09 < glouglou> connais igraph cedric ?
+03:09 <@cedric> nop, je connaissais pas
+03:09 <@cedric> je serais parti sur graphivz
+03:11 <@cedric> mais bon, si tu arrives a en faire qqc ca peut etre tres utiles
+03:12 <@cedric> par contre, je ne suis pas certain que tu es vraiment besoin de vectoriel pour ca
+03:12 <@cedric> tout depend du graph que tu veux faire au final
+03:12 <@cedric> mais des objets edje relie entre eux par des lignes avec un smart objet pour les crees qd ils deviennent visible
+03:13 <@cedric> et les faire disparaitre qd il ne sont plus a l'ecran me semble etre plus efficace et souple
+03:14 < glouglou> pour le debut c'est clair que faire ce que tu dis c'est plus efficace au niveau performances aussi
+03:14 <@cedric> de plus, je suis pas sur que enesim soit ceux dont tu es exactement besoin
+03:15 < glouglou> mais des que je vais vouloir gerer la largeur des liens entre les nodes, ca va poser probleme
+03:15 <@cedric> vu qu'il va te falloir aussi gerer la logique d'evas pour savoir quoi redessiner et tout
+03:15 <@cedric> j'aurais bien une solution simple pour ca, un autre objet edje et evas map
+03:15 <@cedric> enesim est tres bien a mon avis pour un graph mathematique statique, des que tu vas faire du dynamique, il t'en faudra plus
+03:16 < glouglou> je vois pas trop pour le lien en utilisant edje et evas map
+03:16 <@cedric> je sais que pour esvg, ils ont developpe une lib qui fait la partie du boulot dont je parle, j'ai oublie le nom et je ne sais pas si elle te conviendrait en meme temps
+03:17 <@cedric> et bien, ta ligne, c'est un objet edje que tu as deplace et tourne
+03:17 <@cedric> tu peux donc lui envoye des signaux pour l'anime
+03:17 <@cedric> et tu peux aussi la redimensionne
+03:18 < glouglou> ca fait quoi evas map ?
+03:18 <@cedric> rotation, deformation
+03:18 <@cedric> les effets "2.5D" qu'aime bcp apple
+03:18 < glouglou> k
+03:21 < glouglou> effectivement j'ai rien vu dans enesim pour gerer le rendering d'une partie de la surface seulement
+03:24 < glouglou> pour cette limite de environ 3000 objets dans evas, on parle de 3000 objets *visibles* ou au total en comptant ceux qui ne sont pas _show() ?
+03:26 <@cedric> c'est completement flou comme limite
+03:26 <@cedric> le fait est que l'acces aux objets requiree un algo en O(n)
+03:27 <@cedric> donc plus tu as d'objet plus c'est lent
+03:27 < glouglou> donc objets au total
+03:28 <@cedric> oui et non :-)
+03:28 < glouglou> mais quand tu render tu les accede beaucoup plus
+03:28 <@cedric> on a mis plein de d'optim pour que le cas soit le plus tard possible
+03:28 < glouglou> j'imagine
+03:28 <@cedric> dc en general, c'est 3000 objets actifs
+03:28 <@cedric> ce qui fait de toute facon bcp de monde a l'ecran
+03:29 <@cedric> on pourrait avoir l'utilisation d'un algo de type quad tree ou utilisait les bouding box des smart objets pour limiter les recherches
+03:29 <@cedric> mais dans la pratique, on a aucune appli qui montre le probleme
+03:29 <@cedric> dc pas d'optim
+03:29 <@cedric> et le 3000 est lie a ton CPU
+03:29 <@cedric> plus, il est gros, plus le chiffre sera eleve
+03:30 <@cedric> mais avec l'algo que je t'ai decrit, je pense pas que tu es de probleme a gerer ton graph
+03:30 <@cedric> apres lors d'un zoom out, je pense que tu devras fusionner des nodes de ton reseau (juste pour la lisibilite de toute facon)
+03:31 < glouglou> algo pour les nodes ou pour les liens ?
+03:33 <@cedric> bah, les deux
+03:43 <@cedric> https://wiki.ubuntu.com/Nexus7/FAQ \o/
+04:22 < glouglou> cedric: donc pour les nodes tu partirais sur un edje object, et pour les liens un edje object aussi qui est redimensionne a la taille du lien
+04:34 -!- Munto [~frugal@2a01:e35:139d:91e0:221:85ff:fee1:5c3c] has quit [Quit: Quitte]
+05:43 -!- sharinganex [~sharingan@233.208.85.79.rev.sfr.net] has joined #e.fr
diff --git a/gg_map/doc_research/20121205_irc_enesim.txt b/gg_map/doc_research/20121205_irc_enesim.txt
new file mode 100644
index 0000000..94d5e22
--- /dev/null
+++ b/gg_map/doc_research/20121205_irc_enesim.txt
@@ -0,0 +1,28 @@
+===============================================================================
+2012-12-05_06-12
+
+==== enesim ====
+http://code.google.com/p/enesim/
+== #enesim ==
+23:26 < glouglou> i'm working on a network visualisation tool, that would display a network map in real time meaning new nodes appears and disapears (machines),
+ and there are links between them (network traffic)
+23:27 < glouglou> networks can be hundreds of machines
+23:31 < glouglou> i was wondering if evas / enesim was my best choice for visualisation of this type of real time graph
+23:54 < glouglou> btw i'm reading Enesim source code, impressive ... nice work :)
+Day changed to 05 Dec 2012
+00:09 < turran> hey sorry, was on the tv :-/
+00:09 < turran> already late
+00:10 < turran> why dont you drop an email with vincent there too
+00:10 < turran> need to go
+00:10 < turran> ttyl
+== email ==
+00:12 <vtorri> jorgeluis.zapata@gmail.com
+00:13 <vtorri> et le mien : vincent.torri@gmail.com
+00:13 <glouglou> ok note
+00:14 <vtorri> ha et met aussi en cc :
+00:15 <vtorri> jose_ogp@juno.com
+00:15 <vtorri> c'est l'autre contributeur
+00:15 <vtorri> l'auter dev, plutot :)
+
+== evas + edje + efx ==
+
diff --git a/gg_map/doc_research/20121205_ntamas.txt b/gg_map/doc_research/20121205_ntamas.txt
new file mode 100644
index 0000000..0b4c468
--- /dev/null
+++ b/gg_map/doc_research/20121205_ntamas.txt
@@ -0,0 +1,29 @@
+===============================================================================
+2012-12-05_15-19
+
+http://sixdegrees.hu/last.fm/technical_details.html
+http://sixdegrees.hu/last.fm/interactive_map.html
+
+===============================================================================
+
+http://www.last.fm/inbox/compose?to=ntamas
+
+Hello,
+
+I just found your last.fm map while searching about igraph, and wow, nice :) I really love the life that comes out of it.
+
+Then i red the tech details page, and there you are talking about the DrL graph layout algorithm of igraph, and from what i understand it could be used to handle "dynamic" graphs, meaning you can add verticles / edges to an already existing graph. I quickly red http://igraph.sourceforge.net/doc/html/ch18s01.html#idp25263952, and this seems possible but non obvious for me :p
+
+I'm currently writing a real time computer network visualisation tool in C, displaying nodes (machines) and links between them (network traffic).
+Atm i'm thinking about using igraph to compute the coordinates of my nodes and links, before displaying them with an external graphical library.
+In the network, new nodes come and go, as well as links, and in order to have a coherent display I need to have nodes that do not move too much when a node is added / removed.
+Interpolation of old / new coordinates for nodes and links would be handled by the external graphical library.
+
+So i'm interested in interested in adding new edges and verticles to a graph that has an already computed layout, and generate a new layout that includes my new verticles/edges without totaly breaking the previous layout.
+Did you already do such stuff ? Do you know source code that does this with igraph so I can learn ? I'm really just discovering igraph and more or less the graphs in general.
+
+If you have any pointers / advice for me it would be great !
+
+Thanks,
+Laurent
+===============================================================================
diff --git a/gg_map/doc_research/igraph.txt b/gg_map/doc_research/igraph.txt
new file mode 100644
index 0000000..70d4db4
--- /dev/null
+++ b/gg_map/doc_research/igraph.txt
@@ -0,0 +1,27 @@
+Tutorial in python
+http://www.cs.rhul.ac.uk/home/tamas/development/igraph/tutorial/tutorial.html
+
+Interpolation in python
+http://computationallegalstudies.com/2010/01/17/graphmovie-a-library-for-generating-movies-from-dynamic-graphs-with-igraph/
+https://gist.github.com/279578
+https://gist.github.com/279584
+
+Movies in python (ntamas)
+http://vimeo.com/14922587
+https://gist.github.com/577159
+
+Static 3d graph in python
+http://bommaritollc.com/2011/02/21/plotting-3d-graphs-with-python-igraph-and-cairo-cn220-example/
+
+===============================================================================
+DrL author page (smartin)
+http://web.archive.org/web/20100527153105/http://www.cs.sandia.gov/~smartin/software.html
+
+===============================================================================
+General viz
+
+VxInsight - presentation of the need for visualisation tools
+http://www.cs.sandia.gov/~dkjohns/JIIS/Vx_Intro.html
+
+Beautiful Visualization - book
+http://books.google.com/books?id=TKh6fdlKwfMC&pg=PA156&lpg=PA156&dq=http://www.cs.sandia.gov/~smartin/software.html&source=bl&ots=cVPGT-AXkF&sig=FCHUkdjFDpIESWvKpwqXSM0PhmM&hl=en&sa=X&ei=Qma_UMPjHISE4gTglYHQBw&sqi=2&ved=0CEAQ6AEwBA#v=onepage&q=igraph&f=false
diff --git a/gg_map/gg_map.c b/gg_map/gg_map.c
new file mode 100644
index 0000000..dda280c
--- /dev/null
+++ b/gg_map/gg_map.c
@@ -0,0 +1,454 @@
+#include <err.h>
+
+#include <Elementary.h>
+#include <Egraph.h>
+
+#include <libglouglou.h>
+#include <libggnet.h>
+
+int _debug = 0;
+
+Evas_Object *_mainwin;
+Evas_Object *_egraph = NULL;
+struct ggnet *_ggnet;
+struct event_base *_ev_base;
+
+/* link between ecore loop and libevent loop */
+static Eina_Bool
+_cb_ecore_libevent(void *data) {
+ if (event_base_got_exit(_ev_base))
+ return EINA_FALSE;
+ else
+ event_base_loop(_ev_base, EVLOOP_NONBLOCK);
+ return EINA_TRUE;
+}
+
+const char*
+ip_to_str(u_int ip)
+{
+ unsigned char bytes[4];
+ static char buf[16];
+
+ bytes[0] = ip & 0xFF;
+ bytes[1] = (ip >> 8) & 0xFF;
+ bytes[2] = (ip >> 16) & 0xFF;
+ bytes[3] = (ip >> 24) & 0xFF;
+ snprintf(buf, sizeof(buf), "%d.%d.%d.%d",
+ bytes[3], bytes[2], bytes[1], bytes[0]);
+ return buf;
+}
+
+static void
+_cb_ggnet_addgroup(struct ggnet *net, struct ggnet_nodegroup *group,
+ struct ggnet_nodegroup *parent)
+{
+ Egraph_Vertice *vgroup;
+
+ vgroup = egraph_group_add(_egraph, ip_to_str(group->addr.s_addr), group);
+ ggnet_nodegroup_usrdata_set(group, vgroup);
+ if (parent)
+ egraph_group_vertice_attach(_egraph, vgroup,
+ ggnet_nodegroup_usrdata_get(parent));
+}
+
+static void
+_cb_ggnet_delgroup(struct ggnet *net, struct ggnet_nodegroup *group)
+{
+ Egraph_Vertice *vgroup;
+
+ vgroup = ggnet_nodegroup_usrdata_get(group);
+ egraph_vertice_del(_egraph, vgroup);
+}
+
+static Egraph_Vertice *
+_node_to_vertice(struct ggnet_node *n)
+{
+ Egraph_Vertice *v;
+ struct ggnet_nodegroup *group;
+
+ v = ggnet_node_usrdata_get(n);
+ if (!v) {
+ v = egraph_vertice_add(_egraph, ip_to_str(n->addr.s_addr), n);
+ ggnet_node_usrdata_set(n, v);
+ group = ggnet_node_group_get(n);
+ if (group)
+ egraph_group_vertice_attach(_egraph, v,
+ ggnet_nodegroup_usrdata_get(group));
+ }
+ return v;
+}
+
+static void
+_node_attach_parentgroup(struct ggnet_node *n)
+{
+ struct ggnet_nodegroup *g;
+ Egraph_Vertice *vg, *vparentg;
+
+ g = ggnet_node_group_get(n);
+ if (!g || !g->parent)
+ return;
+ vg = ggnet_nodegroup_usrdata_get(g);
+ vparentg = ggnet_nodegroup_usrdata_get(g->parent);
+ egraph_group_vertice_attach(_egraph, vg, vparentg);
+}
+
+static void
+_node_detach_parentgroup(struct ggnet_node *n)
+{
+ struct ggnet_nodegroup *g;
+ Egraph_Vertice *vg, *vparentg;
+
+ g = ggnet_node_group_get(n);
+ if (!g || !g->parent)
+ return;
+ vg = ggnet_nodegroup_usrdata_get(g);
+ vparentg = ggnet_nodegroup_usrdata_get(g->parent);
+ egraph_group_vertice_detach(_egraph, vg, vparentg);
+}
+
+static void
+_conn_add(u_int id, u_int src, u_int dst, u_int proto, u_int8_t pktsize)
+{
+ struct ggnet_conn *conn;
+ Egraph_Edge *e;
+ struct ggnet_node *a, *b;
+ Egraph_Vertice *va, *vb;
+ struct in_addr srcaddr, dstaddr;
+ int size, response;
+
+ GG_PKTDATA_SIZE_DECODE(pktsize, size, response);
+ if (_debug)
+ printf("_conn_add\n");
+ if (response > 0) /* cannot have a new connection that is a response */
+ return;
+
+ srcaddr.s_addr = src;
+ dstaddr.s_addr = dst;
+
+ conn = ggnet_conn_add(_ggnet, &srcaddr, -1, &dstaddr, -1, proto, size, id);
+ a = ggnet_conn_src_get(conn);
+ b = ggnet_conn_dst_get(conn);
+ va = _node_to_vertice(a);
+ vb = _node_to_vertice(b);
+ e = egraph_edge_find(_egraph, va, vb);
+ if (_debug)
+ printf("_conn_add: a %d b %d e %x id %d\n", va->id, vb->id, e, id);
+ if (!e) {
+ if (a->group && a->group->conn_count == 1)
+ _node_detach_parentgroup(a);
+ if (b->group && b->group->conn_count == 1)
+ _node_detach_parentgroup(b);
+ e = egraph_edge_add(_egraph, va, vb, conn);
+ if (_debug)
+ printf("_conn_add: egraph edge added %x\n", e);
+ }
+ ggnet_conn_usrdata_set(conn, e);
+}
+
+static void
+_conn_del(int id) {
+ struct ggnet_conn *conn, *otherconn;
+ struct ggnet_node *a, *b;
+ Egraph_Edge *e;
+
+ conn = ggnet_conn_find_by_id(_ggnet, id);
+ if (conn) {
+ a = ggnet_conn_src_get(conn);
+ b = ggnet_conn_dst_get(conn);
+ e = ggnet_conn_usrdata_get(conn);
+ if (_debug)
+ printf("_conn_del: conn id %d\n", id); // XXX
+ ggnet_conn_del(_ggnet, conn);
+ /* is there other connections between these peers ? */
+ otherconn = ggnet_conn_find_by_node(_ggnet, a, b);
+ if (!otherconn) {
+ // XXX lets keep the edges, igraph layouting behaves badly when you have
+ // a vertice without edge ...
+ if (_debug)
+ printf("_conn_del: edge del %x\n", e); // XXX
+ egraph_edge_del(_egraph, e);
+ if (a->group && a->group->conn_count == 0)
+ _node_attach_parentgroup(a);
+ if (b->group && b->group->conn_count == 0)
+ _node_attach_parentgroup(b);
+ } else {
+ if (_debug)
+ printf("_conn_del: not last one, edge %x *not* deleted\n", e);
+ }
+ } else {
+ if (_debug)
+ printf("_conn_del: does not exist !\n");
+ }
+}
+
+static void
+_conn_data(int id, u_int8_t pktsize) {
+ struct ggnet_conn *conn;
+ Egraph_Vertice *a, *b, *tmp;
+ int size, response;
+ u_int32_t color;
+
+ conn = ggnet_conn_find_by_id(_ggnet, id);
+ if (!conn)
+ return;
+
+ a = ggnet_node_usrdata_get(ggnet_conn_src_get(conn));
+ b = ggnet_node_usrdata_get(ggnet_conn_dst_get(conn));
+
+ GG_PKTDATA_SIZE_DECODE(pktsize, size, response);
+ if (response) {
+ tmp = a; a = b; b = tmp;
+ }
+ size = sqrt(size) / 2;
+ color = ((id+1) * 0x98765400) % 0xFFFFFF00;
+ egraph_vertice_send_blob(_egraph, a, b, size, color);
+}
+
+static void
+_conn_name(u_int32_t addr, u_int8_t pktsize, u_char *fqdn) {
+ struct ggnet_node *n;
+ struct in_addr ip;
+
+ ip.s_addr = addr;
+ n = ggnet_node_find(_ggnet, &ip);
+ if (!n)
+ return;
+
+ egraph_vertice_rename(_egraph, ggnet_node_usrdata_get(n), (char *)fqdn);
+}
+
+int
+_cb_packet(struct gg_client *cli, struct gg_packet *pkt)
+{
+ switch(pkt->type) {
+ case PACKET_NEWCONN:
+ printf(" type PACKET_NEWCONN\n");
+ printf(" newconn_id %d\n", pkt->newconn_id);
+ printf(" newconn_src %4x\n", pkt->newconn_src);
+ printf(" newconn_dst %4x\n", pkt->newconn_dst);
+ printf(" newconn_proto %d\n", pkt->newconn_proto);
+ printf(" newconn_size %d\n", pkt->newconn_size);
+
+ _conn_del(pkt->newconn_id); /* in case we missed a previous del */
+ _conn_add(pkt->newconn_id, pkt->newconn_src, pkt->newconn_dst,
+ pkt->newconn_proto, pkt->newconn_size);
+ break;
+
+ case PACKET_DELCONN:
+ printf(" type PACKET_DELCONN\n");
+ printf(" delconn_id %d\n", pkt->delconn_id);
+
+ _conn_del(pkt->delconn_id);
+ break;
+
+ case PACKET_DATA:
+ //printf(" type PACKET_DATA\n");
+ //printf(" data_connid %d\n", pkt->data_connid);
+ //printf(" data_size %d\n", pkt->data_size);
+
+ _conn_data(pkt->data_connid, pkt->data_size);
+ break;
+
+ case PACKET_NAME:
+ printf(" type PACKET_NAME\n");
+ printf(" name_addr %4x\n", pkt->name_addr);
+ printf(" name_len %d\n", pkt->name_len);
+ printf(" name_name_fqdn %s\n", pkt->name_fqdn);
+
+ _conn_name(pkt->name_addr, pkt->name_len, pkt->name_fqdn);
+ break;
+ }
+
+ return 0;
+}
+
+/* static void
+_cb_del_edges(void *data, Evas_Object *obj, void *event_info)
+{
+ if (!_egraph)
+ return;
+
+ // XXX ggnet_clear(_ggnet);
+ egraph_clear(_egraph);
+} */
+
+static void
+_cb_show_nodes(void *data, Evas_Object *obj, void *event_info)
+{
+ egraph_display_vertices_set(_egraph, elm_check_state_get(obj));
+}
+
+static void
+_cb_show_labels(void *data, Evas_Object *obj, void *event_info)
+{
+ if (!_egraph)
+ return;
+
+ egraph_display_names_set(_egraph, elm_check_state_get(obj));
+}
+
+static void
+_cb_show_edges(void *data, Evas_Object *obj, void *event_info)
+{
+ if (!_egraph)
+ return;
+
+ egraph_display_edges_set(_egraph, elm_check_state_get(obj));
+}
+
+static void
+_cb_layout_changed(void *data, Evas_Object *obj, void *event_info)
+{
+ Elm_Object_Item *it;
+ char *selected;
+ int layout;
+
+ if (!_egraph)
+ return;
+
+ it = event_info;
+ selected = elm_object_item_text_get(it);
+ layout = EGRAPH_LAYOUT_DEFAULT;
+ if (!strcmp(selected, "Kamada K."))
+ layout = EGRAPH_LAYOUT_KAMADAKAWAI;
+ else if (!strcmp(selected, "GraphOpt"))
+ layout = EGRAPH_LAYOUT_GRAPHOPT;
+ else if (!strcmp(selected, "Fruchterman R."))
+ layout = EGRAPH_LAYOUT_FRUCHTERMANREINGOLD;
+
+ egraph_layout_set(_egraph, layout);
+}
+
+
+static void
+_cb_on_done(void *data, Evas_Object *obj, void *event_info)
+{
+ elm_exit();
+}
+
+EAPI_MAIN int
+elm_main(int argc, char **argv)
+{
+ Evas_Object *win, *bg, *egraph, *panes;
+ Evas_Object *bx, *bx2, *ck, *sc, *seg_it, *lb;
+ Evas *evas;
+ struct gg_client *ggcli;
+ int retval = -1;
+
+ win = elm_win_add(NULL, "panes", ELM_WIN_BASIC);
+ evas = evas_object_evas_get(win);
+ elm_win_title_set(win, "Glouglou Network Map");
+ evas_object_smart_callback_add(win, "delete,request", _cb_on_done, NULL);
+
+ bg = elm_bg_add(win);
+ elm_win_resize_object_add(win, bg);
+ evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_color_set(bg, 0, 0, 0, 255);
+ evas_object_show(bg);
+
+ bx = elm_box_add(win);
+ elm_box_horizontal_set(bx, EINA_TRUE);
+ evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ elm_win_resize_object_add(win, bx);
+ evas_object_show(bx);
+
+ panes = elm_panes_add(win);
+ evas_object_size_hint_weight_set(panes, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(panes, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ elm_panes_content_right_size_set(panes, 0.0);
+ elm_box_pack_end(bx, panes);
+ evas_object_show(panes);
+
+ egraph = egraph_new(evas, 1);
+ egraph_layout_set(egraph, EGRAPH_LAYOUT_KAMADAKAWAI);
+ if (!egraph)
+ goto quit;
+ evas_object_size_hint_weight_set(egraph, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(egraph, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_show(egraph);
+ elm_object_part_content_set(panes, "left", egraph);
+
+ bx2 = elm_box_add(win);
+ //elm_box_horizontal_set(bx, EINA_FALSE);
+ evas_object_size_hint_weight_set(bx2, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(bx2, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ evas_object_color_set(bx2, 255, 255, 255, 100); // XXX broken
+ evas_object_show(bx2);
+
+ lb = elm_label_add(win);
+ elm_object_style_set(lb, "marker");
+ evas_object_color_set(lb, 255, 255, 255, 255);
+ elm_object_text_set(lb,
+ "Glouglou Network Mapper<br/>"
+ "<br/>"
+ "Enjoy !<br/>");
+ evas_object_show(lb);
+ elm_box_pack_end(bx2, lb);
+
+ lb = elm_label_add(win);
+ elm_object_text_set(lb, "<b>Layout</b>");
+ evas_object_size_hint_weight_set(lb, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+ evas_object_size_hint_align_set(lb, EVAS_HINT_FILL, EVAS_HINT_FILL);
+ elm_box_pack_end(bx2, lb);
+ sc = elm_segment_control_add(win);
+ //evas_object_size_hint_weight_set(sc, em->weight.w, em->weight.h);
+ //evas_object_size_hint_align_set(sc, em->align.x, em->align.y);
+ seg_it = elm_segment_control_item_add(sc, NULL, "GraphOpt");
+ elm_segment_control_item_selected_set(seg_it, EINA_FALSE);
+ seg_it = elm_segment_control_item_add(sc, NULL, "Kamada K.");
+ elm_segment_control_item_selected_set(seg_it, EINA_TRUE);
+ seg_it = elm_segment_control_item_add(sc, NULL, "Fruchterman R.");
+ elm_segment_control_item_selected_set(seg_it, EINA_FALSE);
+ evas_object_smart_callback_add(sc, "changed", _cb_layout_changed, NULL);
+ evas_object_show(sc);
+ elm_box_pack_end(bx2, sc);
+ ck = elm_check_add(win);
+ elm_object_text_set(ck, "Show Node");
+ elm_check_state_set(ck, EINA_TRUE);
+ evas_object_show(ck);
+ evas_object_smart_callback_add(ck, "changed", _cb_show_nodes, NULL);
+ elm_box_pack_end(bx2, ck);
+ ck = elm_check_add(win);
+ elm_object_text_set(ck, "Show Label");
+ elm_check_state_set(ck, EINA_TRUE);
+ evas_object_show(ck);
+ evas_object_smart_callback_add(ck, "changed", _cb_show_labels, NULL);
+ elm_box_pack_end(bx2, ck);
+ ck = elm_check_add(win);
+ elm_object_text_set(ck, "Show Edges");
+ elm_check_state_set(ck, EINA_TRUE);
+ evas_object_show(ck);
+ evas_object_smart_callback_add(ck, "changed", _cb_show_edges, NULL);
+ elm_box_pack_end(bx2, ck);
+
+ elm_object_part_content_set(panes, "right", bx2);
+
+ evas_object_resize(win, 150, 150); // XXX workaround elm sizing issue
+ evas_object_show(win);
+ evas_object_resize(win, 1300, 715);
+ evas_object_show(win);
+ _egraph = egraph;
+ _mainwin = win;
+
+ _ggnet = ggnet_new(GGNET_MANAGE_CONNID_FALSE);
+ if (!_ggnet)
+ goto quit;
+ ggnet_set_grouping(_ggnet, GGNET_GROUPING_TRUE,
+ _cb_ggnet_addgroup, _cb_ggnet_delgroup);
+ _ev_base = event_base_new();
+
+ ggcli = gg_client_connect(_ev_base, "127.0.0.1", GLOUGLOU_ANALY_DEFAULT_PORT,
+ NULL, _cb_packet, NULL);
+ if (!ggcli)
+ goto quit;
+
+ ecore_timer_add(0.1, _cb_ecore_libevent, NULL);
+
+ elm_run();
+ retval = 0;
+
+quit:
+ elm_shutdown();
+ return retval;
+}
+ELM_MAIN()
diff --git a/gg_sniff/Makefile b/gg_sniff/Makefile
new file mode 100644
index 0000000..297e8a5
--- /dev/null
+++ b/gg_sniff/Makefile
@@ -0,0 +1,34 @@
+CFLAGS += -Wall -O2
+LDFLAGS += -levent -lglouglou -lpcap
+
+BINARY = gg_sniff
+USER = _gg_sniff
+SOURCES = gg_sniff.c pcap.c
+OBJECTS = $(SOURCES:.c=.o)
+
+PREFIX = /usr/local
+BINDIR=$(PREFIX)/bin
+
+$(BINARY): $(OBJECTS)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $(BINARY) $(OBJECTS)
+
+install:
+ @echo "creating group and user $(USER)"
+ @uname -a |grep -i linux >/dev/null ;\
+ if [ $$? -eq 0 ]; then \
+ cmdg="groupadd -r $(USER)" ;\
+ cmdu="useradd -r -g $(USER) -d /var/empty/ -s /sbin/nologin $(USER)" ;\
+ else \
+ cmdg="" ;\
+ cmdu="useradd -g =uid -d /var/empty/ -s /sbin/nologin $(USER)" ;\
+ fi ;\
+ echo $$cmdg; $$($$cmdg) ;\
+ echo $$cmdu; $$($$cmdu) ;\
+ echo done
+ @echo "installation of $(BINARY)"
+ mkdir -p $(BINDIR)
+ install -m 0755 $(BINARY) $(BINDIR)
+
+clean:
+ rm -f $(BINARY) $(OBJECTS)
+
diff --git a/gg_sniff/README.txt b/gg_sniff/README.txt
new file mode 100644
index 0000000..8d71005
--- /dev/null
+++ b/gg_sniff/README.txt
@@ -0,0 +1,45 @@
+gg_sniff - glouglou probe client for network activity
+
+WARNING: Work in progress, don't expect this to work !
+
+Requirements
+============
+
+* libglouglou
+* libpcap
+* libevent2
+* libbsd (linux only)
+
+Installation
+============
+
+git clone git@meg:glouglou
+cd gg_sniff
+make && sudo make install
+
+Usage
+=====
+
+gg_sniff -i eth0
+
+Notes on architecture and security
+==================================
+
+gg_sniff must be run as root. It drops priviledges to user _gg_sniff and chroots
+into _gg_sniff user home (/var/empty).
+gg_sniff does:
+* configuration, glouglou server reporting, droppriv and chroot (gg_sniff.c)
+* read pcapfd to capture network traffic (pcap.c)
+* async DNS resolving using evdns (dns.c)
+
+Note that gg_sniff activates extra protections on libpcap file descriptor, by
+setting it to readonly, for now on OpenBSD only.
+It does so by reimplementing some of libpcap functions, see
+pcap.c my_pcap_open_live()
+
+Limitations
+===========
+
+If your dns server changes during gg_sniff execution, gg_sniff will keep using
+the old ones.
+The only fix is to restart the gg_sniff process.
diff --git a/gg_sniff/gg_sniff.c b/gg_sniff/gg_sniff.c
new file mode 100644
index 0000000..656c38c
--- /dev/null
+++ b/gg_sniff/gg_sniff.c
@@ -0,0 +1,146 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <netdb.h>
+#include <pcap.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <err.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+
+#include <libglouglou.h>
+#include <libggnet.h>
+#include "gg_sniff.h"
+
+#if defined(__OpenBSD__)
+#include "pcap-int.h"
+#endif
+
+#define GG_SNIFF_USER "_gg_sniff"
+#define GG_SNIFF_LOGFILE "/var/log/ggsniff.log"
+
+struct event_base *_ev_base = NULL;
+
+#if defined(__OpenBSD__)
+void __dead
+#else
+void
+#endif
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-hv] [-i interface] [ip [port]]", __progname);
+ exit(1);
+}
+
+static void
+sig_handler(int sig, short why, void *data)
+{
+ gg_log_info("got signal %d", sig);
+ if (sig == SIGINT || sig == SIGTERM)
+ if (_ev_base)
+ event_base_loopexit(_ev_base, NULL);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct gg_client *ggcli = NULL;
+ struct ggnet *net = NULL;
+ struct event *ev_sigint, *ev_sigterm, *ev_sigchld, *ev_sighup;
+ char ggserv_ip[30] = "127.0.0.1";
+ char *iface = NULL;
+ int ggserv_port = GLOUGLOU_PROBE_DEFAULT_PORT;
+ int pcap_init = 0;
+ int loglevel = 0;
+ int active = 0;
+ int retval = -1;
+ int op;
+
+ if (geteuid() != 0)
+ errx(1, "must be root");
+
+ while ((op = getopt(argc, argv, "ahi:v")) != -1) {
+ switch (op) {
+ case 'a':
+ active = 1;
+ break;
+ case 'h':
+ usage();
+ /* NOTREACHED */
+ case 'i':
+ iface = strndup(optarg, 30);
+ break;
+ case 'v':
+ loglevel++;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ switch (argc - optind) {
+ case 2: ggserv_port = atoi(argv[3]);
+ case 1: strncpy(ggserv_ip, argv[2], sizeof(ggserv_ip));
+ case 0:
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+
+ gg_log_init(GG_SNIFF_LOGFILE, loglevel);
+ gg_log_warn("Starting gg_sniff");
+
+ _ev_base = event_base_new();
+
+ net = ggnet_new(GGNET_MANAGE_CONNID_TRUE);
+ if (!net)
+ goto quit;
+ ggcli = gg_client_connect(_ev_base, ggserv_ip, ggserv_port, NULL, NULL, NULL);
+ if (!ggcli)
+ goto quit;
+ pcap_init = ggsniff_pcap_init(_ev_base, ggcli, net, iface, active);
+ if (!pcap_init)
+ goto quit;
+
+ ev_sigint = evsignal_new(_ev_base, SIGINT, sig_handler, NULL);
+ ev_sigterm = evsignal_new(_ev_base, SIGTERM, sig_handler, NULL);
+ ev_sigchld = evsignal_new(_ev_base, SIGCHLD, sig_handler, NULL);
+ ev_sighup = evsignal_new(_ev_base, SIGHUP, sig_handler, NULL);
+ evsignal_add(ev_sigint, NULL);
+ evsignal_add(ev_sigterm, NULL);
+ evsignal_add(ev_sigchld, NULL);
+ evsignal_add(ev_sighup, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ droppriv(GG_SNIFF_USER, 1, NULL);
+
+ gg_log_info("entering event loop");
+ event_base_dispatch(_ev_base);
+ retval = 0;
+
+quit:
+ if (pcap_init)
+ ggsniff_pcap_shutdown();
+ if (ggcli)
+ gg_client_disconnect(ggcli);
+ if (net)
+ ggnet_free(net);
+
+ gg_log_warn("exiting");
+
+ gg_log_shutdown();
+
+ exit(retval);
+}
diff --git a/gg_sniff/gg_sniff.h b/gg_sniff/gg_sniff.h
new file mode 100644
index 0000000..f848c88
--- /dev/null
+++ b/gg_sniff/gg_sniff.h
@@ -0,0 +1,5 @@
+/* pcap.c */
+
+int ggsniff_pcap_init(struct event_base *, struct gg_client *,
+ struct ggnet *, char *, int);
+void ggsniff_pcap_shutdown(void);
diff --git a/gg_sniff/pcap.c b/gg_sniff/pcap.c
new file mode 100644
index 0000000..fd9470f
--- /dev/null
+++ b/gg_sniff/pcap.c
@@ -0,0 +1,662 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <sys/time.h>
+
+#if !defined(__OpenBSD__)
+#define __FAVOR_BSD
+#endif
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <netinet/ip_icmp.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <err.h>
+
+#include <pcap.h>
+#if defined(__linux__)
+#include <pcap/sll.h>
+#endif
+#include <event.h>
+
+#include <libglouglou.h>
+#include <libggnet.h>
+
+#define PCAP_SNAPLEN 100
+#define PCAP_FILTER "not port 4430 and not port 4431 and not port 53"
+#define PCAP_COUNT 20
+#define PCAP_TO 300
+
+#define NODE_MAX_WITHOUT_TIMEOUT 1000
+#define NODE_TIMEOUT 60 // XXX conf ?
+#define CONN_TIMEOUT 30 // XXX conf ?
+#define CONN_TIMEOUT_UDP 30 // XXX conf ?
+#define CONN_TIMEOUT_ICMP 30 // XXX conf ?
+#define CONNTIMER 5 // XXX conf ?
+
+struct phandler {
+ pcap_handler f;
+ int type;
+};
+
+struct _cap_t {
+ pcap_t *pcap;
+ pcap_handler handler;
+ struct event *ev;
+ struct event *conntimer_ev;
+ struct timeval conntimer_tv;
+ struct gg_client *ggcli;
+ struct ggnet *net;
+ int pinvalid;
+ int ptruncated;
+};
+
+static pcap_t *my_pcap_open_live(const char *, int, int, int,
+ char *, u_int, u_int);
+static void ip_handle(struct ip *, const u_char *, u_int);
+static void conn_del(struct ggnet_conn *);
+static void ether_handle(struct ether_header *, const u_char *, u_int);
+static pcap_handler lookup_phandler(int);
+static void phandler_ether(u_char *,
+ const struct pcap_pkthdr *, const u_char *);
+#if defined(__OpenBSD__)
+static void phandler_loop(u_char *,
+ const struct pcap_pkthdr *, const u_char *);
+#endif
+#if defined(__linux__)
+static void phandler_sll(u_char *,
+ const struct pcap_pkthdr *, const u_char *);
+#endif
+static void cb_pcap(int, short, void *);
+static void cb_conntimer(int, short, void *);
+static void cb_nodename(struct ggnet *, struct ggnet_node *);
+
+static struct phandler phandlers[] = {
+ { phandler_ether, DLT_EN10MB },
+ { phandler_ether, DLT_IEEE802 },
+#if defined(__OpenBSD__)
+ { phandler_loop, DLT_LOOP },
+#endif
+#if defined(__linux__)
+ { phandler_sll, DLT_LINUX_SLL },
+#endif
+ { NULL, 0 },
+};
+
+static struct _cap_t _cap;
+
+int
+ggsniff_pcap_init(struct event_base *ev_base, struct gg_client *ggcli,
+ struct ggnet *net, char *iface, int active)
+{
+ char errbuf[PCAP_ERRBUF_SIZE];
+ struct bpf_program bprog;
+ pcap_t *pcap;
+
+#if defined(__OpenBSD__)
+ if (!iface)
+ err(1, "On OpenBSD you cannot listen on ANY interface");
+#endif
+ pcap = my_pcap_open_live(iface, PCAP_SNAPLEN, 1, PCAP_TO,
+ errbuf, -1, 0);
+ if (pcap == NULL)
+ err(1, "capture: pcap_open_live failed on interface %s\n"
+ "with snaplen %d : %s",
+ iface, PCAP_SNAPLEN, errbuf);
+ if (pcap_compile(pcap, &bprog, PCAP_FILTER, 0, 0) < 0)
+ err(1, "capture: pcap_compile failed with filter %s : %s",
+ PCAP_FILTER, pcap_geterr(pcap));
+ if (pcap_setfilter(pcap, &bprog) < 0)
+ err(1, "capture: pcap_setfilter failed : %s",
+ pcap_geterr(pcap));
+
+ _cap.pcap = pcap;
+ _cap.handler = lookup_phandler(pcap_datalink(pcap));
+ //_cap.tv.tv_sec = 0;
+ //_cap.tv.tv_usec = PCAP_TO;
+ fd_nonblock(pcap_fileno(pcap));
+ _cap.ev = event_new(ev_base, pcap_fileno(pcap), EV_READ|EV_PERSIST,
+ cb_pcap, NULL);
+ //event_add(_cap.ev, &_cap->tv);
+ event_add(_cap.ev, NULL);
+
+ _cap.conntimer_tv.tv_sec = CONNTIMER;
+ _cap.conntimer_tv.tv_usec = 0;
+ _cap.conntimer_ev = evtimer_new(ev_base, cb_conntimer, NULL);
+ if (evtimer_add(_cap.conntimer_ev, &_cap.conntimer_tv) == -1)
+ gg_log_fatal("user: event_add conntimer failed: %s", strerror(errno));
+
+ if (active)
+ ggnet_set_dns(net, 1, ev_base, cb_nodename);
+
+ _cap.ggcli = ggcli;
+ _cap.net = net;
+
+ return 1;
+}
+
+void
+ggsniff_pcap_shutdown(void)
+{
+ event_del(_cap.ev);
+ pcap_close(_cap.pcap);
+}
+
+/*
+ * reimplement pcap_open_live with more restrictions on the bpf fd :
+ * - open device read only
+ * - lock the fd
+ * based on OpenBSD tcpdump, privsep_pcap.c v1.16
+ */
+
+static pcap_t *
+my_pcap_open_live(const char *dev, int slen, int promisc, int to_ms,
+ char *ebuf, u_int dlt, u_int dirfilt)
+{
+#if defined(__OpenBSD__)
+ struct bpf_version bv;
+ u_int v;
+ pcap_t *p;
+ char bpf[sizeof "/dev/bpf0000000000"];
+ int fd, n = 0;
+ struct ifreq ifr;
+
+ p = xmalloc(sizeof(*p));
+ bzero(p, sizeof(*p));
+
+ /* priv part */
+
+ do {
+ snprintf(bpf, sizeof(bpf), "/dev/bpf%d", n++);
+ fd = open(bpf, O_RDONLY);
+ } while (fd < 0 && errno == EBUSY);
+ if (fd < 0)
+ return NULL;
+
+ v = 32768; /* XXX this should be a user-accessible hook */
+ ioctl(fd, BIOCSBLEN, &v);
+
+ strlcpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ if (ioctl(fd, BIOCSETIF, &ifr) < 0)
+ return NULL;
+
+ if (dlt != (u_int) -1 && ioctl(fd, BIOCSDLT, &dlt))
+ return NULL;
+
+ if (promisc)
+ /* this is allowed to fail */
+ ioctl(fd, BIOCPROMISC, NULL);
+ if (ioctl(fd, BIOCSDIRFILT, &dirfilt) < 0)
+ return NULL;
+
+ /* lock the descriptor */
+ if (ioctl(fd, BIOCLOCK, NULL) < 0)
+ return NULL;
+
+ /* end of priv part */
+
+ /* fd is locked, can only use 'safe' ioctls */
+ if (ioctl(fd, BIOCVERSION, &bv) < 0) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s",
+ pcap_strerror(errno));
+ return NULL;
+ }
+
+ if (bv.bv_major != BPF_MAJOR_VERSION ||
+ bv.bv_minor < BPF_MINOR_VERSION) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ "kernel bpf filter out of date");
+ return NULL;
+ }
+
+ p->fd = fd;
+ p->snapshot = slen;
+
+ /* Get the data link layer type. */
+ if (ioctl(fd, BIOCGDLT, &v) < 0) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s",
+ pcap_strerror(errno));
+ return NULL;
+ }
+#if _BSDI_VERSION - 0 >= 199510
+ /* The SLIP and PPP link layer header changed in BSD/OS 2.1 */
+ switch (v) {
+
+ case DLT_SLIP:
+ v = DLT_SLIP_BSDOS;
+ break;
+
+ case DLT_PPP:
+ v = DLT_PPP_BSDOS;
+ break;
+ }
+#endif
+ p->linktype = v;
+
+ /* XXX hack from tcpdump */
+ if (p->linktype == DLT_PFLOG && p->snapshot < 160)
+ p->snapshot = 160;
+
+ /* set timeout */
+ if (to_ms != 0) {
+ struct timeval to;
+ to.tv_sec = to_ms / 1000;
+ to.tv_usec = (to_ms * 1000) % 1000000;
+ if (ioctl(p->fd, BIOCSRTIMEOUT, &to) < 0) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT: %s",
+ pcap_strerror(errno));
+ return NULL;
+ }
+ }
+
+ if (ioctl(fd, BIOCGBLEN, &v) < 0) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s",
+ pcap_strerror(errno));
+ return NULL;
+ }
+ p->bufsize = v;
+ p->buffer = (u_char *)malloc(p->bufsize);
+ if (p->buffer == NULL) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
+ pcap_strerror(errno));
+ return NULL;
+ }
+ return p;
+#else /* defined(__OpenBSD__) */
+ return pcap_open_live(dev, slen, promisc, to_ms, ebuf);
+#endif
+}
+
+static void
+cb_pcap(int fd, short why, void *data)
+{
+ gg_log_tmp("cb_pcap");
+ pcap_dispatch(_cap.pcap, PCAP_COUNT, _cap.handler, NULL);
+
+ /* reschedule */
+ //if (event_add(&_cap->ev, &_cap->tv) == -1)
+ // gg_log_fatal("user: event_add pcap failed : %s", strerror(errno));
+}
+
+static void
+cb_conntimer(int fd, short why, void *data)
+{
+ struct ggnet_conn *c, *ctmp;
+ struct ggnet_node *n, *ntmp;
+ int i, to;
+
+ gg_log_debug("ev_timer");
+ ggnet_time_update(_cap.net, time(NULL));
+
+ i = 0;
+ LIST_FOREACH_SAFE(c, &_cap.net->conn_list, entry, ctmp) {
+ switch (c->proto) {
+ case IPPROTO_UDP:
+ to = CONN_TIMEOUT_UDP;
+ break;
+ case IPPROTO_ICMP:
+ to = CONN_TIMEOUT_ICMP;
+ break;
+ default:
+ to = CONN_TIMEOUT;
+ break;
+ }
+ if (_cap.net->time > c->lastseen + to)
+ conn_del(c);
+ else
+ i++;
+ }
+
+ if (_cap.net->node_count > NODE_MAX_WITHOUT_TIMEOUT) {
+ LIST_FOREACH_SAFE(n, &_cap.net->node_list, entry, ntmp) {
+ if (n->used == 0 &&
+ _cap.net->time > n->lastseen + NODE_TIMEOUT)
+ ggnet_node_del(_cap.net, n);
+ }
+ }
+
+ gg_log_debug("user: ev_timer leaving with %d active connections and %d active nodes", i, _cap.net->node_count);
+ if (evtimer_add(_cap.conntimer_ev, &_cap.conntimer_tv) == -1)
+ gg_log_fatal("user: event_add conntimer failed : %s", strerror(errno));
+}
+
+static void
+cb_nodename(struct ggnet *net, struct ggnet_node *n)
+{
+ struct gg_packet pkt;
+ int len;
+
+ len = strnlen(n->fqdn, GGNET_DNSNAME_MAX);
+ if (len > 0) {
+ pkt.ver = PACKET_VERSION;
+ pkt.type = PACKET_NAME;
+ pkt.name_addr = n->addr.s_addr;
+ pkt.name_len = len;
+ strncpy((char *)pkt.name_fqdn, n->fqdn, sizeof(pkt.name_fqdn));
+ gg_client_send(_cap.ggcli, &pkt);
+ }
+}
+
+/*
+ * Parse an IP packet and descide what to do with it.
+ * 'ip' is a pointer the the captured IP packet
+ * 'pend' is a pointer to the end of the captured IP packet
+ * 'wirelen' is the size of the IP packet off the wire
+ */
+#define NOTCAPTURED(v) ((u_char *)v > (u_char *)pend - sizeof(*v))
+#define NOTRECEIVED(v) (wirelen < sizeof(v))
+#define log_pinvalid(fmt, ...) \
+ gg_log_info("ggsniff pinvalid: " fmt, ##__VA_ARGS__)
+static void
+ip_handle(struct ip *ip, const u_char *pend, u_int wirelen)
+{
+ u_int len, ip_hlen, off;
+ const u_char *cp;
+ struct tcphdr *tcph;
+ struct udphdr *udph;
+ struct icmp *icmp;
+ struct in_addr src, dst;
+ u_int src_port, dst_port;
+ u_int proto, close;
+ int response;
+ struct ggnet_conn *conn;
+ struct gg_packet pkt;
+
+ if (NOTCAPTURED(ip)) {
+ log_pinvalid("user: ip truncated (ip %x pend %x sizeof(ip) %d",
+ ip, pend, sizeof(ip));
+ _cap.ptruncated++;
+ return;
+ }
+
+ if (ip->ip_v != IPVERSION) {
+ log_pinvalid("user: invalid ip version");
+ _cap.pinvalid++;
+ return;
+ }
+
+ len = ntohs(ip->ip_len);
+ if (wirelen < len) {
+ log_pinvalid("user: ip too small");
+ _cap.pinvalid++;
+ len = wirelen;
+ }
+
+ ip_hlen = ip->ip_hl * 4;
+ if (ip_hlen < sizeof(struct ip) || ip_hlen > len) {
+ log_pinvalid("user: ip_hlen invalid, %d", ip_hlen);
+ _cap.pinvalid++;
+ return;
+ }
+ len -= ip_hlen;
+
+ src.s_addr = ntohl(ip->ip_src.s_addr);
+ dst.s_addr = ntohl(ip->ip_dst.s_addr);
+ src_port = 0;
+ dst_port = 0;
+ proto = IPPROTO_IP;
+ close = 0;
+
+ off = ntohs(ip->ip_off);
+ if ((off & IP_OFFMASK) == 0) {
+ cp = (const u_char *)ip + ip_hlen;
+ switch (ip->ip_p) {
+
+ case IPPROTO_TCP:
+ tcph = (struct tcphdr *)cp;
+ if (NOTCAPTURED(&tcph->th_flags)) {
+ log_pinvalid("user: tcp truncated");
+ _cap.ptruncated++;
+ return;
+ }
+ if (NOTRECEIVED(*tcph)) {
+ log_pinvalid("user: tcp too small");
+ _cap.pinvalid++;
+ return;
+ }
+ src_port = ntohs(tcph->th_sport);
+ dst_port = ntohs(tcph->th_dport);
+ proto = IPPROTO_TCP;
+ if ((tcph->th_flags & TH_FIN) &&
+ (tcph->th_flags & TH_ACK))
+ close = 1;
+ break;
+
+ case IPPROTO_UDP:
+ udph = (struct udphdr *)cp;
+ if (NOTCAPTURED(&udph->uh_dport)) {
+ log_pinvalid("user: udp truncated, "
+ "ip %x, udph %x, uh_port %x, pend %x, ip_hlen %d",
+ ip, udph, &udph->uh_dport, pend, ip_hlen);
+ _cap.ptruncated++;
+ return;
+ }
+ if (NOTRECEIVED(*udph)) {
+ log_pinvalid("user: udp too small");
+ _cap.pinvalid++;
+ return;
+ }
+ src_port = ntohs(udph->uh_sport);
+ dst_port = ntohs(udph->uh_dport);
+ proto = IPPROTO_UDP;
+ break;
+
+ case IPPROTO_ICMP:
+ icmp = (struct icmp *)cp;
+ if (NOTRECEIVED(*icmp)) {
+ log_pinvalid("user: icmp too small");
+ _cap.pinvalid++;
+ return;
+ }
+ proto = IPPROTO_ICMP;
+ break;
+
+ default:
+ gg_log_warn("user: unknown ip protocol !");
+ break;
+ }
+ } else {
+ /*
+ * if this isn't the first frag, we're missing the
+ * next level protocol header.
+ */
+ gg_log_tmp("user: got a fragmented ip packet !");
+ }
+
+ pkt.ver = PACKET_VERSION;
+ conn = ggnet_conn_find(_cap.net, &src, src_port, &dst, dst_port,
+ proto, &response);
+ if (conn) {
+ if (!close) {
+ pkt.type = PACKET_DATA;
+ pkt.data_connid = conn->id;
+ GG_PKTDATA_SIZE_ENCODE(pkt.data_size, ip->ip_len, response);
+ gg_client_send(_cap.ggcli, &pkt);
+ ggnet_conn_data(_cap.net, conn, ip->ip_len, response);
+ } else {
+ conn_del(conn);
+ }
+ } else {
+ if (!close) {
+ conn = ggnet_conn_add(_cap.net, &src, src_port, &dst, dst_port, proto,
+ ip->ip_len, -1);
+ pkt.type = PACKET_NEWCONN;
+ pkt.newconn_id = conn->id;
+ pkt.newconn_src = src.s_addr;
+ pkt.newconn_dst = dst.s_addr;
+ pkt.newconn_proto = proto;
+ GG_PKTDATA_SIZE_ENCODE(pkt.newconn_size, ip->ip_len, 0);
+ gg_client_send(_cap.ggcli, &pkt);
+ } else {
+ gg_log_warn("user: captured connection close w/o open !");
+ }
+ }
+}
+
+static void
+conn_del(struct ggnet_conn *c)
+{
+ struct gg_packet pkt;
+
+ pkt.ver = PACKET_VERSION;
+ pkt.type = PACKET_DELCONN;
+ pkt.delconn_id = c->id;
+ gg_client_send(_cap.ggcli, &pkt);
+ ggnet_conn_del(_cap.net, c);
+}
+
+static void
+ether_handle(struct ether_header *ether, const u_char *pend, u_int wirelen)
+{
+ struct ip *ip;
+ u_short ether_type;
+
+ wirelen -= sizeof(struct ether_header);
+
+ ether_type = ntohs(ether->ether_type);
+ if (ether_type <= ETHERMTU)
+ gg_log_tmp("llc packet !");
+ else {
+ switch (ether_type) {
+ case ETHERTYPE_IP:
+ gg_log_tmp("loop family AF_LINK IP");
+ ip = (struct ip *)((u_char *)ether + sizeof(struct ether_header));
+ ip_handle(ip, pend, wirelen);
+ break;
+ default:
+ gg_log_tmp("loop non ip packet !");
+ break;
+ }
+ }
+}
+
+static pcap_handler
+lookup_phandler(int type)
+{
+ struct phandler *p;
+
+ for (p = phandlers; p->f; ++p) {
+ if (type == p->type)
+ return p->f;
+ }
+ err(1, "user: unknown data link type 0x%x", type);
+ /* NOTREACHED */
+ return NULL;
+}
+
+static void
+phandler_ether(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
+{
+ struct ether_header *ep;
+ const u_char *pend;
+ u_int len;
+
+ gg_log_debug("user: pcap handler ethernet !");
+
+ /* XXX here i assume that packets are alligned, which might not
+ * be the case when using dump files, says tcpdump sources */
+
+ ep = (struct ether_header *)p;
+ pend = p + h->caplen;
+ len = h->len - sizeof(struct ether_header);
+
+ ether_handle(ep, pend, len);
+}
+
+/*
+ * Handler for Linux cooked, used when capturing on any interface
+ */
+#if defined(__linux__)
+#define LINUX_SLL_P_802_3 0x0001 /* Novell 802.3 frames without 802.2 LLC header */
+#define LINUX_SLL_P_ETHERNET 0x0003 /* Ethernet */
+#define LINUX_SLL_P_802_2 0x0004 /* 802.2 frames (not D/I/X Ethernet) */
+#define LINUX_SLL_P_PPPHDLC 0x0007 /* PPP HDLC frames */
+#define LINUX_SLL_P_CAN 0x000C /* Controller Area Network */
+#define LINUX_SLL_P_IRDA_LAP 0x0017 /* IrDA Link Access Protocol */
+
+static void
+phandler_sll(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
+{
+ struct ip *ip;
+ struct ether_header *ep;
+ u_int family;
+ const u_char *pend;
+ u_int len;
+
+ gg_log_debug("user: phandler_sll !");
+
+ /* XXX here i assume that packets are alligned, which might not
+ * be the case when using dump files, says tcpdump sources */
+
+ pend = p + h->caplen;
+ len = h->len - SLL_HDR_LEN;
+
+ family = ntohs(p[14]);
+ if (family < 1536) { /* linux and wireshark are good for you */
+ switch (family) {
+ case LINUX_SLL_P_ETHERNET:
+ ep = (struct ether_header *)((u_char *)p + SLL_HDR_LEN);
+ ether_handle(ep, pend, len);
+ default:
+ gg_log_tmp("unknown family %x !", family);
+ break;
+ }
+ } else {
+ ip = (struct ip *)(p + SLL_HDR_LEN);
+ ip_handle(ip, pend, len);
+ }
+}
+#endif /* __linux__ */
+
+/*
+ * Handler for OpenBSD Loopback
+ */
+#if defined(__OpenBSD__)
+#define NULL_HDRLEN 4
+
+static void
+phandler_loop(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
+{
+ struct ip *ip;
+ struct ether_header *ep;
+ u_int family;
+ const u_char *pend;
+ u_int len;
+
+ gg_log_debug("user: pcap handler loop !");
+
+ /* XXX here i assume that packets are alligned, which might not
+ * be the case when using dump files, says tcpdump sources */
+
+ pend = p + h->caplen;
+ len = h->len - NULL_HDRLEN;
+
+ memcpy((char *)&family, (char *)p, sizeof(family));
+ family = ntohl(family);
+ switch (family) {
+ case AF_INET:
+ gg_log_tmp("loop family AF_INET");
+ ip = (struct ip *)(p + NULL_HDRLEN);
+ ip_handle(ip, pend, len);
+ break;
+ case AF_LINK:
+ ep = (struct ether_header *)((u_char *)p + NULL_HDRLEN);
+ ether_handle(ep, pend, len);
+ break;
+ default:
+ gg_log_tmp("unknown family %x !", family);
+ break;
+ }
+}
+#endif /* __OpenBSD__ */
diff --git a/gg_trackproc/Makefile b/gg_trackproc/Makefile
new file mode 100644
index 0000000..b49d9ba
--- /dev/null
+++ b/gg_trackproc/Makefile
@@ -0,0 +1,29 @@
+TARGET = gg_trackproc
+USER = _gg_trackproc
+
+PREFIX=/usr/local
+BINDIR=$(PREFIX)/bin
+
+all:
+ $(CC) -O2 -lglouglou -levent -Wall -o $(TARGET) $(TARGET).c
+
+install: $(TARGET)
+ @echo "creating group and user $(USER)"
+ @uname -a |grep -i linux >/dev/null ;\
+ if [ $$? -eq 0 ]; then \
+ cmdg="groupadd -r $(USER)" ;\
+ cmdu="useradd -r -g $(USER) -d /var/empty/ -s /sbin/nologin $(USER)" ;\
+ else \
+ cmdg="" ;\
+ cmdu="useradd -g =uid -d /var/empty/ -s /sbin/nologin $(USER)" ;\
+ fi ;\
+ echo $$cmdg; $$($$cmdg) ;\
+ echo $$cmdu; $$($$cmdu) ;\
+ echo done
+ @echo "installation of $(TARGET)"
+ mkdir -p $(BINDIR)
+ install -m 0755 $(TARGET) $(BINDIR)
+
+clean:
+ rm -f $(TARGET) $(OBJS) *~
+
diff --git a/gg_trackproc/README.txt b/gg_trackproc/README.txt
new file mode 100644
index 0000000..a289d75
--- /dev/null
+++ b/gg_trackproc/README.txt
@@ -0,0 +1,9 @@
+gg_trackproc - track process creation on linux
+
+trackproc example usage
+=======================
+
+$ sudo ./trackproc
+Connected to glougloud server !
+listening to netlink events...
+
diff --git a/gg_trackproc/gg_trackproc.c b/gg_trackproc/gg_trackproc.c
new file mode 100644
index 0000000..850c7a0
--- /dev/null
+++ b/gg_trackproc/gg_trackproc.c
@@ -0,0 +1,346 @@
+/*
+ * Track new process on Linux
+ * 2012 Laurent Ghigonis <laurent@p1sec.com>
+ *
+ * new version of trackproc.c
+ * that was inspired from http://bewareofgeek.livejournal.com/2945.html
+ * and pstree sources
+ */
+
+#include <sys/socket.h>
+#include <linux/netlink.h>
+#include <linux/connector.h>
+#include <linux/cn_proc.h>
+#include <signal.h>
+#include <err.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <event.h>
+#include <libglouglou.h>
+
+#define GG_TRACKPROC_USER "_gg_trackproc"
+#define CHROOT_PATH "/proc"
+#define PROC_BASE "/"
+
+struct gg_client *ggcli;
+struct event_base *ev_base;
+
+int loglevel = 0;
+
+static char*
+get_current_date() {
+ time_t ts;
+ struct tm *tm;
+ static char date_buf[64];
+
+ time(&ts);
+ tm = localtime(&ts);
+ strftime(date_buf, 64, "%b %d %X", tm);
+ return date_buf;
+}
+
+static int
+get_proc_infos(int pid, char **cmd) {
+ char path[128];
+ char readbuf[1024];
+ int size;
+ FILE *file;
+ char *comm, *tmpptr;
+
+ //if ((pid = (pid_t) atoi(de->d_name)) == 0) {
+ snprintf(path, sizeof(path), "%s/%d/stat", PROC_BASE, pid);
+ if ((file = fopen(path, "r")) == NULL) {
+ warn("Error: PID %d: cannot open %s\n", pid, path);
+ return -1;
+ }
+ size = fread(readbuf, 1, sizeof(readbuf), file);
+ if (ferror(file) != 0) {
+ warn("Error: PID %d: error reading %s", pid, path);
+ return -1;
+ }
+ readbuf[size] = 0;
+ /* commands may have spaces or ) in them.
+ * so don't trust anything from the ( to the last ) */
+ if (!(comm = strchr(readbuf, '('))
+ || !(tmpptr = strrchr(comm, ')'))) {
+ warn("Error: PID %d: could not parse %s", pid, path);
+ return -1;
+ }
+ ++comm;
+ *tmpptr = 0;
+ /* We now have readbuf with pid and cmd, and tmpptr+2
+ * with the rest */
+ fclose(file);
+
+ *cmd = comm;
+ return 0;
+}
+
+/*
+ * connect to netlink
+ * returns netlink socket, or -1 on error
+ */
+static int nl_connect()
+{
+ int rc;
+ int nl_sock;
+ struct sockaddr_nl sa_nl;
+
+ nl_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
+ if (nl_sock == -1) {
+ perror("socket");
+ return -1;
+ }
+
+ sa_nl.nl_family = AF_NETLINK;
+ sa_nl.nl_groups = CN_IDX_PROC;
+ sa_nl.nl_pid = getpid();
+
+ rc = bind(nl_sock, (struct sockaddr *)&sa_nl, sizeof(sa_nl));
+ if (rc == -1) {
+ perror("bind");
+ close(nl_sock);
+ return -1;
+ }
+
+ return nl_sock;
+}
+
+/*
+ * subscribe on proc events (process notifications)
+ */
+static int set_proc_ev_listen(int nl_sock, bool enable)
+{
+ int rc;
+ struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
+ struct nlmsghdr nl_hdr;
+ struct __attribute__ ((__packed__)) {
+ struct cn_msg cn_msg;
+ enum proc_cn_mcast_op cn_mcast;
+ };
+ } nlcn_msg;
+
+ memset(&nlcn_msg, 0, sizeof(nlcn_msg));
+ nlcn_msg.nl_hdr.nlmsg_len = sizeof(nlcn_msg);
+ nlcn_msg.nl_hdr.nlmsg_pid = getpid();
+ nlcn_msg.nl_hdr.nlmsg_type = NLMSG_DONE;
+
+ nlcn_msg.cn_msg.id.idx = CN_IDX_PROC;
+ nlcn_msg.cn_msg.id.val = CN_VAL_PROC;
+ nlcn_msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
+
+ nlcn_msg.cn_mcast = enable ? PROC_CN_MCAST_LISTEN : PROC_CN_MCAST_IGNORE;
+
+ rc = send(nl_sock, &nlcn_msg, sizeof(nlcn_msg), 0);
+ if (rc == -1) {
+ perror("netlink send");
+ return -1;
+ }
+
+ return 0;
+}
+
+void cb_nl(evutil_socket_t fd, short what, void *arg)
+{
+ int rc;
+ struct __attribute__ ((aligned(NLMSG_ALIGNTO))) {
+ struct nlmsghdr nl_hdr;
+ struct __attribute__ ((__packed__)) {
+ struct cn_msg cn_msg;
+ struct proc_event proc_ev;
+ };
+ } nlcn_msg;
+ static char *cmd;
+ struct gg_packet pkt;
+
+ rc = recv(fd, &nlcn_msg, sizeof(nlcn_msg), 0);
+ if (rc == 0) {
+ /* shutdown? */
+ if (loglevel)
+ printf("%s: stop listening to netlink events\n",
+ get_current_date());
+ event_base_loopexit(ev_base, NULL);
+ return;
+ } else if (rc == -1) {
+ if (errno == EINTR)
+ return;
+ perror("netlink recv");
+ event_base_loopexit(ev_base, NULL);
+ return;
+ }
+
+ pkt.type = 0;
+ pkt.ver = PACKET_VERSION;
+ /* see /usr/include/linux/cn_proc.h for struct proc_event */
+ switch (nlcn_msg.proc_ev.what) {
+ case PROC_EVENT_NONE:
+ /* XXX what is this event for ?
+ * if (loglevel)
+ * printf("%s: start listening to netlink events...\n",
+ * get_current_date()); */
+ break;
+
+ case PROC_EVENT_FORK:
+ get_proc_infos(nlcn_msg.proc_ev.event_data.fork.parent_pid,
+ &cmd);
+ if ((nlcn_msg.proc_ev.event_data.fork.child_tgid
+ != nlcn_msg.proc_ev.event_data.fork.child_pid)) {
+ if (loglevel)
+ printf("%s: fork %s %d -> %d tid %d\n",
+ get_current_date(),
+ cmd,
+ nlcn_msg.proc_ev.event_data.fork.parent_pid,
+ nlcn_msg.proc_ev.event_data.fork.child_pid,
+ nlcn_msg.proc_ev.event_data.fork.child_tgid);
+ } else {
+ if (loglevel)
+ printf("%s: fork %s %d -> %d\n",
+ get_current_date(),
+ cmd,
+ nlcn_msg.proc_ev.event_data.fork.parent_pid,
+ nlcn_msg.proc_ev.event_data.fork.child_pid);
+ }
+ pkt.type = PACKET_FORK;
+ pkt.fork_pid = nlcn_msg.proc_ev.event_data.fork.parent_pid;
+ pkt.fork_ppid = nlcn_msg.proc_ev.event_data.fork.parent_pid;
+ pkt.fork_cpid = nlcn_msg.proc_ev.event_data.fork.child_pid;
+ pkt.fork_tgid = nlcn_msg.proc_ev.event_data.fork.child_tgid;
+ break;
+
+ case PROC_EVENT_EXEC:
+ get_proc_infos(nlcn_msg.proc_ev.event_data.exec.process_pid,
+ &cmd);
+ if (loglevel)
+ printf("%s: exec %d -> %s\n",
+ get_current_date(),
+ nlcn_msg.proc_ev.event_data.exec.process_pid,
+ cmd);
+ pkt.type = PACKET_EXEC;
+ pkt.exec_pid = nlcn_msg.proc_ev.event_data.fork.parent_pid;
+ pkt.exec_cmdlen = strnlen(cmd, GG_PKTARG_MAX);
+ strncpy((char *)pkt.exec_cmd, cmd, GG_PKTARG_MAX);
+ break;
+#if 0
+ case PROC_EVENT_UID:
+ if (loglevel)
+ printf("uid change: tid=%d pid=%d from %d to %d\n",
+ nlcn_msg.proc_ev.event_data.id.process_pid,
+ nlcn_msg.proc_ev.event_data.id.process_tgid,
+ nlcn_msg.proc_ev.event_data.id.r.ruid,
+ nlcn_msg.proc_ev.event_data.id.e.euid);
+ break;
+
+ case PROC_EVENT_GID:
+ if (loglevel)
+ printf("gid change: tid=%d pid=%d from %d to %d\n",
+ nlcn_msg.proc_ev.event_data.id.process_pid,
+ nlcn_msg.proc_ev.event_data.id.process_tgid,
+ nlcn_msg.proc_ev.event_data.id.r.rgid,
+ nlcn_msg.proc_ev.event_data.id.e.egid);
+ break;
+#endif
+
+ case PROC_EVENT_EXIT:
+ if (loglevel)
+ printf("exit: tid=%d pid=%d exit_code=%d\n",
+ nlcn_msg.proc_ev.event_data.exit.process_pid,
+ nlcn_msg.proc_ev.event_data.exit.process_tgid,
+ nlcn_msg.proc_ev.event_data.exit.exit_code);
+ pkt.type = PACKET_EXIT;
+ pkt.exit_pid = nlcn_msg.proc_ev.event_data.exit.process_pid;
+ pkt.exit_tgid = nlcn_msg.proc_ev.event_data.exit.process_tgid;
+ pkt.exit_ecode = nlcn_msg.proc_ev.event_data.exit.exit_code;
+ break;
+
+ default:
+ if (loglevel)
+ printf("unhandled proc event\n");
+ break;
+ }
+
+ if (pkt.type != 0)
+ gg_client_send(ggcli, &pkt);
+}
+
+static void on_sigint(int unused)
+{
+ event_base_loopexit(ev_base, NULL);
+}
+
+#if defined(__OPENBSD__)
+void __dead
+#else
+void
+#endif
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-vi]", __progname);
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ struct event *ev_nl;
+ int nl_sock;
+ int rc = EXIT_SUCCESS;
+ int op;
+
+ while ((op = getopt(argc, argv, "hv")) != -1) {
+ switch (op) {
+ case 'h':
+ usage();
+ /* NOTREACHED */
+ case 'v':
+ loglevel++;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ if (geteuid() != 0)
+ errx(1, "must be root");
+
+ signal(SIGINT, &on_sigint);
+ siginterrupt(SIGINT, true);
+
+ gg_verbosity_set(loglevel);
+
+ ev_base = event_base_new();
+ ggcli = gg_client_connect(ev_base, "127.0.0.1", GLOUGLOU_PROBE_DEFAULT_PORT,
+ NULL, NULL, NULL);
+
+ nl_sock = nl_connect();
+ if (nl_sock == -1)
+ exit(EXIT_FAILURE);
+ rc = set_proc_ev_listen(nl_sock, true);
+ if (rc == -1) {
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+ rc = set_proc_ev_listen(nl_sock, true);
+ if (rc == -1) {
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+ ev_nl = event_new(ev_base, nl_sock, EV_READ|EV_PERSIST, cb_nl, NULL);
+ event_add(ev_nl, NULL);
+
+ droppriv(GG_TRACKPROC_USER, 1, CHROOT_PATH);
+
+ event_base_dispatch(ev_base);
+
+ set_proc_ev_listen(nl_sock, false);
+
+out:
+ close(nl_sock);
+ exit(rc);
+}
diff --git a/glougloud/Makefile b/glougloud/Makefile
new file mode 100644
index 0000000..5308f40
--- /dev/null
+++ b/glougloud/Makefile
@@ -0,0 +1,33 @@
+PROG = glougloud
+OBJS = glougloud.o
+CFLAGS+=-Wall -g
+LDFLAGS=-levent -lglouglou
+USER = _glougloud
+
+PREFIX=/usr/local
+BINDIR=$(PREFIX)/bin
+
+all:
+ make $(OBJS)
+ $(CC) $(OBJS) -o $(PROG) $(LDFLAGS)
+
+install: $(PROG)
+ @echo "creating group and user $(USER)"
+ @uname -a |grep -i linux >/dev/null ;\
+ if [ $$? -eq 0 ]; then \
+ cmdg="groupadd -r $(USER)" ;\
+ cmdu="useradd -r -g $(USER) -d /var/empty/ -s /sbin/nologin $(USER)" ;\
+ else \
+ cmdg="" ;\
+ cmdu="useradd -g =uid -d /var/empty/ -s /sbin/nologin $(USER)" ;\
+ fi ;\
+ echo $$cmdg; $$($$cmdg) ;\
+ echo $$cmdu; $$($$cmdu) ;\
+ echo done
+ @echo "installation of $(PROG)"
+ mkdir -p $(BINDIR)
+ install -m 0755 $(PROG) $(BINDIR)
+
+clean:
+ rm -f $(PROG) $(OBJS) *~
+
diff --git a/glougloud/README.txt b/glougloud/README.txt
new file mode 100644
index 0000000..23c0cd3
--- /dev/null
+++ b/glougloud/README.txt
@@ -0,0 +1,39 @@
+glougloud - glouglou daemon, for network traffic visualisation in real time
+
+glougloud listen on port 4430 for probes and port 4431 for clients.
+glougloud probes can be network monitoring probes, system process tracker
+probes...
+glougloud clients can be graphical visualisation clients, command line monitoring
+clients ...
+
+Requirements
+============
+
+* libglouglou
+
+Known to work on OpenBSD 5.1 and Linux 3.4
+
+Installation
+============
+
+git clone git@meg:glouglou
+
+sudo useradd -d /var/empty/ -s /sbin/nologin _glougloud
+
+make && sudo make install
+
+Usage
+=====
+
+* Run the daemon
+
+sudo glougloud
+
+It logs to /var/log/glougloud.
+For the moment it monitors on lo0 interface.
+
+* Connect manualy to the daemon
+
+nc -vvv -u 127.0.0.1 4431 |hexdump -C
+
+You get a copy of all what the probes send
diff --git a/glougloud/glougloud.c b/glougloud/glougloud.c
new file mode 100644
index 0000000..4c08dec
--- /dev/null
+++ b/glougloud/glougloud.c
@@ -0,0 +1,130 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <err.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+
+#include <libglouglou.h>
+
+#define GLOUGLOUD_USER "_glougloud"
+
+struct gg_server *ggserv_probes;
+struct gg_server *ggserv_analysers;
+struct event_base *ev_base;
+
+int prb_handle_conn(struct gg_server *, struct gg_user *);
+int cli_handle_conn(struct gg_server *, struct gg_user *);
+int prb_handle_packet(struct gg_server *, struct gg_user *, struct gg_packet *);
+int cli_handle_packet(struct gg_server *, struct gg_user *, struct gg_packet *);
+
+#if defined(__OPENBSD__)
+void __dead
+#else
+void
+#endif
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-vi]", __progname);
+ exit(1);
+}
+
+static void
+sig_handler(int sig, short why, void *data)
+{
+ printf("glougloud: got signal %d\n", sig);
+ if (sig == SIGINT || sig == SIGTERM)
+ event_base_loopexit(ev_base, NULL);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct event *ev_sigint, *ev_sigterm, *ev_sigchld, *ev_sighup;
+ int loglevel = 0;
+ int op;
+
+ while ((op = getopt(argc, argv, "hv")) != -1) {
+ switch (op) {
+ case 'h':
+ usage();
+ /* NOTREACHED */
+ case 'v':
+ loglevel++;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ if (geteuid() != 0)
+ errx(1, "must be root");
+
+ droppriv(GLOUGLOUD_USER, 1, NULL);
+
+ gg_verbosity_set(loglevel);
+
+ ev_base = event_base_new();
+
+ ev_sigint = evsignal_new(ev_base, SIGINT, sig_handler, NULL);
+ ev_sigterm = evsignal_new(ev_base, SIGTERM, sig_handler, NULL);
+ ev_sigchld = evsignal_new(ev_base, SIGCHLD, sig_handler, NULL);
+ ev_sighup = evsignal_new(ev_base, SIGHUP, sig_handler, NULL);
+ evsignal_add(ev_sigint, NULL);
+ evsignal_add(ev_sigterm, NULL);
+ evsignal_add(ev_sigchld, NULL);
+ evsignal_add(ev_sighup, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ ggserv_probes = gg_server_start(ev_base, "127.0.0.1", GLOUGLOU_PROBE_DEFAULT_PORT,
+ prb_handle_conn, prb_handle_packet, NULL);
+ ggserv_analysers = gg_server_start(ev_base, "127.0.0.1", GLOUGLOU_ANALY_DEFAULT_PORT,
+ cli_handle_conn, cli_handle_packet, NULL);
+
+ event_base_dispatch(ev_base);
+
+ gg_server_stop(ggserv_probes);
+ gg_server_stop(ggserv_analysers);
+
+ return 0;
+}
+
+int
+prb_handle_conn(struct gg_server *srv, struct gg_user *usr)
+{
+ return 0;
+}
+
+int
+cli_handle_conn(struct gg_server *srv, struct gg_user *usr)
+{
+ return 0;
+}
+
+int
+prb_handle_packet(struct gg_server *srv, struct gg_user *usr, struct gg_packet *pkt)
+{
+ gg_server_send(ggserv_analysers, pkt, NULL);
+ return 0;
+}
+
+int
+cli_handle_packet(struct gg_server *srv, struct gg_user *usr, struct gg_packet *pkt)
+{
+ gg_server_send(ggserv_probes, pkt, NULL);
+ return 0;
+}
+
diff --git a/libglouglou/Makefile b/libglouglou/Makefile
new file mode 100644
index 0000000..0343e6f
--- /dev/null
+++ b/libglouglou/Makefile
@@ -0,0 +1,26 @@
+CFLAGS += -Wall -O2 -fPIC -shared
+
+PREFIX=/usr/local
+INCLUDEDIR=$(PREFIX)/include
+LIBDIR=$(PREFIX)/lib
+LIBNAME=libglouglou
+
+TARGET = ${LIBNAME}.so
+SOURCES = libglouglou.c sendbuf.c utils.c libggnet.c libggnet_dns.c
+HEADERS = libglouglou.h libggnet.h libggnet_dns.h
+OBJECTS = $(SOURCES:.c=.o)
+
+all: $(TARGET)
+
+$(TARGET): $(OBJECTS)
+ $(CC) $(CFLAGS) -o $(TARGET) $(OBJECTS)
+
+install:
+ @echo "installation of $(LIBNAME)"
+ mkdir -p $(LIBDIR)
+ mkdir -p $(INCLUDEDIR)
+ install -m 0644 $(TARGET) $(LIBDIR)
+ install -m 0644 $(HEADERS) $(INCLUDEDIR)
+
+clean:
+ rm -f $(TARGET) $(OBJECTS)
diff --git a/libglouglou/README.txt b/libglouglou/README.txt
new file mode 100644
index 0000000..003db0a
--- /dev/null
+++ b/libglouglou/README.txt
@@ -0,0 +1,20 @@
+libglouglou - underlaying library for glougloud and glouglou clients
+
+Requirements
+============
+
+* libevent2
+* libbsd (linux only)
+
+libggnet
+========
+
+libglouglou includes libggnet a helper to keep track of network nodes and
+connections.
+
+sendbuf
+=======
+
+libglouglou includes a send buffer implementation to allow concatenation of
+small UDP packets to send larger packets on the wire.
+It can be reused independently by using sendbuf.c and sendbuf.h.
diff --git a/libglouglou/examples/Makefile b/libglouglou/examples/Makefile
new file mode 100644
index 0000000..5dc4bd5
--- /dev/null
+++ b/libglouglou/examples/Makefile
@@ -0,0 +1,10 @@
+CFLAGS += -Wall -g -levent -lglouglou
+
+SOURCES = $(shell echo *.c)
+OBJECTS = $(SOURCES:.c=.o)
+TARGETS = $(SOURCES:.c=)
+
+all: $(TARGETS)
+
+clean:
+ rm -f $(TARGETS) $(OBJECTS)
diff --git a/libglouglou/examples/dnsreverse.c b/libglouglou/examples/dnsreverse.c
new file mode 100644
index 0000000..5111391
--- /dev/null
+++ b/libglouglou/examples/dnsreverse.c
@@ -0,0 +1,53 @@
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <libggnet_dns.h>
+
+static void _cb_dns(struct in_addr *, char *, void *);
+
+struct event_base *_ev_base;
+char *_name = NULL;
+int _answer = 0;
+
+int
+main(int argc, char *argv[])
+{
+ struct ggnet_dns *ggdns;
+ struct in_addr ip;
+ struct timeval tv;
+
+ if (argc < 2) {
+ printf("usage: dnsreverse <fqdn>\n");
+ exit(1);
+ }
+ inet_aton(argv[1], &ip);
+ ip.s_addr = ntohl(ip.s_addr);
+
+ bzero(&tv, sizeof(struct timeval));
+ tv.tv_sec = 5;
+
+ _ev_base = event_base_new();
+
+ ggdns = ggnet_dns_new(_ev_base);
+ ggnet_dns_reverse(ggdns, &ip, _cb_dns, NULL);
+
+ event_base_loopexit(_ev_base, &tv);
+ event_base_dispatch(_ev_base);
+
+ if (_answer)
+ printf("%x: %s\n", ip.s_addr, _name);
+ else
+ printf("no answer\n");
+ return 0;
+}
+
+static void
+_cb_dns(struct in_addr *ip, char *name, void *data)
+{
+ _name = name;
+ _answer = 1;
+ event_base_loopexit(_ev_base, NULL);
+}
diff --git a/libglouglou/libggnet.c b/libglouglou/libggnet.c
new file mode 100644
index 0000000..10c2cf5
--- /dev/null
+++ b/libglouglou/libggnet.c
@@ -0,0 +1,496 @@
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "libggnet.h"
+#include "libggnet_dns.h"
+
+static struct ggnet_nodegroup *nodegroup_add(struct ggnet *,
+ enum ggnet_grouptype, void *,
+ struct ggnet_nodegroup *);
+static void nodegroup_del(struct ggnet *, struct ggnet_nodegroup *);
+static struct ggnet_nodegroup *nodegroup_find(struct ggnet *,
+ enum ggnet_grouptype type,
+ void *param);
+static void nodegroup_set(struct ggnet *, struct ggnet_node *);
+static void nodegroup_unset(struct ggnet *, struct ggnet_node *);
+static void _cb_dns_reverse(struct in_addr *, char *, void *);
+
+struct ggnet *
+ggnet_new(int manage_connid)
+{
+ struct ggnet *net;
+ int i;
+
+ net = calloc(1, sizeof(struct ggnet));
+ if (!net) {
+ printf("could not allocate ggnet\n");
+ exit(1);
+ }
+ net->manage_connid = manage_connid;
+ if (manage_connid)
+ for (i=0; i<GGNET_CONN_FREEIDS_COUNT-1; i++)
+ net->conn_freeids[i] = i;
+
+ return net;
+}
+
+/**
+ * Sets grouping option
+ *
+ * Note: Must be set before any use of ggnet
+ */
+void
+ggnet_set_grouping(struct ggnet *net, int set,
+ void (*cb_addgroup)(struct ggnet *, struct ggnet_nodegroup *, struct ggnet_nodegroup *),
+ void (*cb_delgroup)(struct ggnet *, struct ggnet_nodegroup *))
+{
+ net->use_grouping = set;
+ net->cb_addgroup = cb_addgroup;
+ net->cb_delgroup = cb_delgroup;
+}
+
+void
+ggnet_set_dns(struct ggnet *net, int set,
+ struct event_base *ev_base,
+ void (*cb_nodename)(struct ggnet *, struct ggnet_node *))
+{
+ net->use_dns = set;
+ if (set) {
+ net->ggdns = ggnet_dns_new(ev_base);
+ net->cb_nodename = cb_nodename;
+ }
+ else if (net->use_dns)
+ ggnet_dns_free(net->ggdns);
+}
+
+void
+ggnet_debug_set(struct ggnet *net, int set)
+{
+ net->debug = set;
+}
+
+void
+ggnet_free(struct ggnet *net)
+{
+ struct ggnet_conn *c, *ctmp;
+ struct ggnet_node *n, *ntmp;
+ struct ggnet_nodegroup *g, *gtmp;
+
+ LIST_FOREACH_SAFE(c, &net->conn_list, entry, ctmp)
+ ggnet_conn_del(net, c);
+ LIST_FOREACH_SAFE(n, &net->node_list, entry, ntmp)
+ ggnet_node_del(net, n);
+ LIST_FOREACH_SAFE(g, &net->group_list, entry, gtmp)
+ nodegroup_del(net, g);
+ free(net);
+}
+
+struct ggnet_node *
+ggnet_node_add(struct ggnet *net, struct in_addr *addr)
+{
+ struct ggnet_node *n;
+
+ if (net->debug)
+ printf("ggnet_node_add\n");
+
+ n = calloc(1, sizeof(struct ggnet_node));
+ if (!n) {
+ printf("could not allocate node\n");
+ exit(1);
+ }
+ n->net = net;
+ n->addr.s_addr = addr->s_addr;
+ n->lastseen = net->time;
+ LIST_INSERT_HEAD(&net->node_list, n, entry);
+ net->node_count++;
+ if (net->use_grouping)
+ nodegroup_set(net, n);
+ if (net->use_dns)
+ n->dns_req = ggnet_dns_reverse(net->ggdns, &n->addr, _cb_dns_reverse, n);
+
+ return n;
+}
+
+void
+ggnet_node_del(struct ggnet *net, struct ggnet_node *n)
+{
+ if (n->used) {
+ printf("FATAL: ggnet_node_del: trying to remove a used node !\n");
+ exit(1);
+ }
+ if (net->debug)
+ printf("ggnet_node_del: ggnet_node_del\n");
+
+ if (net->use_grouping)
+ nodegroup_unset(net, n);
+ if (n->dns_req)
+ ggnet_dns_cancel(net->ggdns, n->dns_req);
+
+ LIST_REMOVE(n, entry);
+ free(n);
+ net->node_count--;
+}
+
+struct ggnet_node *
+ggnet_node_find(struct ggnet *net, struct in_addr *remote)
+{
+ struct ggnet_node *n;
+
+ LIST_FOREACH(n, &net->node_list, entry)
+ if (n->addr.s_addr == remote->s_addr)
+ return n;
+ return NULL;
+}
+
+void *
+ggnet_node_usrdata_get(struct ggnet_node *n)
+{
+ return n->usrdata;
+}
+
+void
+ggnet_node_usrdata_set(struct ggnet_node *n, void *usrdata)
+{
+ n->usrdata = usrdata;
+}
+
+struct ggnet_nodegroup *
+ggnet_node_group_get(struct ggnet_node *n)
+{
+ return n->group;
+}
+
+struct ggnet_conn *
+ggnet_conn_add(struct ggnet *net, struct in_addr *src, int src_port,
+ struct in_addr *dst, int dst_port, int proto, int size,
+ int given_id)
+{
+ struct ggnet_conn *c;
+ struct ggnet_node *srcnode;
+ struct ggnet_node *dstnode;
+ int id;
+
+ if (net->debug)
+ printf("ggnet_conn_add, %x:%d->%x:%d %d [%d]\n",
+ src->s_addr, src_port, dst->s_addr, dst_port, proto, size);
+
+ if (net->manage_connid) {
+ if (net->conn_freeids_ptr == GGNET_CONN_FREEIDS_COUNT) {
+ printf("ggnet_conn_add: ERROR: out of connection identifiers !");
+ return NULL;
+ }
+ id = net->conn_freeids[net->conn_freeids_ptr];
+ net->conn_freeids_ptr++;
+ } else {
+ id = given_id;
+ }
+
+ srcnode = ggnet_node_find(net, src);
+ if (!srcnode)
+ srcnode = ggnet_node_add(net, src);
+ srcnode->used++;
+ if (srcnode->group)
+ srcnode->group->conn_count++;
+ dstnode = ggnet_node_find(net, dst);
+ if (!dstnode)
+ dstnode = ggnet_node_add(net, dst);
+ dstnode->used++;
+ if (dstnode->group)
+ dstnode->group->conn_count++;
+
+ c = malloc(sizeof(struct ggnet_conn));
+ if (!c) {
+ printf("could not allocate conn\n");
+ exit(1);
+ }
+ c->id = id;
+ c->state = CONNSTATE_ESTABLISHED;
+ c->src = srcnode;
+ c->src_port = src_port;
+ c->dst = dstnode;
+ c->dst_port = dst_port;
+ c->proto = proto;
+ c->size = size;
+ c->size_response = 0;
+ c->lastseen = net->time;
+ c->usrdata = NULL;
+ LIST_INSERT_HEAD(&net->conn_list, c, entry);
+
+ return c;
+}
+
+void
+ggnet_conn_data(struct ggnet *net, struct ggnet_conn *c, int size, int response)
+{
+ if (net->debug)
+ printf("ggnet_conn_data\n");
+
+ if (!response)
+ c->size = c->size + size;
+ else
+ c->size_response = c->size_response + size;
+ c->lastseen = net->time;
+ c->src->lastseen = net->time;
+ c->dst->lastseen = net->time;
+}
+
+void
+ggnet_conn_del(struct ggnet *net, struct ggnet_conn *c)
+{
+ if (net->debug)
+ printf("ggnet_conn_del\n");
+
+ /* UNSUPPORTED
+ if (c->proto == IPPROTO_TCP) {
+ switch (c->state) {
+ case CONNSTATE_ESTABLISHED:
+ c->state = CONNSTATE_TCPFIN;
+ return;
+ case CONNSTATE_TCPFIN:
+ c->state = CONNSTATE_TCPFIN2;
+ return;
+ case CONNSTATE_TCPFIN2:
+ break;
+ }
+ }
+ */
+
+ if (net->manage_connid) {
+ if (net->conn_freeids_ptr == 0) {
+ printf("FATAL: net->conn_freeids_ptr == 0\n");
+ exit(1);
+ }
+ net->conn_freeids_ptr--;
+ net->conn_freeids[net->conn_freeids_ptr] = c->id;
+ }
+
+ c->src->used--;
+ if (c->src->group)
+ c->src->group->conn_count--;
+ c->dst->used--;
+ if (c->dst->group)
+ c->dst->group->conn_count--;
+
+ LIST_REMOVE(c, entry);
+ free(c);
+}
+
+struct ggnet_conn *
+ggnet_conn_find(struct ggnet *net, struct in_addr *src, int src_port,
+ struct in_addr *dst, int dst_port, int proto, int *response)
+{
+ struct ggnet_conn *c;
+
+ LIST_FOREACH(c, &net->conn_list, entry) {
+ if (((c->src->addr.s_addr == src->s_addr &&
+ c->src_port == src_port &&
+ c->dst->addr.s_addr == dst->s_addr &&
+ c->dst_port == dst_port) ||
+ (c->src->addr.s_addr == dst->s_addr &&
+ c->src_port == dst_port &&
+ c->dst->addr.s_addr == src->s_addr &&
+ c->dst_port == src_port)) &&
+ c->proto == proto) {
+ if (c->src->addr.s_addr == src->s_addr)
+ *response = 0;
+ else
+ *response = 1;
+ return c;
+ }
+ }
+ return NULL;
+}
+
+struct ggnet_conn *
+ggnet_conn_find_by_id(struct ggnet *net, int id)
+{
+ struct ggnet_conn *c;
+
+ LIST_FOREACH(c, &net->conn_list, entry)
+ if (c->id == id)
+ return c;
+ return NULL;
+}
+
+struct ggnet_conn *
+ggnet_conn_find_by_node(struct ggnet *net,
+ struct ggnet_node *a, struct ggnet_node *b)
+{
+ struct ggnet_conn *c;
+
+ LIST_FOREACH(c, &net->conn_list, entry)
+ if ((c->src == a && c->dst == b) ||
+ (c->src == b && c->dst == a))
+ return c;
+ return NULL;
+}
+
+void *
+ggnet_conn_usrdata_get(struct ggnet_conn *c)
+{
+ return c->usrdata;
+}
+
+void
+ggnet_conn_usrdata_set(struct ggnet_conn *c, void *usrdata)
+{
+ c->usrdata = usrdata;
+}
+
+void *
+ggnet_conn_src_get(struct ggnet_conn *c)
+{
+ return c->src;
+}
+
+void *
+ggnet_conn_dst_get(struct ggnet_conn *c)
+{
+ return c->dst;
+}
+
+void *
+ggnet_nodegroup_usrdata_get(struct ggnet_nodegroup *g)
+{
+ return g->usrdata;
+}
+
+void
+ggnet_nodegroup_usrdata_set(struct ggnet_nodegroup *g, void *usrdata)
+{
+ g->usrdata = usrdata;
+}
+
+void
+ggnet_time_update(struct ggnet *net, time_t time)
+{
+ net->time = time;
+}
+
+static struct ggnet_nodegroup *
+nodegroup_add(struct ggnet *net, enum ggnet_grouptype type, void *param,
+ struct ggnet_nodegroup *parent)
+{
+ struct ggnet_nodegroup *group;
+
+ group = calloc(1, sizeof(struct ggnet_nodegroup));
+ if (!group) {
+ printf("could not allocate nodegroup\n");
+ exit(1);
+ }
+ group->type = type;
+ if (parent) {
+ group->parent = parent;
+ parent->child_groups_count++;
+ }
+
+ switch (type) {
+ case GROUP_ADDRESS:
+ group->addr.s_addr = *(u_int *)param;
+ break;
+ case GROUP_WHOIS:
+ case GROUP_DNS:
+ case GROUP_ROUTE:
+ /* UNSUPPORTED */
+ free(group);
+ return NULL;
+ }
+
+ LIST_INSERT_HEAD(&net->group_list, group, entry);
+ net->cb_addgroup(net, group, parent);
+
+ return group;
+}
+
+static void
+nodegroup_del(struct ggnet *net, struct ggnet_nodegroup *group)
+{
+ if (group->node_count || group->conn_count) {
+ printf("FATAL: ggnet_nodegroup_del: trying to remove a used group !\n");
+ exit(1);
+ }
+ if (net->debug)
+ printf("ggnet_nodegroup_del\n");
+
+ net->cb_delgroup(net, group);
+ if (group->parent) {
+ group->parent->child_groups_count--;
+ if (group->parent->child_groups_count == 0)
+ nodegroup_del(net, group->parent);
+ }
+ LIST_REMOVE(group, entry);
+ free(group);
+}
+
+static struct ggnet_nodegroup *
+nodegroup_find(struct ggnet *net, enum ggnet_grouptype type, void *param)
+{
+ struct ggnet_nodegroup *g;
+
+ LIST_FOREACH(g, &net->group_list, entry) {
+ switch (g->type) {
+ case GROUP_ADDRESS:
+ if (g->addr.s_addr == *(u_int *)param)
+ return g;
+ case GROUP_WHOIS:
+ case GROUP_DNS:
+ case GROUP_ROUTE:
+ /* UNSUPPORTED */
+ break;
+ }
+ }
+ return NULL;
+}
+
+static void
+nodegroup_set(struct ggnet *net, struct ggnet_node *n)
+{
+ struct ggnet_nodegroup *group, *groot, *gclassb;
+ u_int addr, addr2;
+
+ addr = n->addr.s_addr & 0xffff0000;
+
+ group = nodegroup_find(net, GROUP_ADDRESS, &addr);
+ if (!group) {
+ addr2 = 0x00000000;
+ groot = nodegroup_find(net, GROUP_ADDRESS, &addr2);
+ if (!groot)
+ groot = nodegroup_add(net, GROUP_ADDRESS, &addr2, NULL);
+ addr2 = addr & 0xff000000;
+ gclassb = nodegroup_find(net, GROUP_ADDRESS, &addr2);
+ if (!gclassb)
+ gclassb = nodegroup_add(net, GROUP_ADDRESS, &addr2, groot);
+ group = nodegroup_add(net, GROUP_ADDRESS, &addr, gclassb);
+ }
+
+ n->group = group;
+ group->node_count++;
+}
+
+static void
+nodegroup_unset(struct ggnet *net, struct ggnet_node *n)
+{
+ if (!n->group)
+ return;
+
+ n->group->node_count--;
+ if (n->group->node_count == 0)
+ nodegroup_del(net, n->group);
+}
+
+static void
+_cb_dns_reverse(struct in_addr *ip, char *name, void *data)
+{
+ struct ggnet *net;
+ struct ggnet_node *n;
+
+ n = data;
+ net = n->net;
+ n->dns_req = NULL;
+ if (name) {
+ snprintf(n->fqdn, sizeof(n->fqdn), "%s", name);
+ net->cb_nodename(net, n);
+ }
+}
+
diff --git a/libglouglou/libggnet.h b/libglouglou/libggnet.h
new file mode 100644
index 0000000..1b17a32
--- /dev/null
+++ b/libglouglou/libggnet.h
@@ -0,0 +1,132 @@
+#ifndef _LIBGGNET_H_
+#define _LIBGGNET_H_
+
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include <event2/dns.h>
+#include <event2/util.h>
+#include <event2/event.h>
+
+#if defined(__OpenBSD__)
+#include <sys/queue.h>
+#else
+#include <bsd/sys/queue.h>
+#endif
+
+#define GGNET_DNSNAME_MAX 60
+#define GGNET_CONN_FREEIDS_COUNT 65536 /* 2^16 as freeids are u_int16_t */
+
+#define GGNET_MANAGE_CONNID_TRUE 1
+#define GGNET_MANAGE_CONNID_FALSE 0
+#define GGNET_GROUPING_TRUE 1
+#define GGNET_GROUPING_FALSE 1
+
+enum ggnet_grouptype {
+ GROUP_ADDRESS,
+ GROUP_WHOIS,
+ GROUP_DNS,
+ GROUP_ROUTE
+};
+
+struct ggnet_nodegroup {
+ LIST_ENTRY(ggnet_nodegroup) entry;
+ struct ggnet_nodegroup *parent;
+ enum ggnet_grouptype type;
+ struct in_addr addr;
+ char name[GGNET_DNSNAME_MAX];
+ int node_count;
+ int conn_count;
+ int child_groups_count;
+ void *usrdata;
+};
+
+struct ggnet_node {
+ LIST_ENTRY(ggnet_node) entry;
+ struct ggnet *net;
+ struct in_addr addr;
+ time_t lastseen;
+ int used;
+ char fqdn[GGNET_DNSNAME_MAX];
+ struct ggnet_nodegroup *group; /* XXX for now we support only one group */
+ struct ggnet_dns_req *dns_req;
+ void *usrdata;
+};
+
+enum ggnet_connstate {
+ CONNSTATE_ESTABLISHED,
+ CONNSTATE_TCPFIN,
+ CONNSTATE_TCPFIN2
+};
+
+struct ggnet_conn {
+ LIST_ENTRY(ggnet_conn) entry;
+ u_int id;
+ enum ggnet_connstate state;
+ struct ggnet_node *src;
+ u_int src_port;
+ struct ggnet_node *dst;
+ u_int dst_port;
+ u_int proto;
+ u_int size;
+ u_int size_response;
+ time_t lastseen;
+ void *usrdata;
+};
+
+struct ggnet {
+ LIST_HEAD(, ggnet_conn) conn_list;
+ LIST_HEAD(, ggnet_node) node_list;
+ LIST_HEAD(, ggnet_nodegroup) group_list;
+ int conn_count;
+ int node_count;
+ u_int16_t conn_freeids[GGNET_CONN_FREEIDS_COUNT];
+ int conn_freeids_ptr;
+ int manage_connid;
+ time_t time;
+ int debug;
+ /* grouping */
+ int use_grouping;
+ void (*cb_addgroup)(struct ggnet *, struct ggnet_nodegroup *, struct ggnet_nodegroup *);
+ void (*cb_delgroup)(struct ggnet *, struct ggnet_nodegroup *);
+ /* dns */
+ int use_dns;
+ struct ggnet_dns *ggdns;
+ void (*cb_nodename)(struct ggnet *, struct ggnet_node *);
+};
+
+struct ggnet *ggnet_new(int);
+void ggnet_set_grouping(struct ggnet *, int,
+ void (*cb_addgroup)(struct ggnet *, struct ggnet_nodegroup *, struct ggnet_nodegroup *),
+ void (*cb_delgroup)(struct ggnet *, struct ggnet_nodegroup *));
+void ggnet_set_dns(struct ggnet *net, int set,
+ struct event_base *ev_base,
+ void (*cb_nodename)(struct ggnet *, struct ggnet_node *));
+void ggnet_debug_set(struct ggnet *, int);
+void ggnet_free(struct ggnet *);
+struct ggnet_node *ggnet_node_add(struct ggnet *, struct in_addr *);
+void ggnet_node_del(struct ggnet *, struct ggnet_node *);
+struct ggnet_node *ggnet_node_find(struct ggnet *, struct in_addr *);
+void *ggnet_node_usrdata_get(struct ggnet_node *);
+void ggnet_node_usrdata_set(struct ggnet_node *, void *);
+struct ggnet_nodegroup *ggnet_node_group_get(struct ggnet_node *);
+struct ggnet_conn *ggnet_conn_add(struct ggnet *, struct in_addr *, int,
+ struct in_addr *, int, int, int, int);
+void ggnet_conn_data(struct ggnet *, struct ggnet_conn *,
+ int, int);
+void ggnet_conn_del(struct ggnet *, struct ggnet_conn *);
+struct ggnet_conn *ggnet_conn_find(struct ggnet *, struct in_addr *, int,
+ struct in_addr *, int, int, int *);
+struct ggnet_conn *ggnet_conn_find_by_id(struct ggnet *, int);
+struct ggnet_conn *ggnet_conn_find_by_node(struct ggnet *, struct ggnet_node *,
+ struct ggnet_node *);
+void *ggnet_conn_usrdata_get(struct ggnet_conn *);
+void ggnet_conn_usrdata_set(struct ggnet_conn *, void *);
+void *ggnet_conn_src_get(struct ggnet_conn *);
+void *ggnet_conn_dst_get(struct ggnet_conn *);
+void *ggnet_nodegroup_usrdata_get(struct ggnet_nodegroup *);
+void ggnet_nodegroup_usrdata_set(struct ggnet_nodegroup *,
+ void *);
+void ggnet_time_update(struct ggnet *, time_t);
+
+#endif /* _LIBGGNET_H_ */
diff --git a/libglouglou/libggnet_dns.c b/libglouglou/libggnet_dns.c
new file mode 100644
index 0000000..25bada3
--- /dev/null
+++ b/libglouglou/libggnet_dns.c
@@ -0,0 +1,116 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "libggnet_dns.h"
+
+/*
+ * For dns with libevent, see
+ * http://www.wangafu.net/~nickm/libevent-2.0/doxygen/html/dns_8h.html
+ * http://www.wangafu.net/~nickm/libevent-book/Ref9_dns.html
+ * spike/evdns_chrooted.c
+ * spike/evdns.c
+ */
+
+static void _cb_evdns_reverse(int, char, int, int, void *, void *);
+
+struct ggnet_dns *
+ggnet_dns_new(struct event_base *ev_base)
+{
+ struct ggnet_dns *ggdns = NULL;
+
+ ggdns = calloc(1, sizeof(struct ggnet_dns));
+ if (!ggdns) {
+ printf("could not allocate ggnet_dns\n");
+ exit(1);
+ }
+ ggdns->ev_base = ev_base;
+ ggdns->evdns_base = evdns_base_new(ev_base, 1);
+ if (!ggdns->evdns_base)
+ goto err;
+ return ggdns;
+
+err:
+ if (ggdns)
+ free(ggdns);
+ return NULL;
+}
+
+void
+ggnet_dns_free(struct ggnet_dns *ggdns)
+{
+ evdns_base_free(ggdns->evdns_base, 1);
+ free(ggdns);
+}
+
+struct ggnet_dns_req *
+ggnet_dns_reverse(struct ggnet_dns *ggdns,
+ struct in_addr *ip,
+ void (*cb_usr)(struct in_addr *, char *, void *),
+ void *data)
+{
+ struct evutil_addrinfo hints;
+ struct ggnet_dns_req *req;
+ struct evdns_request *ereq;
+ struct in_addr nip;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = EVUTIL_AI_CANONNAME;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ req = malloc(sizeof(struct ggnet_dns_req));
+ if (!req) {
+ printf("could not allocate ggnet_dns_req\n");
+ exit(1);
+ }
+ req->ggdns = ggdns;
+ memcpy(&req->ip, ip, sizeof(struct in_addr));
+ req->cb_usr = cb_usr;
+ req->data = data;
+
+ nip.s_addr = htonl(ip->s_addr); /* evdns eats network byte order */
+
+ LIST_INSERT_HEAD(&ggdns->req_list, req, entry);
+ ggdns->req_pending++;
+ ereq = evdns_base_resolve_reverse(ggdns->evdns_base, &nip, 0,
+ _cb_evdns_reverse, req);
+ if (ereq == NULL) {
+ printf("libggnet_dns WARNING: dns request for %d returned immediately\n",
+ ip->s_addr);
+ /* remove req from list and free it happened in the callback. */
+ return NULL;
+ }
+ req->ereq = ereq;
+
+ return req;
+}
+
+void
+ggnet_dns_cancel(struct ggnet_dns *ggdns, struct ggnet_dns_req *req)
+{
+ evdns_cancel_request(ggdns->evdns_base, req->ereq);
+ /* remove req from list and free it happened in the callback. */
+}
+
+static void
+_cb_evdns_reverse(int result, char type, int count,
+ int ttl, void *addresses, void *arg)
+{
+ struct ggnet_dns_req *req;
+ char **name;
+
+ if (count > 1)
+ printf("libggnet_dns XXX: has %d PTR records !\n", count); // XXX
+
+ req = arg;
+ name = addresses;
+ if (result != DNS_ERR_NONE || count == 0)
+ req->cb_usr(&req->ip, NULL, req->data);
+ else
+ req->cb_usr(&req->ip, *name, req->data);
+
+ LIST_REMOVE(req, entry);
+ req->ggdns->req_pending--;
+ free(req);
+}
diff --git a/libglouglou/libggnet_dns.h b/libglouglou/libggnet_dns.h
new file mode 100644
index 0000000..ae7b634
--- /dev/null
+++ b/libglouglou/libggnet_dns.h
@@ -0,0 +1,34 @@
+#include <event2/dns.h>
+#include <event2/util.h>
+#include <event2/event.h>
+
+#if defined(__OpenBSD__)
+#include <sys/queue.h>
+#else
+#include <bsd/sys/queue.h>
+#endif
+
+struct ggnet_dns_req {
+ LIST_ENTRY(ggnet_dns_req) entry;
+ struct ggnet_dns *ggdns;
+ struct evdns_request *ereq;
+ struct in_addr ip;
+ char *name;
+ void (*cb_usr)(struct in_addr *, char *, void *);
+ void *data;
+};
+
+struct ggnet_dns {
+ struct event_base *ev_base;
+ struct evdns_base *evdns_base;
+ int req_pending;
+ LIST_HEAD(, ggnet_dns_req) req_list; /* XXX for now unused. remove ? */
+};
+
+struct ggnet_dns *ggnet_dns_new(struct event_base *);
+void ggnet_dns_free(struct ggnet_dns *);
+struct ggnet_dns_req *ggnet_dns_reverse(struct ggnet_dns *, struct in_addr *,
+ void (*cb_usr)(struct in_addr *, char *, void *),
+ void *);
+void ggnet_dns_cancel(struct ggnet_dns *,
+ struct ggnet_dns_req *);
diff --git a/libglouglou/libglouglou.c b/libglouglou/libglouglou.c
new file mode 100644
index 0000000..79f57c6
--- /dev/null
+++ b/libglouglou/libglouglou.c
@@ -0,0 +1,721 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <err.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <event.h>
+#include <signal.h>
+
+#include "libglouglou.h"
+#include "sendbuf.h"
+
+#define error(fmt, ...) \
+ if (_verbosity >= 0) \
+ printf("libgg: %s: ERROR: " fmt "\n", __func__, ##__VA_ARGS__)
+#define verbose(fmt, ...) \
+ if (_verbosity >= 1) \
+ printf("libgg: %s: " fmt "\n", __func__, ##__VA_ARGS__)
+#define debug(fmt, ...) \
+ if (_verbosity >= 2) \
+ printf("libgg: %s: XXX: " fmt "\n", __func__, ##__VA_ARGS__)
+
+void cb_srv_receive(evutil_socket_t, short, void *);
+struct gg_user *user_add(struct gg_server *, struct sockaddr_in *);
+void user_del(struct gg_server *, struct gg_user *);
+struct gg_user * user_find(struct gg_server *, struct sockaddr_in *);
+int user_send(struct gg_user *, void *, int);
+int cb_usr_send(void *, int, void *);
+int client_send(struct gg_client *, void *, int);
+void cb_cli_receive(evutil_socket_t, short, void *);
+void cb_cli_timer(evutil_socket_t, short, void *);
+int cb_cli_send(void *, int, void *);
+struct gg_packet *pkt_decode(char **, int *);
+int pkt_getsize(struct gg_packet *);
+int pkt_encode(struct gg_packet *, struct gg_packet *);
+
+int _verbosity = 0;
+
+gg_packet_props_t gg_packet_props[] = {
+ [PACKET_NEWCONN] = \
+ { (PACKET_HEADER_SIZE + sizeof((struct gg_packet *)0)->pdat.newconn) },
+ [PACKET_DELCONN] = \
+ { (PACKET_HEADER_SIZE + sizeof((struct gg_packet *)0)->pdat.delconn) },
+ [PACKET_DATA] = \
+ { (PACKET_HEADER_SIZE + sizeof((struct gg_packet *)0)->pdat.data) },
+ [PACKET_NAME] = \
+ { ((PACKET_HEADER_SIZE + sizeof((struct gg_packet *)0)->pdat.name) - GG_PKTARG_MAX) },
+ [PACKET_FORK] = \
+ { (PACKET_HEADER_SIZE + sizeof((struct gg_packet *)0)->pdat.fork) },
+ [PACKET_EXEC] = \
+ { ((PACKET_HEADER_SIZE + sizeof((struct gg_packet *)0)->pdat.exec) - GG_PKTARG_MAX) },
+ [PACKET_EXIT] = \
+ { (PACKET_HEADER_SIZE + sizeof((struct gg_packet *)0)->pdat.exit) },
+};
+
+/*
+ * Server
+ */
+
+/*
+ * start a server
+ * For security reasons, do not set handle_packet if you don't need it.
+ * Also note that the packet passed by handle_packet is static, so you should
+ * not modify it or free it.
+ */
+struct gg_server *
+gg_server_start(struct event_base *ev_base, char *ip, int port,
+ int (*handle_conn)(struct gg_server *, struct gg_user *),
+ int (*handle_packet)(struct gg_server *, struct gg_user *, struct gg_packet *),
+ void *usrdata)
+{
+ struct gg_server *srv;
+ struct sockaddr_in sock_addr;
+ struct event *ev;
+ int s;
+ int sock_on = 1;
+
+ srv = xcalloc(1, sizeof(struct gg_server));
+ srv->ev_base = ev_base;
+ srv->ip = ip;
+ srv->port = port;
+ srv->handle_conn = handle_conn;
+ srv->handle_packet = handle_packet;
+ srv->usrdata = usrdata;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0)
+ goto err;
+ srv->sock = s;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ &sock_on, sizeof(sock_on));
+ fd_nonblock(s);
+
+ bzero(&sock_addr, sizeof(sock_addr));
+ sock_addr.sin_family = AF_INET;
+ sock_addr.sin_addr.s_addr = inet_addr(ip);
+ sock_addr.sin_port = htons(port);
+ addrcpy(&srv->addr, &sock_addr);
+
+ if (bind(s, (struct sockaddr *)&sock_addr,
+ sizeof(sock_addr)) != 0)
+ goto err;
+
+ ev = event_new(ev_base, s, EV_READ|EV_PERSIST, cb_srv_receive, srv);
+ event_add(ev, NULL);
+ srv->ev = ev;
+
+ return srv;
+
+err:
+ error("%s", strerror(errno));
+ gg_server_stop(srv);
+ return NULL;
+}
+
+void
+gg_server_stop(struct gg_server *srv)
+{
+ struct gg_user *usr;
+
+ while ((usr = LIST_FIRST(&srv->user_list))) {
+ user_del(srv, usr);
+ }
+ if (srv->sock)
+ close(srv->sock);
+ if (srv->ev)
+ event_del(srv->ev);
+ free(srv);
+}
+
+int
+gg_server_send(struct gg_server *srv, struct gg_packet *pkt, struct gg_user *usr)
+{
+ static struct gg_packet *newpkt;
+ static struct gg_packet pktbuf;
+ struct gg_user *u;
+ int size;
+ int res = 0;
+
+ /* optimisation to use sendbuf_gettoken */
+ if (!usr && srv->user_count == 1)
+ usr = LIST_FIRST(&srv->user_list);
+
+ if (usr) {
+ size = pkt_getsize(pkt);
+ newpkt = sendbuf_gettoken(usr->sbuf, size);
+ if (!newpkt)
+ return -1;
+ return pkt_encode(pkt, newpkt);
+ } else {
+ size = pkt_encode(pkt, &pktbuf);
+ LIST_FOREACH(u, &srv->user_list, entry) {
+ res = res + sendbuf_append(u->sbuf, &pktbuf, size);
+ }
+ }
+
+ return res;
+}
+
+void
+gg_server_send_flush(struct gg_server *srv, struct gg_user *usr)
+{
+ struct gg_user *u;
+
+ if (usr)
+ sendbuf_flush(usr->sbuf);
+ else
+ LIST_FOREACH(u, &srv->user_list, entry)
+ sendbuf_flush(u->sbuf);
+}
+
+/*
+ * Server - private
+ */
+
+void cb_srv_receive(evutil_socket_t fd, short what, void *arg)
+{
+ struct gg_server *srv;
+ struct gg_user *usr;
+ struct gg_packet *pkt;
+ struct sockaddr_in remote;
+ socklen_t remote_len;
+ char buf[PACKET_BUFFER_SIZE];
+ char *buf_p;
+ int buf_len;
+ int len;
+
+ srv = arg;
+ remote_len = sizeof(struct sockaddr_in);
+ len = recvfrom(fd, buf, sizeof(buf), 0,
+ (struct sockaddr *)&remote, &remote_len);
+ if (len < 0) {
+ error("recvfrom failed");
+ return;
+ }
+
+ usr = user_find(srv, &remote);
+ if (!usr) {
+ usr = user_add(srv, &remote);
+ if (srv->handle_conn)
+ srv->handle_conn(srv, usr);
+ user_send(usr, "", 0);
+ } else {
+ debug("Incoming data from existing user !");
+ if (len == 0) {
+ user_del(srv, usr);
+ return;
+ }
+ if (srv->handle_packet) {
+ buf_p = buf;
+ buf_len = len;
+ while (buf_len > 0 && (pkt = pkt_decode(&buf_p, &buf_len)))
+ srv->handle_packet(srv, usr, pkt);
+ if (buf_len > 0) {
+ /* XXX store incomplete packet for next recv */
+ error("incomplete packet, dropped %d bytes !", buf_len);
+ }
+ }
+ }
+}
+
+struct gg_user *
+user_add(struct gg_server *srv, struct sockaddr_in *remote)
+{
+ struct gg_user *usr;
+ struct sendbuf *sbuf;
+
+ usr = xcalloc(1, sizeof(struct gg_user));
+ usr->id = srv->user_id_count;
+ srv->user_id_count++;
+ srv->user_count++;
+ usr->sock = srv->sock;
+ addrcpy(&usr->addr, remote);
+
+ sbuf = sendbuf_new(srv->ev_base, PACKET_SNDBUF_MAX, 200, cb_usr_send, usr);
+ if (!sbuf)
+ goto err;
+ usr->sbuf = sbuf;
+
+ LIST_INSERT_HEAD(&srv->user_list, usr, entry);
+ verbose("Add user %d !", usr->id);
+
+ return usr;
+
+err:
+ user_del(srv, usr);
+ return NULL;
+}
+
+void
+user_del(struct gg_server *srv, struct gg_user *usr)
+{
+ verbose("Del user %d !", usr->id);
+ if (usr->sbuf)
+ sendbuf_free(usr->sbuf);
+ user_send(usr, "", 0);
+ LIST_REMOVE(usr, entry);
+ srv->user_count--;
+ free(usr);
+}
+
+struct gg_user *
+user_find(struct gg_server *srv, struct sockaddr_in *remote)
+{
+ struct gg_user *usr;
+ struct sockaddr_in *u;
+
+ LIST_FOREACH(usr, &srv->user_list, entry) {
+ u = &usr->addr;
+ if (u->sin_addr.s_addr == remote->sin_addr.s_addr &&
+ u->sin_port == remote->sin_port)
+ return usr;
+ }
+ return NULL;
+}
+
+int
+user_send(struct gg_user *usr, void *data, int size)
+{
+ int sent;
+
+ sent = sendto(usr->sock, data, size, 0, (struct sockaddr *)&usr->addr,
+ sizeof(struct sockaddr_in));
+ if (sent == -1)
+ error("failed: %s", strerror(errno));
+ return sent;
+}
+
+int
+cb_usr_send(void *data, int size, void *usrdata)
+{
+ struct gg_user *usr;
+
+ usr = usrdata;
+ return user_send(usr, data, size);
+}
+
+/*
+ * Client
+ */
+
+/*
+ * connect to a server
+ * For security reasons, do not set handle_packet if you don't need it.
+ * Also note that the packet passed by handle_packet is static, so you should
+ * not modify it or free it.
+ */
+struct gg_client *
+gg_client_connect(struct event_base *ev_base, char *ip, int port,
+ int (*handle_conn)(struct gg_client *),
+ int (*handle_packet)(struct gg_client *, struct gg_packet *),
+ void *usrdata)
+{
+ struct gg_client *cli;
+ struct sockaddr_in sock_addr;
+ struct sendbuf *sbuf;
+ struct event *ev;
+ struct timeval tv;
+ int s;
+ int sock_on = 1;
+
+ cli = xcalloc(1, sizeof(struct gg_client));
+ cli->ev_base = ev_base;
+ cli->ip = ip;
+ cli->port = port;
+ cli->handle_conn = handle_conn;
+ cli->handle_packet = handle_packet;
+ cli->usrdata = usrdata;
+ cli->status = GG_CLIENT_STATUS_CONNECTING;
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0)
+ goto err;
+ cli->sock = s;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ &sock_on, sizeof(sock_on));
+ fd_nonblock(s);
+
+ bzero(&sock_addr, sizeof(sock_addr));
+ sock_addr.sin_family = AF_INET;
+ sock_addr.sin_addr.s_addr=inet_addr(ip);
+ sock_addr.sin_port=htons(port);
+ addrcpy(&cli->addr, &sock_addr);
+
+ ev = event_new(ev_base, s, EV_READ|EV_PERSIST, cb_cli_receive, cli);
+ event_add(ev, NULL);
+ cli->ev = ev;
+
+ ev = evtimer_new(ev_base, cb_cli_timer, cli);
+ cli->ev_timer = ev;
+ bzero(&tv, sizeof(struct timeval));
+ tv.tv_sec = 0;
+ evtimer_add(ev, &tv);
+
+ sbuf = sendbuf_new(cli->ev_base, PACKET_SNDBUF_MAX, 200, cb_cli_send, cli);
+ if (!sbuf)
+ goto err;
+ cli->sbuf = sbuf;
+
+ return cli;
+
+err:
+ error("Error ! %s", strerror(errno));
+ gg_client_disconnect(cli);
+ return NULL;
+}
+
+void
+gg_client_disconnect(struct gg_client *cli)
+{
+ if (cli->sbuf)
+ sendbuf_free(cli->sbuf);
+ if (cli->sock) {
+ client_send(cli, "", 0);
+ close(cli->sock);
+ }
+ if (cli->ev)
+ event_del(cli->ev);
+ if (cli->ev_timer)
+ event_del(cli->ev_timer);
+ free(cli);
+}
+
+int
+gg_client_send(struct gg_client *cli, struct gg_packet *pkt)
+{
+ static struct gg_packet *newpkt;
+ int size;
+
+ size = pkt_getsize(pkt);
+ newpkt = sendbuf_gettoken(cli->sbuf, size);
+ if (!newpkt)
+ return -1;
+ return pkt_encode(pkt, newpkt);
+}
+
+void
+gg_client_send_flush(struct gg_client *cli)
+{
+ sendbuf_flush(cli->sbuf);
+}
+
+/*
+ * Client - private
+ */
+
+int
+client_send(struct gg_client *cli, void *data, int size)
+{
+ int sent;
+
+ sent = sendto(cli->sock, data, size, 0, (struct sockaddr *)&cli->addr,
+ sizeof(struct sockaddr_in));
+ if (sent == -1)
+ error("failed: %s", strerror(errno));
+ return sent;
+}
+
+void cb_cli_receive(evutil_socket_t fd, short what, void *arg)
+{
+ struct gg_client *cli;
+ struct gg_packet *pkt;
+ struct sockaddr_in remote;
+ socklen_t remote_len;
+ char buf[PACKET_BUFFER_SIZE];
+ char *buf_p;
+ int buf_len;
+ int len;
+
+ cli = arg;
+ remote_len = sizeof(struct sockaddr_in);
+ len = recvfrom(fd, buf, sizeof(buf), 0,
+ (struct sockaddr *)&remote, &remote_len);
+ if (addrcmp(&cli->addr, &remote)) {
+ error("receiving from stranger !");
+ return;
+ }
+ if (len < 0) {
+ error("recvfrom failed");
+ return;
+ }
+
+ switch (cli->status) {
+ case GG_CLIENT_STATUS_CONNECTING:
+ verbose("Connected !");
+ cli->status = GG_CLIENT_STATUS_CONNECTED;
+ if (cli->handle_conn)
+ cli->handle_conn(cli);
+ break;
+ case GG_CLIENT_STATUS_CONNECTED:
+ if (len == 0) {
+ verbose("libglouglou: cb_cli_receive: recvfrom = 0");
+ cli->status = GG_CLIENT_STATUS_CONNECTING;
+ return;
+ }
+ debug("Incoming data !");
+ if (cli->handle_packet) {
+ buf_p = buf;
+ buf_len = len;
+ while (buf_len > 0 && (pkt = pkt_decode(&buf_p, &buf_len)))
+ cli->handle_packet(cli, pkt);
+ if (buf_len > 0) {
+ /* XXX store incomplete packet for next recv */
+ error("incomplete packet, dropped %d bytes !", buf_len);
+ }
+ }
+ break;
+ }
+}
+
+void cb_cli_timer(evutil_socket_t fd, short what, void *arg)
+{
+ struct timeval tv;
+ struct gg_client *cli;
+
+ cli = arg;
+
+ switch (cli->status) {
+ case GG_CLIENT_STATUS_CONNECTING:
+ client_send(cli, "", 0);
+ break;
+ case GG_CLIENT_STATUS_CONNECTED:
+ // XXX send keepalive
+ break;
+ }
+
+ bzero(&tv, sizeof(struct timeval));
+ tv.tv_sec = 2;
+ event_add(cli->ev_timer, &tv);
+}
+
+int
+cb_cli_send(void *data, int size, void *usrdata)
+{
+ struct gg_client *cli;
+
+ cli = usrdata;
+ return client_send(cli, data, size);
+}
+
+/*
+ * Packets - private
+ */
+
+struct gg_packet *
+pkt_decode(char **buf, int *buf_len)
+{
+ static struct gg_packet newpkt;
+ struct gg_packet *pkt;
+ int len;
+ int packet_len;
+
+ len = *buf_len;
+
+#define invalid(msg) \
+ { verbose(msg); goto invalid; }
+#define incomplete(msg) \
+ { verbose(msg); goto incomplete; }
+
+ if (len < PACKET_HEADER_SIZE)
+ invalid("len");
+ if (len > PACKET_BUFFER_SIZE)
+ invalid("len");
+ pkt = (struct gg_packet *)*buf;
+ if (pkt->ver != PACKET_VERSION)
+ invalid("ver");
+ if (pkt->type < PACKET_TYPE_MIN || pkt->type > PACKET_TYPE_MAX)
+ invalid("type");
+ packet_len = gg_packet_props[pkt->type].size; // XXX never overflow ?
+ debug("type %d: %d %d", pkt->type, len, packet_len);
+ if (len < packet_len)
+ invalid("type len");
+
+ newpkt.ver = pkt->ver;
+ newpkt.type = pkt->type;
+ switch(pkt->type) {
+ case PACKET_NEWCONN:
+ newpkt.newconn_id = ntohs(pkt->newconn_id);
+ newpkt.newconn_src = ntohl(pkt->newconn_src);
+ newpkt.newconn_dst = ntohl(pkt->newconn_dst);
+ newpkt.newconn_proto = pkt->newconn_proto;
+ newpkt.newconn_size = pkt->newconn_size;
+ break;
+ case PACKET_DELCONN:
+ newpkt.delconn_id = ntohs(pkt->delconn_id);
+ break;
+ case PACKET_DATA:
+ newpkt.data_connid = ntohs(pkt->data_connid);
+ newpkt.data_size = pkt->data_size;
+ break;
+ case PACKET_NAME:
+ newpkt.name_addr = ntohl(pkt->name_addr);
+ newpkt.name_len = pkt->name_len;
+ if (newpkt.name_len > GG_PKTARG_MAX)
+ invalid("type name name_len");
+ if (len < packet_len + newpkt.name_len)
+ goto incomplete;
+ packet_len = packet_len + newpkt.name_len;
+ strncpy((char *)newpkt.name_fqdn, (char *)pkt->name_fqdn,
+ newpkt.name_len);
+ break;
+ case PACKET_FORK:
+ newpkt.fork_pid = ntohl(pkt->fork_pid);
+ newpkt.fork_ppid = ntohl(pkt->fork_ppid);
+ newpkt.fork_cpid = ntohl(pkt->fork_cpid);
+ newpkt.fork_tgid = ntohl(pkt->fork_tgid);
+ break;
+ case PACKET_EXEC:
+ newpkt.exec_pid = ntohl(pkt->exec_pid);
+ newpkt.exec_cmdlen = pkt->exec_cmdlen;
+ if (newpkt.exec_cmdlen > GG_PKTARG_MAX)
+ invalid("type exec cmdlen");
+ if (len < packet_len + newpkt.exec_cmdlen)
+ goto incomplete;
+ packet_len = packet_len + newpkt.exec_cmdlen;
+ strncpy((char *)newpkt.exec_cmd, (char *)pkt->exec_cmd,
+ newpkt.exec_cmdlen);
+ break;
+ case PACKET_EXIT:
+ newpkt.exit_pid = ntohl(pkt->exit_pid);
+ newpkt.exit_tgid = ntohl(pkt->exit_tgid);
+ newpkt.exit_ecode = pkt->exit_ecode;
+ break;
+ default:
+ goto invalid;
+ invalid("type switch");
+ }
+
+ *buf = *buf + packet_len;
+ *buf_len = len - packet_len;
+ return &newpkt;
+
+incomplete:
+ error("incomplete packet");
+ *buf_len = len;
+ return NULL;
+
+invalid:
+ error("invalid packet");
+ *buf_len = 0;
+ return NULL;
+}
+
+/*
+ * encodes a packet and returns the size before sending it on the wire.
+ * it assumes that the packet contains correct content.
+ */
+int
+pkt_encode(struct gg_packet *pkt, struct gg_packet *newpkt)
+{
+ if (pkt->type < PACKET_TYPE_MIN || pkt->type > PACKET_TYPE_MAX)
+ invalid("type");
+
+ newpkt->ver = pkt->ver;
+ newpkt->type = pkt->type;
+ switch(pkt->type) {
+ case PACKET_NEWCONN:
+ newpkt->newconn_id = htons(pkt->newconn_id);
+ newpkt->newconn_src = htonl(pkt->newconn_src);
+ newpkt->newconn_dst = htonl(pkt->newconn_dst);
+ newpkt->newconn_proto = pkt->newconn_proto;
+ newpkt->newconn_size = pkt->newconn_size;
+ break;
+ case PACKET_DELCONN:
+ newpkt->delconn_id = htons(pkt->delconn_id);
+ break;
+ case PACKET_DATA:
+ newpkt->data_connid = htons(pkt->data_connid);
+ newpkt->data_size = pkt->data_size;
+ break;
+ case PACKET_NAME:
+ if (pkt->name_len > GG_PKTARG_MAX)
+ goto invalid;
+ newpkt->name_addr = htonl(pkt->name_addr);
+ newpkt->name_len = pkt->name_len;
+ strncpy((char *)newpkt->name_fqdn, (char *)pkt->name_fqdn,
+ pkt->name_len);
+ break;
+ case PACKET_FORK:
+ newpkt->fork_pid = htonl(pkt->fork_pid);
+ newpkt->fork_ppid = htonl(pkt->fork_ppid);
+ newpkt->fork_cpid = htonl(pkt->fork_cpid);
+ newpkt->fork_tgid = htonl(pkt->fork_tgid);
+ break;
+ case PACKET_EXEC:
+ if (pkt->exec_cmdlen > GG_PKTARG_MAX)
+ goto invalid;
+ newpkt->exec_pid = htonl(pkt->exec_pid);
+ newpkt->exec_cmdlen = pkt->exec_cmdlen;
+ strncpy((char *)newpkt->exec_cmd, (char *)pkt->exec_cmd,
+ pkt->exec_cmdlen);
+ break;
+ case PACKET_EXIT:
+ newpkt->exit_pid = htonl(pkt->exit_pid);
+ newpkt->exit_tgid = htonl(pkt->exit_tgid);
+ newpkt->exit_ecode = pkt->exit_ecode;
+ break;
+ default:
+ error("Unsupported packet type");
+ return -1;
+ }
+ return pkt_getsize(newpkt);
+
+invalid:
+ error("invalid packet");
+ return -1;
+}
+
+int
+pkt_getsize(struct gg_packet *pkt)
+{
+ int packet_len;
+
+ packet_len = gg_packet_props[pkt->type].size; // XXX never overflow ?
+ switch(pkt->type) {
+ case PACKET_NEWCONN:
+ case PACKET_DELCONN:
+ case PACKET_DATA:
+ case PACKET_FORK:
+ case PACKET_EXIT:
+ break;
+ case PACKET_NAME:
+ if (pkt->name_len > GG_PKTARG_MAX)
+ goto invalid;
+ packet_len = packet_len + pkt->name_len;
+ break;
+ case PACKET_EXEC:
+ if (pkt->exec_cmdlen > GG_PKTARG_MAX)
+ goto invalid;
+ packet_len = packet_len + pkt->exec_cmdlen;
+ break;
+ default:
+ error("Unsupported packet type");
+ return -1;
+ }
+ return packet_len;
+
+invalid:
+ error("invalid packet");
+ return -1;
+}
+
+/*
+ * Verbosity
+ */
+
+int
+gg_verbosity_get(void)
+{
+ return _verbosity;
+}
+
+void
+gg_verbosity_set(int verb)
+{
+ _verbosity = verb;
+}
diff --git a/libglouglou/libglouglou.h b/libglouglou/libglouglou.h
new file mode 100644
index 0000000..b081685
--- /dev/null
+++ b/libglouglou/libglouglou.h
@@ -0,0 +1,202 @@
+#ifndef _LIBGLOUGLOU_H_
+#define _LIBGLOUGLOU_H_
+
+#include <sys/types.h>
+#include <event.h>
+
+#if defined(__OpenBSD__)
+#include <sys/queue.h>
+#else
+#include <bsd/sys/queue.h>
+#endif
+
+/* libglouglou.c */
+
+#define GLOUGLOU_PROBE_DEFAULT_PORT 4430
+#define GLOUGLOU_ANALY_DEFAULT_PORT 4431
+
+#define PACKET_VERSION 1
+#define PACKET_BUFFER_SIZE 16384
+#define PACKET_SNDBUF_MAX 500
+#define GG_PKTARG_MAX 60
+
+#define GG_PKTDATA_SIZE_FACTOR 20
+#define GG_PKTDATA_SIZE_ENCODE(pktsize, size, response) \
+ (pktsize = ((size / GG_PKTDATA_SIZE_FACTOR) & 0x7f) | (response << 7))
+#define GG_PKTDATA_SIZE_DECODE(pktsize, size, response) { \
+ response = pktsize >> 7; size = (pktsize & 0x7f) * GG_PKTDATA_SIZE_FACTOR; \
+}
+
+#define PACKET_HEADER_SIZE 2
+#define PACKET_TYPE_MIN 0x00
+#define PACKET_TYPE_MAX 0x12
+enum gg_packet_type { /* u_int8_t */
+ PACKET_NEWCONN = 0x00,
+ PACKET_DELCONN = 0x01,
+ PACKET_DATA = 0x02,
+ PACKET_NAME = 0x03,
+ PACKET_FORK = 0x10,
+ PACKET_EXEC = 0x11,
+ PACKET_EXIT = 0x12,
+};
+
+/* XXX is packed needed everywhere ? */
+struct __attribute__((packed)) gg_packet {
+ u_int8_t ver;
+ u_int8_t type;
+ union {
+ struct __attribute__((packed)) newconn {
+ u_int16_t id;
+ u_int32_t src;
+ u_int32_t dst;
+ u_int8_t proto;
+ u_int8_t size;
+ } newconn;
+ struct __attribute__((packed)) delconn {
+ u_int16_t id;
+ } delconn;
+ struct __attribute__((packed)) data {
+ u_int16_t connid;
+ u_int8_t size;
+ } data;
+ struct __attribute__((packed)) name {
+ u_int32_t addr;
+ u_int8_t len;
+ u_char fqdn[GG_PKTARG_MAX];
+ } name;
+ struct __attribute__((packed)) fork {
+ u_int32_t pid;
+ u_int32_t ppid;
+ u_int32_t cpid;
+ u_int32_t tgid;
+ } fork;
+ struct __attribute__((packed)) exec {
+ u_int32_t pid;
+ u_int8_t cmdlen;
+ u_char cmd[GG_PKTARG_MAX];
+ } exec;
+ struct __attribute__((packed)) exit {
+ u_int32_t pid;
+ u_int32_t tgid;
+ u_int8_t ecode;
+ } exit;
+ } pdat;
+#define newconn_id pdat.newconn.id
+#define newconn_src pdat.newconn.src
+#define newconn_dst pdat.newconn.dst
+#define newconn_proto pdat.newconn.proto
+#define newconn_size pdat.newconn.size
+#define delconn_id pdat.delconn.id
+#define data_connid pdat.data.connid
+#define data_size pdat.data.size
+#define name_addr pdat.name.addr
+#define name_len pdat.name.len
+#define name_fqdn pdat.name.fqdn
+#define fork_pid pdat.fork.pid
+#define fork_ppid pdat.fork.ppid
+#define fork_cpid pdat.fork.cpid
+#define fork_tgid pdat.fork.tgid
+#define exec_pid pdat.exec.pid
+#define exec_cmdlen pdat.exec.cmdlen
+#define exec_cmd pdat.exec.cmd
+#define exit_pid pdat.exit.pid
+#define exit_tgid pdat.exit.tgid
+#define exit_ecode pdat.exit.ecode
+};
+
+typedef struct gg_packet_props_t {
+ int size;
+} gg_packet_props_t;
+
+extern gg_packet_props_t gg_packet_props[];
+
+struct gg_user {
+ LIST_ENTRY(gg_user) entry;
+ int id;
+ int sock;
+ struct sockaddr_in addr;
+ struct sendbuf *sbuf;
+};
+
+struct gg_server {
+ struct event_base *ev_base;
+ const char *ip;
+ int port;
+ struct sockaddr_in addr;
+ struct event *ev;
+ int sock;
+ int (*handle_conn)(struct gg_server *, struct gg_user *);
+ int (*handle_packet)(struct gg_server *, struct gg_user *, struct gg_packet *);
+ void *usrdata;
+ LIST_HEAD(, gg_user) user_list;
+ int user_count;
+ int user_id_count;
+};
+
+enum client_status {
+ GG_CLIENT_STATUS_CONNECTING = 0,
+ GG_CLIENT_STATUS_CONNECTED = 1
+};
+
+struct gg_client {
+ struct event_base *ev_base;
+ const char *ip;
+ int port;
+ struct sockaddr_in addr;
+ struct event *ev;
+ struct event *ev_timer;
+ int sock;
+ enum client_status status;
+ int (*handle_conn)(struct gg_client *);
+ int (*handle_packet)(struct gg_client *, struct gg_packet *);
+ void *usrdata;
+ struct sendbuf *sbuf;
+};
+
+struct gg_server *gg_server_start(struct event_base *, char *, int,
+ int (*handle_conn)(struct gg_server *, struct gg_user *),
+ int (*handle_packet)(struct gg_server *, struct gg_user *, struct gg_packet *),
+ void *);
+void gg_server_stop(struct gg_server *);
+int gg_server_send(struct gg_server *, struct gg_packet *, struct gg_user *);
+void gg_server_send_flush(struct gg_server *, struct gg_user *);
+
+struct gg_client *gg_client_connect(struct event_base *, char *, int,
+ int (*handle_conn)(struct gg_client *),
+ int (*handle_packet)(struct gg_client *, struct gg_packet *),
+ void *);
+void gg_client_disconnect(struct gg_client *);
+int gg_client_send(struct gg_client *, struct gg_packet *);
+void gg_client_send_flush(struct gg_client *);
+
+int gg_verbosity_get(void);
+void gg_verbosity_set(int);
+
+/* utils.c */
+
+#define GGLOG_FORCED -2
+#define GGLOG_FATAL -1
+#define GGLOG_WARN 0
+#define GGLOG_INFO 1
+#define GGLOG_DEBUG 2
+
+int gg_log_init(char *, int);
+void gg_log_shutdown(void);
+void gg_log_tmp(const char *, ...);
+void gg_log_debug(const char *, ...);
+void gg_log_info(const char *, ...);
+void gg_log_warn(const char *, ...);
+#if defined(__OpenBSD__)
+void __dead gg_log_fatal(const char *, ...);
+#else
+void gg_log_fatal(const char *, ...);
+#endif
+
+void *xmalloc(size_t);
+void *xcalloc(size_t, size_t);
+void fd_nonblock(int);
+void addrcpy(struct sockaddr_in *, struct sockaddr_in *);
+int addrcmp(struct sockaddr_in *, struct sockaddr_in *);
+void droppriv(char *, int, char *);
+
+#endif /* _LIBGLOUGLOU_H_ */
diff --git a/libglouglou/sendbuf.c b/libglouglou/sendbuf.c
new file mode 100644
index 0000000..4e83294
--- /dev/null
+++ b/libglouglou/sendbuf.c
@@ -0,0 +1,142 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "sendbuf.h"
+
+static void cb_timer(evutil_socket_t, short, void *);
+
+/*
+ * Public
+ */
+
+/*
+ * Create a sendbuf
+ */
+struct sendbuf *
+sendbuf_new(struct event_base *ev_base, int buffer_size, int msec_max,
+ int (*send_func)(void *, int, void *), void *usrdata)
+{
+ struct sendbuf *sbuf = NULL;
+ struct event *ev_timer;
+
+ sbuf = calloc(1, sizeof(struct sendbuf));
+ if (!sbuf)
+ return NULL;
+ sbuf->ev_base = ev_base;
+ sbuf->msec_max = msec_max;
+ sbuf->buffer_size = buffer_size;
+ sbuf->send_func = send_func;
+ sbuf->usrdata = usrdata;
+ sbuf->buffer = malloc(sbuf->buffer_size);
+ if (!sbuf->buffer)
+ goto err;
+
+ ev_timer = evtimer_new(ev_base, cb_timer, sbuf);
+ sbuf->ev_timer = ev_timer;
+ sbuf->ev_timer_tv.tv_usec = msec_max * 1000;
+ evtimer_add(ev_timer, &sbuf->ev_timer_tv);
+
+ return sbuf;
+
+err:
+ sendbuf_free(sbuf);
+ return NULL;
+}
+
+void
+sendbuf_free(struct sendbuf *sbuf)
+{
+ if (sbuf->ev_timer)
+ event_del(sbuf->ev_timer);
+ if (sbuf->buffer && sbuf->send_func)
+ sendbuf_flush(sbuf);
+ if (sbuf->buffer)
+ free(sbuf->buffer);
+ free(sbuf);
+}
+
+/*
+ * Append to the token buffer data to be sent
+ * uses a memcpy, in contrary to sendbuf_gettoken().
+ * return size on success, -1 on error
+ */
+int
+sendbuf_append(struct sendbuf *sbuf, void *token, int size)
+{
+ if (sbuf->buffer_pos + size >= sbuf->buffer_size)
+ if (sendbuf_flush(sbuf) == -1)
+ return -1;
+
+ memcpy(sbuf->buffer + sbuf->buffer_pos, token, size);
+ sbuf->buffer_pos = sbuf->buffer_pos + size;
+
+ return size;
+}
+
+/*
+ * Returns a token buffer to write data to be sent
+ * avoids memcpy, in contrary to sendbuf_append().
+ * might return NULL if the sendbuf is temporary full
+ */
+void *
+sendbuf_gettoken(struct sendbuf *sbuf, int size)
+{
+ void *token;
+
+ if (sbuf->buffer_pos + size >= sbuf->buffer_size)
+ if (sendbuf_flush(sbuf) == -1)
+ return NULL;
+
+ token = sbuf->buffer + sbuf->buffer_pos;
+ sbuf->buffer_pos = sbuf->buffer_pos + size;
+
+ return token;
+}
+
+/*
+ * Send buffer immediatly
+ * Note that you can still add data to the buffer even if flushing is in
+ * progress
+ * returns 0 on success or -1 on error
+ */
+int
+sendbuf_flush(struct sendbuf *sbuf)
+{
+ int tosend, sent;
+
+ if (sbuf->buffer_pos == 0)
+ return 0;
+
+ sbuf->flushing = 1;
+
+ tosend = sbuf->buffer_pos - sbuf->flushing_pos;
+ sent = sbuf->send_func(sbuf->buffer + sbuf->flushing_pos,
+ tosend, sbuf->usrdata);
+ if (sent == -1) {
+ // XXX handle error
+ return -1;
+ } else if (sent < tosend) {
+ sbuf->flushing_pos = sbuf->flushing_pos + sent;
+ return -1;
+ }
+ sbuf->buffer_pos = 0;
+
+ sbuf->flushing = 0;
+ sbuf->flushing_pos = 0;
+ return 0;
+}
+
+
+/*
+ * Private
+ */
+
+static void
+cb_timer(evutil_socket_t fd, short what, void *arg)
+{
+ struct sendbuf *sbuf;
+
+ sbuf = arg;
+ sendbuf_flush(sbuf);
+ evtimer_add(sbuf->ev_timer, &sbuf->ev_timer_tv);
+}
diff --git a/libglouglou/sendbuf.h b/libglouglou/sendbuf.h
new file mode 100644
index 0000000..6d163d4
--- /dev/null
+++ b/libglouglou/sendbuf.h
@@ -0,0 +1,24 @@
+#include <event.h>
+#include <sys/queue.h>
+
+struct sendbuf {
+ struct event_base *ev_base;
+ struct event *ev_timer;
+ struct timeval ev_timer_tv;
+ int msec_max;
+ int buffer_size;
+ void *buffer;
+ int buffer_pos; /* next to use in buffer */
+ int flushing;
+ int flushing_pos; /* next to send in buffer */
+ int (*send_func)(void *, int, void *);
+ void *usrdata;
+};
+
+struct sendbuf *sendbuf_new(struct event_base *, int, int,
+ int (*send_func)(void *, int, void *),
+ void *);
+void sendbuf_free(struct sendbuf *);
+int sendbuf_append(struct sendbuf *, void *, int);
+void *sendbuf_gettoken(struct sendbuf *, int);
+int sendbuf_flush(struct sendbuf *);
diff --git a/libglouglou/tests/Makefile b/libglouglou/tests/Makefile
new file mode 100644
index 0000000..e059f49
--- /dev/null
+++ b/libglouglou/tests/Makefile
@@ -0,0 +1,48 @@
+CFLAGS += -Wall -g
+LDFLAGS=-L../ -levent -lglouglou
+
+SOURCES = $(shell echo *.c)
+OBJECTS = $(SOURCES:.c=.o)
+TARGETS = $(SOURCES:.c=)
+
+all: $(TARGETS)
+
+run: $(TARGETS)
+ @count=0 ;\
+ errors=0 ;\
+ for test in $(TARGETS); do \
+ echo =============================================================================== ;\
+ echo $$test ;\
+ LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:../ ./$$test ;\
+ if [ $$? -eq 0 ]; then \
+ echo OK ;\
+ else \
+ echo FAILED ;\
+ errors=$$(($$errors + 1)) ;\
+ fi ;\
+ count=$$(($$count + 1)) ;\
+ done ;\
+ echo =============================================================================== ;\
+ echo "$$count tests executed, $$errors errors" ;\
+ exit $$errors
+
+run_loop:
+ @loopcount=20 ;\
+ count=1 ;\
+ while [ $$count -lt $$loopcount ]; do \
+ echo "###############################################################################" ;\
+ make run ;\
+ if [ $$? -ne 0 ]; then \
+ echo ERROR ;\
+ echo "###############################################################################" ;\
+ echo "$$count / $$loopcount loops of tests executed, last one did ERROR" ;\
+ exit $$? ;\
+ fi ;\
+ count=$$(($$count + 1)) ;\
+ done ;\
+ echo "###############################################################################" ;\
+ echo "$$count / $$loopcount loops of tests executed, all OK" ;\
+ exit 0
+
+clean:
+ rm -f $(TARGETS) $(OBJECTS)
diff --git a/libglouglou/tests/README.txt b/libglouglou/tests/README.txt
new file mode 100644
index 0000000..fb19ae8
--- /dev/null
+++ b/libglouglou/tests/README.txt
@@ -0,0 +1,4 @@
+libglouglou unit tests
+
+do "make run" to execute them
+returns the number of tests that failed
diff --git a/libglouglou/tests/connect.c b/libglouglou/tests/connect.c
new file mode 100644
index 0000000..3e46667
--- /dev/null
+++ b/libglouglou/tests/connect.c
@@ -0,0 +1,46 @@
+#include <event.h>
+
+#include "../libglouglou.h"
+
+int srv_connect_ok = 0;
+int cli_connect_ok = 0;
+
+int
+srv_handle_conn(struct gg_server *srv, struct gg_user *usr)
+{
+ if (usr->id == 0)
+ srv_connect_ok = 1;
+ return 0;
+}
+
+int
+cli_handle_conn(struct gg_client *cli)
+{
+ cli_connect_ok = 1;
+ return 0;
+}
+
+int
+main(void)
+{
+ struct event_base *ev_base;
+
+ ev_base = event_base_new();
+ if (!gg_server_start(ev_base, "127.0.0.1", 12345, srv_handle_conn, NULL, NULL)) {
+ printf("error: gg_server_start returned NULL\n");
+ return 1;
+ }
+ if (!gg_client_connect(ev_base, "127.0.0.1", 12345, cli_handle_conn, NULL, NULL)) {
+ printf("error: gg_client_connect returned NULL\n");
+ return 1;
+ }
+ event_base_loop(ev_base, EVLOOP_NONBLOCK);
+ event_base_loop(ev_base, EVLOOP_NONBLOCK);
+
+ if (srv_connect_ok == 0)
+ printf("error: srv_connect_ok == 0\n");
+ if (cli_connect_ok == 0)
+ printf("error: cli_connect_ok == 0\n");
+
+ return (!srv_connect_ok || !cli_connect_ok);
+}
diff --git a/libglouglou/tests/sendrecv.c b/libglouglou/tests/sendrecv.c
new file mode 100644
index 0000000..cbfb402
--- /dev/null
+++ b/libglouglou/tests/sendrecv.c
@@ -0,0 +1,67 @@
+#include <event.h>
+
+#include "../libglouglou.h"
+
+int srv_recv_ok = 0;
+int cli_recv_ok = 0;
+
+int
+srv_handle_packet(struct gg_server *srv, struct gg_user *usr, struct gg_packet *pkt)
+{
+ if (pkt->type == PACKET_NEWCONN)
+ srv_recv_ok = 1;
+ return 0;
+}
+
+int
+cli_handle_packet(struct gg_client *cli, struct gg_packet *pkt)
+{
+ if (pkt->type == PACKET_NEWCONN)
+ cli_recv_ok = 1;
+ return 0;
+}
+
+int
+main(void)
+{
+ struct event_base *ev_base;
+ struct gg_server *srv;
+ struct gg_client *cli;
+ struct gg_packet pkt;
+
+ ev_base = event_base_new();
+ srv = gg_server_start(ev_base, "127.0.0.1", GLOUGLOU_PROBE_DEFAULT_PORT,
+ NULL, srv_handle_packet, NULL);
+ if (!srv) {
+ printf("error: gg_server_start returned NULL\n");
+ return 1;
+ }
+ cli = gg_client_connect(ev_base, "127.0.0.1", GLOUGLOU_PROBE_DEFAULT_PORT,
+ NULL, cli_handle_packet, NULL);
+ if (!cli) {
+ printf("error: gg_client_connect returned NULL\n");
+ return 1;
+ }
+ event_base_loop(ev_base, EVLOOP_NONBLOCK);
+ event_base_loop(ev_base, EVLOOP_NONBLOCK);
+
+ pkt.ver = PACKET_VERSION;
+ pkt.type = PACKET_NEWCONN;
+
+ gg_server_send(srv, &pkt, NULL);
+ gg_server_send_flush(srv, NULL);
+ gg_client_send(cli, &pkt);
+ gg_client_send_flush(cli);
+
+ event_base_loop(ev_base, EVLOOP_NONBLOCK);
+
+ gg_client_disconnect(cli);
+ gg_server_stop(srv);
+
+ if (srv_recv_ok == 0)
+ printf("error: srv_recv_ok == 0\n");
+ if (cli_recv_ok == 0)
+ printf("error: cli_recv_ok == 0\n");
+
+ return (!srv_recv_ok || !cli_recv_ok);
+}
diff --git a/libglouglou/utils.c b/libglouglou/utils.c
new file mode 100644
index 0000000..8ca3660
--- /dev/null
+++ b/libglouglou/utils.c
@@ -0,0 +1,200 @@
+#include <sys/types.h>
+
+#if !defined(__OpenBSD__)
+#define __USE_GNU
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <err.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+
+#include "libglouglou.h"
+
+FILE *_logfile;
+static int _loglevel;
+
+static void logit(int, const char *, const char *, va_list);
+
+/*
+ * Log
+ * handles only one log file per process
+ */
+
+int
+gg_log_init(char *filename, int level)
+{
+ _logfile = fopen(filename, "a+");
+ if (!_logfile) {
+ printf("cannot open log file %s!\n", filename);
+ return -1;
+ }
+ _loglevel = level;
+ return 0;
+}
+
+void
+gg_log_shutdown(void)
+{
+ fclose(_logfile);
+}
+
+void
+gg_log_tmp(const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ logit(GGLOG_FORCED, "XXX ", msg, ap);
+ va_end(ap);
+}
+
+void
+gg_log_debug(const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ logit(GGLOG_DEBUG, "", msg, ap);
+ va_end(ap);
+}
+
+void
+gg_log_info(const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ logit(GGLOG_INFO, "", msg, ap);
+ va_end(ap);
+}
+
+void
+gg_log_warn(const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ logit(GGLOG_WARN, "", msg, ap);
+ va_end(ap);
+}
+
+#if defined(__OpenBSD__)
+void __dead
+#else
+void
+#endif
+gg_log_fatal(const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ logit(GGLOG_FATAL, "fatal: ", msg, ap);
+ va_end(ap);
+
+ exit(1);
+}
+
+/* XXX mpsafe */
+static void
+logit(int level, const char *prefix, const char *msg, va_list ap)
+{
+ time_t clock;
+
+ if (level <= _loglevel) {
+ time(&clock);
+ fprintf(_logfile, "%d ", (int)clock);
+ vfprintf(_logfile, prefix, ap);
+ vfprintf(_logfile, msg, ap);
+ fprintf(_logfile, "\n");
+ fflush(_logfile);
+ }
+}
+
+/*
+ * Various utils
+ */
+
+void *
+xmalloc(size_t size)
+{
+ void *data;
+
+ data = malloc(size);
+ if (!data)
+ err(1, "could not malloc %d", (int)size);
+ return data;
+}
+
+void *
+xcalloc(size_t nmemb, size_t size)
+{
+ void *data;
+
+ data = calloc(nmemb, size);
+ if (!data)
+ err(1, "could not calloc %d", (int)size);
+ return data;
+}
+
+void
+fd_nonblock(int fd)
+{
+ int flags = fcntl(fd, F_GETFL, 0);
+ int rc = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ if (rc == -1)
+ err(1, "failed to set fd %i non-blocking", fd);
+}
+
+void
+addrcpy(struct sockaddr_in *dst, struct sockaddr_in *src)
+{
+ dst->sin_addr.s_addr = src->sin_addr.s_addr;
+ dst->sin_port = src->sin_port;
+ dst->sin_family = src->sin_family;
+}
+
+int
+addrcmp(struct sockaddr_in *a, struct sockaddr_in *b)
+{
+ if (a->sin_addr.s_addr != b->sin_addr.s_addr)
+ return -1;
+ if (a->sin_port != b->sin_port)
+ return -2;
+ if (a->sin_family != b->sin_family)
+ return -3;
+ return 0;
+}
+
+void
+droppriv(char *user, int do_chroot, char *chroot_path)
+{
+ struct passwd *pw;
+
+ pw = getpwnam(user);
+ if (!pw)
+ err(1, "unknown user %s", user);
+ if (do_chroot) {
+ if (!chroot_path)
+ chroot_path = pw->pw_dir;
+ if (chroot(chroot_path) != 0)
+ err(1, "unable to chroot");
+ }
+ if (chdir("/") != 0)
+ err(1, "unable to chdir");
+ if (setgroups(1, &pw->pw_gid) == -1)
+ err(1, "setgroups() failed");
+ if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
+ err(1, "setresgid failed");
+ if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
+ err(1, "setresuid() failed");
+ endpwent();
+}
+
diff --git a/old/glouglou_ruby/README.txt b/old/glouglou_ruby/README.txt
new file mode 100644
index 0000000..c7e512a
--- /dev/null
+++ b/old/glouglou_ruby/README.txt
@@ -0,0 +1,3 @@
+glouglou_ruby - glougloud client using ruby cairo
+
+this is a spike
diff --git a/old/glouglou_ruby/aa_test.rb b/old/glouglou_ruby/aa_test.rb
new file mode 100755
index 0000000..c5d2af8
--- /dev/null
+++ b/old/glouglou_ruby/aa_test.rb
@@ -0,0 +1,304 @@
+#!/usr/bin/env ruby
+
+# ported from examples/aa_test.cpp in AGG source tree.
+
+top = File.expand_path(File.join(File.dirname(__FILE__), "..", ".."))
+src = File.join(top, "src")
+$LOAD_PATH.unshift src
+$LOAD_PATH.unshift File.join(src, "lib")
+
+require 'gtk2'
+
+window = Gtk::Window.new
+window.set_default_size(480, 350)
+
+area = Gtk::DrawingArea.new
+
+def stroke_line(context, x1, y1, x2, y2, line_width, dash_length)
+ context.save do
+ yield if block_given?
+ context.move_to(x1, y1)
+ context.line_to(x2, y2)
+ context.set_dash(dash_length) if dash_length > 0
+ context.line_width = line_width
+ context.line_cap = :round
+ context.stroke
+ end
+end
+
+def fill_triangle(context, x1, y1, x2, y2, x3, y3)
+ context.save do
+ yield if block_given?
+ context.triangle(x1, y1, x2, y2, x3, y3)
+ context.fill
+ end
+end
+
+def set_gradient(context, x1, y1, x2, y2, color_stops)
+ pattern = Cairo::LinearPattern.new(x1, y1, x2, y2)
+ color_stops.each do |offset, color|
+ pattern.add_color_stop(offset, color)
+ end
+ context.set_source(pattern)
+end
+
+def stroke_gradient_line(context, x1, y1, x2, y2,
+ line_width, dash_length, color_stops)
+ stroke_line(context, x1, y1, x2, y2, line_width, dash_length) do
+ set_gradient(context, x1, y1, x2, y2, color_stops)
+ end
+end
+
+def fill_gradient_triangle(context, x1, y1, x2, y2, x3, y3, color_stops)
+ fill_triangle(context, x1, y1, x2, y2, x3, y3) do
+ set_gradient(context,
+ x1, y1,
+ x3 - (x3 - x2) / 2.0,
+ y3 - (y3 - y2) / 2.0,
+ color_stops)
+ end
+end
+
+def draw_background_circle(context, w, h)
+ context.set_source_color([1.0, 1.0, 1.0, 0.2])
+
+ cx = w / 2.0
+ cy = h / 2.0
+ radius = [cx, cy].min
+ 1.upto(180) do |i|
+ n = 2 * Math::PI * i / 180.0
+ stroke_line(context,
+ cx + radius * Math.sin(n),
+ cy + radius * Math.cos(n),
+ cx, cy,
+ 1.0, i < 90 ? i : 0)
+ end
+end
+
+def draw_upper_small_circles(context, i)
+ context.circle(18 + i * 4 + 0.5,
+ 33 + 0.5,
+ i / 20.0)
+ context.fill
+
+ context.circle(18 + i * 4 + (i - 1) / 10.0 + 0.5,
+ 27 + (i - 1) / 10.0 + 0.5,
+ 0.5)
+ context.fill
+end
+
+def draw_upper_circles(context, i)
+ context.set_source_color(:white)
+ context.circle(20 + i * (i + 1) + 0.5,
+ 20.5,
+ i / 2.0)
+ context.fill
+
+ draw_upper_small_circles(context, i)
+end
+
+def draw_upper_gradient_lines(context, i)
+ stroke_gradient_line(context,
+ 20 + i * (i + 1),
+ 40.5,
+ 20 + i * (i + 1) + (i - 1) * 4,
+ 100.5,
+ i,
+ 0,
+ [[0, :white],
+ [1, [i % 2, (i % 3) * 0.5, (i % 5) * 0.25]]])
+end
+
+def draw_middle_small_circles(context, i)
+ stroke_gradient_line(context,
+ 17.5 + i * 4,
+ 107,
+ 17.5 + i * 4 + i / 6.66666667,
+ 107,
+ 1,
+ 0,
+ [[0, :red],
+ [1, :blue]])
+
+ stroke_gradient_line(context,
+ 18 + i * 4,
+ 112.5,
+ 18 + i * 4,
+ 112.5 + i / 6.66666667,
+ 1,
+ 0,
+ [[0, :red],
+ [1, :blue]])
+end
+
+def draw_middle_gradient_lines(context, i)
+ stroke_gradient_line(context,
+ 21.5,
+ 120 + (i - 1) * 3.1,
+ 52.5,
+ 120 + (i - 1) * 3.1,
+ 1,
+ 0,
+ [[0, :red],
+ [1, :white]])
+
+ stroke_gradient_line(context,
+ 52.5,
+ 118 + i * 3,
+ 83.5,
+ 118 + i * 3,
+ 2 - (i - 1) / 10.0,
+ 0,
+ [[0, :green],
+ [1, :white]])
+
+ stroke_gradient_line(context,
+ 83.5,
+ 119 + i * 3,
+ 114.5,
+ 119 + i * 3,
+ 2 - (i - 1) / 10.0,
+ 3.0,
+ [[0, :blue],
+ [1, :white]])
+end
+
+def draw_middle_white_lines(context, i)
+ context.set_source_color(:white)
+ stroke_line(context,
+ 125.5, 119.5 + (i + 2) * (i / 2.0),
+ 135.5, 119.5 + (i + 2) * (i / 2.0),
+ i, 0)
+end
+
+def draw_lower_short_lines(context, i)
+ context.set_source_color(:white)
+
+ stroke_line(context,
+ 17.5 + i * 4, 192,
+ 18.5 + i * 4, 192,
+ i / 10.0, 0)
+
+ stroke_line(context,
+ 17.5 + i * 4 + (i - 1) / 10.0, 186,
+ 18.5 + i * 4 + (i - 1) / 10.0, 186,
+ 1.0, 0)
+end
+
+def draw_right_triangles(context, i, w, h)
+ x1 = w - 150
+ x2 = w - 20
+ y_upper = h - 20 - i * (i + 2)
+ y_middle = h - 20 - i * (i + 1.5)
+ y_lower = h - 20 - i * (i + 1)
+ context.save do
+ pattern = Cairo::LinearPattern.new(x1, y_middle, x2, y_middle)
+ pattern.add_color_stop(0, :white)
+ pattern.add_color_stop(1, [i % 2, (i % 3) * 0.5, (i % 5) * 0.25])
+ context.set_source(pattern)
+ context.move_to(x1, y_middle)
+ context.line_to(x2, y_lower)
+ context.line_to(x2, y_upper)
+ context.fill
+ end
+end
+
+def draw_sample_shapes(context, w, h)
+ context.set_source_color(:black)
+ context.paint
+
+ draw_background_circle(context, w, h)
+
+ 1.upto(20) do |i|
+ draw_upper_circles(context, i)
+ draw_upper_gradient_lines(context, i)
+ draw_middle_small_circles(context, i)
+ draw_middle_gradient_lines(context, i)
+ draw_middle_white_lines(context, i) if i <= 10
+ draw_lower_short_lines(context, i)
+ end
+
+ 1.upto(13) do |i|
+ draw_right_triangles(context, i, w, h)
+ end
+end
+
+def draw_random_shapes(context, w, h)
+ srand(123)
+
+ context.set_source_color(:black)
+ context.paint
+
+ n_circles = 20000
+ start = Time.now
+ n_circles.times do |i|
+ context.circle(rand(w), rand(h), rand(20.0) + 1.0)
+ context.set_source_color([rand, rand, rand, 0.5 + rand / 2])
+ context.fill
+ end
+ circle_draw_time = Time.now - start
+
+ n_lines = 2000
+ start = Time.now
+ n_lines.times do |i|
+ x1 = rand(w)
+ y1 = rand(h)
+ stroke_gradient_line(context,
+ x1, y1,
+ x1 + rand(w * 0.5) - w * 0.25,
+ y1 + rand(h * 0.5) - h * 0.25,
+ 10.0, 0,
+ [[0, [rand, rand, rand, 0.5 + rand / 2]],
+ [1, [rand, rand, rand, rand]]])
+ end
+ line_draw_time = Time.now - start
+
+ n_triangles = 2000
+ start = Time.now
+ n_triangles.times do |i|
+ x1 = rand(w)
+ y1 = rand(h)
+ fill_gradient_triangle(context,
+ x1, y1,
+ x1 + rand(w * 0.4) - w * 0.2,
+ y1 + rand(h * 0.4) - h * 0.2,
+ x1 + rand(w * 0.4) - w * 0.2,
+ y1 + rand(h * 0.4) - h * 0.2,
+ [[0, [rand, rand, rand, 0.5 + rand / 2]],
+ [0.5, [rand, rand, rand, rand]],
+ [1, [rand, rand, rand, rand]]])
+ end
+ triangle_draw_time = Time.now - start
+
+ puts "Points=%.2fK/sec, Lines=%.2fK/sec, Triangles=%.2fK/sec" %
+ [n_circles.to_f / circle_draw_time,
+ n_lines.to_f / line_draw_time,
+ n_triangles.to_f / triangle_draw_time]
+end
+
+random_mode = false
+
+area.add_events(Gdk::Event::BUTTON_PRESS_MASK)
+area.signal_connect("button-press-event") do |widget, event|
+ random_mode = !random_mode
+ widget.queue_draw
+ false
+end
+
+area.signal_connect("expose-event") do |widget, event|
+ context = widget.window.create_cairo_context
+ x, y, w, h = widget.allocation.to_a
+ if random_mode
+ draw_random_shapes(context, w, h)
+ else
+ draw_sample_shapes(context, w, h)
+ end
+ true
+end
+
+window.add(area)
+
+window.show_all
+
+Gtk.main
+
diff --git a/old/glouglou_ruby/glouglou_ruby.rb b/old/glouglou_ruby/glouglou_ruby.rb
new file mode 100755
index 0000000..c5d2af8
--- /dev/null
+++ b/old/glouglou_ruby/glouglou_ruby.rb
@@ -0,0 +1,304 @@
+#!/usr/bin/env ruby
+
+# ported from examples/aa_test.cpp in AGG source tree.
+
+top = File.expand_path(File.join(File.dirname(__FILE__), "..", ".."))
+src = File.join(top, "src")
+$LOAD_PATH.unshift src
+$LOAD_PATH.unshift File.join(src, "lib")
+
+require 'gtk2'
+
+window = Gtk::Window.new
+window.set_default_size(480, 350)
+
+area = Gtk::DrawingArea.new
+
+def stroke_line(context, x1, y1, x2, y2, line_width, dash_length)
+ context.save do
+ yield if block_given?
+ context.move_to(x1, y1)
+ context.line_to(x2, y2)
+ context.set_dash(dash_length) if dash_length > 0
+ context.line_width = line_width
+ context.line_cap = :round
+ context.stroke
+ end
+end
+
+def fill_triangle(context, x1, y1, x2, y2, x3, y3)
+ context.save do
+ yield if block_given?
+ context.triangle(x1, y1, x2, y2, x3, y3)
+ context.fill
+ end
+end
+
+def set_gradient(context, x1, y1, x2, y2, color_stops)
+ pattern = Cairo::LinearPattern.new(x1, y1, x2, y2)
+ color_stops.each do |offset, color|
+ pattern.add_color_stop(offset, color)
+ end
+ context.set_source(pattern)
+end
+
+def stroke_gradient_line(context, x1, y1, x2, y2,
+ line_width, dash_length, color_stops)
+ stroke_line(context, x1, y1, x2, y2, line_width, dash_length) do
+ set_gradient(context, x1, y1, x2, y2, color_stops)
+ end
+end
+
+def fill_gradient_triangle(context, x1, y1, x2, y2, x3, y3, color_stops)
+ fill_triangle(context, x1, y1, x2, y2, x3, y3) do
+ set_gradient(context,
+ x1, y1,
+ x3 - (x3 - x2) / 2.0,
+ y3 - (y3 - y2) / 2.0,
+ color_stops)
+ end
+end
+
+def draw_background_circle(context, w, h)
+ context.set_source_color([1.0, 1.0, 1.0, 0.2])
+
+ cx = w / 2.0
+ cy = h / 2.0
+ radius = [cx, cy].min
+ 1.upto(180) do |i|
+ n = 2 * Math::PI * i / 180.0
+ stroke_line(context,
+ cx + radius * Math.sin(n),
+ cy + radius * Math.cos(n),
+ cx, cy,
+ 1.0, i < 90 ? i : 0)
+ end
+end
+
+def draw_upper_small_circles(context, i)
+ context.circle(18 + i * 4 + 0.5,
+ 33 + 0.5,
+ i / 20.0)
+ context.fill
+
+ context.circle(18 + i * 4 + (i - 1) / 10.0 + 0.5,
+ 27 + (i - 1) / 10.0 + 0.5,
+ 0.5)
+ context.fill
+end
+
+def draw_upper_circles(context, i)
+ context.set_source_color(:white)
+ context.circle(20 + i * (i + 1) + 0.5,
+ 20.5,
+ i / 2.0)
+ context.fill
+
+ draw_upper_small_circles(context, i)
+end
+
+def draw_upper_gradient_lines(context, i)
+ stroke_gradient_line(context,
+ 20 + i * (i + 1),
+ 40.5,
+ 20 + i * (i + 1) + (i - 1) * 4,
+ 100.5,
+ i,
+ 0,
+ [[0, :white],
+ [1, [i % 2, (i % 3) * 0.5, (i % 5) * 0.25]]])
+end
+
+def draw_middle_small_circles(context, i)
+ stroke_gradient_line(context,
+ 17.5 + i * 4,
+ 107,
+ 17.5 + i * 4 + i / 6.66666667,
+ 107,
+ 1,
+ 0,
+ [[0, :red],
+ [1, :blue]])
+
+ stroke_gradient_line(context,
+ 18 + i * 4,
+ 112.5,
+ 18 + i * 4,
+ 112.5 + i / 6.66666667,
+ 1,
+ 0,
+ [[0, :red],
+ [1, :blue]])
+end
+
+def draw_middle_gradient_lines(context, i)
+ stroke_gradient_line(context,
+ 21.5,
+ 120 + (i - 1) * 3.1,
+ 52.5,
+ 120 + (i - 1) * 3.1,
+ 1,
+ 0,
+ [[0, :red],
+ [1, :white]])
+
+ stroke_gradient_line(context,
+ 52.5,
+ 118 + i * 3,
+ 83.5,
+ 118 + i * 3,
+ 2 - (i - 1) / 10.0,
+ 0,
+ [[0, :green],
+ [1, :white]])
+
+ stroke_gradient_line(context,
+ 83.5,
+ 119 + i * 3,
+ 114.5,
+ 119 + i * 3,
+ 2 - (i - 1) / 10.0,
+ 3.0,
+ [[0, :blue],
+ [1, :white]])
+end
+
+def draw_middle_white_lines(context, i)
+ context.set_source_color(:white)
+ stroke_line(context,
+ 125.5, 119.5 + (i + 2) * (i / 2.0),
+ 135.5, 119.5 + (i + 2) * (i / 2.0),
+ i, 0)
+end
+
+def draw_lower_short_lines(context, i)
+ context.set_source_color(:white)
+
+ stroke_line(context,
+ 17.5 + i * 4, 192,
+ 18.5 + i * 4, 192,
+ i / 10.0, 0)
+
+ stroke_line(context,
+ 17.5 + i * 4 + (i - 1) / 10.0, 186,
+ 18.5 + i * 4 + (i - 1) / 10.0, 186,
+ 1.0, 0)
+end
+
+def draw_right_triangles(context, i, w, h)
+ x1 = w - 150
+ x2 = w - 20
+ y_upper = h - 20 - i * (i + 2)
+ y_middle = h - 20 - i * (i + 1.5)
+ y_lower = h - 20 - i * (i + 1)
+ context.save do
+ pattern = Cairo::LinearPattern.new(x1, y_middle, x2, y_middle)
+ pattern.add_color_stop(0, :white)
+ pattern.add_color_stop(1, [i % 2, (i % 3) * 0.5, (i % 5) * 0.25])
+ context.set_source(pattern)
+ context.move_to(x1, y_middle)
+ context.line_to(x2, y_lower)
+ context.line_to(x2, y_upper)
+ context.fill
+ end
+end
+
+def draw_sample_shapes(context, w, h)
+ context.set_source_color(:black)
+ context.paint
+
+ draw_background_circle(context, w, h)
+
+ 1.upto(20) do |i|
+ draw_upper_circles(context, i)
+ draw_upper_gradient_lines(context, i)
+ draw_middle_small_circles(context, i)
+ draw_middle_gradient_lines(context, i)
+ draw_middle_white_lines(context, i) if i <= 10
+ draw_lower_short_lines(context, i)
+ end
+
+ 1.upto(13) do |i|
+ draw_right_triangles(context, i, w, h)
+ end
+end
+
+def draw_random_shapes(context, w, h)
+ srand(123)
+
+ context.set_source_color(:black)
+ context.paint
+
+ n_circles = 20000
+ start = Time.now
+ n_circles.times do |i|
+ context.circle(rand(w), rand(h), rand(20.0) + 1.0)
+ context.set_source_color([rand, rand, rand, 0.5 + rand / 2])
+ context.fill
+ end
+ circle_draw_time = Time.now - start
+
+ n_lines = 2000
+ start = Time.now
+ n_lines.times do |i|
+ x1 = rand(w)
+ y1 = rand(h)
+ stroke_gradient_line(context,
+ x1, y1,
+ x1 + rand(w * 0.5) - w * 0.25,
+ y1 + rand(h * 0.5) - h * 0.25,
+ 10.0, 0,
+ [[0, [rand, rand, rand, 0.5 + rand / 2]],
+ [1, [rand, rand, rand, rand]]])
+ end
+ line_draw_time = Time.now - start
+
+ n_triangles = 2000
+ start = Time.now
+ n_triangles.times do |i|
+ x1 = rand(w)
+ y1 = rand(h)
+ fill_gradient_triangle(context,
+ x1, y1,
+ x1 + rand(w * 0.4) - w * 0.2,
+ y1 + rand(h * 0.4) - h * 0.2,
+ x1 + rand(w * 0.4) - w * 0.2,
+ y1 + rand(h * 0.4) - h * 0.2,
+ [[0, [rand, rand, rand, 0.5 + rand / 2]],
+ [0.5, [rand, rand, rand, rand]],
+ [1, [rand, rand, rand, rand]]])
+ end
+ triangle_draw_time = Time.now - start
+
+ puts "Points=%.2fK/sec, Lines=%.2fK/sec, Triangles=%.2fK/sec" %
+ [n_circles.to_f / circle_draw_time,
+ n_lines.to_f / line_draw_time,
+ n_triangles.to_f / triangle_draw_time]
+end
+
+random_mode = false
+
+area.add_events(Gdk::Event::BUTTON_PRESS_MASK)
+area.signal_connect("button-press-event") do |widget, event|
+ random_mode = !random_mode
+ widget.queue_draw
+ false
+end
+
+area.signal_connect("expose-event") do |widget, event|
+ context = widget.window.create_cairo_context
+ x, y, w, h = widget.allocation.to_a
+ if random_mode
+ draw_random_shapes(context, w, h)
+ else
+ draw_sample_shapes(context, w, h)
+ end
+ true
+end
+
+window.add(area)
+
+window.show_all
+
+Gtk.main
+
diff --git a/old/glouglou_ruby/vars b/old/glouglou_ruby/vars
new file mode 100644
index 0000000..e7d6c90
--- /dev/null
+++ b/old/glouglou_ruby/vars
@@ -0,0 +1,2 @@
+export RUBYLIB=`find /usr/local/lib/ruby/gems/1.8/gems/ -type d -path "*/lib" |xargs |tr ' ' ':'`
+export LD_PRELOAD="/usr/lib/libpthread.so.13.1"
diff --git a/old/glougloud/BUGS.txt b/old/glougloud/BUGS.txt
new file mode 100644
index 0000000..8380e70
--- /dev/null
+++ b/old/glougloud/BUGS.txt
@@ -0,0 +1,10 @@
+#0000 TITLE
+DESCRIPTION
+
+=== TODO ===
+
+#0001 fatal() messages should be printed on stderr
+see util.c fatal()
+
+
+=== DONE ===
diff --git a/old/glougloud/Makefile b/old/glougloud/Makefile
new file mode 100644
index 0000000..d37dde2
--- /dev/null
+++ b/old/glougloud/Makefile
@@ -0,0 +1,12 @@
+PROG = glougloud
+OBJS = glougloud.o server.o user.o util.o external/imsgev.o external/imsg-buffer.o external/imsg.o
+CFLAGS+=-Wall -g
+LDFLAGS=-lpcap -levent -lutil
+
+all:
+ make $(OBJS)
+ $(CC) $(OBJS) -o $(PROG) $(LDFLAGS)
+
+clean:
+ rm -f $(PROG) *~ *.o external/*.o
+
diff --git a/old/glougloud/README.txt b/old/glougloud/README.txt
new file mode 100644
index 0000000..8a8c702
--- /dev/null
+++ b/old/glougloud/README.txt
@@ -0,0 +1,51 @@
+glougloud - glouglou daemon, for network traffic visualisation in real time
+
+
+=== Requirements ===
+
+* libglouglou
+
+Known to work on OpenBSD 5.1 and Linux 3.4
+
+
+=== Installation ===
+
+git clone git@meg:glouglou
+
+sudo useradd -d /var/empty/ -s /sbin/nologin _glougloud
+
+
+=== Usage ===
+
+* Run the daemon
+
+cd glouglou/glougloud/
+sudo ./glougloud
+
+It logs to /var/log/glougloud.
+For the moment it monitors lo0 interface.
+
+* Connect to the daemon
+
+nc -vvv -u 127.0.0.1 4430 |hexdump -C
+
+You get informations of traffic flowing on the monitored interface.
+
+
+=== Notes on architecture and security ===
+
+glougloud is architectured in 3 processes:
+ * main process, runs as root, opens the capture interfaces with
+libpcap and resolves DNS names
+ * server process, runs as _glougloud user and chrooted in _glougloud
+home, listens and accepts or refuses clients connections
+ * user process, runs as _glougloud user and chrooted in _glougloud
+home, parses the captured network traffic and sends a summary to the
+connected clients
+
+The 3 processes exchanges messages througt messages, with OpenBSD imsg
+framework.
+
+Note that glougloud activates extra protections on pcap capture only
+on OpenBSD by reimplementing some of libpcap functions, see
+glougloud.c my_pcap_open_live()
diff --git a/old/glougloud/external/README.txt b/old/glougloud/external/README.txt
new file mode 100644
index 0000000..7e7509f
--- /dev/null
+++ b/old/glougloud/external/README.txt
@@ -0,0 +1,8 @@
+- imsg framework, and IPC mechanism from OpenBSD -
+imsg-buffer.c
+imsg.c
+imsg.h
+imsgev.c
+imsgev.h
+- queue implementation, from OpenBSD -
+queue.h
diff --git a/old/glougloud/external/imsg-buffer.c b/old/glougloud/external/imsg-buffer.c
new file mode 100644
index 0000000..9f04757
--- /dev/null
+++ b/old/glougloud/external/imsg-buffer.c
@@ -0,0 +1,305 @@
+/* $OpenBSD: imsg-buffer.c,v 1.2 2012/06/02 21:46:53 gilles Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "imsg.h"
+
+int ibuf_realloc(struct ibuf *, size_t);
+void ibuf_enqueue(struct msgbuf *, struct ibuf *);
+void ibuf_dequeue(struct msgbuf *, struct ibuf *);
+
+struct ibuf *
+ibuf_open(size_t len)
+{
+ struct ibuf *buf;
+
+ if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
+ return (NULL);
+ if ((buf->buf = malloc(len)) == NULL) {
+ free(buf);
+ return (NULL);
+ }
+ buf->size = buf->max = len;
+ buf->fd = -1;
+
+ return (buf);
+}
+
+struct ibuf *
+ibuf_dynamic(size_t len, size_t max)
+{
+ struct ibuf *buf;
+
+ if (max < len)
+ return (NULL);
+
+ if ((buf = ibuf_open(len)) == NULL)
+ return (NULL);
+
+ if (max > 0)
+ buf->max = max;
+
+ return (buf);
+}
+
+int
+ibuf_realloc(struct ibuf *buf, size_t len)
+{
+ u_char *b;
+
+ /* on static buffers max is eq size and so the following fails */
+ if (buf->wpos + len > buf->max) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ b = realloc(buf->buf, buf->wpos + len);
+ if (b == NULL)
+ return (-1);
+ buf->buf = b;
+ buf->size = buf->wpos + len;
+
+ return (0);
+}
+
+int
+ibuf_add(struct ibuf *buf, const void *data, size_t len)
+{
+ if (buf->wpos + len > buf->size)
+ if (ibuf_realloc(buf, len) == -1)
+ return (-1);
+
+ memcpy(buf->buf + buf->wpos, data, len);
+ buf->wpos += len;
+ return (0);
+}
+
+void *
+ibuf_reserve(struct ibuf *buf, size_t len)
+{
+ void *b;
+
+ if (buf->wpos + len > buf->size)
+ if (ibuf_realloc(buf, len) == -1)
+ return (NULL);
+
+ b = buf->buf + buf->wpos;
+ buf->wpos += len;
+ return (b);
+}
+
+void *
+ibuf_seek(struct ibuf *buf, size_t pos, size_t len)
+{
+ /* only allowed to seek in already written parts */
+ if (pos + len > buf->wpos)
+ return (NULL);
+
+ return (buf->buf + pos);
+}
+
+size_t
+ibuf_size(struct ibuf *buf)
+{
+ return (buf->wpos);
+}
+
+size_t
+ibuf_left(struct ibuf *buf)
+{
+ return (buf->max - buf->wpos);
+}
+
+void
+ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf)
+{
+ ibuf_enqueue(msgbuf, buf);
+}
+
+int
+ibuf_write(struct msgbuf *msgbuf)
+{
+ struct iovec iov[IOV_MAX];
+ struct ibuf *buf;
+ unsigned int i = 0;
+ ssize_t n;
+
+ bzero(&iov, sizeof(iov));
+ TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
+ if (i >= IOV_MAX)
+ break;
+ iov[i].iov_base = buf->buf + buf->rpos;
+ iov[i].iov_len = buf->wpos - buf->rpos;
+ i++;
+ }
+
+again:
+ if ((n = writev(msgbuf->fd, iov, i)) == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ goto again;
+ if (errno == ENOBUFS)
+ errno = EAGAIN;
+ return (-1);
+ }
+
+ if (n == 0) { /* connection closed */
+ errno = 0;
+ return (0);
+ }
+
+ msgbuf_drain(msgbuf, n);
+
+ return (1);
+}
+
+void
+ibuf_free(struct ibuf *buf)
+{
+ free(buf->buf);
+ free(buf);
+}
+
+void
+msgbuf_init(struct msgbuf *msgbuf)
+{
+ msgbuf->queued = 0;
+ msgbuf->fd = -1;
+ TAILQ_INIT(&msgbuf->bufs);
+}
+
+void
+msgbuf_drain(struct msgbuf *msgbuf, size_t n)
+{
+ struct ibuf *buf, *next;
+
+ for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
+ buf = next) {
+ next = TAILQ_NEXT(buf, entry);
+ if (buf->rpos + n >= buf->wpos) {
+ n -= buf->wpos - buf->rpos;
+ ibuf_dequeue(msgbuf, buf);
+ } else {
+ buf->rpos += n;
+ n = 0;
+ }
+ }
+}
+
+void
+msgbuf_clear(struct msgbuf *msgbuf)
+{
+ struct ibuf *buf;
+
+ while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
+ ibuf_dequeue(msgbuf, buf);
+}
+
+int
+msgbuf_write(struct msgbuf *msgbuf)
+{
+ struct iovec iov[IOV_MAX];
+ struct ibuf *buf;
+ unsigned int i = 0;
+ ssize_t n;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ union {
+ struct cmsghdr hdr;
+ char buf[CMSG_SPACE(sizeof(int))];
+ } cmsgbuf;
+
+ bzero(&iov, sizeof(iov));
+ bzero(&msg, sizeof(msg));
+ TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
+ if (i >= IOV_MAX)
+ break;
+ iov[i].iov_base = buf->buf + buf->rpos;
+ iov[i].iov_len = buf->wpos - buf->rpos;
+ i++;
+ if (buf->fd != -1)
+ break;
+ }
+
+ msg.msg_iov = iov;
+ msg.msg_iovlen = i;
+
+ if (buf != NULL && buf->fd != -1) {
+ msg.msg_control = (caddr_t)&cmsgbuf.buf;
+ msg.msg_controllen = sizeof(cmsgbuf.buf);
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ *(int *)CMSG_DATA(cmsg) = buf->fd;
+ }
+
+again:
+ if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ goto again;
+ if (errno == ENOBUFS)
+ errno = EAGAIN;
+ return (-1);
+ }
+
+ if (n == 0) { /* connection closed */
+ errno = 0;
+ return (0);
+ }
+
+ /*
+ * assumption: fd got sent if sendmsg sent anything
+ * this works because fds are passed one at a time
+ */
+ if (buf != NULL && buf->fd != -1) {
+ close(buf->fd);
+ buf->fd = -1;
+ }
+
+ msgbuf_drain(msgbuf, n);
+
+ return (1);
+}
+
+void
+ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
+{
+ TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
+ msgbuf->queued++;
+}
+
+void
+ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
+{
+ TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
+
+ if (buf->fd != -1)
+ close(buf->fd);
+
+ msgbuf->queued--;
+ ibuf_free(buf);
+}
diff --git a/old/glougloud/external/imsg.c b/old/glougloud/external/imsg.c
new file mode 100644
index 0000000..05e57c7
--- /dev/null
+++ b/old/glougloud/external/imsg.c
@@ -0,0 +1,301 @@
+/* $OpenBSD: imsg.c,v 1.2 2012/06/02 21:46:53 gilles Exp $ */
+
+/*
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "imsg.h"
+
+int imsg_fd_overhead = 0;
+
+int imsg_get_fd(struct imsgbuf *);
+
+void
+imsg_init(struct imsgbuf *ibuf, int fd)
+{
+ msgbuf_init(&ibuf->w);
+ bzero(&ibuf->r, sizeof(ibuf->r));
+ ibuf->fd = fd;
+ ibuf->w.fd = fd;
+ ibuf->pid = getpid();
+ TAILQ_INIT(&ibuf->fds);
+}
+
+ssize_t
+imsg_read(struct imsgbuf *ibuf)
+{
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ union {
+ struct cmsghdr hdr;
+ char buf[CMSG_SPACE(sizeof(int) * 1)];
+ } cmsgbuf;
+ struct iovec iov;
+ ssize_t n = -1;
+ int fd;
+ struct imsg_fd *ifd;
+
+ bzero(&msg, sizeof(msg));
+
+ iov.iov_base = ibuf->r.buf + ibuf->r.wpos;
+ iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = &cmsgbuf.buf;
+ msg.msg_controllen = sizeof(cmsgbuf.buf);
+
+ if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL)
+ return (-1);
+
+again:
+ if (getdtablecount() + imsg_fd_overhead +
+ (CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int)
+ >= getdtablesize()) {
+ errno = EAGAIN;
+ return (-1);
+ }
+
+ if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) {
+ if (errno == EMSGSIZE)
+ goto fail;
+ if (errno != EINTR && errno != EAGAIN)
+ goto fail;
+ goto again;
+ }
+
+ ibuf->r.wpos += n;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_RIGHTS) {
+ int i;
+ int j;
+
+ /*
+ * We only accept one file descriptor. Due to C
+ * padding rules, our control buffer might contain
+ * more than one fd, and we must close them.
+ */
+ j = ((char *)cmsg + cmsg->cmsg_len -
+ (char *)CMSG_DATA(cmsg)) / sizeof(int);
+ for (i = 0; i < j; i++) {
+ fd = ((int *)CMSG_DATA(cmsg))[i];
+ if (i == 0) {
+ ifd->fd = fd;
+ TAILQ_INSERT_TAIL(&ibuf->fds, ifd,
+ entry);
+ ifd = NULL;
+ } else
+ close(fd);
+ }
+ }
+ /* we do not handle other ctl data level */
+ }
+
+fail:
+ if (ifd)
+ free(ifd);
+ return (n);
+}
+
+ssize_t
+imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
+{
+ size_t av, left, datalen;
+
+ av = ibuf->r.wpos;
+
+ if (IMSG_HEADER_SIZE > av)
+ return (0);
+
+ memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr));
+ if (imsg->hdr.len < IMSG_HEADER_SIZE ||
+ imsg->hdr.len > MAX_IMSGSIZE) {
+ errno = ERANGE;
+ return (-1);
+ }
+ if (imsg->hdr.len > av)
+ return (0);
+ datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
+ ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
+ if ((imsg->data = malloc(datalen)) == NULL)
+ return (-1);
+
+ if (imsg->hdr.flags & IMSGF_HASFD)
+ imsg->fd = imsg_get_fd(ibuf);
+ else
+ imsg->fd = -1;
+
+ memcpy(imsg->data, ibuf->r.rptr, datalen);
+
+ if (imsg->hdr.len < av) {
+ left = av - imsg->hdr.len;
+ memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left);
+ ibuf->r.wpos = left;
+ } else
+ ibuf->r.wpos = 0;
+
+ return (datalen + IMSG_HEADER_SIZE);
+}
+
+int
+imsg_compose(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
+ pid_t pid, int fd, void *data, u_int16_t datalen)
+{
+ struct ibuf *wbuf;
+
+ if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
+ return (-1);
+
+ if (imsg_add(wbuf, data, datalen) == -1)
+ return (-1);
+
+ wbuf->fd = fd;
+
+ imsg_close(ibuf, wbuf);
+
+ return (1);
+}
+
+int
+imsg_composev(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
+ pid_t pid, int fd, const struct iovec *iov, int iovcnt)
+{
+ struct ibuf *wbuf;
+ int i, datalen = 0;
+
+ for (i = 0; i < iovcnt; i++)
+ datalen += iov[i].iov_len;
+
+ if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
+ return (-1);
+
+ for (i = 0; i < iovcnt; i++)
+ if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
+ return (-1);
+
+ wbuf->fd = fd;
+
+ imsg_close(ibuf, wbuf);
+
+ return (1);
+}
+
+/* ARGSUSED */
+struct ibuf *
+imsg_create(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid,
+ pid_t pid, u_int16_t datalen)
+{
+ struct ibuf *wbuf;
+ struct imsg_hdr hdr;
+
+ datalen += IMSG_HEADER_SIZE;
+ if (datalen > MAX_IMSGSIZE) {
+ errno = ERANGE;
+ return (NULL);
+ }
+
+ hdr.type = type;
+ hdr.flags = 0;
+ hdr.peerid = peerid;
+ if ((hdr.pid = pid) == 0)
+ hdr.pid = ibuf->pid;
+ if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
+ return (NULL);
+ }
+ if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
+ return (NULL);
+
+ return (wbuf);
+}
+
+int
+imsg_add(struct ibuf *msg, void *data, u_int16_t datalen)
+{
+ if (datalen)
+ if (ibuf_add(msg, data, datalen) == -1) {
+ ibuf_free(msg);
+ return (-1);
+ }
+ return (datalen);
+}
+
+void
+imsg_close(struct imsgbuf *ibuf, struct ibuf *msg)
+{
+ struct imsg_hdr *hdr;
+
+ hdr = (struct imsg_hdr *)msg->buf;
+
+ hdr->flags &= ~IMSGF_HASFD;
+ if (msg->fd != -1)
+ hdr->flags |= IMSGF_HASFD;
+
+ hdr->len = (u_int16_t)msg->wpos;
+
+ ibuf_close(&ibuf->w, msg);
+}
+
+void
+imsg_free(struct imsg *imsg)
+{
+ free(imsg->data);
+}
+
+int
+imsg_get_fd(struct imsgbuf *ibuf)
+{
+ int fd;
+ struct imsg_fd *ifd;
+
+ if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL)
+ return (-1);
+
+ fd = ifd->fd;
+ TAILQ_REMOVE(&ibuf->fds, ifd, entry);
+ free(ifd);
+
+ return (fd);
+}
+
+int
+imsg_flush(struct imsgbuf *ibuf)
+{
+ while (ibuf->w.queued)
+ if (msgbuf_write(&ibuf->w) < 0)
+ return (-1);
+ return (0);
+}
+
+void
+imsg_clear(struct imsgbuf *ibuf)
+{
+ int fd;
+
+ msgbuf_clear(&ibuf->w);
+ while ((fd = imsg_get_fd(ibuf)) != -1)
+ close(fd);
+}
diff --git a/old/glougloud/external/imsg.h b/old/glougloud/external/imsg.h
new file mode 100644
index 0000000..25cc2c4
--- /dev/null
+++ b/old/glougloud/external/imsg.h
@@ -0,0 +1,118 @@
+/* $OpenBSD: imsg.h,v 1.2 2010/06/23 07:53:55 nicm Exp $ */
+
+/*
+ * Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
+ * Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _IMSG_H_
+#define _IMSG_H_
+
+/* compat hacks added by laurent */
+#if !defined(__OpenBSD__)
+#define IOV_MAX 1024
+#define getdtablecount(x) 3
+#endif
+
+#define IBUF_READ_SIZE 65535
+#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr)
+#define MAX_IMSGSIZE 16384
+
+struct ibuf {
+ TAILQ_ENTRY(ibuf) entry;
+ u_char *buf;
+ size_t size;
+ size_t max;
+ size_t wpos;
+ size_t rpos;
+ int fd;
+};
+
+struct msgbuf {
+ TAILQ_HEAD(, ibuf) bufs;
+ u_int32_t queued;
+ int fd;
+};
+
+struct ibuf_read {
+ u_char buf[IBUF_READ_SIZE];
+ u_char *rptr;
+ size_t wpos;
+};
+
+struct imsg_fd {
+ TAILQ_ENTRY(imsg_fd) entry;
+ int fd;
+};
+
+struct imsgbuf {
+ TAILQ_HEAD(, imsg_fd) fds;
+ struct ibuf_read r;
+ struct msgbuf w;
+ int fd;
+ pid_t pid;
+};
+
+#define IMSGF_HASFD 1
+
+struct imsg_hdr {
+ u_int32_t type;
+ u_int16_t len;
+ u_int16_t flags;
+ u_int32_t peerid;
+ u_int32_t pid;
+};
+
+struct imsg {
+ struct imsg_hdr hdr;
+ int fd;
+ void *data;
+};
+
+
+/* buffer.c */
+struct ibuf *ibuf_open(size_t);
+struct ibuf *ibuf_dynamic(size_t, size_t);
+int ibuf_add(struct ibuf *, const void *, size_t);
+void *ibuf_reserve(struct ibuf *, size_t);
+void *ibuf_seek(struct ibuf *, size_t, size_t);
+size_t ibuf_size(struct ibuf *);
+size_t ibuf_left(struct ibuf *);
+void ibuf_close(struct msgbuf *, struct ibuf *);
+int ibuf_write(struct msgbuf *);
+void ibuf_free(struct ibuf *);
+void msgbuf_init(struct msgbuf *);
+void msgbuf_clear(struct msgbuf *);
+int msgbuf_write(struct msgbuf *);
+void msgbuf_drain(struct msgbuf *, size_t);
+
+/* imsg.c */
+void imsg_init(struct imsgbuf *, int);
+ssize_t imsg_read(struct imsgbuf *);
+ssize_t imsg_get(struct imsgbuf *, struct imsg *);
+int imsg_compose(struct imsgbuf *, u_int32_t, u_int32_t, pid_t,
+ int, void *, u_int16_t);
+int imsg_composev(struct imsgbuf *, u_int32_t, u_int32_t, pid_t,
+ int, const struct iovec *, int);
+struct ibuf *imsg_create(struct imsgbuf *, u_int32_t, u_int32_t, pid_t,
+ u_int16_t);
+int imsg_add(struct ibuf *, void *, u_int16_t);
+void imsg_close(struct imsgbuf *, struct ibuf *);
+void imsg_free(struct imsg *);
+int imsg_flush(struct imsgbuf *);
+void imsg_clear(struct imsgbuf *);
+
+#endif
diff --git a/old/glougloud/external/imsgev.c b/old/glougloud/external/imsgev.c
new file mode 100644
index 0000000..6b92d79
--- /dev/null
+++ b/old/glougloud/external/imsgev.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2009 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "imsgev.h"
+
+void imsgev_add(struct imsgev *);
+void imsgev_dispatch(int, short, void *);
+void imsgev_disconnect(struct imsgev *, int);
+
+void
+imsgev_init(struct imsgev *iev, int fd, void *data,
+ void (*callback)(struct imsgev *, int, struct imsg *),
+ void (*needfd)(struct imsgev *))
+{
+ imsg_init(&iev->ibuf, fd);
+ iev->terminate = 0;
+
+ iev->data = data;
+ iev->handler = imsgev_dispatch;
+ iev->callback = callback;
+ iev->needfd = needfd;
+
+ iev->events = EV_READ;
+ event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev);
+ event_add(&iev->ev, NULL);
+}
+
+int
+imsgev_compose(struct imsgev *iev, u_int16_t type, u_int32_t peerid,
+ uint32_t pid, int fd, void *data, u_int16_t datalen)
+{
+ int r;
+
+ r = imsg_compose(&iev->ibuf, type, peerid, pid, fd, data, datalen);
+ if (r != -1)
+ imsgev_add(iev);
+
+ return (r);
+}
+
+void
+imsgev_close(struct imsgev *iev)
+{
+ iev->terminate = 1;
+ imsgev_add(iev);
+}
+
+void
+imsgev_clear(struct imsgev *iev)
+{
+ event_del(&iev->ev);
+ msgbuf_clear(&iev->ibuf.w);
+ close(iev->ibuf.fd);
+}
+
+void
+imsgev_add(struct imsgev *iev)
+{
+ short events = 0;
+
+ if (!iev->terminate)
+ events = EV_READ;
+ if (iev->ibuf.w.queued || iev->terminate)
+ events |= EV_WRITE;
+
+ /* optimization: skip event_{del/set/add} if already set */
+ if (events == iev->events)
+ return;
+
+ iev->events = events;
+ event_del(&iev->ev);
+ event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev);
+ event_add(&iev->ev, NULL);
+}
+
+void
+imsgev_dispatch(int fd, short ev, void *humppa)
+{
+ struct imsgev *iev = humppa;
+ struct imsgbuf *ibuf = &iev->ibuf;
+ struct imsg imsg;
+ ssize_t n;
+
+ iev->events = 0;
+
+ if (ev & EV_READ) {
+ if ((n = imsg_read(ibuf)) == -1) {
+ /* if we don't have enough fds, free one up and retry */
+ if (errno == EAGAIN) {
+ iev->needfd(iev);
+ n = imsg_read(ibuf);
+ }
+
+ if (n == -1) {
+ imsgev_disconnect(iev, IMSGEV_EREAD);
+ return;
+ }
+ }
+ if (n == 0) {
+ /*
+ * Connection is closed for reading, and we assume
+ * it is also closed for writing, so we error out
+ * if write data is pending.
+ */
+ imsgev_disconnect(iev,
+ (iev->ibuf.w.queued) ? IMSGEV_EWRITE : IMSGEV_DONE);
+ return;
+ }
+ }
+
+ if (ev & EV_WRITE) {
+ /*
+ * We wanted to write data out but the connection is either
+ * closed, or some error occured. Both case are not recoverable
+ * from the imsg perspective, so we treat it as a WRITE error.
+ */
+ if ((n = msgbuf_write(&ibuf->w)) != 1) {
+ imsgev_disconnect(iev, IMSGEV_EWRITE);
+ return;
+ }
+ }
+
+ while (iev->terminate == 0) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1) {
+ imsgev_disconnect(iev, IMSGEV_EIMSG);
+ return;
+ }
+ if (n == 0)
+ break;
+ iev->callback(iev, IMSGEV_IMSG, &imsg);
+ imsg_free(&imsg);
+ }
+
+ if (iev->terminate && iev->ibuf.w.queued == 0) {
+ imsgev_disconnect(iev, IMSGEV_DONE);
+ return;
+ }
+
+ imsgev_add(iev);
+}
+
+void
+imsgev_disconnect(struct imsgev *iev, int code)
+{
+ iev->callback(iev, code, NULL);
+}
diff --git a/old/glougloud/external/imsgev.h b/old/glougloud/external/imsgev.h
new file mode 100644
index 0000000..a0d0947
--- /dev/null
+++ b/old/glougloud/external/imsgev.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2009 Eric Faurot <eric@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __IMSGEV_H__
+#define __IMSGEV_H__
+
+#include <event.h>
+#include "imsg.h"
+
+#define IMSG_LEN(m) ((m)->hdr.len - IMSG_HEADER_SIZE)
+
+struct imsgev {
+ struct imsgbuf ibuf;
+ void (*handler)(int, short, void *);
+ struct event ev;
+ void *data;
+ short events;
+ int terminate;
+ void (*callback)(struct imsgev *, int, struct imsg *);
+ void (*needfd)(struct imsgev *);
+};
+
+#define IMSGEV_IMSG 0
+#define IMSGEV_DONE 1
+#define IMSGEV_EREAD 2
+#define IMSGEV_EWRITE 3
+#define IMSGEV_EIMSG 4
+
+void imsgev_init(struct imsgev *, int, void *, void (*)(struct imsgev *,
+ int, struct imsg *), void (*)(struct imsgev *));
+int imsgev_compose(struct imsgev *, u_int16_t, u_int32_t, u_int32_t, int,
+ void *, u_int16_t);
+void imsgev_close(struct imsgev *);
+void imsgev_clear(struct imsgev *);
+
+#endif /* __IMSGEV_H__ */
diff --git a/old/glougloud/external/queue.h b/old/glougloud/external/queue.h
new file mode 100644
index 0000000..622301d
--- /dev/null
+++ b/old/glougloud/external/queue.h
@@ -0,0 +1,568 @@
+/* $OpenBSD: queue.h,v 1.36 2012/04/11 13:29:14 naddy Exp $ */
+/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
+#define _Q_INVALIDATE(a) (a) = ((void *)-1)
+#else
+#define _Q_INVALIDATE(a)
+#endif
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_END(head) NULL
+#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_FOREACH(var, head, field) \
+ for((var) = SLIST_FIRST(head); \
+ (var) != SLIST_END(head); \
+ (var) = SLIST_NEXT(var, field))
+
+#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SLIST_FIRST(head); \
+ (var) && ((tvar) = SLIST_NEXT(var, field), 1); \
+ (var) = (tvar))
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) { \
+ SLIST_FIRST(head) = SLIST_END(head); \
+}
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (0)
+
+#define SLIST_REMOVE_AFTER(elm, field) do { \
+ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->slh_first; \
+ \
+ while (curelm->field.sle_next != (elm)) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ _Q_INVALIDATE((elm)->field.sle_next); \
+ } \
+} while (0)
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List access methods
+ */
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_END(head) NULL
+#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field) \
+ for((var) = LIST_FIRST(head); \
+ (var)!= LIST_END(head); \
+ (var) = LIST_NEXT(var, field))
+
+#define LIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = LIST_FIRST(head); \
+ (var) && ((tvar) = LIST_NEXT(var, field), 1); \
+ (var) = (tvar))
+
+/*
+ * List functions.
+ */
+#define LIST_INIT(head) do { \
+ LIST_FIRST(head) = LIST_END(head); \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.le_next = (head)->lh_first) != NULL) \
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (0)
+
+#define LIST_REMOVE(elm, field) do { \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+ _Q_INVALIDATE((elm)->field.le_prev); \
+ _Q_INVALIDATE((elm)->field.le_next); \
+} while (0)
+
+#define LIST_REPLACE(elm, elm2, field) do { \
+ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
+ (elm2)->field.le_next->field.le_prev = \
+ &(elm2)->field.le_next; \
+ (elm2)->field.le_prev = (elm)->field.le_prev; \
+ *(elm2)->field.le_prev = (elm2); \
+ _Q_INVALIDATE((elm)->field.le_prev); \
+ _Q_INVALIDATE((elm)->field.le_next); \
+} while (0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_END(head) NULL
+#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for((var) = SIMPLEQ_FIRST(head); \
+ (var) != SIMPLEQ_END(head); \
+ (var) = SIMPLEQ_NEXT(var, field))
+
+#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SIMPLEQ_FIRST(head); \
+ (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \
+ (var) = (tvar))
+
+/*
+ * Simple queue functions.
+ */
+#define SIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+} while (0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+} while (0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (0)
+
+#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
+ == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (0)
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * tail queue access methods
+ */
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_END(head) NULL
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+/* XXX */
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define TAILQ_EMPTY(head) \
+ (TAILQ_FIRST(head) == TAILQ_END(head))
+
+#define TAILQ_FOREACH(var, head, field) \
+ for((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_NEXT(var, field))
+
+#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = TAILQ_FIRST(head); \
+ (var) != TAILQ_END(head) && \
+ ((tvar) = TAILQ_NEXT(var, field), 1); \
+ (var) = (tvar))
+
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for((var) = TAILQ_LAST(head, headname); \
+ (var) != TAILQ_END(head); \
+ (var) = TAILQ_PREV(var, headname, field))
+
+#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
+ for ((var) = TAILQ_LAST(head, headname); \
+ (var) != TAILQ_END(head) && \
+ ((tvar) = TAILQ_PREV(var, headname, field), 1); \
+ (var) = (tvar))
+
+/*
+ * Tail queue functions.
+ */
+#define TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+ _Q_INVALIDATE((elm)->field.tqe_prev); \
+ _Q_INVALIDATE((elm)->field.tqe_next); \
+} while (0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
+ (elm2)->field.tqe_next->field.tqe_prev = \
+ &(elm2)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm2)->field.tqe_next; \
+ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
+ *(elm2)->field.tqe_prev = (elm2); \
+ _Q_INVALIDATE((elm)->field.tqe_prev); \
+ _Q_INVALIDATE((elm)->field.tqe_next); \
+} while (0)
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head) \
+ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue access methods
+ */
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+#define CIRCLEQ_END(head) ((void *)(head))
+#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
+#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
+#define CIRCLEQ_EMPTY(head) \
+ (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head))
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for((var) = CIRCLEQ_FIRST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_NEXT(var, field))
+
+#define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = CIRCLEQ_FIRST(head); \
+ (var) != CIRCLEQ_END(head) && \
+ ((tvar) = CIRCLEQ_NEXT(var, field), 1); \
+ (var) = (tvar))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for((var) = CIRCLEQ_LAST(head); \
+ (var) != CIRCLEQ_END(head); \
+ (var) = CIRCLEQ_PREV(var, field))
+
+#define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
+ for ((var) = CIRCLEQ_LAST(head, headname); \
+ (var) != CIRCLEQ_END(head) && \
+ ((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \
+ (var) = (tvar))
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = CIRCLEQ_END(head); \
+ (head)->cqh_last = CIRCLEQ_END(head); \
+} while (0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = CIRCLEQ_END(head); \
+ if ((head)->cqh_last == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.cqe_next = CIRCLEQ_END(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (0)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+ _Q_INVALIDATE((elm)->field.cqe_prev); \
+ _Q_INVALIDATE((elm)->field.cqe_next); \
+} while (0)
+
+#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_last = (elm2); \
+ else \
+ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \
+ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \
+ CIRCLEQ_END(head)) \
+ (head).cqh_first = (elm2); \
+ else \
+ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \
+ _Q_INVALIDATE((elm)->field.cqe_prev); \
+ _Q_INVALIDATE((elm)->field.cqe_next); \
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/old/glougloud/glougloud.c b/old/glougloud/glougloud.c
new file mode 100644
index 0000000..49a1608
--- /dev/null
+++ b/old/glougloud/glougloud.c
@@ -0,0 +1,487 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <netdb.h>
+#include <pcap.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <err.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+
+#include "glougloud.h"
+#include "external/imsgev.h"
+#if defined(__OpenBSD__)
+#include "pcap-int.h"
+#endif
+
+struct server_proc {
+ pid_t pid;
+ int fd[2];
+ struct imsgev iev;
+};
+struct user_proc {
+ pid_t pid;
+ int fd[2];
+ struct imsgev iev;
+};
+enum user_status {
+ USER_REQUESTED,
+ USER_ACCEPTED
+};
+struct user {
+ LIST_ENTRY(user) entry;
+ struct sockaddr_in addr;
+ enum user_status status;
+};
+
+// XXX CONF
+#define PCAP_INTERFACE "lo"
+#define PCAP_SNAPLEN 100
+#define PCAP_FILTER "not port 4430 and not port 53"
+
+static void imsgev_server(struct imsgev *, int, struct imsg *);
+static void imsgev_user(struct imsgev *, int, struct imsg *);
+static void imsgev_server_needfd(struct imsgev *);
+static void imsgev_user_needfd(struct imsgev *);
+
+struct server_proc *srv_proc;
+struct user_proc *usr_proc;
+LIST_HEAD(, user) usr_list;
+int net_socket;
+
+#if defined(__OPENBSD__)
+void __dead
+#else
+void
+#endif
+usage(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-vi]",
+ __progname);
+ exit(1);
+}
+
+static void
+sig_handler(int sig, short why, void *data)
+{
+ log_info("got signal %d", sig);
+ if (sig == SIGINT || sig == SIGTERM)
+ event_loopexit(NULL);
+}
+
+/*
+ * reimplement pcap_open_live with more restrictions on the bpf fd :
+ * - open device read only
+ * - lock the fd
+ * based on OpenBSD tcpdump, privsep_pcap.c v1.16
+ */
+
+static pcap_t *
+my_pcap_open_live(const char *dev, int slen, int promisc, int to_ms,
+ char *ebuf, u_int dlt, u_int dirfilt)
+{
+#if defined(__OpenBSD__)
+ struct bpf_version bv;
+ u_int v;
+ pcap_t *p;
+ char bpf[sizeof "/dev/bpf0000000000"];
+ int fd, n = 0;
+ struct ifreq ifr;
+
+ p = xmalloc(sizeof(*p));
+ bzero(p, sizeof(*p));
+
+ /* priv part */
+
+ do {
+ snprintf(bpf, sizeof(bpf), "/dev/bpf%d", n++);
+ fd = open(bpf, O_RDONLY);
+ } while (fd < 0 && errno == EBUSY);
+ if (fd < 0)
+ return NULL;
+
+ v = 32768; /* XXX this should be a user-accessible hook */
+ ioctl(fd, BIOCSBLEN, &v);
+
+ strlcpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
+ if (ioctl(fd, BIOCSETIF, &ifr) < 0)
+ return NULL;
+
+ if (dlt != (u_int) -1 && ioctl(fd, BIOCSDLT, &dlt))
+ return NULL;
+
+ if (promisc)
+ /* this is allowed to fail */
+ ioctl(fd, BIOCPROMISC, NULL);
+ if (ioctl(fd, BIOCSDIRFILT, &dirfilt) < 0)
+ return NULL;
+
+ /* lock the descriptor */
+ if (ioctl(fd, BIOCLOCK, NULL) < 0)
+ return NULL;
+
+ /* end of priv part */
+
+ /* fd is locked, can only use 'safe' ioctls */
+ if (ioctl(fd, BIOCVERSION, &bv) < 0) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCVERSION: %s",
+ pcap_strerror(errno));
+ return NULL;
+ }
+
+ if (bv.bv_major != BPF_MAJOR_VERSION ||
+ bv.bv_minor < BPF_MINOR_VERSION) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ "kernel bpf filter out of date");
+ return NULL;
+ }
+
+ p->fd = fd;
+ p->snapshot = slen;
+
+ /* Get the data link layer type. */
+ if (ioctl(fd, BIOCGDLT, &v) < 0) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGDLT: %s",
+ pcap_strerror(errno));
+ return NULL;
+ }
+#if _BSDI_VERSION - 0 >= 199510
+ /* The SLIP and PPP link layer header changed in BSD/OS 2.1 */
+ switch (v) {
+
+ case DLT_SLIP:
+ v = DLT_SLIP_BSDOS;
+ break;
+
+ case DLT_PPP:
+ v = DLT_PPP_BSDOS;
+ break;
+ }
+#endif
+ p->linktype = v;
+
+ /* XXX hack from tcpdump */
+ if (p->linktype == DLT_PFLOG && p->snapshot < 160)
+ p->snapshot = 160;
+
+ /* set timeout */
+ if (to_ms != 0) {
+ struct timeval to;
+ to.tv_sec = to_ms / 1000;
+ to.tv_usec = (to_ms * 1000) % 1000000;
+ if (ioctl(p->fd, BIOCSRTIMEOUT, &to) < 0) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCSRTIMEOUT: %s",
+ pcap_strerror(errno));
+ return NULL;
+ }
+ }
+
+ if (ioctl(fd, BIOCGBLEN, &v) < 0) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "BIOCGBLEN: %s",
+ pcap_strerror(errno));
+ return NULL;
+ }
+ p->bufsize = v;
+ p->buffer = (u_char *)malloc(p->bufsize);
+ if (p->buffer == NULL) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
+ pcap_strerror(errno));
+ return NULL;
+ }
+ return p;
+#else /* defined(__OpenBSD__) */
+ return pcap_open_live(dev, slen, promisc, to_ms, ebuf);
+#endif
+}
+
+int
+main(int argc, char **argv)
+{
+ struct sockaddr_in sock_addr;
+ struct event ev_sigint, ev_sigterm, ev_sigchld, ev_sighup;
+ char errbuf[PCAP_ERRBUF_SIZE];
+ struct bpf_program bprog;
+ pcap_t *pcap;
+ int loglevel = 0, logpinvalid = 0;
+ int op;
+ int sock_on = 1;
+
+ if (geteuid() != 0)
+ errx(1, "need root privileges");
+
+ while ((op = getopt(argc, argv, "hvi")) != -1) {
+ switch (op) {
+ case 'h':
+ usage();
+ /* NOTREACHED */
+ case 'v':
+ loglevel++;
+ break;
+ case 'i':
+ logpinvalid = 1;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ log_init(loglevel, logpinvalid);
+ event_init();
+
+ net_socket = socket(AF_INET, SOCK_DGRAM, 0);
+ if (net_socket < 0)
+ fatal("server: socket failed");
+ setsockopt(net_socket, SOL_SOCKET, SO_REUSEADDR,
+ &sock_on, sizeof(sock_on));
+ memset(&sock_addr, 0, sizeof(sock_addr));
+ sock_addr.sin_family = AF_INET;
+ sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ sock_addr.sin_port = htons(4430);
+ if (bind(net_socket, (struct sockaddr *)&sock_addr,
+ sizeof(sock_addr)) != 0)
+ fatal("server: socket bind failed, %s", strerror(errno));
+ fd_nonblock(net_socket);
+
+ srv_proc = xmalloc(sizeof(struct server_proc));
+ socketpair_prepare(srv_proc->fd);
+ srv_proc->pid = server_init(srv_proc->fd);
+ close(srv_proc->fd[1]);
+ imsgev_init(&srv_proc->iev, srv_proc->fd[0], NULL, imsgev_server, imsgev_server_needfd);
+
+ pcap = my_pcap_open_live(PCAP_INTERFACE, PCAP_SNAPLEN, 1, PCAP_TO, errbuf, -1, 0);
+ if (pcap == NULL)
+ fatal("capture: pcap_open_live failed on interface %s\n"
+ "with snaplen %d : %s",
+ PCAP_INTERFACE, PCAP_SNAPLEN, errbuf);
+ if (pcap_compile(pcap, &bprog, PCAP_FILTER, 0, 0) < 0)
+ fatal("capture: pcap_compile failed with filter %s : %s",
+ PCAP_FILTER, pcap_geterr(pcap));
+ if (pcap_setfilter(pcap, &bprog) < 0)
+ fatal("capture: pcap_setfilter failed : %s",
+ pcap_geterr(pcap));
+
+ usr_proc = xmalloc(sizeof(struct user_proc));
+ socketpair_prepare(usr_proc->fd);
+ usr_proc->pid = user_init(usr_proc->fd, pcap);
+ close(usr_proc->fd[1]);
+ imsgev_init(&usr_proc->iev, usr_proc->fd[0], NULL, imsgev_user, imsgev_user_needfd);
+
+ signal_set(&ev_sigint, SIGINT, sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, sig_handler, NULL);
+ signal_set(&ev_sigchld, SIGCHLD, sig_handler, NULL);
+ signal_set(&ev_sighup, SIGHUP, sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal_add(&ev_sigchld, NULL);
+ signal_add(&ev_sighup, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ log_info("entering event loop");
+ event_dispatch();
+
+ log_info("exiting");
+ exit(0);
+}
+
+static struct user *
+finduser(struct sockaddr_in *remote) {
+ struct user *usr;
+ struct sockaddr_in *u;
+
+ LIST_FOREACH(usr, &usr_list, entry) {
+ u = &usr->addr;
+ if (u->sin_addr.s_addr == remote->sin_addr.s_addr &&
+ u->sin_port == remote->sin_port)
+ return usr;
+ }
+ return NULL;
+}
+
+static void
+srvconn(struct imsgev *iev, struct imsg *imsg)
+{
+ struct imsg_srvconn *req;
+ struct imsg_srvconn res;
+ struct imsg_usrconn req2;
+ struct user *usr;
+
+ req = imsg->data;
+
+ if (req->deco) {
+ log_tmp("srvconn deco");
+ usr = finduser(&req->addr);
+ if (!usr)
+ fatal("trying to deco an inexistant user !");
+
+ addrcpy(&req2.addr, &usr->addr);
+ req2.deco = 1;
+ imsgev_compose(&usr_proc->iev, IMSG_USRCONN_REQ, 0, 0, -1,
+ &req2, sizeof(req2));
+
+ LIST_REMOVE(usr, entry);
+ free(usr);
+
+ return;
+ }
+
+ if (req->addr.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) {
+ log_tmp("srvconn accepted");
+
+ usr = xmalloc(sizeof(struct user));
+ addrcpy(&usr->addr, &req->addr);
+ usr->status = USER_REQUESTED;
+ LIST_INSERT_HEAD(&usr_list, usr, entry);
+
+ addrcpy(&req2.addr, &req->addr);
+ req2.deco = 0;
+ imsgev_compose(&usr_proc->iev, IMSG_USRCONN_REQ, 0, 0, -1,
+ &req2, sizeof(req2));
+ }
+ else
+ {
+ log_tmp("srvconn refused");
+ res.ok = 0;
+ addrcpy(&res.addr, &req->addr);
+ imsgev_compose(iev, IMSG_SRVCONN_RES, 0, 0, -1,
+ &res, sizeof(res));
+ }
+
+}
+
+static void
+imsgev_server(struct imsgev *iev, int code, struct imsg *imsg)
+{
+ switch (code) {
+ case IMSGEV_IMSG:
+ log_debug("%s, got imsg %i on fd %i",
+ __func__, imsg->hdr.type, iev->ibuf.fd);
+ switch (imsg->hdr.type) {
+ case IMSG_SRVCONN_REQ:
+ srvconn(iev, imsg);
+ break;
+ default:
+ fatal("%s, unexpected imsg %d",
+ __func__, imsg->hdr.type);
+ }
+ break;
+ case IMSGEV_EREAD:
+ case IMSGEV_EWRITE:
+ case IMSGEV_EIMSG:
+ fatal("imsgev server read/write error");
+ /* NOTREACHED */
+ break;
+ case IMSGEV_DONE:
+ event_loopexit(NULL);
+ break;
+ }
+}
+
+static void
+usrconn(struct imsgev *iev, struct imsg *imsg)
+{
+ struct user *usr;
+ struct imsg_usrconn *res;
+ struct imsg_srvconn res2;
+
+ res = imsg->data;
+
+ usr = finduser(&res->addr);
+ if (!usr || usr->status != USER_REQUESTED)
+ fatal("received usrconn result that wasn't initiated !");
+ usr->status = USER_ACCEPTED;
+
+ addrcpy(&res2.addr, &res->addr);
+ res2.ok = 1;
+ imsgev_compose(&srv_proc->iev, IMSG_SRVCONN_RES, 0, 0, -1,
+ &res2, sizeof(res2));
+}
+
+/* XXX drop imsg when msgbuf->queued is too high ? */
+/* XXX review that func for correctness and security */
+static void
+usrdns(struct imsgev *iev, struct imsg *imsg)
+{
+ struct imsg_usrdns_req *req;
+ struct imsg_usrdns_res res;
+ struct hostent *ent;
+ int len;
+ char *name;
+ extern int h_errno;
+ struct in_addr addr;
+
+ req = imsg->data;
+
+ res.addr.s_addr = req->addr.s_addr;
+ addr.s_addr = htonl(req->addr.s_addr);
+ ent = gethostbyaddr(&addr, sizeof(addr), AF_INET);
+ if (ent) {
+ len = strlen(ent->h_name);
+ if (len > DNSNAME_MAX)
+ name = ent->h_name - DNSNAME_MAX; /* keep right part */
+ else
+ name = ent->h_name;
+ strncpy(res.name, name, sizeof(res.name));
+ } else {
+ log_debug("usrdns failed for %x : %s",
+ addr, hstrerror(h_errno));
+ res.name[0] = '\0';
+ }
+
+ imsgev_compose(&usr_proc->iev, IMSG_USRDNS_RES, 0, 0, -1,
+ &res, sizeof(res));
+}
+
+static void
+imsgev_user(struct imsgev *iev, int code, struct imsg *imsg)
+{
+ switch (code) {
+ case IMSGEV_IMSG:
+ log_debug("%s, got imsg %i on fd %i",
+ __func__, imsg->hdr.type, iev->ibuf.fd);
+ switch (imsg->hdr.type) {
+ case IMSG_USRCONN_RES:
+ usrconn(iev, imsg);
+ break;
+ case IMSG_USRDNS_REQ:
+ usrdns(iev, imsg);
+ break;
+ default:
+ fatal("%s, unexpected imsg %d",
+ __func__, imsg->hdr.type);
+ }
+ break;
+ case IMSGEV_EREAD:
+ case IMSGEV_EWRITE:
+ case IMSGEV_EIMSG:
+ fatal("imsgev user read/write error");
+ /* NOTREACHED */
+ break;
+ case IMSGEV_DONE:
+ event_loopexit(NULL);
+ break;
+ }
+}
+
+static void
+imsgev_server_needfd(struct imsgev *iev)
+{
+ fatal("imsgev_server_needfd");
+}
+
+static void
+imsgev_user_needfd(struct imsgev *iev)
+{
+ fatal("imsgev_user_needfd");
+}
diff --git a/old/glougloud/glougloud.h b/old/glougloud/glougloud.h
new file mode 100644
index 0000000..c4f0a4a
--- /dev/null
+++ b/old/glougloud/glougloud.h
@@ -0,0 +1,79 @@
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <pcap.h>
+
+#include <libglouglou.h>
+
+#include "external/queue.h"
+
+#if !defined(__OpenBSD__)
+#define setproctitle(x) {}
+#endif
+
+#define GLOUGLOUD_USER "_glougloud"
+#define PCAP_COUNT 20
+#define PCAP_TO 300
+
+/* ipc */
+
+enum imsg_type {
+ IMSG_SRVCONN_REQ,
+ IMSG_SRVCONN_RES,
+ IMSG_USRCONN_REQ,
+ IMSG_USRCONN_RES,
+ IMSG_USRDNS_REQ,
+ IMSG_USRDNS_RES
+};
+
+/* XXX restore the _req _res structs, so that we have less bytes going on
+ * the pipe */
+struct imsg_srvconn {
+ struct sockaddr_in addr;
+ u_short ok;
+ u_short deco;
+};
+struct imsg_usrconn {
+ struct sockaddr_in addr;
+ u_short ok;
+ u_short deco;
+};
+struct imsg_usrdns_req {
+ struct in_addr addr;
+};
+struct imsg_usrdns_res {
+ struct in_addr addr;
+ char name[DNSNAME_MAX];
+};
+
+/* glougloud.c */
+
+extern int net_socket;
+
+/* server.c */
+
+int server_init(int *);
+
+/* user.c */
+
+int user_init(int *, pcap_t *pcap);
+
+/* util.c */
+
+void log_init(int, int);
+void log_pinvalid(const char *, ...);
+void log_tmp(const char *, ...);
+void log_debug(const char *, ...);
+void log_info(const char *, ...);
+void log_warn(const char *, ...);
+void fatal(const char *, ...);
+
+void *xmalloc(size_t);
+void *xcalloc(size_t, size_t);
+void fd_nonblock(int);
+void droppriv();
+void addrcpy(struct sockaddr_in *, struct sockaddr_in *);
+void socketpair_prepare(int *);
+
diff --git a/old/glougloud/server.c b/old/glougloud/server.c
new file mode 100644
index 0000000..4fb0be6
--- /dev/null
+++ b/old/glougloud/server.c
@@ -0,0 +1,238 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <err.h>
+#include <errno.h>
+#include <time.h>
+
+#include "glougloud.h"
+#include "external/imsgev.h"
+
+#define USER_TIMEOUT 300
+#define USERTIMER 10
+
+struct server {
+ struct imsgev iev;
+ struct event ev;
+
+ struct event usrtimer_ev;
+ struct timeval usrtimer_tv;
+ time_t time;
+};
+enum user_status {
+ USER_REQUESTED,
+ USER_ACCEPTED,
+ USER_REFUSED
+};
+struct user {
+ LIST_ENTRY(user) entry;
+ struct sockaddr_in addr;
+ enum user_status status;
+ time_t lastseen;
+};
+
+static void imsgev_main(struct imsgev *, int, struct imsg *);
+static void imsgev_main_needfd(struct imsgev *);
+static void receive(int, short, void *);
+static void ev_usrtimer(int, short, void *);
+
+struct server *srv;
+LIST_HEAD(, user) usr_list;
+
+static void
+sig_handler(int sig, short why, void *data)
+{
+ log_info("server: got signal %d", sig);
+ if (sig == SIGINT || sig == SIGTERM)
+ event_loopexit(NULL);
+}
+
+int
+server_init(int fd[2])
+{
+ struct event ev_sigint, ev_sigterm, ev_sigchld, ev_sighup;
+ int pid;
+
+ pid = fork();
+ if (pid < 0)
+ fatal("server fork");
+ if (pid > 0)
+ return pid;
+
+ setproctitle("server");
+ event_init();
+ close(fd[0]);
+
+ signal_set(&ev_sigint, SIGINT, sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, sig_handler, NULL);
+ signal_set(&ev_sigchld, SIGCHLD, sig_handler, NULL);
+ signal_set(&ev_sighup, SIGHUP, sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal_add(&ev_sigchld, NULL);
+ signal_add(&ev_sighup, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ srv = xmalloc(sizeof(struct server));
+ imsgev_init(&srv->iev, fd[1], NULL, imsgev_main, imsgev_main_needfd);
+ srv->time = time(NULL);
+
+ event_set(&srv->ev, net_socket, EV_READ|EV_PERSIST, receive, NULL);
+ event_add(&srv->ev, NULL);
+
+ srv->usrtimer_tv.tv_sec = USERTIMER;
+ srv->usrtimer_tv.tv_usec = 0;
+ evtimer_set(&srv->usrtimer_ev, ev_usrtimer, NULL);
+ if (event_add(&srv->usrtimer_ev, &srv->usrtimer_tv) == -1)
+ fatal("server: event_add usrtimr failed: %s", strerror(errno));
+
+ droppriv();
+
+ log_info("server: entering event loop");
+ event_dispatch();
+
+ log_info("server: exiting");
+ exit(0);
+}
+
+static struct user *
+finduser(struct sockaddr_in *remote) {
+ struct user *usr;
+ struct sockaddr_in *u;
+
+ LIST_FOREACH(usr, &usr_list, entry) {
+ u = &usr->addr;
+ if (u->sin_addr.s_addr == remote->sin_addr.s_addr &&
+ u->sin_port == remote->sin_port)
+ return usr;
+ }
+ return NULL;
+}
+
+static void
+srvconn(struct imsgev *iev, struct imsg *imsg)
+{
+ struct imsg_srvconn *res;
+ struct user *usr;
+
+ res = imsg->data;
+ usr = finduser(&res->addr);
+ if (!usr || usr->status != USER_REQUESTED)
+ fatal("server: received srvconn response for inexistant connection");
+ if (res->ok) {
+ usr->status = USER_ACCEPTED;
+ } else {
+ usr->status = USER_REFUSED;
+ }
+}
+
+static void
+imsgev_main(struct imsgev *iev, int code, struct imsg *imsg)
+{
+ switch (code) {
+ case IMSGEV_IMSG:
+ log_debug("server: %s got imsg %i on fd %i",
+ __func__, imsg->hdr.type, iev->ibuf.fd);
+ switch (imsg->hdr.type) {
+ case IMSG_SRVCONN_RES:
+ srvconn(iev, imsg);
+ break;
+ default:
+ fatal("%s, unexpected imsg %d",
+ __func__, imsg->hdr.type);
+ }
+ break;
+ case IMSGEV_EREAD:
+ case IMSGEV_EWRITE:
+ case IMSGEV_EIMSG:
+ fatal("server: imsgev read/write error (%d)", code);
+ /* NOTREACHED */
+ break;
+ case IMSGEV_DONE:
+ event_loopexit(NULL);
+ /* NOTREACHED */
+ break;
+ }
+}
+
+static void
+imsgev_main_needfd(struct imsgev *iev)
+{
+ fatal("server: imsgev_main_needfd");
+}
+
+static void
+receive(int fd, short why, void *data)
+{
+ struct sockaddr_in remote;
+ struct imsg_srvconn req;
+ socklen_t len;
+ char buf[16384];
+ struct user *usr;
+ int rv;
+
+ len = sizeof(remote);
+ rv = recvfrom(fd, buf, sizeof(buf), 0,
+ (struct sockaddr *)&remote, &len);
+ if (rv < 0) {
+ log_warn("server: recvfrom failed");
+ return;
+ }
+ usr = finduser(&remote);
+ if (usr) {
+ switch (usr->status) {
+ case USER_ACCEPTED:
+ log_tmp("server: data for existing user");
+ break;
+ case USER_REQUESTED:
+ log_warn("server: data for not yet accepted user");
+ break;
+ case USER_REFUSED:
+ log_warn("server: data for banned user !");
+ break;
+ }
+ } else {
+ log_debug("server: new user request");
+
+ usr = xmalloc(sizeof(struct user));
+ addrcpy(&usr->addr, &remote);
+ usr->status = USER_REQUESTED;
+ usr->lastseen = srv->time;
+ LIST_INSERT_HEAD(&usr_list, usr, entry);
+
+ addrcpy(&req.addr, &remote);
+ req.deco = 0;
+ imsgev_compose(&srv->iev, IMSG_SRVCONN_REQ, 0, 0, -1,
+ &req, sizeof(req));
+ }
+}
+
+static void
+ev_usrtimer(int fd, short why, void *data)
+{
+ struct user *usr, *usrtmp;
+ struct imsg_srvconn req;
+
+ srv->time = time(NULL);
+
+ LIST_FOREACH_SAFE(usr, &usr_list, entry, usrtmp) {
+ if (srv->time > usr->lastseen + USER_TIMEOUT) {
+ addrcpy(&req.addr, &usr->addr);
+ req.deco = 1;
+ imsgev_compose(&srv->iev, IMSG_SRVCONN_REQ, 0, 0, -1,
+ &req, sizeof(req));
+
+ LIST_REMOVE(usr, entry);
+ free(usr);
+ }
+ }
+
+ if (event_add(&srv->usrtimer_ev, &srv->usrtimer_tv) == -1)
+ fatal("server: event_add usrtimer failed : %s", strerror(errno));
+}
diff --git a/old/glougloud/user.c b/old/glougloud/user.c
new file mode 100644
index 0000000..ae9734f
--- /dev/null
+++ b/old/glougloud/user.c
@@ -0,0 +1,806 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <sys/time.h>
+
+#if !defined(__OpenBSD__)
+#define __FAVOR_BSD
+#endif
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <netinet/ip_icmp.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <pcap.h>
+
+#include "glougloud.h"
+#include "external/imsgev.h"
+
+/* XXX a user process should be able to have multiple clients, so that we
+ * can send the same data to multiple machines with almost no overhead */
+
+#define NULL_HDRLEN 4 // XXX portable ?
+#define NODE_MAX_WITHOUT_TIMEOUT 1000
+#define NODE_TIMEOUT 300 // XXX conf ?
+#define CONN_TIMEOUT 300 // XXX conf ?
+#define CONN_TIMEOUT_UDP 20 // XXX conf ?
+#define CONN_TIMEOUT_ICMP 10 // XXX conf ?
+#define CONN_FREEIDS_COUNT 65536 /* 2^16 as long as freeids are u_int16_t */
+#define CONNTIMER 5 // XXX conf ?
+
+struct node {
+ LIST_ENTRY(node) entry;
+ struct in_addr addr;
+ time_t lastseen;
+ int used;
+ short namelen;
+#define NODENAME_WAITING -1
+#define NODENAME_FAILED -2
+ char *name;
+};
+
+enum connstate {
+ CONNSTATE_ESTABLISHED,
+ CONNSTATE_TCPFIN,
+ CONNSTATE_TCPFIN2
+};
+
+struct conn {
+ LIST_ENTRY(conn) entry;
+ u_int id;
+ enum connstate state;
+ struct node *src;
+ u_int src_port;
+ struct node *dst;
+ u_int dst_port;
+ u_int proto;
+ u_int size;
+ time_t lastseen;
+};
+
+struct user {
+ LIST_ENTRY(user) entry;
+ struct sockaddr_in addr;
+};
+
+struct capture {
+ int fd;
+ struct imsgev iev;
+
+ pcap_t *pcap;
+ struct event pcap_ev;
+ struct timeval pcap_tv;
+ pcap_handler pcap_handler;
+
+ LIST_HEAD(, conn) conn_list;
+ LIST_HEAD(, node) node_list;
+ int node_count;
+ u_int16_t conn_freeids[CONN_FREEIDS_COUNT];
+ int conn_freeids_ptr;
+ struct event conntimer_ev;
+ struct timeval conntimer_tv;
+ time_t time;
+
+ int pinvalid;
+ int ptruncated;
+};
+
+struct phandler {
+ pcap_handler f;
+ int type;
+};
+
+static void ip_handle(struct ip *, const u_char *, u_int);
+static void sendto_all(struct packet *, int);
+static struct node *node_add(struct in_addr *);
+static void node_del(struct node *);
+static struct node *node_find(struct in_addr *);
+static void conn_add(struct in_addr *, int, struct in_addr *, int, int, int);
+static void conn_data(struct conn *, int, int);
+static void conn_del(struct conn *);
+static pcap_handler lookup_phandler(int);
+static struct user *finduser(struct sockaddr_in *);
+
+static void phandler_ether(u_char *,
+ const struct pcap_pkthdr *, const u_char *);
+static void phandler_loop(u_char *,
+ const struct pcap_pkthdr *, const u_char *);
+static void ev_pcap(int, short, void *);
+static void ev_timer(int, short, void *);
+
+static void usrconn(struct imsgev *, struct imsg *);
+static void imsgev_main(struct imsgev *, int, struct imsg *);
+static void imsgev_main_needfd(struct imsgev *);
+
+static struct phandler phandlers[] = {
+ { phandler_ether, DLT_EN10MB },
+ { phandler_ether, DLT_IEEE802 },
+ { phandler_loop, DLT_LOOP },
+ { NULL, 0 },
+};
+struct capture *cap;
+LIST_HEAD(, user) usr_list;
+int usr_count = 0;
+
+static void
+sig_handler(int sig, short why, void *data)
+{
+ log_info("user: got signal %d", sig);
+ if (sig == SIGINT || sig == SIGTERM)
+ event_loopexit(NULL);
+}
+
+int
+user_init(int fd[2], pcap_t *pcap)
+{
+ struct event ev_sigint, ev_sigterm, ev_sigchld, ev_sighup;
+ int pid, i;
+
+ pid = fork();
+ if (pid < 0)
+ fatal("user fork");
+ if (pid > 0)
+ return pid;
+
+ setproctitle("user");
+ event_init();
+ close(fd[0]);
+
+ signal_set(&ev_sigint, SIGINT, sig_handler, NULL);
+ signal_set(&ev_sigterm, SIGTERM, sig_handler, NULL);
+ signal_set(&ev_sigchld, SIGCHLD, sig_handler, NULL);
+ signal_set(&ev_sighup, SIGHUP, sig_handler, NULL);
+ signal_add(&ev_sigint, NULL);
+ signal_add(&ev_sigterm, NULL);
+ signal_add(&ev_sigchld, NULL);
+ signal_add(&ev_sighup, NULL);
+ signal(SIGPIPE, SIG_IGN);
+
+ cap = xcalloc(1, sizeof(struct capture));
+ cap->fd = fd[1];
+ imsgev_init(&cap->iev, cap->fd, NULL, imsgev_main, imsgev_main_needfd);
+ for (i=0; i<CONN_FREEIDS_COUNT-1; i++)
+ cap->conn_freeids[i] = i;
+ cap->time = time(NULL);
+
+ cap->pcap = pcap;
+ cap->pcap_handler = lookup_phandler(pcap_datalink(pcap));
+ cap->pcap_tv.tv_sec = 0;
+ cap->pcap_tv.tv_usec = PCAP_TO;
+ event_set(&cap->pcap_ev, pcap_fileno(cap->pcap),
+ EV_READ, ev_pcap, NULL);
+
+ cap->conntimer_tv.tv_sec = CONNTIMER;
+ cap->conntimer_tv.tv_usec = 0;
+ evtimer_set(&cap->conntimer_ev, ev_timer, NULL);
+ if (event_add(&cap->conntimer_ev, &cap->conntimer_tv) == -1)
+ fatal("user: event_add conntimer failed: %s", strerror(errno));
+
+ droppriv();
+
+ log_info("user: entering event loop");
+ event_dispatch();
+
+ log_info("user: exiting");
+ exit(0);
+}
+
+/*
+ * Parse an IP packet and descide what to do with it.
+ * 'ip' is a pointer the the captured IP packet
+ * 'pend' is a pointer to the end of the captured IP packet
+ * 'wirelen' is the size of the IP packet off the wire
+ */
+#define NOTCAPTURED(v) ((u_char *)v > (u_char *)pend - sizeof(*v))
+#define NOTRECEIVED(v) (wirelen < sizeof(v))
+static void
+ip_handle(struct ip *ip, const u_char *pend, u_int wirelen)
+{
+ u_int len, ip_hlen, off;
+ const u_char *cp;
+ struct tcphdr *tcph;
+ struct udphdr *udph;
+ struct icmp *icmp;
+ struct in_addr src, dst;
+ u_int src_port, dst_port;
+ u_int size, proto, close, response;
+ struct conn *c, *conn;
+
+ if (NOTCAPTURED(ip)) {
+ log_pinvalid("user: ip truncated (ip %x pend %x sizeof(ip) %d",
+ ip, pend, sizeof(ip));
+ cap->ptruncated++;
+ return;
+ }
+
+ if (ip->ip_v != IPVERSION) {
+ log_pinvalid("user: invalid ip version");
+ cap->pinvalid++;
+ return;
+ }
+
+ len = ntohs(ip->ip_len);
+ if (wirelen < len) {
+ log_pinvalid("user: ip too small");
+ cap->pinvalid++;
+ len = wirelen;
+ }
+
+ ip_hlen = ip->ip_hl * 4;
+ if (ip_hlen < sizeof(struct ip) || ip_hlen > len) {
+ log_pinvalid("user: ip_hlen invalid, %d", ip_hlen);
+ cap->pinvalid++;
+ return;
+ }
+ len -= ip_hlen;
+
+ src.s_addr = ntohl(ip->ip_src.s_addr);
+ dst.s_addr = ntohl(ip->ip_dst.s_addr);
+ src_port = 0;
+ dst_port = 0;
+ proto = IPPROTO_IP;
+ size = len;
+ close = 0;
+
+ off = ntohs(ip->ip_off);
+ if ((off & IP_OFFMASK) == 0) {
+ cp = (const u_char *)ip + ip_hlen;
+ switch (ip->ip_p) {
+
+ case IPPROTO_TCP:
+ tcph = (struct tcphdr *)cp;
+ if (NOTCAPTURED(&tcph->th_flags)) {
+ log_pinvalid("user: tcp truncated");
+ cap->ptruncated++;
+ return;
+ }
+ if (NOTRECEIVED(*tcph)) {
+ log_pinvalid("user: tcp too small");
+ cap->pinvalid++;
+ return;
+ }
+ src_port = ntohs(tcph->th_sport);
+ dst_port = ntohs(tcph->th_dport);
+ proto = IPPROTO_TCP;
+ size = len - sizeof(*tcph);
+ if ((tcph->th_flags & TH_FIN) &&
+ (tcph->th_flags & TH_ACK))
+ close = 1;
+ break;
+
+ case IPPROTO_UDP:
+ udph = (struct udphdr *)cp;
+ if (NOTCAPTURED(&udph->uh_dport)) {
+ log_pinvalid("user: udp truncated, "
+ "ip %x, udph %x, uh_port %x, pend %x, ip_hlen %d",
+ ip, udph, &udph->uh_dport, pend, ip_hlen);
+ cap->ptruncated++;
+ return;
+ }
+ if (NOTRECEIVED(*udph)) {
+ log_pinvalid("user: udp too small");
+ cap->pinvalid++;
+ return;
+ }
+ src_port = ntohs(udph->uh_sport);
+ dst_port = ntohs(udph->uh_dport);
+ proto = IPPROTO_UDP;
+ size = len - sizeof(*udph);
+ break;
+
+ case IPPROTO_ICMP:
+ icmp = (struct icmp *)cp;
+ if (NOTRECEIVED(*icmp)) {
+ log_pinvalid("user: icmp too small");
+ cap->pinvalid++;
+ return;
+ }
+ proto = IPPROTO_ICMP;
+ size = len - sizeof(*icmp);
+ break;
+
+ default:
+ log_warn("user: unknown ip protocol !");
+ break;
+ }
+ } else {
+ /*
+ * if this isn't the first frag, we're missing the
+ * next level protocol header.
+ */
+ log_tmp("user: got a fragmented ip packet !");
+ }
+
+ conn = NULL;
+ LIST_FOREACH(c, &cap->conn_list, entry) {
+ if (((c->src->addr.s_addr == src.s_addr &&
+ c->src_port == src_port &&
+ c->dst->addr.s_addr == dst.s_addr &&
+ c->dst_port == dst_port) ||
+ (c->src->addr.s_addr == dst.s_addr &&
+ c->src_port == dst_port &&
+ c->dst->addr.s_addr == src.s_addr &&
+ c->dst_port == src_port)) &&
+ c->proto == proto) {
+ conn = c;
+ if (c->src->addr.s_addr == src.s_addr)
+ response = 0;
+ else
+ response = 1;
+ break;
+ }
+ }
+
+ if (conn) {
+ if (!close) {
+ conn_data(conn, size, response);
+ } else {
+ conn_del(conn);
+ }
+ } else {
+ if (!close) {
+ conn_add(&src, src_port, &dst, dst_port, proto, size);
+ } else {
+ log_warn("user: captured connection close w/o open !");
+ }
+ }
+}
+
+/* XXX all the packets must have data htos */
+static void
+sendto_all(struct packet *p, int size)
+{
+ struct user *usr;
+
+ log_debug("SEND PACKET (size %.2d): %x %x", size, p->ver, p->type);
+
+ LIST_FOREACH(usr, &usr_list, entry) {
+ if (sendto(net_socket, p, size, 0,
+ (struct sockaddr *)&usr->addr, sizeof(usr->addr)) == -1)
+ log_warn("send_to failed: %s", strerror(errno));
+ }
+}
+
+static struct node *
+node_add(struct in_addr *addr)
+{
+ struct node *n;
+ struct imsg_usrdns_req req;
+
+ log_debug("user: node_add");
+
+ n = xcalloc(1, sizeof(struct node));
+ n->addr.s_addr = addr->s_addr;
+ n->namelen = NODENAME_WAITING;
+ n->lastseen = cap->time;
+ LIST_INSERT_HEAD(&cap->node_list, n, entry);
+ cap->node_count++;
+
+ req.addr.s_addr = addr->s_addr;
+ imsgev_compose(&cap->iev, IMSG_USRDNS_REQ, 0, 0, -1,
+ &req, sizeof(req));
+
+ return n;
+}
+
+static void
+node_del(struct node *n)
+{
+ if (n->used)
+ fatal("user: trying to remove a used node !");
+ log_debug("user: node_del");
+
+ LIST_REMOVE(n, entry);
+ free(n->name);
+ free(n);
+ cap->node_count--;
+}
+
+static struct node *
+node_find(struct in_addr *remote)
+{
+ struct node *n;
+
+ LIST_FOREACH(n, &cap->node_list, entry)
+ if (n->addr.s_addr == remote->s_addr)
+ return n;
+ return NULL;
+}
+
+static void
+conn_add(struct in_addr *src, int src_port, struct in_addr *dst, int dst_port, int proto, int size)
+{
+ struct packet p;
+ struct conn *c;
+ struct node *srcnode;
+ struct node *dstnode;
+ int id;
+
+ log_debug("user: conn_add, %x:%d->%x:%d %d [%d]",
+ src->s_addr, src_port, dst->s_addr, dst_port, proto, size);
+ if (cap->conn_freeids_ptr == CONN_FREEIDS_COUNT) {
+ log_warn("user: out of connection identifiers !");
+ return;
+ }
+
+ id = cap->conn_freeids[cap->conn_freeids_ptr];
+ cap->conn_freeids_ptr++;
+
+ srcnode = node_find(src);
+ if (!srcnode)
+ srcnode = node_add(src);
+ srcnode->used++;
+ dstnode = node_find(dst);
+ if (!dstnode)
+ dstnode = node_add(dst);
+ dstnode->used++;
+
+ c = xmalloc(sizeof(struct conn));
+ c->id = id;
+ c->state = CONNSTATE_ESTABLISHED;
+ c->src = srcnode;
+ c->src_port = src_port;
+ c->dst = dstnode;
+ c->dst_port = dst_port;
+ c->proto = proto;
+ c->size = size;
+ c->lastseen = cap->time;
+ LIST_INSERT_HEAD(&cap->conn_list, c, entry);
+
+ bzero(&p, sizeof(p));
+ p.ver = PACKET_VERSION;
+ p.type = PACKET_NEWCONN;
+ p.newconn_id = id;
+ p.newconn_src = htonl(src->s_addr);
+ p.newconn_dst = htonl(dst->s_addr);
+ p.newconn_proto = proto;
+ p.newconn_size = htons(size << 8);
+ sendto_all(&p, PACKET_NEWCONN_SIZE);
+}
+
+static void
+conn_data(struct conn *c, int size, int response)
+{
+ struct packet p;
+
+ log_debug("user: conn_data");
+
+ c->lastseen = cap->time;
+
+ bzero(&p, sizeof(p));
+ p.ver = PACKET_VERSION;
+ p.type = PACKET_DATA;
+ p.data_connid = c->id;
+ p.data_size = htons(size << 8 | response); //XXX
+ sendto_all(&p, PACKET_DATA_SIZE);
+}
+
+static void
+conn_del(struct conn *c)
+{
+ struct packet p;
+
+ log_debug("user: conn_del");
+
+ if (c->proto == IPPROTO_TCP) {
+ switch (c->state) {
+ case CONNSTATE_ESTABLISHED:
+ c->state = CONNSTATE_TCPFIN;
+ return;
+ case CONNSTATE_TCPFIN:
+ c->state = CONNSTATE_TCPFIN2;
+ return;
+ case CONNSTATE_TCPFIN2:
+ break;
+ }
+ }
+
+ bzero(&p, sizeof(p));
+ p.ver = PACKET_VERSION;
+ p.type = PACKET_DELCONN;
+ p.delconn_id = c->id;
+ sendto_all(&p, PACKET_DELCONN_SIZE);
+
+ if (cap->conn_freeids_ptr == 0)
+ fatal("cap->conn_freeids_ptr == 0");
+ cap->conn_freeids_ptr--;
+ cap->conn_freeids[cap->conn_freeids_ptr] = c->id;
+
+ c->src->used--;
+ c->dst->used--;
+
+ LIST_REMOVE(c, entry);
+ free(c);
+}
+
+static pcap_handler
+lookup_phandler(int type)
+{
+ struct phandler *p;
+
+ for (p = phandlers; p->f; ++p) {
+ if (type == p->type)
+ return p->f;
+ }
+ fatal("user: unknown data link type 0x%x", type);
+ /* NOTREACHED */
+ return NULL;
+}
+
+static struct user *
+finduser(struct sockaddr_in *remote)
+{
+ struct user *usr;
+ struct sockaddr_in *u;
+
+ LIST_FOREACH(usr, &usr_list, entry) {
+ u = &usr->addr;
+ if (u->sin_addr.s_addr == remote->sin_addr.s_addr &&
+ u->sin_port == remote->sin_port)
+ return usr;
+ }
+ return NULL;
+}
+
+static void
+phandler_ether(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
+{
+ struct ether_header *ep;
+ struct ip *ip;
+ u_short ether_type;
+ const u_char *pend;
+ u_int len;
+
+ log_debug("user: pcap handler ethernet !");
+
+ /* XXX here i assume that packets are alligned, which might not
+ * be the case when using dump files, says tcpdump sources */
+
+ ep = (struct ether_header *)p;
+ pend = p + h->caplen;
+ len = h->len - sizeof(struct ether_header);
+
+
+ ether_type = ntohs(ep->ether_type);
+ if (ether_type <= ETHERMTU)
+ log_tmp("llc packet !");
+ else {
+ switch (ether_type) {
+ case ETHERTYPE_IP:
+ log_tmp("ether IP");
+ ip = (struct ip *)((u_char *)ep + sizeof(struct ether_header));
+ ip_handle(ip, pend, len);
+ break;
+ default:
+ log_tmp("non ip packet !");
+ break;
+ }
+ }
+}
+
+static void
+phandler_loop(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
+{
+ struct ip *ip;
+ struct ether_header *ep;
+ u_short ether_type;
+ u_int family;
+ const u_char *pend;
+ u_int len;
+
+ log_debug("user: pcap handler loop !");
+
+ /* XXX here i assume that packets are alligned, which might not
+ * be the case when using dump files, says tcpdump sources */
+
+ pend = p + h->caplen;
+ len = h->len;
+
+ memcpy((char *)&family, (char *)p, sizeof(family));
+ family = ntohl(family);
+ switch (family) {
+ case AF_INET:
+ log_tmp("loop family AF_INET");
+ ip = (struct ip *)(p + NULL_HDRLEN);
+ len -= NULL_HDRLEN;
+ ip_handle(ip, pend, len);
+ break;
+#if defined(__OpenBSD__)
+ case AF_LINK:
+#else
+ case AF_LOCAL:
+#endif
+ ep = (struct ether_header *)(p + NULL_HDRLEN);
+ ether_type = ntohs(ep->ether_type);
+ if (ether_type <= ETHERMTU)
+ log_tmp("llc packet !");
+ else {
+ switch (ether_type) {
+ case ETHERTYPE_IP:
+ log_tmp("loop family AF_LINK IP");
+ ip = (struct ip *)((u_char *)ep + sizeof(*ep));
+ len -= NULL_HDRLEN + sizeof(*ep);
+ ip_handle(ip, pend, len);
+ break;
+ default:
+ log_tmp("loop non ip packet !");
+ break;
+ }
+ }
+ default:
+ log_tmp("unknown family %x !", family);
+ break;
+ }
+}
+
+static void
+ev_pcap(int fd, short why, void *data)
+{
+ log_tmp("ev_pcap");
+ pcap_dispatch(cap->pcap, PCAP_COUNT, cap->pcap_handler, NULL);
+
+ /* reschedule */
+ if (event_add(&cap->pcap_ev, &cap->pcap_tv) == -1)
+ fatal("user: event_add pcap failed : %s", strerror(errno));
+}
+
+static void
+ev_timer(int fd, short why, void *data)
+{
+ struct conn *c, *ctmp;
+ struct node *n, *ntmp;
+ int i, to;
+
+ log_debug("ev_timer");
+ cap->time = time(NULL);
+
+ i = 0;
+ LIST_FOREACH_SAFE(c, &cap->conn_list, entry, ctmp) {
+ switch (c->proto) {
+ case IPPROTO_UDP:
+ to = CONN_TIMEOUT_UDP;
+ break;
+ case IPPROTO_ICMP:
+ to = CONN_TIMEOUT_ICMP;
+ break;
+ default:
+ to = CONN_TIMEOUT;
+ break;
+ }
+ if (cap->time > c->lastseen + to)
+ conn_del(c);
+ else
+ i++;
+ }
+
+ if (cap->node_count > NODE_MAX_WITHOUT_TIMEOUT) {
+ LIST_FOREACH_SAFE(n, &cap->node_list, entry, ntmp) {
+ if (n->used == 0 &&
+ cap->time > n->lastseen + NODE_TIMEOUT)
+ node_del(n);
+ }
+ }
+
+ log_debug("user: ev_timer leaving with %d active connections and %d active nodes", i, cap->node_count);
+ if (event_add(&cap->conntimer_ev, &cap->conntimer_tv) == -1)
+ fatal("user: event_add conntimer failed : %s", strerror(errno));
+}
+
+static void
+usrconn(struct imsgev *iev, struct imsg *imsg)
+{
+ struct imsg_usrconn *req;
+ struct imsg_usrconn res;
+ struct user *usr;
+
+ req = imsg->data;
+
+ if (req->deco) {
+ usr = finduser(&req->addr);
+ if (!usr)
+ fatal("user: trying to deco an inexistant user !");
+ LIST_REMOVE(usr, entry);
+ free(usr);
+ usr_count--;
+ if (usr_count == 0)
+ event_del(&cap->pcap_ev);
+
+ return;
+ }
+
+ usr = xmalloc(sizeof(struct user));
+ addrcpy(&usr->addr, &req->addr);
+ LIST_INSERT_HEAD(&usr_list, usr, entry);
+ if (usr_count == 0) {
+ if (event_add(&cap->pcap_ev, &cap->pcap_tv) == -1)
+ fatal("user: event_add failed : %s", strerror(errno));
+ }
+ usr_count++;
+
+ addrcpy(&res.addr, &usr->addr);
+ res.ok = 1;
+ imsgev_compose(&cap->iev, IMSG_USRCONN_RES, 0, 0, -1,
+ &res, sizeof(res));
+}
+
+static void
+usrdns(struct imsgev *iev, struct imsg *imsg)
+{
+ struct imsg_usrdns_res *res;
+ struct node *n;
+ struct packet p;
+
+ res = imsg->data;
+
+ n = node_find(&res->addr);
+ if (!n)
+ fatal("user: received usrdns response for inexistant node !");
+ if (n->namelen != NODENAME_WAITING)
+ fatal("user: received usrdns response for a nonwaiting node!");
+
+ if (res->name[0] == '\0') {
+ log_debug("user: resolv for %x failed", res->addr.s_addr);
+ n->namelen = NODENAME_FAILED;
+ return;
+ }
+ n->namelen = strnlen(res->name, DNSNAME_MAX);
+ n->name = strndup(res->name, DNSNAME_MAX);
+
+ log_debug("user: sending node name of %x is %s",
+ n->addr.s_addr, n->name);
+ bzero(&p, sizeof(p));
+ p.ver = PACKET_VERSION;
+ p.type = PACKET_NAME;
+ p.name_addr = htonl(n->addr.s_addr);
+ p.name_len = n->namelen;
+ strncpy(p.name_fqdn, n->name, sizeof(p.name_fqdn));
+ sendto_all(&p, PACKET_NAME_SIZE + p.name_len);
+}
+
+static void
+imsgev_main(struct imsgev *iev, int code, struct imsg *imsg)
+{
+ switch (code) {
+ case IMSGEV_IMSG:
+ log_debug("user: %s got imsg %i on fd %i",
+ __func__, imsg->hdr.type, iev->ibuf.fd);
+ switch (imsg->hdr.type) {
+ case IMSG_USRCONN_REQ:
+ usrconn(iev, imsg);
+ break;
+ case IMSG_USRDNS_RES:
+ usrdns(iev, imsg);
+ break;
+ default:
+ fatal("user: %s, unexpected imsg %d",
+ __func__, imsg->hdr.type);
+ }
+ break;
+ case IMSGEV_EREAD:
+ case IMSGEV_EWRITE:
+ case IMSGEV_EIMSG:
+ fatal("user: imsgev read/write error");
+ /* NOTREACHED */
+ break;
+ case IMSGEV_DONE:
+ event_loopexit(NULL);
+ /* NOTREACHED */
+ break;
+ }
+}
+
+static void
+imsgev_main_needfd(struct imsgev *iev)
+{
+ fatal("server: imsgev_main_needfd");
+}
diff --git a/old/glougloud/util.c b/old/glougloud/util.c
new file mode 100644
index 0000000..c077bd3
--- /dev/null
+++ b/old/glougloud/util.c
@@ -0,0 +1,192 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <time.h>
+#include <pwd.h>
+#include <string.h>
+
+#include "glougloud.h"
+
+FILE *logfile;
+int loglevel;
+int logpinvalid;
+
+#define LOGFILE "/var/log/glougloud"
+#define LOG_FORCED -2
+#define LOG_FATAL -1
+#define LOG_WARN 0
+#define LOG_INFO 1
+#define LOG_DEBUG 2
+
+static void logit(int, const char *, const char *, va_list);
+
+void
+log_init(int level, int pinvalid)
+{
+ logfile = fopen(LOGFILE, "a+");
+ if (!logfile) {
+ printf("cannot open log file %s!\n", LOGFILE);
+ exit(1);
+ }
+ loglevel = level;
+ logpinvalid = pinvalid;
+}
+
+void
+log_tmp(const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ logit(LOG_FORCED, "XXX ", msg, ap);
+ va_end(ap);
+}
+void
+log_pinvalid(const char *msg, ...)
+{
+ va_list ap;
+
+ if (!logpinvalid)
+ return;
+
+ va_start(ap, msg);
+ logit(LOG_FORCED, "pinvalid: ", msg, ap);
+ va_end(ap);
+}
+void
+log_debug(const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ logit(LOG_DEBUG, "", msg, ap);
+ va_end(ap);
+}
+void
+log_info(const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ logit(LOG_INFO, "", msg, ap);
+ va_end(ap);
+}
+void
+log_warn(const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ logit(LOG_WARN, "", msg, ap);
+ va_end(ap);
+}
+#if defined(__OpenBSD__)
+void __dead
+#else
+void
+#endif
+fatal(const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ logit(LOG_FATAL, "fatal: ", msg, ap);
+ va_end(ap);
+
+ exit(1);
+}
+
+/* XXX mpsafe */
+static void
+logit(int level, const char *prefix, const char *msg, va_list ap)
+{
+ time_t clock;
+
+ if (level <= loglevel) {
+ time(&clock);
+ fprintf(logfile, "%d ", (int)clock);
+ vfprintf(logfile, prefix, ap);
+ vfprintf(logfile, msg, ap);
+ fprintf(logfile, "\n");
+ fflush(logfile);
+ }
+}
+
+void *
+xmalloc(size_t size)
+{
+ void *ptr;
+
+ if (size == 0)
+ fatal("xmalloc: zero size");
+ ptr = malloc(size);
+ if (ptr == NULL)
+ fatal("xmalloc: out of memory (allocating %lu bytes)", (u_long) size);
+ return ptr;
+}
+
+void *
+xcalloc(size_t nmemb, size_t size)
+{
+ void *ptr;
+
+ if (size == 0)
+ fatal("xcalloc: zero size");
+ ptr = calloc(nmemb, size);
+ if (ptr == NULL)
+ fatal("xcalloc: out of memory (allocating %lu bytes)", (u_long) size);
+ return ptr;
+}
+
+void
+fd_nonblock(int fd)
+{
+ int flags = fcntl(fd, F_GETFL, 0);
+ int rc = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ if (rc == -1)
+ log_warn("failed to set fd %i non-blocking", fd);
+}
+
+void
+droppriv()
+{
+ struct passwd *pw;
+
+ pw = getpwnam(GLOUGLOUD_USER);
+ if (!pw)
+ fatal("unknown user %s", GLOUGLOUD_USER);
+ if (chroot(pw->pw_dir) != 0)
+ fatal("unable to chroot");
+ if (chdir("/") != 0)
+ fatal("unable to chdir");
+ if (setgroups(1, &pw->pw_gid) == -1)
+ fatal("setgroups() failed");
+ if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
+ fatal("setresgid failed");
+ if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
+ fatal("setresuid() failed");
+ endpwent();
+}
+
+void
+addrcpy(struct sockaddr_in *dst, struct sockaddr_in *src)
+{
+ dst->sin_addr.s_addr = src->sin_addr.s_addr;
+ dst->sin_port = src->sin_port;
+ dst->sin_family = src->sin_family;
+}
+
+void
+socketpair_prepare(int fd[2])
+{
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fd) == -1)
+ fatal("socketpair_prepare");
+ fd_nonblock(fd[0]);
+ fd_nonblock(fd[1]);
+}
+
diff --git a/spike/Makefile b/spike/Makefile
new file mode 100644
index 0000000..4d2cebe
--- /dev/null
+++ b/spike/Makefile
@@ -0,0 +1,9 @@
+evdns:
+ $(CC) -o evdns evdns.c -levent
+evdns_chrooted:
+ $(CC) -o evdns_chrooted evdns_chrooted.c -levent
+
+igraph_edges:
+ $(CC) -o igraph_edges igraph_edges.c -ligraph
+igraph_graph:
+ $(CC) -o igraph_graph igraph_graph.c -ligraph
diff --git a/spike/efl_ephysics_heavy.c b/spike/efl_ephysics_heavy.c
new file mode 100644
index 0000000..af1d844
--- /dev/null
+++ b/spike/efl_ephysics_heavy.c
@@ -0,0 +1,76 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "ephysics_test.h"
+
+static void
+_add_sphere(Test_Data *test_data, int i)
+{
+ EPhysics_Body *body;
+ Evas_Object *sphere;
+ static const char *colors[] = {"blue-ball", "red-ball", "green-ball"};
+
+ sphere = elm_image_add(test_data->win);
+ elm_image_file_set(
+ sphere, PACKAGE_DATA_DIR "/" EPHYSICS_TEST_THEME ".edj", colors[i % 3]);
+ evas_object_move(sphere, 50 + (i % 34) * 12, 40 + i / 34 * 12);
+ evas_object_resize(sphere, 10, 10);
+ evas_object_show(sphere);
+ test_data->evas_objs = eina_list_append(test_data->evas_objs, sphere);
+
+ body = ephysics_body_circle_add(test_data->world);
+ ephysics_body_restitution_set(body, 0.85);
+ ephysics_body_evas_object_set(body, sphere, EINA_TRUE);
+ test_data->bodies = eina_list_append(test_data->bodies, body);
+}
+
+static void
+_world_populate(Test_Data *test_data)
+{
+ int i;
+ for (i = 0; i < 1000; i++)
+ _add_sphere(test_data, i);
+}
+
+static void
+_restart(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
+{
+ test_clean(data);
+ _world_populate(data);
+}
+
+void
+test_heavy(void *data __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
+{
+ EPhysics_Body *boundary;
+ EPhysics_World *world;
+ Test_Data *test_data;
+
+ if (!ephysics_init())
+ return;
+
+ test_data = test_data_new();
+ test_win_add(test_data, "Heavy", EINA_TRUE);
+ elm_object_signal_emit(test_data->layout, "borders,show", "ephysics_test");
+ elm_layout_signal_callback_add(test_data->layout, "restart", "test-theme",
+ _restart, test_data);
+ elm_layout_signal_emit(test_data->layout, "loading_bar,show",
+ "ephysics_test");
+
+ world = ephysics_world_new();
+ ephysics_world_render_geometry_set(world, 50, 40, -50,
+ 900 - 100, 500 - 40, DEPTH);
+ evas_object_resize(test_data->win, 1300, 600);
+ evas_object_show(test_data->win);
+ test_data->world = world;
+
+ boundary = ephysics_body_bottom_boundary_add(test_data->world);
+ ephysics_body_restitution_set(boundary, 1);
+
+ ephysics_body_top_boundary_add(test_data->world);
+ ephysics_body_left_boundary_add(test_data->world);
+ ephysics_body_right_boundary_add(test_data->world);
+
+ _world_populate(test_data);
+}
diff --git a/spike/efl_ephysics_heavy.txt b/spike/efl_ephysics_heavy.txt
new file mode 100644
index 0000000..cb49d43
--- /dev/null
+++ b/spike/efl_ephysics_heavy.txt
@@ -0,0 +1,7 @@
+testing if it's reasonable to use ephysics as part of network map graph.
+in gg_map for example.
+cp efl_ephysics_heavy.c $E_SVN/trunk/ephysics/src/bin
+cd $E_SVN/trunk/ephysics/src/bin
+make
+./ephysics_test
+# go select "Heavy"
diff --git a/spike/evdns.c b/spike/evdns.c
new file mode 100644
index 0000000..852afa0
--- /dev/null
+++ b/spike/evdns.c
@@ -0,0 +1,112 @@
+#include <event2/dns.h>
+#include <event2/util.h>
+#include <event2/event.h>
+
+#include <sys/socket.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+int n_pending_requests = 0;
+struct event_base *base = NULL;
+
+struct user_data {
+ char *name; /* the name we're resolving */
+ int idx; /* its position on the command line */
+};
+
+void callback(int errcode, struct evutil_addrinfo *addr, void *ptr)
+{
+ struct user_data *data = ptr;
+ const char *name = data->name;
+ if (errcode) {
+ printf("%d. %s -> %s\n", data->idx, name, evutil_gai_strerror(errcode));
+ } else {
+ struct evutil_addrinfo *ai;
+ printf("%d. %s", data->idx, name);
+ if (addr->ai_canonname)
+ printf(" [%s]", addr->ai_canonname);
+ puts("");
+ for (ai = addr; ai; ai = ai->ai_next) {
+ char buf[128];
+ const char *s = NULL;
+ if (ai->ai_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr;
+ s = evutil_inet_ntop(AF_INET, &sin->sin_addr, buf, 128);
+ } else if (ai->ai_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr;
+ s = evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf, 128);
+ }
+ if (s)
+ printf(" -> %s\n", s);
+ }
+ evutil_freeaddrinfo(addr);
+ }
+ free(data->name);
+ free(data);
+ if (--n_pending_requests == 0)
+ event_base_loopexit(base, NULL);
+}
+
+/* Take a list of domain names from the command line and resolve them in
+ * parallel. */
+int main(int argc, char **argv)
+{
+ int i;
+ struct evdns_base *dnsbase;
+
+ if (argc == 1) {
+ puts("No addresses given.");
+ return 0;
+ }
+ base = event_base_new();
+ if (!base)
+ return 1;
+ dnsbase = evdns_base_new(base, 1);
+ if (!dnsbase)
+ return 2;
+
+ for (i = 1; i < argc; ++i) {
+ struct evutil_addrinfo hints;
+ struct evdns_getaddrinfo_request *req;
+ struct user_data *user_data;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = EVUTIL_AI_CANONNAME;
+ /* Unless we specify a socktype, we'll get at least two entries for
+ * each address: one for TCP and one for UDP. That's not what we
+ * want. */
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ if (!(user_data = malloc(sizeof(user_data)))) {
+ perror("malloc");
+ exit(1);
+ }
+ if (!(user_data->name = strdup(argv[i]))) {
+ perror("strdup");
+ exit(1);
+ }
+ user_data->idx = i;
+
+ ++n_pending_requests;
+ req = evdns_getaddrinfo(
+ dnsbase, argv[i], NULL /* no service name given */,
+ &hints, callback, user_data);
+ if (req == NULL) {
+ printf(" [request for %s returned immediately]\n", argv[i]);
+ /* No need to free user_data or decrement n_pending_requests; that
+ * happened in the callback. */
+ }
+ }
+
+ if (n_pending_requests)
+ event_base_dispatch(base);
+
+ evdns_base_free(dnsbase, 0);
+ event_base_free(base);
+
+ return 0;
+}
diff --git a/spike/evdns_chrooted.c b/spike/evdns_chrooted.c
new file mode 100644
index 0000000..edb7ea3
--- /dev/null
+++ b/spike/evdns_chrooted.c
@@ -0,0 +1,146 @@
+#include <event2/dns.h>
+#include <event2/util.h>
+#include <event2/event.h>
+
+#include <sys/socket.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+/* for droppriv */
+#include <unistd.h>
+#include <pwd.h>
+
+int n_pending_requests = 0;
+struct event_base *base = NULL;
+
+struct user_data {
+ char *name; /* the name we're resolving */
+ int idx; /* its position on the command line */
+};
+
+void callback(int errcode, struct evutil_addrinfo *addr, void *ptr)
+{
+ struct user_data *data = ptr;
+ const char *name = data->name;
+ if (errcode) {
+ printf("%d. %s -> %s\n", data->idx, name, evutil_gai_strerror(errcode));
+ } else {
+ struct evutil_addrinfo *ai;
+ printf("%d. %s", data->idx, name);
+ if (addr->ai_canonname)
+ printf(" [%s]", addr->ai_canonname);
+ puts("");
+ for (ai = addr; ai; ai = ai->ai_next) {
+ char buf[128];
+ const char *s = NULL;
+ if (ai->ai_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr;
+ s = evutil_inet_ntop(AF_INET, &sin->sin_addr, buf, 128);
+ } else if (ai->ai_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr;
+ s = evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf, 128);
+ }
+ if (s)
+ printf(" -> %s\n", s);
+ }
+ evutil_freeaddrinfo(addr);
+ }
+ free(data->name);
+ free(data);
+ if (--n_pending_requests == 0)
+ event_base_loopexit(base, NULL);
+}
+
+#define fatal(msg) { printf(msg "\n"); exit(1); }
+void
+droppriv()
+{
+ struct passwd *pw;
+
+ /* bad example, never use user 'nobody' in real code, create your own */
+ pw = getpwnam("nobody");
+ if (!pw)
+ fatal("unknown user");
+ if (chroot(pw->pw_dir) != 0)
+ fatal("unable to chroot");
+ if (chdir("/") != 0)
+ fatal("unable to chdir");
+ if (setgroups(1, &pw->pw_gid) == -1)
+ fatal("setgroups() failed");
+ if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
+ fatal("setresgid failed");
+ if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
+ fatal("setresuid() failed");
+ endpwent();
+}
+
+/* Take a list of domain names from the command line and resolve them in
+ * parallel. */
+int main(int argc, char **argv)
+{
+ int i;
+ struct evdns_base *dnsbase;
+
+ if (argc == 1) {
+ puts("No addresses given.");
+ return 0;
+ }
+
+ if (geteuid() != 0)
+ errx(1, "need root privileges");
+
+ droppriv();
+ printf("droppriv + chroot DONE\n");
+
+ base = event_base_new();
+ if (!base)
+ return 1;
+ dnsbase = evdns_base_new(base, 1);
+ if (!dnsbase)
+ return 2;
+
+ for (i = 1; i < argc; ++i) {
+ struct evutil_addrinfo hints;
+ struct evdns_getaddrinfo_request *req;
+ struct user_data *user_data;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = EVUTIL_AI_CANONNAME;
+ /* Unless we specify a socktype, we'll get at least two entries for
+ * each address: one for TCP and one for UDP. That's not what we
+ * want. */
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ if (!(user_data = malloc(sizeof(user_data)))) {
+ perror("malloc");
+ exit(1);
+ }
+ if (!(user_data->name = strdup(argv[i]))) {
+ perror("strdup");
+ exit(1);
+ }
+ user_data->idx = i;
+
+ ++n_pending_requests;
+ req = evdns_getaddrinfo(
+ dnsbase, argv[i], NULL /* no service name given */,
+ &hints, callback, user_data);
+ if (req == NULL) {
+ printf(" [request for %s returned immediately]\n", argv[i]);
+ /* No need to free user_data or decrement n_pending_requests; that
+ * happened in the callback. */
+ }
+ }
+
+ if (n_pending_requests)
+ event_base_dispatch(base);
+
+ evdns_base_free(dnsbase, 0);
+ event_base_free(base);
+
+ return 0;
+}
diff --git a/spike/igraph_edges.c b/spike/igraph_edges.c
new file mode 100644
index 0000000..69ccedd
--- /dev/null
+++ b/spike/igraph_edges.c
@@ -0,0 +1,106 @@
+/* -*- mode: C -*- */
+/*
+ IGraph library.
+ Copyright (C) 2006-2012 Gabor Csardi <csardi.gabor@gmail.com>
+ 334 Harvard st, Cambridge MA, 02139 USA
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA
+
+*/
+
+#include <igraph/igraph.h>
+#include <math.h>
+
+
+void print_vector(igraph_vector_t *v, FILE *f) {
+ long int i;
+ for (i=0; i<igraph_vector_size(v); i++) {
+ fprintf(f, " %li", (long int) VECTOR(*v)[i]);
+ }
+ fprintf(f, "\n");
+}
+
+int main() {
+
+ igraph_t g;
+ igraph_matrix_t coords;
+ long int i, n;
+ igraph_vector_t v;
+ int ret;
+
+ /* Create graph */
+ igraph_vector_init(&v, 8);
+ VECTOR(v)[0]=0; VECTOR(v)[1]=1;
+ VECTOR(v)[2]=1; VECTOR(v)[3]=2;
+ VECTOR(v)[4]=2; VECTOR(v)[5]=3;
+ VECTOR(v)[6]=2; VECTOR(v)[7]=2;
+ igraph_create(&g, &v, 0, 1);
+
+ /* Add edges */
+ igraph_vector_resize(&v, 4);
+ VECTOR(v)[0]=2; VECTOR(v)[1]=1;
+ VECTOR(v)[2]=3; VECTOR(v)[3]=3;
+ igraph_add_edges(&g, &v, 0);
+
+ /* Check result */
+ igraph_get_edgelist(&g, &v, 0);
+ igraph_vector_sort(&v);
+ print_vector(&v, stdout);
+
+ /* Error, vector length */
+ igraph_set_error_handler(igraph_error_handler_ignore);
+ igraph_vector_resize(&v, 3);
+ VECTOR(v)[0]=0; VECTOR(v)[1]=1;
+ VECTOR(v)[2]=2;
+ ret=igraph_add_edges(&g, &v, 0);
+ if (ret != IGRAPH_EINVEVECTOR) {
+ return 1;
+ }
+
+ /* Check result */
+ igraph_get_edgelist(&g, &v, 0);
+ igraph_vector_sort(&v);
+ print_vector(&v, stdout);
+
+ /* Error, vector ids */
+ igraph_vector_resize(&v, 4);
+ VECTOR(v)[0]=0; VECTOR(v)[1]=1;
+ VECTOR(v)[2]=2; VECTOR(v)[3]=4;
+ ret=igraph_add_edges(&g, &v, 0);
+ if (ret != IGRAPH_EINVVID) {
+ return 2;
+ }
+
+ /* Check result */
+ igraph_get_edgelist(&g, &v, 0);
+ igraph_vector_sort(&v);
+ print_vector(&v, stdout);
+
+ //f=fopen("igraph_layout_reingold_tilford.in", "r");
+ //igraph_read_graph_edgelist(&g, f, 0, 1);
+ igraph_matrix_init(&coords, 0, 0);
+ igraph_layout_reingold_tilford(&g, &coords, IGRAPH_IN, 0, 0);
+
+ n=igraph_vcount(&g);
+ for (i=0; i<n; i++) {
+ printf("%6.3f %6.3f\n", MATRIX(coords, i, 0), MATRIX(coords, i, 1));
+ }
+
+ igraph_vector_destroy(&v);
+ igraph_matrix_destroy(&coords);
+ igraph_destroy(&g);
+ return 0;
+}
diff --git a/spike/igraph_evas_broken.c b/spike/igraph_evas_broken.c
new file mode 100644
index 0000000..0ab2b48
--- /dev/null
+++ b/spike/igraph_evas_broken.c
@@ -0,0 +1,233 @@
+/* -*- mode: C -*- */
+/*
+ IGraph library.
+ Copyright (C) 2006-2012 Gabor Csardi <csardi.gabor@gmail.com>
+ 334 Harvard st, Cambridge MA, 02139 USA
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA
+
+*/
+
+#include <igraph/igraph.h>
+#include <math.h>
+#include <Efx.h>
+#include <Ecore.h>
+#include <Ecore_Evas.h>
+
+igraph_t g;
+static Evas *e;
+static Eina_List *objs = NULL;
+int _count = 0;
+
+static Eina_Bool _start(void *d __UNUSED__);
+static void _del(void *data __UNUSED__, Efx_Map_Data *emd __UNUSED__, Evas_Object *r);
+static void _center(void *data __UNUSED__, Efx_Map_Data *emd __UNUSED__, Evas_Object *r);
+static void _create(void *data __UNUSED__, Efx_Map_Data *emd __UNUSED__, Evas_Object *r);
+
+static Efx_End_Cb callbacks[4][6] =
+{
+ {_create, _create, _create, _center, NULL, _del},
+ {NULL, NULL, _center, NULL, NULL, _del},
+ {NULL, _center, NULL, NULL, NULL, _del},
+ {_center, NULL, NULL, NULL, NULL, _del}
+};
+
+static Evas_Point points[] =
+{
+ {-100, -100},
+ {550, -100},
+ {-100, 550},
+ {550, 550}
+};
+
+static Evas_Object *
+_rect_create(void)
+{
+ Evas_Object *r;
+
+ r = evas_object_rectangle_add(e);
+ objs = eina_list_append(objs, r);
+ switch (eina_list_count(objs))
+ {
+ case 1:
+ evas_object_color_set(r, 255, 0, 0, 255);
+ break;
+ case 2:
+ evas_object_color_set(r, 0, 255, 0, 255);
+ break;
+ case 3:
+ evas_object_color_set(r, 0, 0, 255, 255);
+ break;
+ default:
+ evas_object_color_set(r, 0, 0, 0, 255);
+ }
+ evas_object_resize(r, 72, 72);
+ evas_object_move(r, 25, 25);
+ evas_object_show(r);
+ return r;
+}
+
+static void
+_del(void *data __UNUSED__, Efx_Map_Data *emd __UNUSED__, Evas_Object *r)
+{
+ objs = eina_list_remove_list(objs, objs);
+ evas_object_del(r);
+ if (objs) return;
+ ecore_timer_add(1.0, (Ecore_Task_Cb)_start, NULL);
+}
+
+static void
+_center(void *data __UNUSED__, Efx_Map_Data *emd __UNUSED__, Evas_Object *r)
+{
+ efx_rotate(r, EFX_EFFECT_SPEED_LINEAR, 360, NULL, 1.5, NULL, NULL);
+}
+
+static void
+_create(void *data __UNUSED__, Efx_Map_Data *emd __UNUSED__, Evas_Object *r)
+{
+ r = rect_create();
+ efx_queue_append(r, EFX_EFFECT_SPEED_ACCELERATE,
+ EFX_QUEUED_EFFECT(EFX_EFFECT_MOVE(350, 25)),
+ 1.0, callbacks[eina_list_count(objs) - 1][0], NULL);
+ if (eina_list_count(objs) < 4)
+ efx_queue_append(r, EFX_EFFECT_SPEED_DECELERATE,
+ EFX_QUEUED_EFFECT(EFX_EFFECT_MOVE(350, 350)),
+ 1.0, callbacks[eina_list_count(objs) - 1][1], NULL);
+ if (eina_list_count(objs) < 3)
+ efx_queue_append(r, EFX_EFFECT_SPEED_ACCELERATE,
+ EFX_QUEUED_EFFECT(EFX_EFFECT_MOVE(25, 350)),
+ 1.0, callbacks[eina_list_count(objs) - 1][2], NULL);
+ if (eina_list_count(objs) < 2)
+ efx_queue_append(r, EFX_EFFECT_SPEED_DECELERATE,
+ EFX_QUEUED_EFFECT(EFX_EFFECT_MOVE(25, 25)),
+ 1.0, callbacks[eina_list_count(objs) - 1][3], NULL);
+ efx_queue_append(r, EFX_EFFECT_SPEED_DECELERATE,
+ EFX_QUEUED_EFFECT(EFX_EFFECT_MOVE(203, 203)),
+ 1.5, callbacks[eina_list_count(objs) - 1][4], NULL);
+ efx_queue_append(r, EFX_EFFECT_SPEED_ACCELERATE,
+ EFX_QUEUED_EFFECT(EFX_EFFECT_MOVE(points[eina_list_count(objs) - 1].x, points[eina_list_count(objs) - 1].y)),
+ 1.0, callbacks[eina_list_count(objs) - 1][5], NULL);
+ efx_queue_run(r);
+}
+
+
+static Eina_Bool
+_start(void *d __UNUSED__)
+{
+ _create(NULL, NULL, NULL);
+
+ _insert_node();
+
+ _g_layout();
+
+ return EINA_FALSE;
+}
+
+static void
+_end(Ecore_Evas *ee __UNUSED__)
+{
+ ecore_main_loop_quit();
+}
+
+void
+_insert_node(void) {
+ Evas_Object *r;
+
+ r = _rect_create();
+ printf("[-] add one edges\n");
+ igraph_add_vertices(&g, 1, 0);
+ igraph_add_edge(&g, 4, 6 + _count);
+ ETEAN(&g, "weight", 2, 100.0);
+
+ _count++;
+}
+
+void _g_layout(void) {
+ igraph_vector_t v;
+ igraph_matrix_t coords;
+
+ printf("[-] apply layout\n");
+ igraph_matrix_init(&coords, 0, 0);
+ igraph_layout_reingold_tilford(&g, &coords, IGRAPH_IN, 0, 0);
+
+ printf("[-] check edges\n");
+ printf("edge count: %d\n", igraph_ecount(&g));
+ igraph_vector_init(&v, igraph_ecount(&g));
+ igraph_get_edgelist(&g, &v, 0);
+ print_vector(&v, stdout);
+
+ printf("[-] show vertices\n");
+ n=igraph_vcount(&g);
+ for (i=0; i<n; i++) {
+ printf("%d: %6.3f %6.3f\n", i, MATRIX(coords, i, 0), MATRIX(coords, i, 1));
+ }
+
+
+}
+
+void print_vector(igraph_vector_t *v, FILE *f) {
+ long int i;
+ for (i=0; i<igraph_vector_size(v); i++) {
+ fprintf(f, " %li", (long int) VECTOR(*v)[i]);
+ }
+ fprintf(f, "\n");
+}
+
+int main() {
+ Ecore_Evas *ee;
+ Evas_Object *r;
+ long int i, n;
+ int ret;
+
+ efx_init();
+ ecore_evas_init();
+ eina_log_domain_level_set("igraph_evas", EINA_LOG_LEVEL_DBG);
+ ee = ecore_evas_software_x11_new(NULL, 0, 0, 0, 450, 450);
+ ecore_evas_callback_delete_request_set(ee, _end);
+ ecore_evas_title_set(ee, "queue");
+ ecore_evas_show(ee);
+ e = ecore_evas_get(ee);
+ r = evas_object_rectangle_add(e);
+ evas_object_resize(r, 450, 450);
+ evas_object_show(r);
+
+ printf("[-] create graph\n");
+ igraph_empty(&g, 0, 0);
+
+ printf("[-] add edges\n");
+ igraph_add_vertices(&g, 1, 0);
+ igraph_add_vertices(&g, 1, 0);
+ igraph_add_edge(&g, 0, 1);
+ igraph_add_vertices(&g, 1, 0);
+ igraph_add_edge(&g, 0, 2);
+ igraph_add_vertices(&g, 1, 0);
+ igraph_add_edge(&g, 0, 3);
+ igraph_add_vertices(&g, 1, 0);
+ igraph_add_edge(&g, 0, 4);
+ igraph_add_vertices(&g, 1, 0);
+ igraph_add_edge(&g, 4, 5);
+
+ _g_layout();
+
+ ecore_timer_add(1.0, (Ecore_Task_Cb)_start, NULL);
+ ecore_main_loop_begin();
+
+ printf("[*] destroying graph\n");
+ //igraph_vector_destroy(&v);
+ igraph_matrix_destroy(&coords);
+ igraph_destroy(&g);
+ return 0;
+}
diff --git a/spike/igraph_graph.c b/spike/igraph_graph.c
new file mode 100644
index 0000000..998cb5e
--- /dev/null
+++ b/spike/igraph_graph.c
@@ -0,0 +1,96 @@
+/* -*- mode: C -*- */
+/*
+ IGraph library.
+ Copyright (C) 2006-2012 Gabor Csardi <csardi.gabor@gmail.com>
+ 334 Harvard st, Cambridge MA, 02139 USA
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA
+
+*/
+
+#include <igraph/igraph.h>
+#include <math.h>
+
+
+void print_vector(igraph_vector_t *v, FILE *f) {
+ long int i;
+ for (i=0; i<igraph_vector_size(v); i++) {
+ fprintf(f, " %li", (long int) VECTOR(*v)[i]);
+ }
+ fprintf(f, "\n");
+}
+
+int main() {
+
+ igraph_t g;
+ igraph_matrix_t coords;
+ long int i, n;
+ igraph_vector_t v;
+ int ret;
+ int c;
+
+ printf("[-] create graph\n");
+ /*VECTOR(v)[0]=0; VECTOR(v)[1]=1;
+ VECTOR(v)[2]=1; VECTOR(v)[3]=2;
+ VECTOR(v)[4]=2; VECTOR(v)[5]=3;
+ VECTOR(v)[6]=2; VECTOR(v)[7]=3;
+ igraph_create(&g, &v, 0, 0);*/ /* Creates a graph with the specified edges */
+ igraph_empty(&g, 0, 0);
+
+ printf("[-] add edges\n");
+ igraph_add_vertices(&g, 1, 0);
+ igraph_add_vertices(&g, 1, 0);
+ igraph_add_edge(&g, 0, 1);
+ igraph_add_vertices(&g, 1, 0);
+ igraph_add_edge(&g, 0, 2);
+ igraph_add_vertices(&g, 1, 0);
+ igraph_add_edge(&g, 0, 3);
+ igraph_add_vertices(&g, 1, 0);
+ igraph_add_edge(&g, 0, 4);
+ igraph_add_vertices(&g, 1, 0);
+ igraph_add_edge(&g, 4, 5);
+
+ printf("[-] entering loop\n");
+
+ for (c=0; c<=2; c++) {
+ printf("[*] === loop %d ===\n", c);
+ printf("[-] add one edges\n");
+ igraph_add_vertices(&g, 1, 0);
+ igraph_add_edge(&g, 4, 6 + c);
+
+ printf("[-] check edges\n");
+ printf("edge count: %d\n", igraph_ecount(&g));
+ igraph_vector_init(&v, igraph_ecount(&g));
+ igraph_get_edgelist(&g, &v, 0);
+ print_vector(&v, stdout);
+
+ printf("[-] apply layout\n");
+ igraph_matrix_init(&coords, 0, 0);
+ igraph_layout_reingold_tilford(&g, &coords, IGRAPH_IN, 0, 0);
+
+ printf("[-] show vertices\n");
+ n=igraph_vcount(&g);
+ for (i=0; i<n; i++) {
+ printf("%d: %6.3f %6.3f\n", i, MATRIX(coords, i, 0), MATRIX(coords, i, 1));
+ }
+ }
+
+ printf("[*] destroying graph\n");
+ //igraph_vector_destroy(&v);
+ igraph_matrix_destroy(&coords);
+ igraph_destroy(&g);
+ return 0;
+}