#! /opt/RICHPse/bin/se 
/*
 *  pragma ident "@(#)tcp_monitor.se 1.4    96/03/15 SMI" 
 *
 *  this program monitors the TCP/IP stack and provides an easy method
 *  to change parameters which would effect the operation of the
 *  stack, hopefully to allow online tuning of your TCP performance.
 *
 *  Mike.Bennett@corp.sun.com		February 26, 1996
 *  Hacked by Adrian.Cockcroft@sun.com	Aug 19th, 1996
 *
 *  A lot of the gui code was snitched from multi_meter.se and xit.se,
 *  thanks to richard pettit and adrian cockcroft.  Of course, any
 *  wierdness you find is due to me... (mb)
 */  
#include <stdlib.se>
#include <string.se>
#include <unistd.se>
#include <sysdepend.se>
#include <kstat.se>
#include <time.se>
#include <gui_lib.se>
#include <mib.se>
#include <ndd.se>

/*
 *  global defines
 */
#define BAR_WIDTH         200
#define BAR_HEIGHT         10

#define INTERVAL_MIN        1
#define INITIAL_INTERVAL   15
#define INTERVAL_MAX      120
#define SECS		 1000

/*  
 *  the GUI resources 
 */
/* toplevel */
resource_t gui[7] = {
/* type         fg        bg         label      data  cb            *
 * ---------------------------------------------------------------- */
{ R_VFRAME                                                          },
{ R_MENUBAR,    ST_WHITE, ST_BLUE                                   },
{ R_ROW,        ST_WHITE, ST_BLUE,   nil,       nil,  nil           },
{ R_SEPARATOR                                                       },
{ R_HSCALE,     ST_WHITE, ST_BLUE,  "Update Interval (secs)", nil,  "interval_cb" },
{ R_SEPARATOR                                                       },
{ R_END                                                             }
};

/* array indices for gui */
#define MENUBAR_I   1
#define ROW_I       2
#define SCALE_I     4

/* menubar */
resource_t menubar[4] = {
/* type         fg        bg         label         data  cb            *
 * ------------------------------------------------------------------- */
{ R_OPTION,     ST_WHITE, ST_BLUE,   "Params...",  nil,  "m_params_cb" },
{ R_OPTION,     ST_WHITE, ST_BLUE,   "About...",   nil,  "m_about_cb"  },
{ R_OPTION,     ST_WHITE, ST_BLUE,   "Exit",       nil,  "m_exit_cb"   },
{ R_END                                                                }
};
#define MENUBAR_PARAMS_I	0

resource_t cols[4] = {
/* type         fg        bg         label      data    cb          *
 * ---------------------------------------------------------------- */
{ R_COLUMN,	ST_WHITE, ST_BLUE,   nil,       nil,	nil   	    },
{ R_SEPARATOR                                                       },
{ R_COLUMN,	ST_WHITE, ST_BLUE,   nil,       nil,	nil   	    },
{ R_END                                                             }
};
#define COL_STATS_I_1  0
#define COL_STATS_I_2  2

resource_t istats[10] = {
/* type         fg        bg         label                     *
 * ----------------------------------------------------------- */
{ R_HBAR_CHART, ST_GREEN, ST_WHITE,  "Accepts/Sec" 		},
{ R_SEPARATOR                                			},
{ R_HBAR_CHART, ST_GREEN, ST_WHITE,  "Connects/Sec" 	        },
{ R_SEPARATOR                                			},
{ R_HBAR_CHART, ST_RED,   ST_WHITE,  "Resets"                   },
{ R_SEPARATOR                                			},
{ R_HBAR_CHART, ST_RED,   ST_WHITE,  "Failures"                 },
{ R_SEPARATOR                                			},
{ R_HBAR_CHART, ST_GREEN, ST_WHITE,  "Established Sessions" 	},
{ R_END                                      			}
};
#define ISTATS_ACCEPTS	      0
#define ISTATS_CONNECTS	      2
#define ISTATS_RESETS	      4
#define ISTATS_FAILS  	      6
#define ISTATS_SESSIONS	      8

