#!/usr/bin/wish -f ######################################## # External modules if {[catch set env(CONTROL_PANEL_LIB_DIR)] != 0} { set env(CONTROL_PANEL_LIB_DIR) /usr/lib/rhs/control-panel } if {[catch {source $env(CONTROL_PANEL_LIB_DIR)/dialog.tcl}] != 0} { puts "Couldn't load dialog.tcl" puts "Start from control-panel or set environment variable" puts "CONTROL_PANEL_LIB_DIR to the control-panel library dir." puts "(normally this is /usr/lib/rhs/control-panel)" exit 0 } if {[catch {source $env(CONTROL_PANEL_LIB_DIR)/bindings.tcl}] != 0} { puts "Couldn't load bindings.tcl" puts "Start from control-panel or set environment variable" puts "CONTROL_PANEL_LIB_DIR to the control-panel library dir." puts "(normally this is /usr/lib/rhs/control-panel)" exit 0 } set cleardd "@$env(CONTROL_PANEL_LIB_DIR)/loopy/cleardd.xbm" set dropdown "@$env(CONTROL_PANEL_LIB_DIR)/loopy/dropdown.xbm" #if { ! [quota available] } { #} set quotas_available 0 ######################################## # Global variables (too many of these, if you ask me) set orphaneduser nobody set orphanedgroup nobody # Used to generate unique numbers set tmpnum 0 set tmpa(0) 0 # Internal representations of /etc/passwd, /etc/group, and /etc/shells set passwdlist {} set grouplist {} set shellslist {} # Current number of open add user/group windows set numadds 0 # Current editing mode -- user or group set curmode "user" # Variables used by assorted entry widgets set name(0) "" set password(0) "" set uid(0) "" set gid(0) "" set group(0) "" set gecos(0) "" set shell(0) "" set home(0) "" set visframe "user" set adduserup 0 set configup 0 set pathsup 0 set addingsgroups(0) 0 set addinggmems(0) 0 ######################################## # Utilities ########## # Sets the global configuration variables to default values proc setdefaults {} { global passwdfilename groupfilename shellfilename maildir defhomepath global skeldir excludedirs useruidbegin usergidbegin sysgidbegin global dispname disppassword dispuid dispgroup dispgecos dispshell global disphome showupg passstyle gidstyle confirmremove # Assorted paths set passwdfilename "/etc/passwd" set groupfilename "/etc/group" set shellfilename "/etc/shells" set maildir "/var/spool/mail" set defhomepath "/home" set skeldir "/etc/skel" # Directories that will NOT be searched when looking for orphaned files set excludedirs "/mnt;/proc" # First user UID, user GID, and system GID set useruidbegin 500 set usergidbegin 500 set sysgidbegin 100 # The disp* variables control which fields of /etc/passwd are shown in the # user list. set dispname 1 set disppassword 1 set dispuid 0 set dispgroup 1 set dispgecos 1 set dispshell 0 set disphome 1 # If showupg = 0, user private groups are not displayed in the group frame. set showupg 0 # passstyle = 0 : Passwords are shown as `exists', `blank', or `locked' # = 1 : Passwords are shown as they appear in /etc/passwd set passstyle 0 # gidstyle = 0 : Groups are shown by name # = 1 : Groups are shown by GID set gidstyle 1 # confirmremove = 1 : Users are prompted for confirmation before users or # groups are removed set confirmremove 1 } ########## # Saves the current configuration into ~/.rh.usercfg proc saveconfig {} { global env global passwdfilename groupfilename shellfilename maildir defhomepath global skeldir excludedirs useruidbegin usergidbegin sysgidbegin global dispname disppassword dispuid dispgroup dispgecos dispshell global disphome showupg passstyle gidstyle confirmremove if [catch {set homedir $env(HOME)}] { error "No home directory" } set fd [open $homedir/.rh.usercfg "w"] puts $fd "set passwdfilename \"$passwdfilename\"" puts $fd "set groupfilename \"$groupfilename\"" puts $fd "set shellfilename \"$shellfilename\"" puts $fd "set maildir \"$maildir\"" puts $fd "set defhomepath \"$defhomepath\"" puts $fd "set skeldir \"$skeldir\"" puts $fd "set excludedirs \"$excludedirs\"" puts $fd "set useruidbegin \"$useruidbegin\"" puts $fd "set usergidbegin \"$usergidbegin\"" puts $fd "set sysgidbegin \"$sysgidbegin\"" puts $fd "set dispname \"$dispname\"" puts $fd "set disppassword \"$disppassword\"" puts $fd "set dispuid \"$dispuid\"" puts $fd "set dispgroup \"$dispgroup\"" puts $fd "set dispgecos \"$dispgecos\"" puts $fd "set dispshell \"$dispshell\"" puts $fd "set disphome \"$disphome\"" puts $fd "set showupg \"$showupg\"" puts $fd "set passstyle \"$passstyle\"" puts $fd "set gidstyle \"$gidstyle\"" puts $fd "set confirmremove \"$confirmremove\"" close $fd } ########## # Reads the configuration from ~/.rh.usercfg or sets internal defaults proc getuserconfig {} { global env global passwdfilename groupfilename shellfilename maildir defhomepath global skeldir excludedirs useruidbegin usergidbegin sysgidbegin global dispname disppassword dispuid dispgroup dispgecos dispshell global disphome showupg passstyle gidstyle confirmremove set homedir "" catch {set homedir $env(HOME)} if { ! [string length $homedir ] } { setdefaults } elseif [file exists $homedir/.rh.usercfg] { source $homedir/.rh.usercfg } else { setdefaults } } ########## # Get a unique UID, with a minimum value of $m. proc getuniqueuid {m} { global passwdlist set i $m set f 0 while {$f == 0} { set f 1 foreach x $passwdlist { if {$i == [lindex $x 2]} { incr i set f 0 } } } return $i } ########## # Get a unique GID, with a minimum value of $m. proc getuniquegid {m} { global grouplist set i $m set f 0 while {$f == 0} { set f 1 foreach x $grouplist { if {$i == [lindex $x 2]} { incr i set f 0 } } } return $i } ########## # Return a list made up from the contents of listbox $box. proc listfrombox {box} { set l {} for {set x 0} {$x < [$box size]} {incr x} { lappend l [$box get $x] } return $l } ########## # Return a list made up of the selected items in listbox $box. proc selfrombox {box} { set l {} set s [$box curselection] foreach x $s { lappend l [$box get $x] } return $l } ########## # Read the password file into the password list. proc getpasswd {} { global passwdfilename if {[catch {set f [open $passwdfilename r]}] != 0} { rhs_error_dialog "Couldn't read from $passwdfilename." return } while {[gets $f str] >= 0} { lappend passwdlist [split "${str}:0" :] } close $f return $passwdlist } ########## # Read the group file into the group list. proc getgroup {} { global groupfilename if {[catch {set f [open $groupfilename r]}] != 0} { rhs_error_dialog "Couldn't read from $groupfilename." return } while {[gets $f str] >= 0} { set t [split "${str}:0" :] lappend grouplist [lreplace $t 3 3 [split [lindex $t 3] ,]] } close $f return $grouplist } ########## # Read the shells file into the shells list. proc getshells {} { global shellfilename if {[catch {set f [open $shellfilename r]}] != 0} { rhs_error_dialog "Couldn't read $shellfilename." return } while {[gets $f str] >= 0} { if {[string index $str 0] != "#"} { lappend shellslist $str } } return $shellslist } ########## # Replace an entry in the password list. # i - The value of the lock field (7) in the password list # name - The value of the name field (0) in the password list # ent - The entry to substitute into the list proc passwdlistreplace {i name ent} { global passwdlist set j 0 foreach x $passwdlist { if {[lindex $x 0] == $name} { if {[lindex $x 7] == $i} { set passwdlist [lreplace $passwdlist $j $j $ent] } } incr j } } ########## # Replace an entry in the group list. # i - The value of the lock field (4) in the group list # name - The value of the name field (0) in the group list # ent - The entry to substitute into the list proc grouplistreplace {i name ent} { global grouplist set j 0 foreach x $grouplist { if {[lindex $x 0] == $name} { if {[lindex $x 4] == $i} { set grouplist [lreplace $grouplist $j $j $ent] } } incr j } } ########## # If $display is false, this does nothing. Otherwise $value is formatted # at $width, added onto the string named by $stringname, and then $label # is added onto the string named by labelname proc addfield { stringname labelname display value width title } { upvar $stringname string upvar $labelname label if {$display} { set string $string[format "%${width}s " $value] set label "${label}$title" } } ########## # Rebuild the contents of the .user.users listbox from the password list. # The label .user.lframe.label is also regenerated. proc regenuserlist {} { global passwdlist grouplist global dispname disppassword dispuid dispgroup dispgecos disphome global dispshell passstyle gidstyle set idx [.usercfg.user.users nearest 0] .usercfg.user.users delete 0 end foreach x $passwdlist { set s "" set l "" addfield s l $dispname [lindex $x 0] -10 "Name " if {$disppassword == 1} { if {$passstyle == 0} { set t [lindex $x 1] if {[string length $t] == 0} { set pw "blank" } elseif {[string index $t 0] == "*"} { set pw "locked" } else { set pw "exists" } addfield s l 1 $pw -6 "Passwd " } elseif {$passstyle == 1} { addfield s l 1 [lindex $x 1] -15 "Password " } } addfield s l $dispuid [lindex $x 2] -5 "UID " if {$dispgroup == 1} { if {$gidstyle == 0} { addfield s l 1 [lindex $x 3] -5 "GID " } elseif {$gidstyle == 1} { set t "-none-" foreach y $grouplist { if {[lindex $x 3] == [lindex $y 2]} { set t [lindex $y 0] break } } addfield s l 1 $t -10 "Group " } } addfield s l $disphome [lindex $x 4] -25 "Full Name " addfield s l $disphome [lindex $x 5] -15 "Home directory " addfield s l $dispshell [lindex $x 6] -15 "Shell " .usercfg.user.users insert end $s } .usercfg.user.lframe.label configure -text $l set ulistwidth [string length $l] if {$ulistwidth == 0} { set ulistwidth 1 } .usercfg.user.users configure -width ${ulistwidth} -height 10 .usercfg.user.users yview $idx } ########## # Rebuild the contents of the .group.groups listbox from the group list. # The label .group.lframe.label is also regenerated. # MJC: the array grouplistidx stores a mapping from the indices # of the listbox entries to the grouplist list entries proc regengrouplist {} { global grouplist usergidbegin showupg grouplistidx set idx [.usercfg.group.groups nearest 0] .usercfg.group.groups delete 0 end set i 0 set grouplistidx {} foreach x $grouplist { if {([lindex $x 2] < $usergidbegin) || ($showupg == 1)} { set s [format "%-10s %-5s %-5s" [lindex $x 0] [lindex $x 2] \ [llength [lindex $x 3]]] .usercfg.group.groups insert end $s lappend grouplistidx $i } incr i } .usercfg.group.groups yview $idx } ########## # Given a list of selected items in the listbox, return a corresponding # list of indices into the grouplist proc maptogrouplist {l} { global grouplistidx set ll {} foreach i $l { lappend ll [lindex $grouplistidx $i] } return $ll } ########## # Write the password and group files back to the disk. proc syncfiles {} { global passwdfilename passwdlist groupfilename grouplist if {[catch {set p [open $passwdfilename w]}] != 0} { rhs_error_dialog "Couldn't write to $passwdfilename." return } if {[catch {set g [open $groupfilename w]}] != 0} { rhs_error_dialog "Couldn't write to $groupfilename." return } foreach x $passwdlist { set x [lreplace $x 7 7] puts $p [join $x :] } foreach x $grouplist { set x [lreplace $x 4 4] set y [lindex $x 3] set x [lreplace $x 3 3] puts $g "[join $x :]:[join $y ,]" } close $p close $g } ########## # Change files owned by specified user and/or group IDs to a new UID/GID. # uid - A UID to change. (Set to -1 to skip searching for UIDs.) # gid - A GID to change. (Set to -1 to skip searching for GIDs.) # newuser - The new user ID or username. # newgroup - The new group ID or groupname. # basedir - The directory to search. (All subdirs of this directory will # be searched. Directories listed in $excludedirs will NOT be # searched.) proc cleanorphans {uid gid newuser newgroup basedir} { global passwdlist grouplist excludedirs set newu $newuser if {$uid != -1} { foreach u $passwdlist { if {[lindex $u 0] == $newuser} { set newu [lindex $u 2] continue } } } set newg $newgroup if {$gid != -1} { foreach g $grouplist { if {[lindex $g 0] == $newgroup} { set newg [lindex $g 2] continue } } } if {$newu == "" && $newg == ""} { return } if {$uid != -1} { set com "/usr/bin/find $basedir" foreach d [split $excludedirs \;] { set com "$com \\\( -path $d -prune \\\) -o" } set com "$com -uid $uid -exec /bin/chown $newu \\\{\\\} \\\; >&/dev/null &" catch {eval exec $com} } if {$gid != -1} { set com "/usr/bin/find $basedir" foreach d [split $excludedirs \;] { set com "$com \\\( -path $d -prune \\\) -o" } set com "$com -gid $gid -exec /bin/chown .$newg \\\{\\\} \\\; >&/dev/null &" catch {eval exec $com} } } ######################################## # Password section ########## # Variables # The encrypted password. set tpass(0) "" # The password currently being entered. (Unencrypted, of course.) set epass(0) "" # A set of hashmarks (#) to display instead of the password being edited. set ephash(0) "" # The password being verified. If this is "", then any password entered is # assumed to be a first attempt. set epver(0) "" # A lock variable to prevent simultaneous edits of the same password. set editingpass(0) 0 ########## # Fill ephash($i) with as many hashmarks as there are characters in epass($i). proc pwdgenhash {i} { global epass ephash set ephash($i) "" for {set x [string length $epass($i)]} {$x > 0} {incr x -1} { set ephash($i) "$ephash($i)#" } } ########## # Delete the last letter of the password being edited. Invoked when BackSpace, # Delete, or C-h is pressed. proc delpwdletter {i} { global epass set epass($i) [string range $epass($i) 0 \ [expr "[string length $epass($i)]-2"]] pwdgenhash $i } ########## # Add $c to the end of the password being edited. proc addpwdletter {i c} { global epass set epass($i) $epass($i)$c pwdgenhash $i puts "adding: $c" } ########## # If this is the first attempt at entering the password, save it and switch # to verify mode. Otherwise, process the entered password. proc crypt {p} { return [exec rhcrypt --crypt-only $p] } proc pwdhitenter {i} { global tpass epass ephash epver if {$epver($i) == ""} { if {$epass($i) != ""} { .editpwd$i.pwd.label configure -text "Verify:" set epver($i) $epass($i) set epass($i) "" set ephash($i) "" } } else { if {$epass($i) == $epver($i)} { .editpwd$i.pwd.label configure -text "Password:" set tpass($i) [crypt $epass($i)] set epass($i) "" set ephash($i) "" set epver($i) "" } else { .editpwd$i.pwd.label configure -text "Password:" set epass($i) "" set ephash($i) "" set epver($i) "" } } } ########## # Enter the new password into password($i) and exit. proc pwdeditok {i} { global password tpass epass ephash epver editingpass set password($i) $tpass($i) unset tpass($i) epass($i) ephash($i) epver($i) set editingpass($i) 0 destroy .editpwd$i } ########## # Exit and discard the new password. proc pwdeditcancel {i} { global tpass epass ephash epver editingpass unset tpass($i) epass($i) ephash($i) epver($i) set editingpass($i) 0 destroy .editpwd$i } ########## # Open a window to allow editing a password. proc editpasswd {i} { global password tpass epass ephash epver editingpass name if {$editingpass($i) == 1} { return } set editingpass($i) 1 set tpass($i) $password($i) set epver($i) "" set editingpass($i) 1 toplevel .editpwd$i wm withdraw .editpwd$i frame .editpwd$i.ents pack .editpwd$i.ents -side top -expand 1 -fill both -padx 4 -pady 4 frame .editpwd$i.enc pack .editpwd$i.enc -side top -expand 1 -fill x -padx 4 \ -in .editpwd$i.ents label .editpwd$i.enc.label -text "Encrypted password:" entry .editpwd$i.enc.entry -width 15 -relief sunken -bd 2 \ -textvariable tpass($i) -width 30 set_emacs_entry_bindings .editpwd$i.enc.entry pack .editpwd$i.enc.label -side left -anchor w pack .editpwd$i.enc.entry -side right -anchor e frame .editpwd$i.pwd pack .editpwd$i.pwd -side top -expand 1 -fill x -padx 4 \ -in .editpwd$i.ents label .editpwd$i.pwd.label -text "Password:" entry .editpwd$i.pwd.entry -width 15 -relief sunken -bd 2 \ -textvariable epass($i) -width 30 -show * set_emacs_entry_bindings .editpwd$i.pwd.entry pack .editpwd$i.pwd.label -side left -anchor w pack .editpwd$i.pwd.entry -side right -anchor e frame .editpwd$i.buttons pack .editpwd$i.buttons -side top -expand 1 -fill x -padx 4 -pady 4 button .editpwd$i.ok -width 7 -text "Ok" -command "pwdeditok $i" button .editpwd$i.cancel -width 7 -text "Cancel" \ -command "pwdeditcancel $i" pack .editpwd$i.ok .editpwd$i.cancel -side left -expand 1 \ -in .editpwd$i.buttons set epass($i) "" set ephash($i) "" bind .editpwd$i.enc.entry "focus .editpwd$i.pwd.entry" # bind .editpwd$i.pwd.entry "addpwdletter $i %A" # bind .editpwd$i.pwd.entry "delpwdletter $i" # bind .editpwd$i.pwd.entry "delpwdletter $i" # bind .editpwd$i.pwd.entry "delpwdletter $i" bind .editpwd$i.pwd.entry "pwdhitenter $i" center_dialog .editpwd$i wm deiconify .editpwd$i focus .editpwd$i.pwd.entry if {$name($i) != ""} { wm title .editpwd$i "Edit password for $name($i)" } else { wm title .editpwd$i "Edit password for " } } ######################################## # User section #################### # Functions common to adding and editing users. ########## # Check to see if a home directory exists. If not, offer to create it. # i - The index of the user data in the name, uid, etc. arrays. proc makehomedir {i} { global name password uid group gecos home shell global skeldir if {[file isdirectory $home($i)] == 0} { set x [rhs_dialog .veruser$i "Question" \ "The directory $home($i) does not exist.\nDo you want to create it?" \ question 0 "Yes" "No"] if {$x == 0} { set e 0 if {[catch {exec /bin/cp -apR $skeldir $home($i)}] != 0} { set e 1 } else { if {[catch {exec /bin/chown -R $uid($i).$group($i) $home($i)}] != 0} { set e 1 } } if {$e == 1} { rhs_error_dialog "The home directory was not created properly." } } } } ########## # Verify that a given set of user parameters make sense. Returns 1 if # everything is Ok, 0 is something went wrong. # i - The index of the user data in the name, uid, etc. arrays. # checkuniqname - 1 if the username unicity should be checked. # checkuniquid - 1 if the UID unicity should be checked. proc verifyuserent {i checkuniqname checkuniquid} { global passwdlist grouplist useruidbegin usergidbegin global name password uid group gecos home shell # Check for a blank username. if {[string length $name($i)] == 0} { rhs_error_dialog "The user name may not be left blank." return 0 } # Check for a blank primary group. if {[string length $uid($i)] == 0} { rhs_error_dialog "The primary group may not be left blank." return 0 } # Verify that the UID is valid. set x 0 for { set j 0 } { $j < [string length $uid($i)] } { incr j } { if {0==[string match "\[0123456789\]" [string index $uid($i) $j]]} { set x 1 } } if {$x == 0} { if {[string length $uid($i)] == 0} { set x 1 } } if {$x == 0} { if {$uid($i) > 32767} { set x 1 } } if {$x == 1} { set x [rhs_dialog .veruser$i "Warning" \ "\"$uid($i)\" is not a valid user ID." \ warning 0 "Generate valid ID" "Abort"] if {$x == 1} { return 0 } set uid($i) [getuniqueuid $useruidbegin] rhs_dialog .veruser$i "Information" \ "The user ID has been set to $uid($i)." \ info 0 "Ok" } if {$checkuniqname == 1 || $checkuniquid == 1} { foreach u $passwdlist { if {$checkuniqname == 1} { # Verify that the username is unique. if {$name($i) == [lindex $u 0]} { rhs_error_dialog "There is already a user named $name($i)." return 0 } } if {$checkuniquid == 1} { # Verify that the UID is unique. if {$uid($i) == [lindex $u 2]} { set x [rhs_dialog .veruser$i "Warning" \ "There is already a user with UID $uid($i)." \ warning 1 "Continue" "Generate valid ID" "Abort"] if {$x == 1} { # Generate a unique UID. set uid($i) [getuniqueuid $useruidbegin] rhs_dialog .veruser$i "Information" \ "The user ID has been set to $uid($i)." \ info 0 "Ok" } if {$x == 2} { return 0 } } } } } # Verify that the home directory is an absolute path. if {$home($i) != "" && [string first / $home($i)] != 0} { set x [rhs_dialog .veruser$i "Warning" \ "The home directory is not an absolute path." \ warning 1 "Continue" "Abort"] if {$x == 1} { return 0 } } # Verify that the primary group is valid. set x 0 for { set j 0 } { $j < [string length $group($i)] } { incr j } { if {0==[string match "\[0123456789\]" \ [string index $group($i) $j]]} { set x 1 } } if {$x == 0} { # The primary group has been specified as a GID. if {$group($i) > 32767} { rhs_error_dialog "$group($i) is not a valid group ID." return 0 } set j 1 foreach y $grouplist { if {[lindex $y 2] == $group($i)} { set j 0 break } } if {$j == 1} { rhs_error_dialog "There is no group with ID $group($i)." return 0 } } else { # The primary group has been specified as a groupname. set j 1 foreach y $grouplist { if {[lindex $y 0] == $group($i)} { set j 0 set group($i) [lindex $y 2] break } } if {$j == 1} { if {$group($i) == $name($i)} { set r 0 } else { set r [rhs_dialog .veruser$i "Warning" \ "There is no group named \"$group($i)\"." \ warning 0 "Create it" "Abort"] } if {$r == 1} { return 0 } # Generate the group. set newg {} lappend newg $group($i) "" [getuniquegid $usergidbegin] $name($i) 0 set group($i) [lindex $newg 2] lappend grouplist $newg regengrouplist } } return 1 } ########## # Adds the selected groups from the .addsg$i listbox into the .euserui$i # listbox. proc addsg {i} { global grouplist set l [.addsg$i.lframe.list curselection] foreach x $l { .euserui$i.lframe.list insert end \ [.addsg$i.lframe.list get $x] } while {[llength $l] > 0} { .addsg$i.lframe.list delete [lindex $l 0] [lindex $l 0] set l [.addsg$i.lframe.list curselection] } } ########## # Opens a window with a listbox to select secondary groups from. proc addsgroup {i} { global grouplist addingsgroups name if {$addingsgroups($i) == 1} { return } set addingsgroups($i) 1 toplevel .addsg$i wm withdraw .addsg$i frame .addsg$i.lframe pack .addsg$i.lframe -side top listbox .addsg$i.lframe.list -relief sunken -borderwidth 2 \ -yscrollcommand ".addsg$i.lframe.scroll set" -font fixed \ -width 10 -height 5 pack .addsg$i.lframe.list -side left -expand 1 -fill both scrollbar .addsg$i.lframe.scroll\ -command ".addsg$i.lframe.list yview" pack .addsg$i.lframe.scroll -side left -fill y frame .addsg$i.bframe pack .addsg$i.bframe -side top -fill x button .addsg$i.add -text "Add" -command "addsg $i" button .addsg$i.cancel -text "Done" \ -command "set addingsgroups($i) 0 ; destroy .addsg$i" pack .addsg$i.add .addsg$i.cancel -side left \ -expand 1 -in .addsg$i.bframe -padx 4 -pady 4 set sg [listfrombox .euserui$i.lframe.list] foreach x $grouplist { if {[lsearch $sg [lindex $x 0]] == -1} { .addsg$i.lframe.list insert end [lindex $x 0] } } if {$name($i) != ""} { wm title .addsg$i "Add secondary group for $name($i)" } else { wm title .addsg$i "Add secondary group for " } update idletasks set x [expr - [winfo reqwidth .addsg$i] + [winfo rootx .euserui$i.lframe] \ - 15] set y [expr [winfo height .euserui$i.lframe]/2 - \ [winfo reqheight .addsg$i]/2 + [winfo rooty .euserui$i.lframe] \ + [winfo vrooty .euserui$i.lframe]] wm group .addsg$i .euserui$i.lframe wm transient .addsg$i .euserui$i.lframe wm geometry .addsg$i +$x+$y wm deiconify .addsg$i } ########## # Deletes the selected secondary groups from the .euserui$i listbox. proc rmsgroup {i} { global passwdlist set l [.euserui$i.lframe.list curselection] foreach x $l { lappend d [.euserui$i.lframe.list get $x] } foreach y $d { for {set x 0} {$x < [.euserui$i.lframe.list size]} {incr x} { if {$y == [.euserui$i.lframe.list get $x]} { .euserui$i.lframe.list delete $x $x break } } } } ########## # Generates the primary interface componants for adding/editing groups. proc edituserui {i} { global passwdlist grouplist tmpnum useruidbegin defhomepath shellslist global name password uid group gecos home shell global editingpass addingsgroups cleardd dropdown set editingpass($i) 0 set addingsgroups($i) 0 toplevel .euserui$i frame .euserui$i.fa pack .euserui$i.fa -side top -expand 1 -fill both -padx 4 -pady 4 frame .euserui$i.ents pack .euserui$i.ents -side left -expand 1 -fill x -in .euserui$i.fa frame .euserui$i.name pack .euserui$i.name -side top -expand 1 -fill x -padx 4 \ -in .euserui$i.ents label .euserui$i.name.label -text "Username:" label .euserui$i.name.bit -bitmap $cleardd entry .euserui$i.name.entry -width 30 -relief sunken -bd 2 \ -textvariable name($i) set_emacs_entry_bindings .euserui$i.name.entry pack .euserui$i.name.label -side left -anchor w pack .euserui$i.name.entry .euserui$i.name.bit -side right -anchor e frame .euserui$i.password pack .euserui$i.password -side top -expand 1 -fill x -padx 4 \ -in .euserui$i.ents label .euserui$i.password.label -text "Password:" menubutton .euserui$i.password.bit -bitmap $dropdown -relief raised \ -menu .euserui$i.password.bit.menu label .euserui$i.password.entry -width 30 -relief sunken -bd 2 \ -textvariable password($i) -anchor w pack .euserui$i.password.label -side left -anchor w pack .euserui$i.password.entry .euserui$i.password.bit -side right \ -anchor e menu .euserui$i.password.bit.menu .euserui$i.password.bit.menu add command -label "- edit -" \ -command "editpasswd $i" .euserui$i.password.bit.menu add command -label "- none -" \ -command "set password($i) {}" .euserui$i.password.bit.menu add command -label "- lock -" \ -command "set password($i) *" bind .euserui$i.password.entry "editpasswd $i" frame .euserui$i.uid pack .euserui$i.uid -side top -expand 1 -fill x -padx 4 \ -in .euserui$i.ents label .euserui$i.uid.label -text "UID:" label .euserui$i.uid.bit -bitmap $cleardd entry .euserui$i.uid.entry -width 30 -relief sunken -bd 2 \ -textvariable uid($i) set_emacs_entry_bindings .euserui$i.uid.entry pack .euserui$i.uid.label -side left -anchor w pack .euserui$i.uid.entry .euserui$i.uid.bit -side right -anchor e frame .euserui$i.group pack .euserui$i.group -side top -expand 1 -fill x -padx 4 \ -in .euserui$i.ents label .euserui$i.group.label -text "Primary Group:" label .euserui$i.group.bit -bitmap $cleardd entry .euserui$i.group.entry -width 30 -relief sunken -bd 2 \ -textvariable group($i) set_emacs_entry_bindings .euserui$i.group.entry pack .euserui$i.group.label -side left -anchor w pack .euserui$i.group.entry .euserui$i.group.bit -side right -anchor e frame .euserui$i.gecos pack .euserui$i.gecos -side top -expand 1 -fill x -padx 4 \ -in .euserui$i.ents label .euserui$i.gecos.label -text "Full name:" label .euserui$i.gecos.bit -bitmap $cleardd entry .euserui$i.gecos.entry -width 30 -relief sunken -bd 2 \ -textvariable gecos($i) set_emacs_entry_bindings .euserui$i.gecos.entry pack .euserui$i.gecos.label -side left -anchor w pack .euserui$i.gecos.entry .euserui$i.gecos.bit -side right -anchor e frame .euserui$i.home pack .euserui$i.home -side top -expand 1 -fill x -padx 4 \ -in .euserui$i.ents label .euserui$i.home.label -text "Home:" label .euserui$i.home.bit -bitmap $cleardd entry .euserui$i.home.entry -width 30 -relief sunken -bd 2 \ -textvariable home($i) set_emacs_entry_bindings .euserui$i.home.entry pack .euserui$i.home.label -side left -anchor w pack .euserui$i.home.entry .euserui$i.home.bit -side right -anchor e frame .euserui$i.shell pack .euserui$i.shell -side top -expand 1 -fill x -padx 4 \ -in .euserui$i.ents label .euserui$i.shell.label -text "Shell:" menubutton .euserui$i.shell.bit -bitmap $dropdown -relief raised \ -menu .euserui$i.shell.bit.menu entry .euserui$i.shell.entry -width 30 -relief sunken -bd 2 \ -textvariable shell($i) set_emacs_entry_bindings .euserui$i.shell.entry pack .euserui$i.shell.label -side left -anchor w pack .euserui$i.shell.entry .euserui$i.shell.bit -side right -anchor e menu .euserui$i.shell.bit.menu foreach x $shellslist { .euserui$i.shell.bit.menu add command -label $x \ -command "set shell($i) $x" } frame .euserui$i.fb pack .euserui$i.fb -side left -padx 4 -in .euserui$i.fa frame .euserui$i.fc pack .euserui$i.fc -side left -in .euserui$i.fa -expand 1 -fill both frame .euserui$i.lframe pack .euserui$i.lframe -side top -fill both -expand 1 -in .euserui$i.fc label .euserui$i.lframe.label -text "Secondary groups" pack .euserui$i.lframe.label -side top -fill x -expand 0 listbox .euserui$i.lframe.list -relief sunken -borderwidth 2 \ -yscrollcommand ".euserui$i.lframe.scroll set" -font fixed \ -width 10 -height 5 pack .euserui$i.lframe.list -side left -expand 1 -fill both scrollbar .euserui$i.lframe.scroll -command ".euserui$i.lframe.list yview" pack .euserui$i.lframe.scroll -side left -fill y frame .euserui$i.gbuttons pack .euserui$i.gbuttons -side bottom -fill x -expand 0 -in .euserui$i.fc button .euserui$i.addg -width 7 -text "Add" -command "addsgroup $i" button .euserui$i.delg -width 7 -text "Remove" -command "rmsgroup $i" pack .euserui$i.addg .euserui$i.delg -side left -expand 1 \ -in .euserui$i.gbuttons bind .euserui$i.name.entry " focus .euserui$i.uid.entry " bind .euserui$i.password.entry " focus .euserui$i.uid.entry " bind .euserui$i.uid.entry " focus .euserui$i.group.entry " bind .euserui$i.group.entry " focus .euserui$i.gecos.entry " bind .euserui$i.gecos.entry " focus .euserui$i.home.entry " bind .euserui$i.home.entry " focus .euserui$i.shell.entry " bind .euserui$i.shell.entry " focus .euserui$i.name.entry " focus .euserui$i.name.entry } #################### # Add user ########## # Finishes an add user operation. If $t is 1, the new user is created. proc endadduser {i t} { global passwdlist grouplist global name password uid group gecos home shell global editingpass addingsgroups numadds if {$editingpass($i) == 1} { destroy .editpwd$i } if {$addingsgroups($i) == 1} { destroy .addsg$i } set name($i) [string trim $name($i)] set password($i) [string trim $password($i)] set uid($i) [string trim $uid($i)] set group($i) [string trim $group($i)] set gecos($i) [string trim $gecos($i)] set home($i) [string trim $home($i)] set shell($i) [string trim $shell($i)] if {$t == 1} { set x [verifyuserent $i 1 1] if {$x == 0} { return } lappend passwdlist [split "$name($i):$password($i):$uid($i):$group($i):$gecos($i):$home($i):$shell($i):0" :] set sg [listfrombox .euserui$i.lframe.list] foreach x $sg { foreach y $grouplist { if {[lindex $y 0] == $x} { if {[lsearch [lindex $y 3] $name($i)] == -1} { set y [lreplace $y 3 3 \ [lsort [linsert [lindex $y 3] 0 $name($i)]]] grouplistreplace [lindex $y 4] [lindex $y 0] $y } } } } regenuserlist regengrouplist syncfiles makehomedir $i } unset name($i) password($i) uid($i) group($i) gecos($i) unset home($i) shell($i) unset editingpass($i) destroy .euserui$i incr numadds -1 } ########## # When a new username is entered, sets group($i) and home($i) to reasonable # defaults. proc addnameproc {i} { global defhomepath global name password uid group gecos home shell focus .euserui$i.password.entry if {$name($i) != ""} { if {$group($i) == ""} { set group($i) $name($i) } if {$home($i) == ""} { set home($i) $defhomepath/$name($i) } } } ########## # Begin adding a user. proc adduser {} { global passwdlist grouplist tmpnum useruidbegin defhomepath shellslist global name password uid group gecos home shell global editingpass numadds set i [incr tmpnum] incr numadds edituserui $i bind .euserui$i.name.entry "addnameproc $i" foreach x $grouplist { if {[lindex $x 0] == "users"} { .euserui$i.lframe.list insert end "users" } } set name($i) "" set password($i) "" set uid($i) [getuniqueuid $useruidbegin] set group($i) "" set gecos($i) "" set home($i) "" set shell($i) [lindex $shellslist 0] frame .euserui$i.buttons pack .euserui$i.buttons -side top -expand 0 -fill x -padx 4 -pady 4 button .euserui$i.add -width 7 -text "Add" -command "endadduser $i 1" button .euserui$i.cancel -width 7 -text "Cancel" -command "endadduser $i 0" pack .euserui$i.add .euserui$i.cancel -side left -expand 1 \ -in .euserui$i.buttons focus .euserui$i.name.entry wm title .euserui$i "Add user" } ########## # Deactivate user ########## # Deactivate the users selected in the primary user list. proc deactivateuser {} { global passwdlist tmpnum tmpa defhomepath set i [incr tmpnum] # Lock the required user entries. set l {} set el {} set s [.usercfg.user.users curselection] foreach x $s { set u [lindex $passwdlist $x] if {[lindex $u 7] != 0} { lappend el $u } else { set u [lreplace $u 7 7 $i] set passwdlist [lreplace $passwdlist $x $x $u] lappend l $u } } if {[llength $el] > 0} { set str "I cannot deactivate the following user(s)\nuntil you finish editing them:\n" foreach x $el { set str "$str [lindex $x 0]\n" } rhs_error_dialog $str } foreach x $l { set y 0 # Check that the user is not already deactivated. if {[string index [lindex $x 1] 0] == "*"} { rhs_error_dialog "[lindex $x 0] is already deactivated." # unlock user set x [lreplace $x 7 7 0] passwdlistreplace $i [lindex $x 0] $x continue } # Lock the password. set x [lreplace $x 1 1 "*[lindex $x 1]"] # Update the password list. set x [lreplace $x 7 7 0] passwdlistreplace $i [lindex $x 0] $x # Check to see if anything needs to be done with the user's home directory. set r 2 if {[lindex $x 5] != "/"} { if {[file isdirectory [lindex $x 5]] == 1} { set r [rhs_dialog .deactuser$i "Question" \ "Should I compress [lindex $x 5], delete it, or ignore it?" \ question 0 "Compress" "Delete" "Ignore"] } } if {$r == 0} { # Compress the home directory. toplevel .deactuser$i wm withdraw .deactuser$i grab .deactuser$i listbox .deactuser$i.list -width 60 -height 5 pack .deactuser$i.list -expand 1 -fill both -side top set tmpa($i) 0 button .deactuser$i.button -text "Ok" -command "set tmpa($i) 1" \ -state disabled pack .deactuser$i.button -side top -expand 1 -padx 4 -pady 4 wm title .deactuser$i "Progress report" center_dialog .deactuser$i tkwait visibility .deactuser$i .deactuser$i.list insert end \ "Packing [lindex $x 5] into $defhomepath/[lindex $x 0].tar..." update if {[catch {exec /bin/tar -cPf $defhomepath/[lindex $x 0].tar [lindex $x 5]}] \ != 0} { .deactuser$i.list insert end \ "An error occurred while packing." } else { .deactuser$i.list insert end \ "Compressing $defhomepath/[lindex $x 0].tar..." update if {[catch {exec /bin/gzip -9 $defhomepath/[lindex $x 0].tar}] != 0} { .deactuser$i.list insert end \ "An error occurred while compressing." } else { .deactuser$i.list insert end \ "Setting permissions on $defhomepath/[lindex $x 0].tar.gz..." update if {[catch {exec chmod 600 $defhomepath/[lindex $x 0].tar.gz}] != 0} { .deactuser$i.list insert end \ "An error occurred while setting permissions." } else { .deactuser$i.list insert end \ "Deleting [lindex $x 5]..." update if {[catch {exec /bin/rm -rf [lindex $x 5]}] != 0} { .deactuser$i.list insert end \ "An error occurred while deleting." } else { .deactuser$i.list insert end "Done." } } } } .deactuser$i.button configure -state normal set tmpa($i) 0 tkwait variable tmpa($i) unset tmpa($i) destroy .deactuser$i } elseif {$r == 1} { # Delete the home directory. toplevel .deactuser$i wm withdraw .deactuser$i grab .deactuser$i listbox .deactuser$i.list -width 60 -height 2 pack .deactuser$i.list -expand 1 -fill both set tmpa($i) 0 button .deactuser$i.button -text "Ok" -command "set tmpa($i) 1" \ -state disabled pack .deactuser$i.button -side top -expand 1 -padx 4 -pady 4 wm title .deactuser$i "Progress report" center_dialog .deactuser$i tkwait visibility .deactuser$i.list .deactuser$i.list insert end \ "Deleting [lindex $x 5]..." update if {[catch {exec /bin/rm -rf [lindex $x 5]}] != 0} { .deactuser$i.list insert end \ "An error occurred while deleting." } else { .deactuser$i.list insert end "Done." } .deactuser$i.button configure -state normal set tmpa($i) 0 tkwait variable tmpa($i) unset tmpa($i) destroy .deactuser$i } } regenuserlist syncfiles } #################### # Reactivate user ########## # Reactivate the selected users. proc reactivateuser {} { global passwdlist defhomepath tmpnum tmpa editingpass password global name uid home group set i [incr tmpnum] # Lock the selected users set l {} set el {} set s [.usercfg.user.users curselection] foreach x $s { set u [lindex $passwdlist $x] if {[lindex $u 7] != 0} { lappend el $u } else { set u [lreplace $u 7 7 $i] set passwdlist [lreplace $passwdlist $x $x $u] lappend l $u } } if {[llength $el] > 0} { set str "I cannot reactivate the following user(s)\nuntil you finish editing them:\n" foreach x $el { set str "$str [lindex $x 0]\n" } rhs_error_dialog $str } foreach x $l { set y 0 # Check that the user is deactivated. if {[string index [lindex $x 1] 0] != "*"} { rhs_error_dialog "[lindex $x 0] is not deactivated." # unlock user set x [lreplace $x 7 7 0] passwdlistreplace $i [lindex $x 0] $x continue } # Generate and verify the new password. set newp [string range [lindex $x 1] 1 end] for {set j 0} {$j < [string length $newp]} {incr j} { if {[string match "\[./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\]" [string index $newp $j]] == 0} { set y 1 } } # Would password be blank after reactivation? if {[lindex $x 1] == "*"} { set y 1 } if {$y == 1} { set r [rhs_dialog .reactuser$i "Error" \ "The password for \"[lindex $x 0]\" could not be restored." \ warning 0 "Enter a new password" "Leave it blank" "Abort"] if {$r == 0} { # Get a new password from the user. set name($i) [lindex $x 0] set password($i) "" set editingpass($i) 0 editpasswd $i tkwait window .editpwd$i set newp $password($i) unset password($i) unset editingpass($i) set y 0 } elseif {$r == 1} { set newp "" set y 0 } } if {$y == 0} { # Update the password file set x [lreplace $x 1 1 $newp] set x [lreplace $x 7 7 0] passwdlistreplace $i [lindex $x 0] $x } # Check for any compressed home dirs lying around. if {[file exists [lindex $x 5]] == 0} { if {[file exists $defhomepath/[lindex $x 0].tar.gz] == 1} { # Uncompress the home directory. toplevel .reactuser$i wm withdraw .reactuser$i grab .reactuser$i listbox .reactuser$i.list -width 60 -height 3 pack .reactuser$i.list -expand 1 -fill both set tmpa($i) 0 button .reactuser$i.button -text "Ok" -command "set tmpa($i) 1" \ -state disabled pack .reactuser$i.button -side top -expand 1 -padx 4 -pady 4 wm title .reactuser$i "Progress report" center_dialog .reactuser$i tkwait visibility .reactuser$i.list set errstat 0 .reactuser$i.list insert end \ "Unpacking $defhomepath/[lindex $x 0].tar.gz..." update if {[catch {exec /bin/tar -xzpPf $defhomepath/[lindex $x 0].tar.gz}] \ != 0} { .reactuser$i.list insert end \ "An error occurred while unpacking." set errstat 1 } else { .reactuser$i.list insert end \ "Deleting $defhomepath/[lindex $x 0].tar.gz..." update if {[catch {exec /bin/rm -f $defhomepath/[lindex $x 0].tar.gz}] \ != 0} { .reactuser$i.list insert end \ "An error occurred while deleting." set errstat 1 } } if {$errstat == 0} { .reactuser$i.list insert end "Done." } .reactuser$i.button configure -state normal set tmpa($i) 0 tkwait variable tmpa($i) unset tmpa($i) destroy .reactuser$i } else { # No home directory exists, so ask if one should be made. set uid($i) [lindex $x 2] set group($i) [lindex $x 3] set home($i) [lindex $x 5] makehomedir $i unset uid($i) unset group($i) unset home($i) } } } regenuserlist syncfiles } #################### # Delete a user ########## # Delete the selected users proc removeuser {} { global passwdlist defhomepath grouplist tmpnum tmpa confirmremove maildir global orphaneduser orphanedgroup set i [incr tmpnum] set delusers [.usercfg.user.users curselection] if {[llength $delusers] == 0} { return } foreach x $delusers { lappend t [lindex $passwdlist $x] } # Lock the selected users. set el {} set l {} foreach x $t { if {[lindex $x 7] != 0} { lappend el $x } else { lappend l $x set x [lreplace $x 7 7 $i] passwdlistreplace 0 [lindex $x 0] $x } } if {[llength $el] > 0} { set str "I cannot remove the following user(s)\nuntil you finish editing them:\n" foreach x $el { set str "$str [lindex $x 0]\n" } rhs_error_dialog $str } foreach x $l { # Ask for confirmation. if {$confirmremove == 1} { set r [rhs_dialog .deluser$i "Question" \ "Are you certain you want to\ remove [lindex $x 0]?" \ question 0 "Yes" "No"] if {$r == 1} { passwdlistreplace $i [lindex $x 0] $x continue } } # Check if the home directory should be deleted. if {[file exists [lindex $x 5]] == 1} { if {[lindex $x 5] != "/"} { set r [rhs_dialog .deluser$i "Question" \ "Do you want to delete [lindex $x 5]\nand $maildir/[lindex $x 0]?" \ question 0 "Yes" "No" "Cancel"] if {$r == 0} { # Delete the home directory. toplevel .deluser$i wm withdraw .deluser$i listbox .deluser$i.list -width 60 -height 3 pack .deluser$i.list -expand 1 -fill both set tmpa($i) 0 button .deluser$i.button -text "Ok" -command "set tmpa($i) 1" \ -state disabled pack .deluser$i.button -side top -expand 1 -padx 4 -pady 4 wm title .deluser$i "Progress report" center_dialog .deluser$i tkwait visibility .deluser$i.list set errstat 0 .deluser$i.list insert end \ "Deleting [lindex $x 5]..." update if {[catch {exec /bin/rm -rf [lindex $x 5]}] != 0} { .deluser$i.list insert end \ "An error occurred while deleting [lindex $x 5]." set errstat 1 } elseif {[file exists $maildir/[lindex $x 0]] == 1} { .deluser$i.list insert end \ "Deleting $maildir/[lindex $x 0]..." if {[catch {exec /bin/rm -f $maildir/[lindex $x 0]}] != 0} { .deluser$i.list insert end \ "An error occurred while deleting $maildir/[lindex $x 0]." set errstat 1 } } if {$errstat == 0} { .deluser$i.list insert end "Done." } .deluser$i.button configure -state normal set tmpa($i) 0 tkwait variable tmpa($i) unset tmpa($i) destroy .deluser$i } elseif {$r == 2} { passwdlistreplace $i [lindex $x 0] $x continue } } } # Check if an old, compressed home dir exists and should be deleted. if {[file exists $defhomepath/[lindex $x 0].tar.gz] == 1} { set r [rhs_dialog .deluser$i "Question" \ "Do you want to delete\n$defhomepath/[lindex $x 0].tar.gz?" \ question 0 "Yes" "No" "Cancel"] if {$r == 0} { # Delete the old home dir. if {[catch {exec /bin/rm -f $defhomepath/[lindex $x 0].tar.gz}] != 0} { rhs_error_dialog "An error occured while deleting $defhomepath/[lindex $x 0].tar.gz." } } elseif {$r == 2} { passwdlistreplace $i [lindex $x 0] $x continue } } # Find the index of the user in the user list. set j 0 set k -1 foreach y $passwdlist { if {[lindex $y 0] == [lindex $x 0]} { if {[lindex $y 7] == $i} { set k $j } } incr j } # Delete the user. if {$k != -1} { set passwdlist [lreplace $passwdlist $k $k] } # Check to see if there is a user private group that should be deleted. set remgrp -1 set ng {} foreach g $grouplist { set su [lindex $g 3] set j [lsearch -exact $su [lindex $x 0]] if {$j != -1} { set su [lreplace $su $j $j] } if {$su == ""} { set su {} } set g [lreplace $g 3 3 $su] set t 1 if {[lindex $g 0] == [lindex $x 0]} { if {[llength [lindex $g 3]] == 0} { set t 0 foreach u $passwdlist { if {[lindex $g 2] == [lindex $u 3]} { set t 1 } } } } if {$t == 1} { lappend ng $g } else { set remgrp [lindex $g 2] } } set grouplist $ng regenuserlist regengrouplist syncfiles # Check to see if files orphaned by the deletion should be cleaned up. set r [rhs_dialog .deluser$i "Question" \ "Do you want to search the filesystem for\nfiles that have been orphaned by removing\n[lindex $x 0]? (This will be done in the background.)\nThe files will be owner/group $orphaneduser/$orphanedgroup." \ question 1 "Yes" "No"] if {$r == 0} { cleanorphans [lindex $x 2] $remgrp $orphaneduser $orphanedgroup / } } } #################### # Edit user ########## # Finishes an end user operation. If $t = 1, the user is updated. $u contains # the old password list entry of the user. proc endedituser {i t u} { global passwdlist grouplist global name password uid group gecos home shell global editingpass addingsgroups global orphaneduser orphanedgroup if {$editingpass($i) == 1} { destroy .editpwd$i } if {$addingsgroups($i) == 1} { destroy .addsg$i } set name($i) [string trim $name($i)] set password($i) [string trim $password($i)] set uid($i) [string trim $uid($i)] set group($i) [string trim $group($i)] set gecos($i) [string trim $gecos($i)] set home($i) [string trim $home($i)] set shell($i) [string trim $shell($i)] if {$t == 1} { # Perform user verification. if {$name($i) == [lindex $u 0]} { set uniqname 0 } else { set uniqname 1 } if {$uid($i) == [lindex $u 2]} { set uniquid 0 } else { set uniquid 1 } set x [verifyuserent $i $uniqname $uniquid] if {$x == 0} { return } # Handle orphaned files. if {$uid($i) != [lindex $u 2] && $group($i) != [lindex $u 3]} { set r [rhs_dialog .edituserend$i "Question" \ "You have changed [lindex $u 0]'s UID and GID.\nThis means that all files that were owned by\n[lindex $u 0] must be updated to reflect the new\nUID and GID if [lindex $u 0] is to use them. You\ncan process only the files in [lindex $u 0]'s home\ndirectory, or you can process all the files on the\nsystem. Which would you like to do?" \ question 0 "Home directory" "Entire system" "None" "Abort"] if {$r == 0} { cleanorphans [lindex $u 2] [lindex $u 3] $uid($i) $group($i) \ [lindex $u 5] } elseif {$r == 1} { cleanorphans [lindex $u 2] [lindex $u 3] $uid($i) $group($i) / } elseif {$r == 3} { return } } elseif {$uid($i) != [lindex $u 2]} { set r [rhs_dialog .edituserend$i "Question" \ "You have changed [lindex $u 0]'s UID. This means\nthat all files that were owned by [lindex $u 0] must\nbe updated to reflect the new UID if [lindex $u 0]\nis to use them. You can process only the files in\n[lindex $u 0]'s home directory, or you can process\nall the files on the system.\nWhich would you like to do?" \ question 0 "Home directory" "Entire system" "None" "Abort"] if {$r == 0} { cleanorphans [lindex $u 2] -1 $uid($i) $orphanedgroup [lindex $u 5] } elseif {$r == 1} { cleanorphans [lindex $u 2] -1 $uid($i) $orphanedgroup / } elseif {$r == 3} { return } } elseif {$group($i) != [lindex $u 3]} { set r [rhs_dialog .edituserend$i "Question" \ "You have changed [lindex $u 0]'s GID. This means\nthat all files that were owned by [lindex $u 0] must\nbe updated to reflect the new GID if [lindex $u 0]\nis to use them. You can process only the files in\n[lindex $u 0]'s home directory, or you can process\nall the files on the system.\nWhich would you like to do?" \ question 0 "Home directory" "Entire system" "None" "Abort"] if {$r == 0} { cleanorphans -1 [lindex $u 3] $orphaneduser $group($i) [lindex $u 5] } elseif {$r == 1} { cleanorphans -1 [lindex $u 3] $orphaneduser $group($i) / } elseif {$r == 3} { return } } # Handle changed home directory locations. if {$home($i) != [lindex $u 5]} { if {[file exists $home($i)] == 0 && [file exists [lindex $u 5]] == 1} { if {[lindex $u 5] != "/"} { set r [rhs_dialog .edituserend$i "Question" \ "You have changed [lindex $u 0]'s home\ndirectory. Would you like to move\nthe old directory to the new location?" \ question 0 "Yes" "No"] if {$r == 0} { if {[catch {exec mv -f [lindex $u 5] $home($i) >&/dev/null}] != 0} { if {[catch {exec cp -af [lindex $u 5] $home($i) >&/dev/null}] != 0} { rhs_error_dialog "An error occurred while moving the directory." } else { catch {exec rm -rf [lindex $u 5] >&/dev/null} } } } } } } # Update the user list. passwdlistreplace $i [lindex $u 0] [split "$name($i):$password($i):$uid($i):$group($i):$gecos($i):$home($i):$shell($i):0" :] # If the username has changed, check for secondary group listings that # must be moved. if {$name($i) != [lindex $u 0]} { foreach x $grouplist { set sg [lindex $x 3] set y [lsearch -exact $sg [lindex $u 0]] if {$y != -1} { set sg [lreplace $sg $y $y $name($i)] set x [lreplace $x 3 3 $sg] grouplistreplace [lindex $x 4] [lindex $x 0] $x } } } # Update secondary groups. set sg [listfrombox .euserui$i.lframe.list] set ng {} foreach x $sg { foreach y $grouplist { if {[lindex $y 0] == $x} { if {[lsearch -exact [lindex $y 3] $name($i)] == -1} { set sgt [lindex $y 3] lappend sgt $name($i) set sgt [lsort $sgt] set y [lreplace $y 3 3 $sgt] grouplistreplace [lindex $y 4] [lindex $y 0] $y } } } } regenuserlist regengrouplist syncfiles makehomedir $i } else { set u [lreplace $u 7 7 0] passwdlistreplace $i [lindex $u 0] $u } unset name($i) password($i) uid($i) group($i) gecos($i) unset home($i) shell($i) unset editingpass($i) destroy .euserui$i } ########## # Begin an edit user operation on a specific user. $u contains the original # password list entry of the user. proc edituser {i u} { global passwdlist grouplist tmpnum shellslist global name password uid group gecos home shell global quotas_available edituserui $i set name($i) [lindex $u 0] set password($i) [lindex $u 1] set uid($i) [lindex $u 2] set gecos($i) [lindex $u 4] set home($i) [lindex $u 5] set shell($i) [lindex $u 6] foreach x $grouplist { if {[lindex $x 2] == [lindex $u 3]} { set group($i) [lindex $x 0] } if {[lsearch [lindex $x 3] [lindex $u 0]] != -1} { .euserui$i.lframe.list insert end [lindex $x 0] } } frame .euserui$i.buttons pack .euserui$i.buttons -side top -expand 1 -fill x -padx 4 -pady 4 button .euserui$i.ok -width 7 -text Ok -command "endedituser $i 1 {$u}" button .euserui$i.quotas -width 7 -text "Quotas" \ -command "edituserquota [lindex $u 0] [lindex $u 2]" if { ! $quotas_available } { .euserui$i.quotas config -state disabled } button .euserui$i.cancel -width 7 -text Cancel \ -command "endedituser $i 0 {$u}" pack .euserui$i.ok .euserui$i.quotas .euserui$i.cancel -side left -expand 1 \ -in .euserui$i.buttons wm title .euserui$i "Edit user [lindex $u 0]" } ########## # Edit the selected users. proc beginedituser {l} { global passwdlist tmpnum set sl {} set fl {} set el {} foreach x $l { set u [lindex $passwdlist $x] if {[lindex $u 7] == 0} { set i [incr tmpnum] set u [lreplace $u 7 7 $i] passwdlistreplace 0 [lindex $u 0] $u lappend sl $u } elseif {[winfo exists .euserui[lindex $u 7]] == 1} { lappend fl $u } else { lappend el $u } } foreach x $fl { catch {wm deiconify .euserui[lindex $x 7]} catch {raise .euserui[lindex $x 7]} } foreach x $sl { edituser [lindex $x 7] $x } } #################### # The primary user frame. ########## # Set up the user frame. proc setupuserframe {} { frame .usercfg.user frame .usercfg.user.lframe pack .usercfg.user.lframe -side top -fill both -expand 1 -padx 4 -pady 4 label .usercfg.user.lframe.label -font fixed -anchor w pack .usercfg.user.lframe.label -side top -fill x -expand 0 -padx 2 scrollbar .usercfg.user.userscroll -command ".usercfg.user.users yview" pack .usercfg.user.userscroll -side right -fill y -in .usercfg.user.lframe listbox .usercfg.user.users -relief sunken -borderwidth 2 \ -yscrollcommand ".usercfg.user.userscroll set" -font fixed \ -width 80 -height 10 -setgrid 1 pack .usercfg.user.users -side left -expand 1 -fill both -in .usercfg.user.lframe bind .usercfg.user.users { beginedituser [.usercfg.user.users nearest %y] } frame .usercfg.user.bframe pack .usercfg.user.bframe -side top -fill x button .usercfg.user.add -width 10 -text "Add" -command { adduser } button .usercfg.user.deactivate -width 10 -text "Deactivate" \ -command { deactivateuser } button .usercfg.user.reactivate -width 10 -text "Reactivate" \ -command { reactivateuser } button .usercfg.user.remove -width 10 -text "Remove" -command { removeuser } button .usercfg.user.edit -text "Edit" -width 10 \ -command { beginedituser [.usercfg.user.users curselection] } button .usercfg.user.exit -text "Exit" -width 10 \ -command { exit } pack .usercfg.user.add .usercfg.user.deactivate .usercfg.user.reactivate \ .usercfg.user.remove .usercfg.user.edit .usercfg.user.exit \ -side left -expand 1 -in .usercfg.user.bframe -padx 4 -pady 4 } ######################################## # Group section #################### # Functions common to adding and editing groups. ########## # Verify that a given set of group parameters make sense. Returns 1 if # everything is Ok, 0 is something went wrong. # i - The index of the group data in the name, gid, etc. arrays. # checkuniqname - 1 if the groupname unicity should be checked. # checkuniqgid - 1 if the GID unicity should be checked. proc verifygroupent {i checkuniqname checkuniqgid} { global sysgidbegin global name gid global grouplist # If gen is set to 1, a new GID must be generated. set gen 0 # Check for a blank groupname. if {[string length $name($i)] == 0} { rhs_error_dialog "You have not specified a group name." \ return 0 } # Verify that the GID is valid. set x 0 for { set j 0 } { $j < [string length $gid($i)] } { incr j } { if {0==[string match "\[0123456789\]" \ [string index $gid($i) $j]]} { set x 1 } } if {$x == 0} { if {[string length $gid($i)] == 0} { set x 1 } } if {$x == 0} { if {$gid($i) > 32767} { set x 1 } } if {$x == 1} { set x [rhs_dialog .veruser$i "Warning" \ "\"$gid($i)\" is not a valid group ID." \ warning 0 "Generate valid ID" "Abort"] if {$x == 1} { return 0 } set gen 1 } if {$checkuniqname == 1 || $checkuniqgid == 1} { foreach x $grouplist { # Check groupname unicity. if {[lindex $x 0] == $name($i)} { if {$checkuniqname == 1} { rhs_error_dialog "There is already a group named \"$name($i)\"." return 0 } } # Check GID unicity. if {[lindex $x 2] == $gid($i)} { if {$checkuniqgid == 1 && $gen != 1} { set r [rhs_dialog .veruser$i "Warning" \ "There is already a group with ID $gid($i)." \ warning 1 "Continue" "Generate valid ID" "Abort"] if {$r == 1} { set gen 1 } elseif {$r == 2} { return 0 } } } } } if {$gen == 1} { # Generate a new GID. set gid($i) [getuniquegid $sysgidbegin] rhs_dialog .veruser$i "Information" \ "The group ID has been set to $gid($i)." \ info 0 "Ok" } return 1 } ########## # Add the selected users from .addgmem$i's listbox to .egroupui$i's list # of group members. proc addgmem {i} { global passwdlist set l [.addgmem$i.lframe.list curselection] foreach x $l { .egroupui$i.lframe.list insert end \ [.addgmem$i.lframe.list get $x] } while {[llength $l] > 0} { .addgmem$i.lframe.list delete [lindex $l 0] [lindex $l 0] set l [.addgmem$i.lframe.list curselection] } } ########## # Open a window to allow selection of group members to be added to the # listbox in .egroupui$i. proc addgroupmember {i} { global passwdlist addinggmems name if {$addinggmems($i) == 1} { return } set addinggmems($i) 1 toplevel .addgmem$i wm withdraw .addgmem$i frame .addgmem$i.lframe pack .addgmem$i.lframe -side top listbox .addgmem$i.lframe.list -relief sunken -borderwidth 2 \ -yscrollcommand ".addgmem$i.lframe.scroll set" -font fixed \ -width 10 -height 5 pack .addgmem$i.lframe.list -side left -expand 1 -fill both scrollbar .addgmem$i.lframe.scroll\ -command ".addgmem$i.lframe.list yview" pack .addgmem$i.lframe.scroll -side left -fill y frame .addgmem$i.bframe pack .addgmem$i.bframe -side top -fill x button .addgmem$i.add -text "Add" -command "addgmem $i" button .addgmem$i.cancel -text "Done" \ -command "set addinggmems($i) 0 ; destroy .addgmem$i" pack .addgmem$i.add .addgmem$i.cancel -side left \ -expand 1 -in .addgmem$i.bframe -padx 4 -pady 4 foreach x $passwdlist { set y 0 for {set j 0} {$j < [.egroupui$i.lframe.list size]} {incr j} { set t [.egroupui$i.lframe.list get $j] set s [string trim [string range $t 0 9]] if {$s == [lindex $x 0]} { set y 1 } } if {$y == 0} { .addgmem$i.lframe.list insert end [lindex $x 0] } } if {$name($i) != ""} { wm title .addgmem$i "Add member for $name($i)" } else { wm title .addgmem$i "Add member for " } update idletasks set x [expr - [winfo reqwidth .addgmem$i] + \ [winfo rootx .egroupui$i.lframe] + [winfo width .egroupui$i.lframe]] set y [expr [winfo height .egroupui$i.lframe]/2 - \ [winfo reqheight .addgmem$i]/2 + [winfo rooty .egroupui$i.lframe] \ + [winfo vrooty .egroupui$i.lframe]] wm group .addgmem$i wm transient .addgmem$i .egroupui$i.lframe wm geometry .addgmem$i +$x+$y wm deiconify .addgmem$i } ########## # Remove a group member from .egroupui$i's list. proc rmgroupmember {i} { global passwdlist set l [.egroupui$i.lframe.list curselection] foreach x $l { lappend d [.egroupui$i.lframe.list get $x] } foreach y $d { for {set x 0} {$x < [.egroupui$i.lframe.list size]} {incr x} { if {$y == [.egroupui$i.lframe.list get $x]} { .egroupui$i.lframe.list delete $x $x break } } } } ########## # Generate the basic user interface for adding or editing users. proc editgroupui {i} { global grouplist tmpnum sysgidbegin global name gid global addinggmems cleardd dropdown set addinggmems($i) 0 toplevel .egroupui$i frame .egroupui$i.ents pack .egroupui$i.ents -side top -expand 1 -fill both -padx 4 -pady 4 frame .egroupui$i.name pack .egroupui$i.name -side top -expand 1 -fill x -padx 4 \ -in .egroupui$i.ents label .egroupui$i.name.label -text "Name:" label .egroupui$i.name.bit -bitmap $cleardd entry .egroupui$i.name.entry -width 30 -relief sunken -bd 2 \ -textvariable name($i) set_emacs_entry_bindings .egroupui$i.name.entry pack .egroupui$i.name.label -side left -anchor w pack .egroupui$i.name.entry .egroupui$i.name.bit -side right -anchor e frame .egroupui$i.gid pack .egroupui$i.gid -side top -expand 1 -fill x -padx 4 \ -in .egroupui$i.ents label .egroupui$i.gid.label -text "GID:" label .egroupui$i.gid.bit -bitmap $cleardd entry .egroupui$i.gid.entry -width 30 -relief sunken -bd 2 \ -textvariable gid($i) set_emacs_entry_bindings .egroupui$i.gid.entry pack .egroupui$i.gid.label -side left -anchor w pack .egroupui$i.gid.entry .egroupui$i.gid.bit -side right -anchor e frame .egroupui$i.lframe pack .egroupui$i.lframe -side top -fill both -expand 1 frame .egroupui$i.lframe.leftpad pack .egroupui$i.lframe.leftpad -side left -fill y -padx 2 frame .egroupui$i.lframe.rightpad pack .egroupui$i.lframe.rightpad -side right -fill y -padx 2 label .egroupui$i.lframe.label -text "Members" pack .egroupui$i.lframe.label -side top -fill x -expand 1 frame .egroupui$i.lframe.bframe pack .egroupui$i.lframe.bframe -side left -fill y -padx 4 button .egroupui$i.lframe.bframe.add -text "Add" \ -command "addgroupmember $i" button .egroupui$i.lframe.bframe.remove -text "Remove" \ -command "rmgroupmember $i" pack .egroupui$i.lframe.bframe.add .egroupui$i.lframe.bframe.remove \ -side top -expand 1 -fill x listbox .egroupui$i.lframe.list -relief sunken -borderwidth 2 \ -yscrollcommand ".egroupui$i.lframe.scroll set" -font fixed \ -width 10 -height 5 pack .egroupui$i.lframe.list -side left -expand 1 -fill both scrollbar .egroupui$i.lframe.scroll\ -command ".egroupui$i.lframe.list yview" pack .egroupui$i.lframe.scroll -side left -fill y bind .egroupui$i.name.entry "focus .egroupui$i.gid.entry" bind .egroupui$i.gid.entry "focus .egroupui$i.name.entry" focus .egroupui$i.name.entry } #################### # Add group ########## # Finish an add group operation. If $t = 1, the new group is created. proc endaddgroup {i t} { global grouplist global name gid global addinggmems numadds if {$addinggmems($i) == 1} { destroy .addgmem$i } set name($i) [string trim $name($i)] set gid($i) [string trim $gid($i)] if {$t == 1} { set x [verifygroupent $i 1 1] if {$x == 0} { return } set l {} for {set j 0} {$j < [.egroupui$i.lframe.list size]} {incr j} { lappend l [.egroupui$i.lframe.list get $j] } set l [lsort $l] set g [split "$name($i)::$gid($i)" :] lappend g $l lappend g 0 lappend grouplist $g regengrouplist syncfiles } unset name($i) unset gid($i) destroy .egroupui$i incr numadds -1 } ########## # Begin an add group operation. proc addgroup {} { global grouplist tmpnum sysgidbegin numadds global name gid set i [incr tmpnum] incr numadds editgroupui $i frame .egroupui$i.buttons pack .egroupui$i.buttons -side top -expand 1 -fill x -padx 4 -pady 4 button .egroupui$i.ok -text Add -command "endaddgroup $i 1" button .egroupui$i.cancel -text Cancel -command "endaddgroup $i 0" pack .egroupui$i.ok .egroupui$i.cancel -side left -expand 1 \ -in .egroupui$i.buttons set gid($i) [getuniquegid $sysgidbegin] wm title .egroupui$i "Add group" } #################### # Remove group ########## # Remove the selected groups from the group list. proc removegroup {} { global grouplist global orphaneduser orphanedgroup set delgroups [.usercfg.group.groups curselection] set delgroups [maptogrouplist $delgroups] if {[llength $delgroups] == 0} { return } set t {} set el {} foreach x $delgroups { set g [lindex $grouplist $x] if {[lindex $g 4] != 0} { lappend el $g } else { lappend t $g } } if {[llength $el] > 0} { set str "I cannot remove the following group(s)\nuntil you finish editing them:\n" foreach x $el { set str "$str [lindex $x 0]\n" } rhs_error_dialog $str } foreach x $t { set y [lsearch $grouplist $x] set grouplist [lreplace $grouplist $y $y] regengrouplist syncfiles set r [rhs_dialog .remgrp "Question" \ "Do you want to search the filesystem\nfor files that have been orphaned by\nremoving [lindex $x 0]? (This will be done\nin the background.)" \ question 1 "Yes" "No"] if {$r == 0} { cleanorphans -1 [lindex $x 2] $orphaneduser $orphanedgroup / } } } #################### # Edit group ########## # Finish an edit group operation. If $t = 1, the modifed group is saved. # $g contains the original group list entry of the group. proc endeditgroup {i t g} { global grouplist passwdlist global name gid global addinggmems global orphaneduser orphanedgroup if {$addinggmems($i) == 1} { destroy .addgmem$i } set name($i) [string trim $name($i)] set gid($i) [string trim $gid($i)] if {$t == 1} { # Verify the new group parameters. if {$name($i) != [lindex $g 0]} { set vern 1 } else { set vern 0 } if {$gid($i) != [lindex $g 2]} { set veri 1 } else { set veri 0 } set x [verifygroupent $i $vern $veri] if {$x == 0} { return } # Check for orphaned files, if necessary. if {$gid($i) != [lindex $g 2]} { set r [rhs_dialog .edituserend$i "Question" \ "You have changed [lindex $g 0]'s GID. This\nmeans that all files that were owned by\n[lindex $g 0] should be updated to reflect the\nnew GID. Do you want to update these files?\n(This will be done in the background.)" \ question 0 "Yes" "No" "Abort"] if {$r == 0} { cleanorphans -1 [lindex $g 2] $orphaneduser $gid($i) / } elseif {$r == 2} { return } } # Save the edited group. set l {} for {set j 0} {$j < [.egroupui$i.lframe.list size]} {incr j} { lappend l [.egroupui$i.lframe.list get $j] } set ng [split "$name($i)::$gid($i)" :] lappend ng $l lappend ng 0 grouplistreplace $i [lindex $g 0] $ng # Update GID references in the password list, if necessary. if {$gid($i) != [lindex $g 2]} { foreach u $passwdlist { if {[lindex $u 3] == [lindex $g 2]} { set u [lreplace $u 3 3 $gid($i)] passwdlistreplace [lindex $u 7] [lindex $u 0] $u } } } regenuserlist regengrouplist syncfiles } else { set g [lreplace $g 4 4 0] grouplistreplace $i [lindex $g 0] $g } unset name($i) unset gid($i) unset addinggmems($i) destroy .egroupui$i } ########## # Edit a specific group. $g contains the group's group list entry. proc editgroup {i g} { global grouplist tmpnum sysgidbegin global name gid editgroupui $i set name($i) [lindex $g 0] set gid($i) [lindex $g 2] foreach x [lindex $g 3] { .egroupui$i.lframe.list insert end $x } frame .egroupui$i.buttons pack .egroupui$i.buttons -side top -expand 1 -fill x -padx 4 -pady 4 button .egroupui$i.ok -text Ok -command "endeditgroup $i 1 {$g}" button .egroupui$i.cancel -text Cancel -command "endeditgroup $i 0 {$g}" pack .egroupui$i.ok .egroupui$i.cancel -side left -expand 1 \ -in .egroupui$i.buttons wm title .egroupui$i "Edit group [lindex $g 0]" } ########## # Edit the selected groups. proc begineditgroup {} { global grouplist tmpnum set l [.usercfg.group.groups curselection] set l [maptogrouplist $l] set sl {} set fl {} set el {} foreach x $l { set g [lindex $grouplist $x] if {[lindex $g 4] == 0} { set i [incr tmpnum] set g [lreplace $g 4 4 $i] grouplistreplace 0 [lindex $g 0] $g lappend sl $g } elseif {[winfo exists .egroupui[lindex $g 4]] == 1} { lappend fl $g } else { lappend el $g } } foreach x $fl { catch {wm deiconify .egroupui[lindex $x 4]} catch {raise .egroupui[lindex $x 4]} } foreach x $sl { editgroup [lindex $x 4] $x } } #################### # The primary group frame. ########## # Set up the group frame. proc setupgroupframe {} { frame .usercfg.group frame .usercfg.group.lframe pack .usercfg.group.lframe -side top -fill both -expand 1 -padx 4 -pady 4 label .usercfg.group.lframe.label -font fixed -anchor w \ -text "Group GID Members" pack .usercfg.group.lframe.label -side top -fill x -padx 2 scrollbar .usercfg.group.groupscroll -command ".usercfg.group.groups yview" pack .usercfg.group.groupscroll -side right -fill y -in .usercfg.group.lframe listbox .usercfg.group.groups -relief sunken -borderwidth 2 \ -yscrollcommand ".usercfg.group.groupscroll set" -font fixed \ -width 23 -height 10 -setgrid 1 pack .usercfg.group.groups -side left -expand 1 -fill both -in .usercfg.group.lframe bind .usercfg.group.groups { begineditgroup } frame .usercfg.group.bframe pack .usercfg.group.bframe -side top -fill x button .usercfg.group.add -text "Add" -command addgroup -width 6 button .usercfg.group.remove -text "Remove" -command removegroup -width 7 button .usercfg.group.edit -text "Edit" -command begineditgroup -width 6 pack .usercfg.group.add .usercfg.group.remove .usercfg.group.edit -side left \ -expand 1 -in .usercfg.group.bframe -padx 4 -pady 4 } ######################################## # Edit paths #################### # Finish editing the paths, and save the modified values. proc endeditpaths {} { global pathsup shellslist global shellfilename defhomepath skeldir maildir excludedirs set pathsup 0 destroy .paths set shellfilename [string trim $shellfilename] set defhomepath [string trim $defhomepath] set skeldir [string trim $skeldir] set maildir [string trim $maildir] set excludedirs [string trim $excludedirs] set shellslist [getshells] } #################### # Edit the paths. proc editpaths {} { global pathsup global shellfilename defhomepath skeldir maildir excludedirs if {$pathsup == 1} { return } set pathsup 1 toplevel .paths wm withdraw .paths frame .paths.ents pack .paths.ents -side top -expand 1 -fill both -padx 4 -pady 4 frame .paths.shell pack .paths.shell -side top -expand 1 -fill x -padx 4 \ -in .paths.ents label .paths.shell.label -text "Shells file:" entry .paths.shell.entry -width 30 -relief sunken -bd 2 \ -textvariable shellfilename set_emacs_entry_bindings .paths.shell.entry pack .paths.shell.label -side left -anchor w pack .paths.shell.entry -side right -anchor e frame .paths.home pack .paths.home -side top -expand 1 -fill x -padx 4 \ -in .paths.ents label .paths.home.label -text "Home directory:" entry .paths.home.entry -width 30 -relief sunken -bd 2 \ -textvariable defhomepath set_emacs_entry_bindings .paths.home.entry pack .paths.home.label -side left -anchor w pack .paths.home.entry -side right -anchor e frame .paths.skel pack .paths.skel -side top -expand 1 -fill x -padx 4 \ -in .paths.ents label .paths.skel.label -text "Skeleton directory:" entry .paths.skel.entry -width 30 -relief sunken -bd 2 \ -textvariable skeldir set_emacs_entry_bindings .paths.skel.entry pack .paths.skel.label -side left -anchor w pack .paths.skel.entry -side right -anchor e frame .paths.mail pack .paths.mail -side top -expand 1 -fill x -padx 4 \ -in .paths.ents label .paths.mail.label -text "Mail directory:" entry .paths.mail.entry -width 30 -relief sunken -bd 2 \ -textvariable maildir set_emacs_entry_bindings .paths.mail.entry pack .paths.mail.label -side left -anchor w pack .paths.mail.entry -side right -anchor e frame .paths.exclude pack .paths.exclude -side top -expand 1 -fill x -padx 4 \ -in .paths.ents label .paths.exclude.label -text "Search excludes:" entry .paths.exclude.entry -width 30 -relief sunken -bd 2 \ -textvariable excludedirs set_emacs_entry_bindings .paths.exclude.entry pack .paths.exclude.label -side left -anchor w pack .paths.exclude.entry -side right -anchor e bind .paths.shell.entry "focus .paths.home.entry" bind .paths.home.entry "focus .paths.skel.entry" bind .paths.skel.entry "focus .paths.mail.entry" bind .paths.mail.entry "focus .paths.mail.entry" bind .paths.exclude.entry "focus .paths.shell.entry" focus .paths.shell.entry frame .paths.buttons pack .paths.buttons -side top -fill x -expand 1 -padx 4 -pady 4 button .paths.buttons.ok -text "Ok" -command endeditpaths pack .paths.buttons.ok -side top -expand 1 wm title .paths "Set paths" center_dialog .paths grab .paths } ######################################## # Edit configuration ########## # End editing the config, and save the new values. proc endeditconfig {} { global configup set configup 0 destroy .config regenuserlist regengrouplist } ########## # Edit the program configuration. proc editconfig {} { global configup global dispname disppassword dispuid dispgroup dispgecos disphome global dispshell passstyle gidstyle confirmremove showupg if {$configup == 1} { return } set configup 1 toplevel .config wm withdraw .config frame .config.top pack .config.top -side top -expand 1 -fill both frame .config.fa pack .config.fa -side left -fill both -expand 1 -in .config.top frame .config.fields -relief groove -borderwidth 2 pack .config.fields -side top -fill x -expand 1 -padx 4 -pady 4 \ -in .config.fa frame .config.fields.in pack .config.fields.in -expand 1 -fill both -side top -padx 4 -pady 4 label .config.fields.label -text "Displayed fields" checkbutton .config.fields.name -text "Name" -variable dispname \ -anchor w checkbutton .config.fields.password -text "Password" \ -variable disppassword -anchor w checkbutton .config.fields.uid -text "UID" -variable dispuid \ -anchor w checkbutton .config.fields.group -text "Group" -variable dispgroup \ -anchor w checkbutton .config.fields.gecos -text "Full name" \ -variable dispgecos -anchor w checkbutton .config.fields.home -text "Home" -variable disphome \ -anchor w checkbutton .config.fields.shell -text "Shell" -variable dispshell \ -anchor w pack .config.fields.label .config.fields.name .config.fields.password \ .config.fields.uid .config.fields.group .config.fields.gecos \ .config.fields.home .config.fields.shell \ -side top -fill x -in .config.fields.in frame .config.fb pack .config.fb -side left -fill both -expand 1 -in .config.top frame .config.pwd -relief groove -borderwidth 2 pack .config.pwd -side top -fill x -expand 1 -padx 4 -pady 4 \ -in .config.fb frame .config.pwd.in pack .config.pwd.in -expand 1 -fill both -side top -padx 4 -pady 4 label .config.pwd.label -text "Password display" radiobutton .config.pwd.short -text "Short" -variable passstyle \ -anchor w -value 0 radiobutton .config.pwd.long -text "Long" -variable passstyle \ -anchor w -value 1 pack .config.pwd.label .config.pwd.short .config.pwd.long -side top \ -fill x -in .config.pwd.in frame .config.gid -relief groove -borderwidth 2 pack .config.gid -side top -fill x -expand 1 -padx 4 -pady 4 -in .config.fb frame .config.gid.in pack .config.gid.in -expand 1 -fill both -side top -padx 4 -pady 4 label .config.gid.label -text "GID display" radiobutton .config.gid.number -text "Number" -variable gidstyle \ -anchor w -value 0 radiobutton .config.gid.name -text "Name" -variable gidstyle \ -anchor w -value 1 pack .config.gid.label .config.gid.number .config.gid.name -side top \ -fill x -in .config.gid.in frame .config.confrem -relief groove -borderwidth 2 pack .config.confrem -side top -fill x -expand 1 -padx 4 -pady 4 \ -in .config.fb frame .config.confrem.in pack .config.confrem.in -expand 1 -fill both -side top -padx 4 -pady 4 checkbutton .config.confrem.button -text "Confirm removals" \ -variable confirmremove -anchor w pack .config.confrem.button -side top -fill x -in .config.confrem.in frame .config.bottom pack .config.bottom -side top -expand 1 -fill both frame .config.upg -relief groove -borderwidth 2 pack .config.upg -side top -fill x -expand 1 -padx 4 -pady 4 \ -in .config.bottom frame .config.upg.in pack .config.upg.in -expand 1 -fill both -side top -padx 4 -pady 4 checkbutton .config.upg.button -text "Show user private groups" \ -variable showupg -anchor w pack .config.upg.button -side top -fill x -in .config.upg.in frame .config.buttons pack .config.buttons -side top -fill x -expand 1 -padx 4 -pady 4 button .config.buttons.ok -text "Ok" -command endeditconfig button .config.buttons.save -text "Save" -command "saveconfig; endeditconfig" button .config.buttons.defaults -text "Defaults" -command setdefaults pack .config.buttons.ok .config.buttons.save .config.buttons.defaults \ -side left -expand 1 wm title .config "Configuration" center_dialog .config grab .config } ################# # Displays an "about" box proc about {} { toplevel .about wm withdraw .about wm title .about "Configuration" label .about.title -text "UserCfg - Red Hat User Configuration" label .about.copyright -text "(C) 1995 Red Hat Software" label .about.gpl1 -text "You may copy and use this" label .about.gpl2 -text "in accordance with the GNU" label .about.gpl3 -text "Pulic License (GPL)" button .about.okay -text "Okay" -command "destroy .about" pack configure .about.title .about.copyright .about.gpl1 .about.gpl2 \ .about.gpl3 .about.okay -pady 0 center_dialog .about grab .about } ######################################## # Basic setup and maintenance ########## # Set up the root menu. proc setupmenu {} { global visframe frame .usercfg.mbar -relief raised -bd 2 pack .usercfg.mbar -side top -fill x menubutton .usercfg.mbar.usercfg -text "UserCfg" \ -menu .usercfg.mbar.usercfg.menu menu .usercfg.mbar.usercfg.menu .usercfg.mbar.usercfg.menu add command -label "Set Paths" -command editpaths .usercfg.mbar.usercfg.menu add command -label "Options" -command editconfig .usercfg.mbar.usercfg.menu add separator .usercfg.mbar.usercfg.menu add radiobutton -label "Edit users" \ -variable visframe -value "user" -command { toggleframes } .usercfg.mbar.usercfg.menu add radiobutton -label "Edit groups" \ -variable visframe -value "group" -command { toggleframes } .usercfg.mbar.usercfg.menu add separator .usercfg.mbar.usercfg.menu add command -label "Quit" -command exit pack .usercfg.mbar.usercfg -side left # menubutton .usercfg.mbar.help -text "Help" -menu .mbar.help.menu # pack .usercfg.mbar.help -side right # menu .usercfg.mbar.help.menu # .usercfg.mbar.help.menu add command -label "Help" # .usercfg.mbar.help.menu add command -label "About" -command "about" # tk_menuBar .usercfg.mbar .usercfg.mbar.usercfg .usercfg.mbar.help tk_menuBar .usercfg.mbar .usercfg.mbar.usercfg } ########## # Switch between user and group mode. proc toggleframes {} { global visframe curmode numadds passwdlist grouplist set ok 1 if {$numadds > 0} { set ok 0 } if {$curmode == "user"} { foreach u $passwdlist { if {[lindex $u 7] != 0} { set ok 0 break } } } else { foreach g $grouplist { if {[lindex $g 4] != 0} { set ok 0 break } } } if {$ok == 0} { rhs_error_dialog "Please finish all edit and add operations before changing between user mode and group mode." set visframe $curmode return } set curmode $visframe if {$visframe == "user"} { catch {[pack forget .usercfg.group]} catch {[pack .usercfg.user -side top -expand 1 -fill both]} wm minsize .usercfg 67 5 } else { catch {[pack forget .usercfg.user]} catch {[pack .usercfg.group -side top -expand 1 -fill both]} wm minsize .usercfg 30 10 } } proc usercfg {} { global passwdlist global grouplist global shellslist toplevel .usercfg set passwdlist [getpasswd] set grouplist [getgroup] set shellslist [getshells] setupmenu setupuserframe setupgroupframe regenuserlist regengrouplist toggleframes wm title .usercfg "RHS Linux User/Group Manager" # wm maxsize .usercfg {} {} wm withdraw . } if {[exec id -u] != 0} { rhs_error_dialog "You must be root to run the File System Tool." exit 1 } if {[file isfile "/etc/shadow"]} { rhs_error_dialog "The User and Group Configurator does not support shadow passwords." exit 1 } getuserconfig usercfg