/* 1605, Thu 17 Feb 00 X_OB.C: X/Motif outer block routines for x_nm_rc.c Copyright (C) 1996-2002 by Nevil Brownlee, CAIDA | University of Auckland x_nm_rc development began with O'Reilly code, as noted below */ /* Written by Dan Heller and Paula Ferguson. * Copyright 1994, O'Reilly & Associates, Inc. * Permission to use, copy, and modify this program without * restriction is hereby granted, as long as this copyright * notice appears in each copy of the program source code. * This program is freely distributable without licensing fees and * is provided without guarantee or warrantee expressed or implied. * This program is -not- in the public domain. */ /* * $Log: x_ob.c,v $ * Revision 1.1.1.2.2.10 2002/02/23 01:57:24 nevil * Moving srl examples to examples/ directory. Modified examples/Makefile.in * * Revision 1.1.1.2.2.6 2000/08/08 19:44:47 nevil * 44b8 release * * Revision 1.1.1.2.2.4 2000/06/06 03:38:15 nevil * Combine NEW_ATR with TCP_ATR, various bug fixes * * Revision 1.1.1.2.2.1 1999/11/29 00:17:23 nevil * Make changes to support NetBSD on an Alpha (see version.history for details) * * Revision 1.1.1.2 1999/10/03 21:06:21 nevil * *** empty log message *** * * Revision 1.1.1.1.2.3 1999/09/29 22:29:13 nevil * Changes (mainly changing // to /* comments) for Irix * * Revision 1.1.1.1.2.2 1999/05/25 22:30:51 nevil * Make sure IPv6 managers interwork properly with IPv4 meters * - Determine meter's RULE_ADDR_SIZE by reading mask from rule 1 * of default ruleset. * - Print warning if meter rulesize < manager rulesize. * - Use smaller of these when downloading rules. * * Revision 1.1.1.1.2.1 1999/01/08 01:38:34 nevil * Distribution file for 4.3b7 * * Revision 1.1.1.1 1998/11/16 03:57:28 nevil * Import of NeTraMet 4.3b3 * * Revision 1.1.1.1 1998/11/16 03:22:01 nevil * Import of release 4.3b3 * * Revision 1.1.1.1 1998/10/28 20:31:26 nevil * Import of NeTraMet 4.3b1 * * Revision 1.1.3.2.2.1 1998/10/27 04:39:15 nevil * 4.3b1 release * * Revision 1.1.3.2 1998/10/18 23:44:16 nevil * Added Nicolai's patches, some 'tidying up' of the source * * Revision 1.1.3.1 1998/10/13 02:48:31 nevil * Import of Nicolai's 4.2.2 * * Revision 1.1.1.1 1998/08/24 12:09:29 nguba * NetraMet 4.2 Original Distribution * * Revision 1.3 1998/05/07 04:28:53 rtfm * Implement NetFlowMet, the Cisco NetFlow RTFM meter */ #if HAVE_CONFIG_H #include #else #define ver_str "test" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_SYS_SELECT_H #include #endif #include "ausnmp.h" /* SNMP include files needed by nmc.h */ #include "asn1.h" #include "snmp.h" #include "snmpimpl.h" #include "snmpapi.h" #include "snmpclnt.h" #include "mib.h" #include "nmc.h" #include "n_plot.h" #include "x_nm_rc.h" #define noW_DEBUG String fallback_resources[] = { "*title: nifty: Nevil's RTFM Network Flow Analyser", NULL }; GC gc; Pixmap pixmap; Dimension da_width,da_height; /* Current size of the DrawingArea */ Widget main_w, drawing_a, label; void configure(), draw(), expose(), destroy(), clear_it(); void set_color(Widget widget, XtPointer client_data, XtPointer call_data); typedef struct _menu_item { char *label; /* Label for the item */ WidgetClass *class; /* Pushbutton, label, separator... */ char mnemonic; /* Mnemonic; 0 if none */ char *accelerator; /* Accelerator; NULL if none */ char *accel_text; /* To be converted to compound string */ void (*callback)(); /* Routine to call; NULL if none */ XtPointer callback_data; /* Client_data for callback() */ struct _menu_item *subitems; /* Pullright menu items, if not NULL */ } MenuItem; /* Pulldown menus are built from cascade buttons, so this function * also includes pullright menus. Create the menu, the cascade button * that owns the menu, and then the submenu items. */ Widget BuildPulldownMenu(Widget parent, char *menu_title,char menu_mnemonic, Boolean tear_off, MenuItem *items) { Widget PullDown, cascade, widget; int i; XmString str; PullDown = XmCreatePulldownMenu(parent, "_pulldown", NULL, 0); if (tear_off) XtVaSetValues(PullDown, XmNtearOffModel, XmTEAR_OFF_ENABLED, NULL); str = XmStringCreateLocalized(menu_title); cascade = XtVaCreateManagedWidget(menu_title, xmCascadeButtonGadgetClass, parent, XmNsubMenuId, PullDown, XmNlabelString, str, XmNmnemonic, menu_mnemonic, NULL); XmStringFree(str); /* Now add the menu items */ for (i = 0; items[i].label != NULL; i++) { /* If subitems exist, create the pull-right menu by calling this * function recursively. Since the function returns a cascade * button, the widget returned is used.. */ if (items[i].subitems) widget = BuildPulldownMenu(PullDown, items[i].label, items[i].mnemonic, tear_off, items[i].subitems); else widget = XtVaCreateManagedWidget(items[i].label, *items[i].class, PullDown, NULL); /* Whether the item is a real item or a cascade button with a * menu, it can still have a mnemonic. */ if (items[i].mnemonic) XtVaSetValues(widget, XmNmnemonic, items[i].mnemonic, NULL); /* any item can have an accelerator, except cascade menus. But, * we don't worry about that; we know better in our declarations. */ if (items[i].accelerator) { str = XmStringCreateLocalized(items[i].accel_text); XtVaSetValues(widget, XmNaccelerator, items[i].accelerator, XmNacceleratorText, str, NULL); XmStringFree(str); } if (items[i].callback) XtAddCallback(widget, (items[i].class == &xmToggleButtonWidgetClass || items[i].class == &xmToggleButtonGadgetClass) ? XmNvalueChangedCallback : /* ToggleButton class */ XmNactivateCallback, /* PushButton class */ items[i].callback, items[i].callback_data); } return cascade; } /* Callback functions for menu items declared below */ static void set_log(Widget w, XtPointer client_data, XtPointer call_data) { set_log_type(client_data); } static void set_name(Widget w, XtPointer client_data, XtPointer call_data) { set_name_type(client_data); } static void set_ordinate(Widget w, XtPointer client_data, XtPointer call_data) { set_ordinate_type(client_data); configure(w); } static void set_metric(Widget w, XtPointer client_data, XtPointer call_data) { set_metric_type(client_data); configure(w); } static void set_select(Widget w, XtPointer client_data, XtPointer call_data) { set_select_type(client_data); configure(w); } static void set_axis(Widget w, XtPointer client_data, XtPointer call_data) { set_axis_range(client_data); configure(w); } static void quit_action(Widget w, XtPointer client_data, XtPointer call_data) { shutdown_nifty(); } MenuItem log_menu[] = { {"Sample", &xmPushButtonGadgetClass, 0, NULL, NULL, set_log, (XtPointer)LGSAMPLE, (MenuItem *)NULL}, {"Points", &xmPushButtonGadgetClass, 0, NULL, NULL, set_log, (XtPointer)LGPOINT, (MenuItem *)NULL}, {"None", &xmPushButtonGadgetClass, 0, NULL, NULL, set_log, (XtPointer)LGNONE, (MenuItem *)NULL}, NULL }; MenuItem peer_addr_menu[] = { {"IP Address", &xmPushButtonGadgetClass, 0, NULL, NULL, set_name, (XtPointer)0, (MenuItem *)NULL}, {"Domain Name", &xmPushButtonGadgetClass, 0, NULL, NULL, set_name, (XtPointer)1, (MenuItem *)NULL}, NULL }; MenuItem ordinate_menu[] = { {"Bytes", &xmPushButtonGadgetClass, 0, NULL, NULL, set_ordinate, (XtPointer)PQBYTES, (MenuItem *)NULL}, {"Packets", &xmPushButtonGadgetClass, 0, NULL, NULL, set_ordinate, (XtPointer)PQPKTS, (MenuItem *)NULL}, NULL }; MenuItem metric_menu[] = { {"Rate", &xmPushButtonGadgetClass, 0, NULL, NULL, set_metric, (XtPointer)PTSAMPKB, (MenuItem *)NULL}, {"Count", &xmPushButtonGadgetClass, 0, NULL, NULL, set_metric, (XtPointer)PTFLOWKB, (MenuItem *)NULL}, {"Percent", &xmPushButtonGadgetClass, 0, NULL, NULL, set_metric, (XtPointer)PTSAMPPC, (MenuItem *)NULL}, NULL }; MenuItem select_menu[] = { {"Last sample", &xmPushButtonGadgetClass, 0, NULL, NULL, set_select, (XtPointer)STLAST, (MenuItem *)NULL}, {"Recent samples", &xmPushButtonGadgetClass, 0, NULL, NULL, set_select, (XtPointer)STRECENT, (MenuItem *)NULL}, {"All samples", &xmPushButtonGadgetClass, 0, NULL, NULL, set_select, (XtPointer)STALL, (MenuItem *)NULL}, NULL }; MenuItem xaxis_menu[] = { {"100 s", &xmPushButtonGadgetClass, 0, NULL, NULL, set_axis, (XtPointer)SX100S, (MenuItem *)NULL}, {"15 m", &xmPushButtonGadgetClass, 0, NULL, NULL, set_axis, (XtPointer)SX15M, (MenuItem *)NULL}, {"2 h", &xmPushButtonGadgetClass, 0, NULL, NULL, set_axis, (XtPointer)SX2H, (MenuItem *)NULL}, NULL }; MenuItem yaxis_menu[] = { {"40", &xmPushButtonGadgetClass, 0, NULL, NULL, set_axis, (XtPointer)SY40P, (MenuItem *)NULL}, {"900", &xmPushButtonGadgetClass, 0, NULL, NULL, set_axis, (XtPointer)SY900, (MenuItem *)NULL}, {"9k", &xmPushButtonGadgetClass, 0, NULL, NULL, set_axis, (XtPointer)SY9K, (MenuItem *)NULL}, {"90k", &xmPushButtonGadgetClass, 0, NULL, NULL, set_axis, (XtPointer)SY90K, (MenuItem *)NULL}, {"900k", &xmPushButtonGadgetClass, 0, NULL, NULL, set_axis, (XtPointer)SY900K, (MenuItem *)NULL}, {"9M", &xmPushButtonGadgetClass, 0, NULL, NULL, set_axis, (XtPointer)SY9M, (MenuItem *)NULL}, {"90M", &xmPushButtonGadgetClass, 0, NULL, NULL, set_axis, (XtPointer)SY90M, (MenuItem *)NULL}, NULL }; MenuItem file_menus[] = { {"Logging", &xmCascadeButtonGadgetClass, 0, NULL, NULL, 0, 0, log_menu}, {"Peer Address", &xmCascadeButtonGadgetClass, 0, NULL, NULL, 0, 0, peer_addr_menu}, {"Quit", &xmCascadeButtonGadgetClass, 0, NULL, NULL, quit_action, 0, (MenuItem *)NULL }, NULL }; MenuItem option_menus[] = { {"Ordinate", &xmCascadeButtonGadgetClass, 0, NULL, NULL, 0, 0, ordinate_menu}, {"Metric", &xmCascadeButtonGadgetClass, 0, NULL, NULL, 0, 0, metric_menu}, {"Selection", &xmCascadeButtonGadgetClass, 0, NULL, NULL, 0, 0, select_menu}, NULL }; MenuItem plot_menus[] = { {"X axis", &xmCascadeButtonGadgetClass, 0, NULL, NULL, 0, 0, xaxis_menu}, {"Y axis", &xmCascadeButtonGadgetClass, 0, NULL, NULL, 0, 0, yaxis_menu}, NULL }; void change_label_text(Widget lbl, char *msg) { XmString label_str; if (msg[0] != '\0') { XtVaGetValues(lbl, XmNlabelString, &label_str, NULL); XmStringFree(label_str); label_str = XmStringCreateLtoR(msg,XmSTRING_DEFAULT_CHARSET); XtVaSetValues(lbl, XmNlabelString, label_str, NULL); } } XtIntervalId timer; static int need_new_points; void t_handler(Widget w, XtIntervalId *id) { Display *display; Window window; Cursor cursor; int ms; XmString label_str; char msg[250]; #ifdef W_DEBUG printf("t_handler():\n"); #endif if (request_stop) /* Set by sigint_handler() */ shutdown_nifty(); display = XtDisplay(main_w); window = XtWindow(main_w); if (need_new_points) { cursor = XCreateFontCursor(display,XC_watch); /* Wristwatch cursor */ XDefineCursor(display, window, cursor); need_new_points = 0; ms = 10; /* X idle loop will display cursor */ } else { ms = 1000*nm_timer_callback(msg,1); /* Generate the set of points */ nm_select_points(); nm_compute_points(); if (pixmap != (Pixmap)NULL) XCopyArea(XtDisplay(drawing_a), pixmap, XtWindow(drawing_a), gc, 0,0, da_width,da_height, 0,0); nm_plot_points(); /* Plot the points */ change_label_text(label,msg); XUndefineCursor(display, window); /* Back to arrow cursor */ need_new_points = 1; } timer = XtAppAddTimeOut(XtWidgetToApplicationContext(w), ms, (XtTimerCallbackProc)t_handler, w); } void start_timer(Widget w, int seconds) { char msg[250]; seconds = nm_timer_callback(msg,0); /* Main X loop not running yet! */ timer = XtAppAddTimeOut(XtWidgetToApplicationContext(w), seconds*1000, (XtTimerCallbackProc)t_handler, w); need_new_points = 1; } static int nifty_running; /* Set after first plot has been drawn */ void nm_x_startup(int argc, char *argv[], int next_event_s) { Widget toplevel; XEvent x_event; XtAppContext app_context; XGCValues gcv; Widget menubar; Arg args[20]; Cardinal nargs; XtActionsRec actions[] = { {"configure", configure}, {"draw", draw}, }; String translations = /* for the DrawingArea widget */ ": configure()\n" ": draw(b1-down)\n" ": draw(b1-up)\n" ": draw(b2-down)\n" ": draw(b3-down)"; XtSetLanguageProc (NULL, NULL, NULL); toplevel = XtVaAppInitialize(&app_context, "toplevel", NULL, 0, &argc, argv, fallback_resources, NULL); /* Create a MainWindow to contain the drawing area */ main_w = XtVaCreateManagedWidget ("main_w", xmFormWidgetClass, toplevel, XmNwidth, N_MAIN_WIDTH, XmNheight, N_MAIN_HEIGHT, NULL); /* Create a GC for drawing (callback). Used a lot -- make global */ gcv.foreground = WhitePixelOfScreen (XtScreen (main_w)); gc = XCreateGC(XtDisplay(main_w), RootWindowOfScreen(XtScreen(main_w)), GCForeground, &gcv); nargs = 0; XtSetArg(args[nargs], XmNmarginHeight, 0); ++nargs; XtSetArg(args[nargs], XmNbackground, WhitePixelOfScreen(XtScreen (main_w))); ++nargs; XtSetArg(args[nargs], XmNtopAttachment, XmATTACH_FORM); ++nargs; XtSetArg(args[nargs], XmNtopOffset, 0); ++nargs; XtSetArg(args[nargs], XmNleftAttachment, XmATTACH_FORM); ++nargs; XtSetArg(args[nargs], XmNleftOffset, 0); ++nargs; XtSetArg(args[nargs], XmNrightAttachment,XmATTACH_FORM); ++nargs; XtSetArg(args[nargs], XmNrightOffset, 0); ++nargs; menubar = XmCreateMenuBar(main_w, "menubar", (ArgList)args, nargs); BuildPulldownMenu(menubar, "File", 0, False, file_menus); BuildPulldownMenu(menubar, "Options", 0, False, option_menus); BuildPulldownMenu(menubar, "Plot", 0, False, plot_menus); XtManageChild(menubar); label = XtVaCreateManagedWidget("label", xmLabelWidgetClass, main_w, XmNbackground, WhitePixelOfScreen(XtScreen (main_w)), XmNlabelString, XmStringCreateLtoR( "nifty " ver_str /* Check on meter version done in nmc_snmp.c */ ", (C) 1996-2002 by Nevil Brownlee, CAIDA | University of Auckland", XmSTRING_DEFAULT_CHARSET), XmNalignment, XmALIGNMENT_BEGINNING, XmNheight, N_LABEL_HEIGHT, XmNmarginLeft, N_LABEL_LEFT_MARGIN, XmNbottomAttachment, XmATTACH_FORM, XmNbottomOffset, N_BOTTOM_BORDER, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, N_LEFT_BORDER, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, N_RIGHT_BORDER, NULL); drawing_a = XtVaCreateManagedWidget ("drawing_a", xmDrawingAreaWidgetClass, main_w, XmNtranslations, XtParseTranslationTable (translations), XmNbackground, WhitePixelOfScreen (XtScreen (main_w)), XmNresizePolicy, XmRESIZE_ANY, XmNtopAttachment, XmATTACH_WIDGET, XmNtopWidget, menubar, XmNtopOffset, N_TOP_BORDER, XmNleftAttachment, XmATTACH_FORM, XmNleftOffset, N_LEFT_BORDER, XmNrightAttachment, XmATTACH_FORM, XmNrightOffset, N_RIGHT_BORDER, XmNbottomAttachment, XmATTACH_WIDGET, XmNbottomWidget, label, XmNbottomOffset, N_MID_SPACE, NULL); XtAddCallback(drawing_a, XmNexposeCallback, expose, NULL); XtAppAddActions(app_context, actions, XtNumber(actions)); da_width = da_height = 0; XtRealizeWidget(toplevel); set_initial_parameters(); pixmap = (Pixmap)NULL; configure(drawing_a); /* Don't know size until window is realised! */ start_timer(drawing_a, next_event_s); nifty_running = 0; for (;;) { /* X App main loop */ XtAppNextEvent(app_context, &x_event); if (x_event.type == ClientMessage && x_event.xany.window == XtWindow(toplevel)) { if (nifty_running) shutdown_nifty(); /* Some X window managers send the message when windows open, as well as when they close */ } else XtDispatchEvent(&x_event); } } void configure(Widget w, XEvent *event, String *args, int *num_args) { Dimension new_width,new_height; XtVaGetValues(drawing_a, /* Get new window size */ XmNwidth,&new_width, XmNheight,&new_height, NULL); #ifdef W_DEBUG printf("configure(): pixmap=%u, %d,%d -> %d,%d\n", pixmap, da_width,da_height, new_width,new_height); #endif if (pixmap != (Pixmap)NULL) XFreePixmap(XtDisplay(drawing_a), pixmap); /* Create a pixmap the same size as the drawing area. */ pixmap = XCreatePixmap(XtDisplay(drawing_a), RootWindowOfScreen(XtScreen(drawing_a)), new_width,new_height, DefaultDepthOfScreen(XtScreen(drawing_a))); /* Clear pixmap with white */ set_color(drawing_a, "White", NULL); XFillRectangle(XtDisplay(drawing_a), pixmap, gc, 0,0, new_width,new_height); set_color(drawing_a, "Black", NULL); da_width = new_width, da_height = new_height; plot_graph(); /* Set up the plot environment */ XCopyArea(XtDisplay(drawing_a), /* Display the pixmap */ pixmap, XtWindow(drawing_a), gc, 0,0, da_width,da_height, 0,0); nm_plot_points(); /* Plot them */ } /* Action procedure to respond to any of the events from the * translation table declared in main(). This function is called * in response to events like Button Down, Up and Motion */ static int np; /* Index of nearest point (for button-1) */ void draw(Widget w, XEvent *event, String *args, int *num_args) { XButtonEvent *bevent = (XButtonEvent *) event; Display *display = XtDisplay(main_w); Window window = XtWindow(main_w); Cursor cursor; char msg[500]; #ifdef W_DEBUG printf("draw(%d): %s\n", *num_args, args[0]); #endif if (*num_args != 1) XtError ("draw(): Wrong number of args!"); if (strcmp(args[0],"b1-down") == 0) { if ((np = nm_nearest_flow(bevent->x, da_height-bevent->y)) < 0) return; /* Not near a point */ if (show_names) { cursor = XCreateFontCursor(display,XC_crosshair); /* Change cursor */ XDefineCursor(display, window, cursor); } return; } else if (strcmp(args[0],"b1-up") == 0) { if (np < 0) return; nm_flow_details(msg, 1, np); /* Use np from b1-down */ if (show_names) XUndefineCursor(display, window); /* Back to arrow cursor */ } else { if ((np = nm_nearest_flow(bevent->x, da_height-bevent->y)) < 0) return; /* Not near a point */ if (strcmp(args[0],"b3-down") == 0) nm_flow_details(msg, 3, np); else if (strcmp(args[0],"b2-down") == 0) nm_flow_details(msg, 2, np); } change_label_text(label, msg); } #if 0 void show_resolving(char *msg) { change_label_text(label, msg); } #endif /* Called whenever any portion of the drawing area is exposed */ void expose(Widget w, XtPointer client_data, XtPointer call_data) { XmDrawingAreaCallbackStruct *cbs = (XmDrawingAreaCallbackStruct *)call_data; #ifdef W_DEBUG printf("expose():\n"); #endif if (pixmap != (Pixmap)NULL) XCopyArea(cbs->event->xexpose.display, pixmap, cbs->window, gc, 0,0, da_width,da_height, 0,0); nm_plot_points(); } /* Set foreground colour for a widget */ void set_color(Widget widget, XtPointer client_data, XtPointer call_data) { String color = (String)client_data; Display *dpy = XtDisplay(widget); Colormap cmap = DefaultColormapOfScreen(XtScreen(widget)); XColor col, unused; if (!XAllocNamedColor(dpy, cmap, color, &col, &unused)) { char buf[32]; sprintf(buf, "Can't alloc %s", color); XtWarning(buf); return; } XSetForeground (dpy, gc, col.pixel); } void set_plot_color(String cp) { set_color(drawing_a,cp,NULL); } plot_graph(void) { plot_window(drawing_a, gc); plot_device(pixmap); swindo(N_GRAPH_MARGIN,da_width-2*N_GRAPH_MARGIN, N_GRAPH_MARGIN,da_height-2*N_GRAPH_MARGIN); dwindo(pxmin,pxmax, pymin,pymax); settrn(P_LOG_LOG); draw_xaxis(); draw_yaxis(); movabs(da_width-N_TITLE_RIGHT_MARGIN, da_height-N_TITLE_TOP_MARGIN); draw_title(); plot_device(XtWindow(drawing_a)); nifty_running = 1; }