resource_t ostats[10] = {
/* type         fg        bg         label                     *
 * ----------------------------------------------------------- */
{ R_HBAR_CHART, ST_GREEN, ST_WHITE,  "IN Msgs/Sec"  		},
{ R_SEPARATOR                                			},
{ R_HBAR_CHART, ST_GREEN, ST_WHITE,  "OUT Msgs/Sec"  		},
{ R_SEPARATOR                                			},
{ R_HBAR_CHART, ST_RED,   ST_WHITE,  "Retrans Bytes %" 		},
{ R_SEPARATOR                                			},
{ R_HBAR_CHART, ST_GREEN, ST_WHITE,  "IN Bytes/Sec"  		},
{ R_SEPARATOR                                			},
{ R_HBAR_CHART, ST_GREEN, ST_WHITE,  "OUT Bytes/Sec"  		},
{ R_END                                      			}
};
#define OSTATS_IN_MSG_RATE    0
#define OSTATS_OUT_MSG_RATE   2
#define OSTATS_RETRANS	      4
#define OSTATS_IN_BYTE_RATE   6
#define OSTATS_OUT_BYTE_RATE  8

/*--------------------------------------------------------------------------- */
/*--------------------  The PARAMETER GUI subframes ------------------------- */
/*--------------------------------------------------------------------------- */
resource_t param_popup[6] = {
/* type         fg        bg         label      data  cb             *
 * ----------------------------------------------------------------- */
{ R_VFRAME                                                           },
{ R_MENUBAR,    ST_WHITE, ST_BLUE                                    },
{ R_ROW,        ST_WHITE, ST_BLUE,   nil,       nil,  nil            },
{ R_END                                                              }
};
#define PPOPUP_MENUBAR  1
#define PPOPUP_COLS     2

resource_t param_popup_cols[4] = {
/* type         fg        bg         label      data  cb             *
 * ----------------------------------------------------------------- */
{ R_COLUMN,    ST_WHITE, ST_BLUE,   nil,       nil,  nil             },
{ R_SEPARATOR                                                        },
{ R_COLUMN,    ST_WHITE, ST_BLUE,   nil,       nil,  nil             },
{ R_END                                                              }
};
#define PPOPUPC_ROW1  0
#define PPOPUPC_ROW2  2

resource_t param_menubar[4] = {
/* type         fg        bg         label      data  cb             *
 * ----------------------------------------------------------------- */
{ R_OPTION,    ST_WHITE, ST_BLUE,   "Apply",   nil,  "m_param_apply" },
{ R_OPTION,    ST_WHITE, ST_BLUE,   "Reset",   nil,  "m_param_reset" },
{ R_OPTION,    ST_WHITE, ST_BLUE,   "Close",   nil,  "m_popdown_cb"  },
{ R_END                                                              }
};

resource_t params1[10] = {
/* type         fg        bg         label              data  cb              *
 * -------------------------------------------------------------------------- */
{ R_HSCALE,    ST_BLUE, ST_WHITE,  "tcp_conn_req_max",         nil, "ndd_connreq_cb"    },
{ R_SEPARATOR                                						},
{ R_HSCALE,    ST_BLUE, ST_WHITE,  "tcp_close_wait_interval (secs)",  nil, "ndd_closewait_cb"},
{ R_SEPARATOR                                						},
{ R_HSCALE,    ST_BLUE, ST_WHITE,  "tcp_xmit_hiwat",   	       nil, "ndd_xmithiwat_cb"  },
{ R_SEPARATOR                                						},
{ R_HSCALE,    ST_BLUE, ST_WHITE,  "tcp_recv_hiwat",   	       nil, "ndd_recvhiwat_cb"  },
{ R_SEPARATOR                                						},
{ R_HSCALE,    ST_BLUE, ST_WHITE,  "tcp_rexmit_interval_min (msec)",  nil, "ndd_rexmit_min_cb" },
{ R_END                                      						}
};
#define PARAM1_CONNREQ_I	 0
#define PARAM1_CLOSE_WAIT_I	 2
#define PARAM1_XMIT_HIWAT_I	 4
#define PARAM1_RECV_HIWAT_I	 6
#define PARAM1_REXMIT_MIN_I    	 8

