#include #include #include #include #include int _loglevel = 0; Evas_Object *_mainwin; Evas_Object *_egraph = NULL; struct ggnet *_ggnet; struct event_base *_ev_base; #if defined(__OpenBSD__) void __dead #else void #endif usage(void) { extern char *__progname; fprintf(stderr, "usage: %s [-hv] [ip [port]]\n", __progname); exit(1); } /* 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 (_loglevel >= 2) 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 (_loglevel >= 2) 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 (_loglevel >= 2) 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 (_loglevel >= 2) 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 (_loglevel >= 2) 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 (_loglevel >= 2) printf("_conn_del: not last one, edge %x *not* deleted\n", e); } } else { if (_loglevel >= 2) 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: if (_loglevel >= 1) { 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: if (_loglevel >= 1) { printf(" type PACKET_DELCONN\n"); printf(" delconn_id %d\n", pkt->delconn_id); } _conn_del(pkt->delconn_id); break; case PACKET_DATA: if (_loglevel >= 1) { //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: if (_loglevel >= 1) { 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; char gg_serv_ip[30] = "127.0.0.1"; int gg_serv_port = GLOUGLOU_ANALY_DEFAULT_PORT; int retval = -1; int op; while ((op = getopt(argc, argv, "hv")) != -1) { switch (op) { case 'h': usage(); /* NOTREACHED */ case 'v': _loglevel++; break; default: usage(); /* NOTREACHED */ } } switch (argc - optind) { case 2: gg_serv_port = atoi(argv[3]); case 1: strncpy(gg_serv_ip, argv[2], sizeof(gg_serv_ip)); case 0: break; default: usage(); /* NOTREACHED */ } 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
" "
" "Enjoy !
"); evas_object_show(lb); elm_box_pack_end(bx2, lb); lb = elm_label_add(win); elm_object_text_set(lb, "Layout"); 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, gg_serv_ip, gg_serv_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()