#!/bin/sh # \ exec wish "$0" "$@" # # xeznet - Tcl/Tk interface for 'eznet' # xeznet,v 1.8 1999/02/19 00:55:48 migh Exp # option add *borderWidth 1 option add *Label*anchor w option add *Label*foreground DarkBlue bind Entry {%W configure -background gray95; show_help %W} bind Entry {%W configure -background gray85} set upd_rate 60 ;# status update rate (see menu settings) set nquick 0 ;# number of quick updates for service up/down set rc_file ~/.xeznet_rc # # Return a list of selected service names # proc get_sel { } { msg "" set svcs {} foreach ix [.lbox.list curselection] { set line [.lbox.list get $ix] set svc [string trim \ [string range $line 1 \ [string wordend $line 1]]] if {$svc == ""} { continue } lappend svcs $svc } if {[llength $svcs] == 0} { msg "Select a service, then click the button." } return $svcs } # # Make a new action window for each selected service name # proc do_sel {action} { if {$action == "add"} { msg "" svc_window add new } { foreach svc [get_sel] { svc_window $action $svc } } return } # # Bring the selected service(s) up or down # TODO: we sometimes exit when we bring down a service # proc do_updn {dir} { set svcs [get_sel] foreach svc $svcs { msg "Bringing $dir $svc." catch {exec eznet $dir $svc >/dev/null &} } if {[llength $svcs] > 0} { global nquick set nquick 12 svc_status } return } # # Refresh the current service status # proc svc_status {{repeat 0}} { set fid 0 if {[catch {open "|eznet status" r} fid] == 0} { set sel [lindex [.lbox.list curselection] 0] .lbox.list delete 0 end while {[gets $fid line] > 0} { set p [string first ":" $line] set svc [string range $line 0 [expr {$p - 1}]] set stat [string range $line [expr {$p + 1}] end] .lbox.list insert end [format " %-10s %s" $svc $stat] } catch {close $fid} if {$sel != ""} { .lbox.list selection set $sel .lbox.list activate $sel .lbox.list see $sel } } { msg "Unable to get status from 'eznet'." } # do we repeat? how soon? set dt 0 global nquick if {$nquick > 0} { set dt 5000 incr nquick -1 } elseif {$repeat > 0} { global upd_rate set dt [expr {$upd_rate * 1000}] } if {$dt > 0} { after $dt svc_status 1 } return } # # Handle the "OK" button on the service information window # proc do_svc_ok {w action} { set svc [$w.service.e get] if {$action == "delete"} { msg "Deleted $svc" catch {exec eznet delete $svc >/dev/null} } elseif {$action != "show"} { set cmd "eznet $action $svc" set args "" global all_entries foreach x $all_entries { set val [$w.$x.e get] # TODO: How do we clear an entry??? if {$val != ""} { if {$x == "password" && [string index $val 0] == "*"} { continue } { append args " \"$x=$val\"" } } } if {$args != ""} { msg "$cmd ..." catch {eval exec $cmd $args >/dev/null} } } destroy $w update idletasks if {$action != "show"} { svc_status } return } array set entry_help { phone "Phone number for modem to dial" user "User login name" password "Login password" tty "Serial port for the modem" baud "Baud rate for the serial port" service "Name of the ISP" chat "`no' to ignore `login:' prompts, etc." autostart "`no' to not start pppd if no `login:'" pppversion "Version of pppd in use" debug "`yes' to turn on pppd debugging" expectN "Extra expect string" replyN "Reply to issue after expect string" initN "Extra modem initialization string" diald "Name of diald executable" pppd "Name of pppd executable" local "Local IP address for diald slip link" remote "Remote IP address for diald slip link" idle "Number of seconds for idle timeout" persist "`yes' for a permanent connection" defaultroute "`no' to not create a default route" pppoptN "Extra parameters to pppd" routeN "Add this route when pppd comes up" ip "Network accessed by this service" netmask "Netmask for the ip network" dialtimeout "Time allowed for modem connect" chattimeout "Time allowed for chat dialog" } proc show_help {w} { global entry_help set parts [split $w .] set pw [lindex $parts 1] set ent [lindex $parts 2] if {[winfo exists .$pw.help]} { regsub {[0-9]} $ent N ent if {![info exists entry_help($ent)]} { set entry_help($ent) "(unknown)" } .$pw.help configure -text "$ent - $entry_help($ent)" } return } array set win_txt { add "Create Service" show "Show Service for $svc" edit "Edit Service for $svc" delete "Delete Service for $svc ?" } set win_id 0 # # Split all the available entries/settings into subsets for display # set basic_entries { service phone user password tty baud } set modem_entries { chat diald dialtimeout chattimeout idle init0 init1 init2 init3 init4 } set chat_entries { expect0 reply0 expect1 reply1 expect2 reply2 expect3 reply3 expect4 reply4 } set ppp_entries { pppd pppversion pppopt0 pppopt1 pppopt2 pppopt3 pppopt4 autostart debug } set ip_entries { local remote ip netmask persist } set route_entries { defaultroute route0 route1 route2 route3 route4 } set all_entries [concat $basic_entries $modem_entries $chat_entries\ $ppp_entries $ip_entries $route_entries] # # List of frame names and titles # (names should match above entry lists) # set frame_list { {basic "Basic Settings"} {modem "Modem Settings"} {chat "Chat Dialog Settings"} {ppp "PPP Settings"} {ip "IP Settings"} {route "Route Settings"} } # # Unpack the current selection frame; pack a new one # proc newframe {w opt} { # unpack the old frame (find it by name "f_xyz") foreach slave [pack slaves $w] { if {[string first "f_" $slave] > 0} { pack forget $slave } } # new text on menu button global frame_defs $w.fs configure -text $frame_defs($opt) # pack the new frame pack $w.f_$opt -side top -fill both -expand yes # select first entry (to update the help-text, if there) global ${opt}_entries set x [lindex [set ${opt}_entries] 0] $w.$x.e select range 0 end focus $w.$x.e } # # Make a service-information window # proc svc_window {action svc} { global win_id win_txt # make a new window set w .w[incr win_id] toplevel $w wm title $w [subst $win_txt($action)] # TODO: wm iconbitmap $w ... ? # make a menu button to choose among the frames menubutton $w.fs -indicatoron 1 -menu $w.fs.menu -anchor w \ -text "Basic Settings" -relief raised menu $w.fs.menu -tearoff 0 # make some frames to hold subsets of entries global frame_list frame_defs foreach x $frame_list { foreach {base label} $x { } # save name-label association for later # add menu button to the menu set frame_defs($base) $label $w.fs.menu add radiobutton -label $label -indicatoron 0 \ -command "newframe $w $base" # make the frame and fill it with its labelled entries global ${base}_entries frame $w.f_$base foreach x [set ${base}_entries] { labent $w $x pack $w.$x -in $w.f_$base -side top -fill x } } # make the bottom button row buttons $w.b left \ [list OK "do_svc_ok $w $action"] \ [list Cancel "destroy $w"] # put the frame-selector on top, button-row on bottom, frame inbetween pack $w.fs -side top -fill x pack $w.b -side bottom -fill x pack $w.f_basic -side top -fill both -expand yes # make the 'OK' button look special on the delete-window if {$action == "delete"} { $w.b.b1 configure -activeforeground Red -fg Red -text Delete } # for editable windows, add a prompt-line for help messages if {$action == "add" || $action == "edit"} { label $w.help -text "" -fg Black -relief sunken -padx 1m \ -font Helvetica pack $w.help -side bottom -fill x } if {$action == "add"} { $w.service.e insert 0 service-name $w.service.e select range 0 end focus $w.service.e } { update if {[catch {open "|eznet list $svc" r} fid] == 0} { while {[gets $fid buf] > 0} { set p [string first "=" $buf] incr p -1 set var [string trim [string range $buf 0 $p]] incr p 2 set val [string trim [string range $buf $p end]] if {[winfo exists $w.$var.e]} { $w.$var.e insert 0 $val } # TODO: else report missing values? } catch {close $fid} } } # make the 'show' and 'delete' windows read-only if {$action == "show" || $action == "delete"} { global all_entries foreach x $all_entries { $w.$x.e configure -state disabled } if {$action == "show"} { bind $w [list $w.b.b1 invoke] } } return } # # Put messages or errors in the message line # proc err {msg} { .msg configure -text "Error! $msg" return } proc msg {msg} { .msg configure -text $msg return } # proc ask_yn {msg} { # return [tk_messageBox -title Question -message $msg \ # -icon question -type yesno] # } ### ### Some GUI-creation helper routines... ### # # Make a menubar with menus # proc menu_bar {w args} { if {[winfo exists $w] == 0} { frame $w -relief raised -borderwidth 2 } set mnum 0 ;# menu counter set inum 0 ;# item counter foreach arg $args { foreach {type name var val v2} $arg { } if {$inum == 0 && [string match "menu*" $type] == 0} { err "menu_bar: need menu to hold $type/$name menu-item" return } switch $type { menu { set mbase $w.m[incr mnum] set new $mbase.menu menubutton $mbase -text $name -menu $new menu $new -disabledforeground DarkBlue set inum 1 } sep { $new add sep } label { $new add command -label $name -state disabled incr inum } cmd { $new add command -label $name -command $var incr inum } radio { global $var $new add radiobutton -label $name \ -variable $var -value $val incr inum } check { global $var $new add checkbutton -label $name \ -variable $var -offvalue $val -onvalue $v2 incr inum } end { # pack this menu on the given side pack $mbase -side $name -padx 1m set inum 0 } default { err "menu_bar: unknown type '$type' for '$name'" return } } } return } # # Make a button row from names and commands # proc buttons {w dir args} { if {[winfo exists $w] == 0} { frame $w } set i 0 set btns "" foreach arg $args { foreach {name cmd} $arg { } set btn $w.b[incr i] append btns " $btn" button $btn -text $name -command $cmd -width 4 } eval pack $btns -side $dir -fill x -expand yes return } # # Make a labeled entry # proc labent {w x} { if {[winfo exists $w.$x] == 0} { frame $w.$x } label $w.$x.l -text $x -anchor e -width 10 -padx 3m entry $w.$x.e -width 25 pack $w.$x.l -side left -fill x pack $w.$x.e -side left -fill x -expand yes return } # # Make a listbox/scrollbar pair, connect them # proc list_box {w title action} { if {[winfo exists $w] == 0} { frame $w } label $w.hdr -text $title -font fixed -anchor w scrollbar $w.sb -command "$w.list yview" listbox $w.list -yscroll "$w.sb set" \ -font fixed -setgrid 1 -height 5 pack $w.hdr -side top -fill x pack $w.sb -side right -fill y pack $w.list -side left -fill both -expand yes if {$action != ""} { bind $w.list $action bind $w.list $action } bind $w.list {tkListboxUpDown %W -1} bind $w.list {tkListboxUpDown %W 1} bind $w.list {tkListboxUpDown %W -1} bind $w.list {tkListboxUpDown %W 1} return } # # Make a text-box/scrollbar pair, connect them # proc text_box {w {title ""}} { if {[winfo exists $w] == 0} { frame $w } if {$title != ""} { label $w.hdr -text $title -font fixed pack $w.hdr -side top -fill x } scrollbar $w.sb -command "$w.text yview" text $w.text -yscroll "$w.sb set" -setgrid 1 # -font fixed pack $w.sb -side right -fill y pack $w.text -side left -fill both -expand yes $w.text insert end "Please wait..." return } # # Given some text, fill a text-box # proc fill_textbox {tb ro txt} { set t $tb.text $t configure -state normal $t delete 1.0 end $t mark set insert 0.0 $t insert end $txt if {$ro == "ro"} { $t configure -state disabled $t mark set curs 0.0 } { $t mark set insert 0.0 } return } # # Make a simple text window # proc simple_text {w title} { if {[winfo exists $w] == 0} { toplevel $w # TODO: wm iconbitmap . ... if {$title != ""} { wm title $w $title } text_box $w.tbox buttons $w.b left [list Done "destroy $w"] pack $w.tbox -side top -fill both -expand yes pack $w.b -side bottom -fill x bind $w "destroy $w" focus $w.tbox.text } { wm deiconify $w } return } ### ### End of GUI helpers ### # # Save our current settings # proc save_settings { } { global rc_file if {[catch {open $rc_file w} fid] == 0} { puts $fid "wm geometry . [wm geometry .]" set sel [lindex [.lbox.list curselection] 0] if {$sel != ""} { puts $fid "set lbsel $sel" } { puts $fid "set lbsel -1" } catch {close $fid} } } # # Are we 'root'? # proc as_root { } { global env set rt 0 if {[info exists env(TESTING_XEZNET)]} { set rt 1 } elseif {[catch {open "|id" r} fid] == 0} { set id [read $fid] catch {close $fid} if {[string first "(root)" $id] > 0} { set rt 1 } } return $rt } # # Make a new window, fill it with 'eznet log' # proc see_log { } { simple_text .w_log "Log file from eznet" update if {[catch {open "|eznet log" r} fid] == 0} { set txt [read $fid] catch {close $fid} } { set txt "Problems running 'eznet log'!" } fill_textbox .w_log.tbox ro $txt return } set eznet_icon { #define eznet_width 32 #define eznet_height 32 static unsigned char eznet_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3f, 0xfe, 0x1f, 0xf8, 0x3f, 0xfe, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x1c, 0x18, 0x00, 0x00, 0x0e, 0x18, 0x00, 0x00, 0x07, 0x18, 0x00, 0x80, 0x03, 0x18, 0x1f, 0xc0, 0x01, 0x18, 0x1f, 0xe0, 0x00, 0x18, 0x00, 0x70, 0x00, 0x18, 0x00, 0x38, 0x00, 0x18, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3f, 0xfe, 0x3f, 0xf8, 0x3f, 0xfe, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; } # # Work-around a Tcl/Tk "feature": # - bitmaps work OK on buttons, but are not accepted by the 'wm' command # proc wm_set_icon {w icon} { set iconFile /tmp/.eznet_icon.[pid] if {[catch {open $iconFile w} fid] == 0} { puts $fid $icon close $fid wm iconbitmap $w @$iconFile catch {file delete $iconFile} } return } # # Make the main control and status panel # proc make_ui { } { global upd_rate eznet_icon wm title . "EZNET Interface" wm iconname . "xeznet" wm command . "xeznet" wm_set_icon . $eznet_icon menu_bar .mbar \ {menu File} \ {cmd "See log..." see_log} \ {cmd Save save_settings} \ {sep} \ {cmd Quit exit} \ {end left} \ \ {menu Update} \ {cmd Now svc_status} \ {radio "10 sec" upd_rate 10} \ {radio "30 sec" upd_rate 30} \ {radio " 1 min" upd_rate 60} \ {radio " 5 min" upd_rate 300} \ {end left} \ \ {menu Help} \ {cmd About do_about} \ {cmd Usage do_use} \ {end right} list_box .lbox " Service Status" {do_sel show} bind . {focus .lbox.list} buttons .cmd left \ {Show {do_sel show}} \ {Edit {do_sel edit}} \ {Add {do_sel add}} \ {Delete {do_sel delete}} \ {Up {do_updn up}} \ {Down {do_updn down}} \ {Quit exit} # if not root, pull off Edit/Add/Delete buttons if {[as_root] == 0} { destroy .cmd.b2 destroy .cmd.b3 destroy .cmd.b4 } label .msg -text "" pack .mbar -side top -fill x pack .lbox -side top -fill both -expand yes pack .cmd .msg -side bottom -fill x return } proc do_about { } { simple_text .w_about "About xeznet" .w_about.tbox.text configure -height 9 fill_textbox .w_about.tbox ro { xeznet is a graphical front-end for the 'eznet' command. This is version 1.8 made on 1999/02/19 00:55:48. Mike Hall is the author of 'xeznet'. D. Richard Hipp" is the author of 'eznet', and provided essential suggestions to improve 'xeznet'. } return } proc do_use { } { simple_text .w_usage "How to use 'xeznet'" fill_textbox .w_usage.tbox ro { OPERATION: ---------- NOTE: The Edit, Add, and Delete buttons will appear only for the 'root' user! To create a new service, you must be 'root': Click on the Add button. A new window will appear; fill in the forms. Click on the OK button to have 'eznet' create the new service. To edit or delete an existing service, you must be 'root': Select an entry, then click on the Edit (or Delete) button. A new window will appear, showing you the service. Click on the OK button to post your edits to 'eznet' (or click on the Delete button to delete the service). To show the settings for a service: Method 1: select an entry, then click on the Show button. Method 2: select an entry, then press the Return key Method 3: double-click on an entry A new window will appear, showing you the service parameters. Click on the OK button to close the window. To bring a service up or down: Select the service, then click on the Up or Down button. The listbox will track the progress of the connection. SERVICE INFORMATION WINDOW: ------------------------------- There are several sections in the service popup window: - Selection menu, to select groups of settings - List of entries, to examine or change 'eznet' settings - Optional setting description, to explain the meaning of a setting - OK and Cancel buttons, to accept the settings or cancel One of several groups of entries can be selected from the menu: Basic Settings Modem Settings Chat Dialog Settings PPP Settings IP Settings Route Settings MENUS: ------ File / See log ... Show the 'eznet' log file. Save Save the current position, size, and service. Quit Exit the 'xeznet' program. Update / Now Update the status panel immediately. 10 sec Update the status panel every 10 seconds. 30 sec .. .. 30 seconds 1 min .. .. minute 5 min .. .. 5 minutes Note: when bringing a service up or down, the status is automatically updated every 5 seconds for one minute. Help / About Pops up a small version panel. Usage Brings up this text panel. } return } # # Make the interface, start status updates # make_ui # no entries to highlight; read in the RC file for position/settings set lbsel -1 if {[file exists $rc_file]} { source $rc_file } # draw the interface update # start the updates (fill the listbox) svc_status 1 # highlight an entry if one was saved last time if {$lbsel >= 0} { .lbox.list selection set $lbsel .lbox.list activate $lbsel }