resource_t params2[10] = {
/* type         fg        bg         label                    data  cb        *
 * -------------------------------------------------------------------------- */
{ R_HSCALE,    ST_BLUE, ST_WHITE,  "tcp_smallest_anon_port        ",   nil, "ndd_smallanon_cb"  },
{ R_SEPARATOR                                						},
{ R_HSCALE,    ST_BLUE, ST_WHITE,  "tcp_keepalive_interval (secs)",   nil, "ndd_keepalive_cb"  },
{ R_SEPARATOR                                						},
{ R_HSCALE,    ST_BLUE, ST_WHITE,  "tcp_ip_abort_interval (secs)",    nil, "ndd_abort_cb"      },
{ R_SEPARATOR                                						},
{ R_HSCALE,    ST_BLUE, ST_WHITE,  "tcp_ip_abort_cinterval (secs)",   nil, "ndd_cabort_cb"     },
{ R_SEPARATOR                                						},
{ R_HSCALE,    ST_BLUE, ST_WHITE,  "tcp_conn_grace_period",  nil, "ndd_conngrace_cb"  },
{ R_END                                      						}
};
#define PARAM2_ANON_PORT_I	 0
#define PARAM2_KEEPALIVE_I	 2
#define PARAM2_ABORT_I	 	 4
#define PARAM2_CABORT_I	 	 6
#define PARAM2_CONNGRACE_I    	 8

#define CPU_METER_ICON     "/opt/RICHPse/examples/data/cpu_meter_icon"

/* internal value of the kvm struct used to drive ndd */
struct parm_arr_s {
	ulong  min;
	ulong  max;
	ulong  val;
	ulong  name_addr;
};

/*--------------------------------------------------------------------------- */
/*------------------------------  Globals  ---------------------------------- */
/*--------------------------------------------------------------------------- */
ulong   cm_frame;
ulong   cm_cols;
ulong   cm_params;
ulong   cm_stats_1;
ulong   cm_stats_2;
ulong   cm_menubar;
ulong   cm_params_col1;
ulong   cm_params_col2;

int     interval = INITIAL_INTERVAL;
int	is_root;	/* are we running as root ? (ie, is kvm$ available?) */

ndd_tcp_t  ndd$tcp;	/* global scope declaration for tcp's ndd params */
ndd_tcp_t  ndd_tmp;	/* temporary version of the ndd variables */


#define BUILD_I_BAR( ndx, max ) \
	istats[ndx].r_data = gui_bar_data(BAR_WIDTH, BAR_HEIGHT, 0, max, 0 )
#define BUILD_O_BAR( ndx, max ) \
	ostats[ndx].r_data = gui_bar_data(BAR_WIDTH, BAR_HEIGHT, 0, max, 0 )

	
/*
 *  finally - the main routine 
 */
main(int argc, string argv[])
{
	int 	  i;
	ulong 	  member;
	ulong 	  icon;

	/*
	 *  warn if we're not root  - se just gave a 'warning ' message for us
	 */
	if ( geteuid() != 0 ) {
		printf("Since you didn't run this as root, you can't update tcp parameters.\n");
		printf("You can, however, monitor the tcp stack operation\n");
		is_root = 0;
		menubar[MENUBAR_PARAMS_I].r_type = R_LABEL;
	} else {
		is_root = 1;
	}

	/* build the interval counter */
	argv[0] 	    = "TCP Monitor";
	gui[SCALE_I].r_data = 
		gui_scale_data( INTERVAL_MIN, INTERVAL_MAX, INITIAL_INTERVAL );

	/* build the default stats area */
	BUILD_I_BAR( ISTATS_ACCEPTS, 	8 );
	BUILD_I_BAR( ISTATS_CONNECTS, 	8 );
	BUILD_I_BAR( ISTATS_RESETS, 	8 );
	BUILD_I_BAR( ISTATS_FAILS, 	8 );
	BUILD_I_BAR( ISTATS_SESSIONS, 	64 );
	BUILD_O_BAR( OSTATS_IN_MSG_RATE,   64 );
	BUILD_O_BAR( OSTATS_OUT_MSG_RATE,  64 );
	BUILD_O_BAR( OSTATS_RETRANS, 	   100 );
	BUILD_O_BAR( OSTATS_IN_BYTE_RATE,  (1024*1024) );
	BUILD_O_BAR( OSTATS_OUT_BYTE_RATE, (1024*1024) );

	/* create the toplevel */
	cm_frame = gui_create_frame( argc, argv, gui );

	/* create the icon */
	icon     = gui_load_bitmap( CPU_METER_ICON, ST_BLACK, ST_WHITE );
	gui_set_icon(icon, 0);

	/* create the menu bar */
	cm_menubar   = gui_get_object( cm_frame, MENUBAR_I );
	gui_add_group( cm_menubar, menubar );

	/* create the rows and columns */
	cm_cols   = gui_get_object( cm_frame, ROW_I );
	gui_add_group( cm_cols, cols );

	cm_stats_1 = gui_get_object( cm_cols, COL_STATS_I_1 );
	cm_stats_2 = gui_get_object( cm_cols, COL_STATS_I_2 );
	gui_add_group( cm_stats_1, istats );
	gui_add_group( cm_stats_2, ostats );

	/*
	 *  start the main loop
	 */
	gui_main_loop("loop", nil, INITIAL_INTERVAL);
}

/* exit button pressed */
m_exit_cb(ulong handle, string client_data, string call_data)
{
	exit(0);
}

/* about button pressed */
m_about_cb(ulong handle, string client_data, string call_data)
{
	string infomsg = "This program monitors the state of the TCP/IP stack.\n\nVersion 1.4a 96/06/28\n\nComments to : Mike.Bennett@corp.sun.com\n";
	gui_info_dialog(infomsg);
}

/* parameter button pressed */
m_params_cb(ulong handle, string client_data, string call_data)
{
	popup_params();
}

/* scale slider callback */
interval_cb(ulong handle, string client_data, string call_data)
{
	interval = atoi(call_data);
	gui_set_loop_interval("loop", call_data, interval );
}

/* lifted directly from multi_meter.se */
int
flatten( int current, int max ) 
{
   	while ( current > max ) {
		max *= 2;
	}
	return max;
}

/* flatten, given two arguments that need to keep the same scale */
int
flatten2( int current, int current2, int max ) 
{

	if ( current2 > current ) {
		return flatten( current2, max );
	} else {
		return flatten( current, max );
	}
}

/* main processing loop - called every 'interval' */
loop(ulong handle, string client_data, string call_data)
{
	int        called = 0;

	mib2_tcp_t mib$tcp;
	mib2_tcp_t kvm$tcp_mib;
	mib2_tcp_t t_tmp;
	mib2_tcp_t t_last;

	long	   now;
	long       then;
	long       secs;
	ulong      diff;

	ulong      tcp_in;
	ulong      tcp_out;
	ulong      tcp_in_b;
	ulong      tcp_out_b;

	ulong      a_rate;
	ulong      c_rate;
	ulong      retran_rate;
	ulong      ti_rate;
	ulong      to_rate;
	ulong      ti_b_rate;
	ulong      to_b_rate;

	int	   arate_max     = 8;
	int	   sessions_max  = 8;
	int	   fails_max     = 8;
	int	   resets_max    = 8;
	int	   segrate_max   = 100;
	int	   byterate_max  = 100;

	/*
	 *  get the mib information
	 */
	if ( is_root != 0 ) {
		t_tmp = kvm$tcp_mib;
	} else {
		t_tmp = mib$tcp;
	}

	/* handle first-time */
	if ( called == 0 )  {
		t_last = t_tmp;
		then   = time();
		called = 1;
	}

	/*
	 *  determine the elapsed time
	 */
	now    = time();
	secs   = ( now - then );
	if ( secs == 0 ) {
		secs = 1;
	}

	a_rate = (t_tmp.tcpPassiveOpens - t_last.tcpPassiveOpens*1.00)/secs;
	c_rate = (t_tmp.tcpActiveOpens  - t_last.tcpActiveOpens*1.00)/secs;
	
	arate_max  = flatten2( a_rate, c_rate, 8 );
	istats[ISTATS_ACCEPTS].r_data =
		gui_bar_data(BAR_WIDTH, BAR_HEIGHT, 0, arate_max, a_rate );
	istats[ISTATS_CONNECTS].r_data =
		gui_bar_data(BAR_WIDTH, BAR_HEIGHT, 0, arate_max, c_rate );

	sessions_max = flatten( t_tmp.tcpCurrEstab, 8 );
	istats[ISTATS_SESSIONS].r_data =
		gui_bar_data(BAR_WIDTH, BAR_HEIGHT, 0, sessions_max, t_tmp.tcpCurrEstab );

	diff       = t_tmp.tcpOutRsts - t_last.tcpOutRsts;
	resets_max = flatten( diff, 8 );
	istats[ISTATS_RESETS].r_data =
		gui_bar_data(BAR_WIDTH, BAR_HEIGHT, 0, resets_max, diff );

	diff       = t_tmp.tcpAttemptFails - t_last.tcpAttemptFails;
	fails_max  = flatten( diff, 8 );
	istats[ISTATS_FAILS].r_data =
		gui_bar_data(BAR_WIDTH, BAR_HEIGHT, 0, fails_max, diff );

	/* don't use AckSegs as it tracks what we sent, not received *
 	 *  info courtesy of adrian, 2/29/96 */
	tcp_in     = ( t_tmp.tcpInDataInorderSegs  + t_tmp.tcpInDataUnorderSegs )
	           - ( t_last.tcpInDataInorderSegs + t_last.tcpInDataUnorderSegs);
	/* MB : tcpOutSegs isn't updated with kvm$tcp_mib; use tcpOutDataSegs */
	/*tcp_out  = t_tmp.tcpOutSegs - t_last.tcpOutSegs;*/
	tcp_out  = t_tmp.tcpOutDataSegs - t_last.tcpOutDataSegs;
	ti_rate  = ((tcp_in)  * 1.00)/secs;
	to_rate  = ((tcp_out) * 1.00)/secs;

	segrate_max = flatten2( ti_rate, to_rate, 100 );
	ostats[OSTATS_IN_MSG_RATE].r_data =
		gui_bar_data(BAR_WIDTH, BAR_HEIGHT, 0, segrate_max, ti_rate );
	ostats[OSTATS_OUT_MSG_RATE].r_data =
		gui_bar_data(BAR_WIDTH, BAR_HEIGHT, 0, segrate_max, to_rate );

	/* don't use AckBytes as it tracks what we sent, not received *
 	 *  info courtesy of adrian, 2/29/96 */
	tcp_in_b   = ( t_tmp.tcpInDataInorderBytes  + t_tmp.tcpInDataUnorderBytes )
	           - ( t_last.tcpInDataInorderBytes + t_last.tcpInDataUnorderBytes );
	tcp_out_b  = t_tmp.tcpOutDataBytes - t_last.tcpOutDataBytes;
	ti_b_rate  = ((tcp_in_b)  * 1.00)/secs;
	to_b_rate  = ((tcp_out_b) * 1.00)/secs;

	byterate_max = flatten2( ti_b_rate, to_b_rate, 1024 );
	ostats[OSTATS_IN_BYTE_RATE].r_data =
		gui_bar_data(BAR_WIDTH, BAR_HEIGHT, 0, byterate_max, ti_b_rate );
	ostats[OSTATS_OUT_BYTE_RATE].r_data =
		gui_bar_data(BAR_WIDTH, BAR_HEIGHT, 0, byterate_max, to_b_rate );

	/* don't use segments as it gets recounted incorrectly --
 	 *  info courtesy of adrian, 2/29/96 */
	diff = t_tmp.tcpRetransBytes - t_last.tcpRetransBytes;
	if (( diff > 0 ) && ( tcp_out_b > 0 ) ) {
		retran_rate = (diff * 100.0)/tcp_out_b;
	} else {
		retran_rate = 0.0;
	}  
	ostats[OSTATS_RETRANS].r_data =
		gui_bar_data(BAR_WIDTH, BAR_HEIGHT, 0, 100, retran_rate );

	/*
	 *  update the stats window
	 */
	gui_update_group( cm_stats_1, istats );
	gui_update_group( cm_stats_2, ostats );

	/*
	 *  save values for next delta  
	 */
	t_last = t_tmp;
	then   = now;
}

/*
 *  routines for handling the 'ndd' parameter gui 
 */

/* callback for close button from subframe */
m_popdown_cb(ulong handle, string client_data, string call_data)
{
	ulong	popup_handle = gui_get_frame( handle );

	gui_popdown_subframe( popup_handle );
}


/*
 * get the 'real' min/max values from kvm so as to set the ndd 
 * parameters correctly 
 */
parm_arr_s
get_param_id( string param_name )
{
	parm_arr_s kvm$tcp_param_arr;
	parm_arr_s pt;
	int	   i;
	string     kvm$parmname;
	ulong      addr;
	int	   parm_arr_sz = (sizeof(pt));
	int        called;
	ulong      start_addr;
	long	   slong;

	/*
	 *  we're called multiple times, so save the starting address
	 */
	if ( called == 0 ) {
		start_addr = kvm_address( kvm$tcp_param_arr );
		called = 1;
	}

	/*
	 *  set up at the beginning of the parameters
	 */
	addr = start_addr;
       	kvm_cvt( kvm$tcp_param_arr, addr );
	pt   = kvm$tcp_param_arr;

	/*
	 *  loop until we find something that's out of spec (ie min<=val<=max)
	 *  or the parameter name is what we're looking for
	 */
	while (( pt.min <= pt.min ) && ( pt.val <= pt.max ) && 
	       ( pt.name_addr != 0 )) {
		kvm_cvt( kvm$parmname, pt.name_addr );

		if (kvm$parmname == param_name ) {
			slong = pt.max;
			if ( slong < 0 ) {
				pt.max = pt.max/4;
			}
			return pt;
		}
	        addr += parm_arr_sz;
       		kvm_cvt( kvm$tcp_param_arr, addr );
		pt   = kvm$tcp_param_arr;
	}

	/*
	 *  something bad happened - we can't find a parameter in the tcp parameters
	 */
	gui_fatal_dialog("Internal Error -- can't find ndd parameter %s\nplease report this to mike.bennett@corp.sun.com", 
			param_name );
}

/*
 *  build the parameter array values
 */
build_parameter_area( )
{
	parm_arr_s pt;

	ndd_tmp = ndd$tcp;

	pt = get_param_id ("tcp_conn_req_max" );
	params1[PARAM1_CONNREQ_I].r_data =
		gui_scale_data(pt.min, pt.max, ndd_tmp.tcp_conn_req_max);

	pt = get_param_id ("tcp_close_wait_interval" );
	params1[PARAM1_CLOSE_WAIT_I].r_data =
		gui_scale_data(pt.min/SECS, pt.max/SECS, ndd_tmp.tcp_close_wait_interval/SECS);

	pt = get_param_id ("tcp_xmit_hiwat" );
	params1[PARAM1_XMIT_HIWAT_I].r_data =
		gui_scale_data(pt.min, pt.max, ndd_tmp.tcp_xmit_hiwat );

	pt = get_param_id ("tcp_recv_hiwat" );
	params1[PARAM1_RECV_HIWAT_I].r_data =
		gui_scale_data(pt.min, pt.max, ndd_tmp.tcp_recv_hiwat );

	pt = get_param_id ("tcp_rexmit_interval_min" );
	if ( ndd_tmp.tcp_rexmit_interval_max < pt.max ) {
		pt.max = ndd_tmp.tcp_rexmit_interval_max;
		/* scale is too coarse 1 -> 60000 default, so reduce */
		if (pt.max > 4000) {
			pt.max = 4000;
		}
	}
	params1[PARAM1_REXMIT_MIN_I].r_data =
		gui_scale_data(pt.min, pt.max, ndd_tmp.tcp_rexmit_interval_min );

	params2[PARAM2_ANON_PORT_I].r_data =
		gui_scale_data( ndd_tmp.tcp_smallest_nonpriv_port, 
				ndd_tmp.tcp_largest_anon_port, 
				ndd_tmp.tcp_smallest_anon_port );

	pt = get_param_id ("tcp_keepalive_interval" );
	params2[PARAM2_KEEPALIVE_I].r_data =
		gui_scale_data(pt.min/SECS, pt.max/SECS, ndd_tmp.tcp_keepalive_interval/SECS );

	pt = get_param_id ("tcp_ip_abort_interval" );
	params2[PARAM2_ABORT_I].r_data =
		gui_scale_data(pt.min/SECS, pt.max/SECS, ndd_tmp.tcp_ip_abort_interval/SECS );

	pt = get_param_id ("tcp_ip_abort_cinterval" );
	params2[PARAM2_CABORT_I].r_data =
		gui_scale_data(pt.min/SECS, pt.max/SECS, ndd_tmp.tcp_ip_abort_cinterval/SECS );

	pt = get_param_id ("tcp_conn_grace_period" );
	params2[PARAM2_CONNGRACE_I].r_data =
		gui_scale_data(pt.min, pt.max, ndd_tmp.tcp_conn_grace_period );

}

/* routine to create (if necessary) and pop up the parameter subframe */
popup_params()
{
	ulong	param_handle;
	ulong   params_id;
	ulong   params_col_id;
	ulong   params_menu_id;
	int	ndx;

	/*
	 *  if we're not root, warn the user and don't allow changes 
	 */
	if ( is_root == 0 ) {
		gui_warning_dialog( "You must run this as root to change tcp parameters\n");
		return;
	}

	/*
	 *  build the parameter popup
	 */
	if ( param_handle == 0 ) {
		param_handle = gui_create_subframe("TCP Parameters", param_popup );

		params_menu_id = gui_get_object( param_handle, PPOPUP_MENUBAR );
		params_col_id  = gui_get_object( param_handle, PPOPUP_COLS );

		gui_add_group( params_menu_id, param_menubar );
		gui_add_group( params_col_id,  param_popup_cols );

		cm_params_col1 = gui_get_object( params_col_id, PPOPUPC_ROW1 );
		cm_params_col2 = gui_get_object( params_col_id, PPOPUPC_ROW2 );

		/* build the parameter area */
		build_parameter_area();

		/* add the popup */
		gui_add_group( cm_params_col1, params1 );
		gui_add_group( cm_params_col2, params2 );
	}

	/*
	 *  display the popup 
	 */
	gui_popup_subframe( param_handle );
}

/* handle the 'apply' button being pressed on the parameter screen --
   tell TCP what the new values are --
   MB - should we test for variance before doing the set ? */
m_param_apply(ulong handle, string client_data, string call_data)
{
	ndd$tcp.tcp_conn_req_max 	= ndd_tmp.tcp_conn_req_max;
	ndd$tcp.tcp_smallest_anon_port  = ndd_tmp.tcp_smallest_anon_port;
	ndd$tcp.tcp_close_wait_interval = ndd_tmp.tcp_close_wait_interval;
	ndd$tcp.tcp_xmit_hiwat 		= ndd_tmp.tcp_xmit_hiwat;
	ndd$tcp.tcp_recv_hiwat 		= ndd_tmp.tcp_recv_hiwat;
	ndd$tcp.tcp_rexmit_interval_min = ndd_tmp.tcp_rexmit_interval_min;
	ndd$tcp.tcp_keepalive_interval  = ndd_tmp.tcp_keepalive_interval;
	ndd$tcp.tcp_ip_abort_interval 	= ndd_tmp.tcp_ip_abort_interval;
	ndd$tcp.tcp_ip_abort_cinterval 	= ndd_tmp.tcp_ip_abort_cinterval;
	ndd$tcp.tcp_conn_grace_period 	= ndd_tmp.tcp_conn_grace_period;
}

/* handle the 'reset' button being pressed on the parameter screen --
   change the parameters displayed to match what TCP thinks they are */
m_param_reset(ulong handle, string client_data, string call_data)
{
	/* build the parameter area */
	build_parameter_area();

	/* update the popup */
	gui_update_group( cm_params_col1, params1 );
	gui_update_group( cm_params_col2, params2 );
}

/*** this is really ugly, but I don't see any way to to a late binding 
 *** or parametization of a name such as ndd$param.  Otherwise this
 *** could be much cleaner, but right now one callback per parameter
 */

/* ndd parameter slider scale callback */
ndd_connreq_cb(ulong handle, string client_data, string call_data)
{
	int   newval;
	int   told; 

	newval = atoi(call_data);
	ndd_tmp.tcp_conn_req_max = newval;
	/* be helpful */
	if ( told == 0 ) {
		gui_info_dialog("you must restart the application program (httpd server, etc.) for the new connreq value to take effect\n");
		told = 1;
	}
}

ndd_smallanon_cb(ulong handle, string client_data, string call_data)
{
	int   newval;

	newval = atoi(call_data);
	ndd_tmp.tcp_smallest_anon_port = newval;
}

ndd_keepalive_cb(ulong handle, string client_data, string call_data)
{
	int   newval;

	newval = atoi(call_data);
	ndd_tmp.tcp_keepalive_interval = (newval * SECS);
}

ndd_closewait_cb(ulong handle, string client_data, string call_data)
{
	int   newval;

	newval = atoi(call_data);
	ndd_tmp.tcp_close_wait_interval = (newval * SECS);
}

ndd_xmithiwat_cb(ulong handle, string client_data, string call_data)
{
	int   newval;

	newval = atoi(call_data);
	ndd_tmp.tcp_xmit_hiwat = newval;
}

ndd_recvhiwat_cb(ulong handle, string client_data, string call_data)
{
	int   newval;

	newval = atoi(call_data);
	ndd_tmp.tcp_recv_hiwat = newval;
}

ndd_rexmit_min_cb(ulong handle, string client_data, string call_data)
{
	int   newval;

	/* tcp_rexmit_interval_initial is kind of broken - use .._min, instead
	 *  info courtesy of adrian, 2/27/96 */
	newval = atoi(call_data);
	ndd_tmp.tcp_rexmit_interval_min = newval;
}

ndd_abort_cb(ulong handle, string client_data, string call_data)
{
	int   newval;

	newval = atoi(call_data);
	ndd_tmp.tcp_ip_abort_interval = newval;
}

ndd_cabort_cb(ulong handle, string client_data, string call_data)
{
	int   newval;

	newval = atoi(call_data);
	ndd_tmp.tcp_ip_abort_cinterval = newval;
}

ndd_conngrace_cb(ulong handle, string client_data, string call_data)
{
	int   newval;

	newval = atoi(call_data);
	ndd_tmp.tcp_conn_grace_period = newval;
}
