#!/bin/sh # # Copyright (c) 2002-2004 Michael Telahun Makonnen. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # Email: Mike Makonnen # # $FreeBSD: stable/11/usr.sbin/adduser/adduser.sh 232146 2012-02-25 07:58:59Z deischen $ # # err msg # Display $msg on stderr, unless we're being quiet. # proc err { if test -z $quietflag { echo 1>&2 $(THISCMD): ERROR: $ifsjoin(Argv)!1 > !2 ${THISCMD}: ERROR: $* } } # info msg # Display $msg on stdout, unless we're being quiet. # proc info { if test -z $quietflag { echo $(THISCMD): INFO: $ifsjoin(Argv) } } # get_nextuid # Output the value of $_uid if it is available for use. If it # is not, output the value of the next higher uid that is available. # If a uid is not specified, output the first available uid, as indicated # by pw(8). # proc get_nextuid { setglobal _uid = $1 setglobal _nextuid = '' if test -z $_uid { setglobal _nextuid = $[$(PWCMD) usernext | cut -f1 -d:] } else { while : { $(PWCMD) usershow $_uid > /dev/null !2 > !1 if test ! "$Status" -eq 0 { setglobal _nextuid = $_uid break } setglobal _uid = $shExpr('$_uid + 1') } } echo $_nextuid } # show_usage # Display usage information for this utility. # proc show_usage { echo "usage: $(THISCMD) [options]" echo " options may include:" echo " -C save to the configuration file only" echo " -D do not attempt to create the home directory" echo " -E disable this account after creation" echo " -G additional groups to add accounts to" echo " -L login class of the user" echo " -M file permission for home directory" echo " -N do not read configuration file" echo " -S a nonexistent shell is not an error" echo " -d home directory" echo " -f file from which input will be received" echo " -g default login group" echo " -h display this usage message" echo " -k path to skeleton home directory" echo " -m user welcome message file" echo " -q absolute minimal user feedback" echo " -s shell" echo " -u uid to start at" echo " -w password type: no, none, yes or random" } # valid_shells # Outputs a list of valid shells from /etc/shells. Only the # basename of the shell is output. # proc valid_shells { setglobal _prefix = '' cat $(ETCSHELLS) | while read _path _junk { match $_path { with \#*|'' with * echo -n "$(_prefix)$[basename $_path]" setglobal _prefix = '' '' } } # /usr/sbin/nologin is a special case test -x $(NOLOGIN_PATH) && echo -n " $(NOLOGIN)" } # fullpath_from_shell shell # Given $shell, which is either the full path to a shell or # the basename component of a valid shell, get the # full path to the shell from the /etc/shells file. # proc fullpath_from_shell { setglobal _shell = $1 test -z $_shell && return 1 # /usr/sbin/nologin is a special case; it needs to be handled # before the cat | while loop, since a 'return' from within # a subshell will not terminate the function's execution, and # the path to the nologin shell might be printed out twice. # if test $_shell = $(NOLOGIN) -o \ $_shell = $(NOLOGIN_PATH) { echo $(NOLOGIN_PATH) return 0; } cat $(ETCSHELLS) | while read _path _junk { match $_path { with \#*|'' with * if test $_path = $_shell -o \ $[basename $_path] = $_shell { echo $_path return 0 } } } return 1 } # shell_exists shell # If the given shell is listed in ${ETCSHELLS} or it is # the nologin shell this function will return 0. # Otherwise, it will return 1. If shell is valid but # the path is invalid or it is not executable it # will emit an informational message saying so. # proc shell_exists { setglobal _sh = $1 setglobal _shellchk = ""$(GREPCMD) '^$_sh$' $(ETCSHELLS) > /dev/null 2>&1"" if ! eval $_shellchk { # The nologin shell is not listed in /etc/shells. if test $_sh != $(NOLOGIN_PATH) { err "Invalid shell ($_sh) for user $username." return 1 } } ! test -x $_sh && info "The shell ($_sh) does not exist or is not executable." return 0 } # save_config # Save some variables to a configuration file. # Note: not all script variables are saved, only those that # it makes sense to save. # proc save_config { echo "# Configuration file for adduser(8)." > $(ADDUSERCONF) echo "# NOTE: only *some* variables are saved." >> $(ADDUSERCONF) echo "# Last Modified on $[$(DATECMD)]." >> $(ADDUSERCONF) echo '' >> $(ADDUSERCONF) echo "defaultHomePerm=$uhomeperm" >> $(ADDUSERCONF) echo "defaultLgroup=$ulogingroup" >> $(ADDUSERCONF) echo "defaultclass=$uclass" >> $(ADDUSERCONF) echo "defaultgroups=$ugroups" >> $(ADDUSERCONF) echo "passwdtype=$passwdtype" >> $(ADDUSERCONF) echo "homeprefix=$homeprefix" >> $(ADDUSERCONF) echo "defaultshell=$ushell" >> $(ADDUSERCONF) echo "udotdir=$udotdir" >> $(ADDUSERCONF) echo "msgfile=$msgfile" >> $(ADDUSERCONF) echo "disableflag=$disableflag" >> $(ADDUSERCONF) echo "uidstart=$uidstart" >> $(ADDUSERCONF) } # add_user # Add a user to the user database. If the user chose to send a welcome # message or lock the account, do so. # proc add_user { # Is this a configuration run? If so, don't modify user database. # if test -n $configflag { save_config return } setglobal _uid = '' setglobal _name = '' setglobal _comment = '' setglobal _gecos = '' setglobal _home = '' setglobal _group = '' setglobal _grouplist = '' setglobal _shell = '' setglobal _class = '' setglobal _dotdir = '' setglobal _expire = '' setglobal _pwexpire = '' setglobal _passwd = '' setglobal _upasswd = '' setglobal _passwdmethod = '' setglobal _name = ""-n '$username'"" test -n $uuid && setglobal _uid = ''-u "$uuid"'' test -n $ulogingroup && setglobal _group = ''-g "$ulogingroup"'' test -n $ugroups && setglobal _grouplist = ''-G "$ugroups"'' test -n $ushell && setglobal _shell = ''-s "$ushell"'' test -n $uclass && setglobal _class = ''-L "$uclass"'' test -n $ugecos && setglobal _comment = ''-c "$ugecos"'' test -n $udotdir && setglobal _dotdir = ''-k "$udotdir"'' test -n $uexpire && setglobal _expire = ''-e "$uexpire"'' test -n $upwexpire && setglobal _pwexpire = ''-p "$upwexpire"'' if test -z $Dflag -a -n $uhome { # The /nonexistent home directory is special. It # means the user has no home directory. if test $uhome = $NOHOME { setglobal _home = ''-d "$uhome"'' } else { # Use home directory permissions if specified if test -n $uhomeperm { setglobal _home = ''-m -d "$uhome" -M "$uhomeperm"'' } else { setglobal _home = ''-m -d "$uhome"'' } } } elif test -n $Dflag -a -n $uhome { setglobal _home = ''-d "$uhome"'' } match $passwdtype { with no setglobal _passwdmethod = '"-w no'" setglobal _passwd = '"-h -'" with yes # Note on processing the password: The outer double quotes # make literal everything except ` and \ and $. # The outer single quotes make literal ` and $. # We can ensure the \ isn't treated specially by specifying # the -r switch to the read command used to obtain the input. # setglobal _passwdmethod = '"-w yes'" setglobal _passwd = '"-h 0'" setglobal _upasswd = ''echo "$upass" |'' with none setglobal _passwdmethod = '"-w none'" with random setglobal _passwdmethod = '"-w random'" } setglobal _pwcmd = ""$_upasswd $(PWCMD) useradd $_uid $_name $_group $_grouplist $_comment"" setglobal _pwcmd = ""$_pwcmd $_shell $_class $_home $_dotdir $_passwdmethod $_passwd"" setglobal _pwcmd = ""$_pwcmd $_expire $_pwexpire"" if ! setglobal _output = $[eval $_pwcmd] { err "There was an error adding user ($username)." return 1 } else { info "Successfully added ($username) to the user database." if test "random" = $passwdtype { setglobal randompass = $_output info "Password for ($username) is: $randompass" } } if test -n $disableflag { if $(PWCMD) lock $username { info "Account ($username) is locked." } else { info "Account ($username) could NOT be locked." } } setglobal _line = '' setglobal _owner = '' setglobal _perms = '' if test -n $msgflag { test -r $msgfile && do { # We're evaluating the contents of an external file. # Let's not open ourselves up for attack. _perms will # be empty if it's writeable only by the owner. _owner # will *NOT* be empty if the file is owned by root. # setglobal _dir = $[dirname $msgfile] setglobal _file = $[basename $msgfile] setglobal _perms = $[/usr/bin/find $_dir -name $_file -perm +07022 -prune] setglobal _owner = $[/usr/bin/find $_dir -name $_file -user 0 -prune] if test -z $_owner -o -n $_perms { err "The message file ($msgfile) may be writeable only by root." return 1 } cat $msgfile | while read _line { eval echo $_line } | $(MAILCMD) -s"Welcome" $(username) info "Sent welcome message to ($username)." } } } # get_user # Reads username of the account from standard input or from a global # variable containing an account line from a file. The username is # required. If this is an interactive session it will prompt in # a loop until a username is entered. If it is batch processing from # a file it will output an error message and return to the caller. # proc get_user { setglobal _input = '' # No need to take down user names if this is a configuration saving run. test -n $configflag && return while : { if test -z $fflag { echo -n "Username: " read _input } else { setglobal _input = $[echo $fileline | cut -f1 -d:] } # There *must* be a username, and it must not exist. If # this is an interactive session give the user an # opportunity to retry. # if test -z $_input { err "You must enter a username!" test -z $fflag && continue } $(PWCMD) usershow $_input > /dev/null !2 > !1 if test "$Status" -eq 0 { err "User exists!" test -z $fflag && continue } break } setglobal username = $_input } # get_gecos # Reads extra information about the user. Can be used both in interactive # and batch (from file) mode. # proc get_gecos { setglobal _input = '' # No need to take down additional user information for a configuration run. test -n $configflag && return if test -z $fflag { echo -n "Full name: " read _input } else { setglobal _input = $[echo $fileline | cut -f7 -d:] } setglobal ugecos = $_input } # get_shell # Get the account's shell. Works in interactive and batch mode. It # accepts either the base name of the shell or the full path. # If an invalid shell is entered it will simply use the default shell. # proc get_shell { setglobal _input = '' setglobal _fullpath = '' setglobal ushell = $defaultshell # Make sure the current value of the shell is a valid one if test -z $Sflag { if ! shell_exists $ushell { info "Using default shell $(defaultshell)." setglobal ushell = $defaultshell } } if test -z $fflag { echo -n "Shell ($shells) [$[basename $ushell]]: " read _input } else { setglobal _input = $[echo $fileline | cut -f9 -d:] } if test -n $_input { if test -n $Sflag { setglobal ushell = $_input } else { setglobal _fullpath = $[fullpath_from_shell $_input] if test -n $_fullpath { setglobal ushell = $_fullpath } else { err "Invalid shell ($_input) for user $username." info "Using default shell $(defaultshell)." setglobal ushell = $defaultshell } } } } # get_homedir # Reads the account's home directory. Used both with interactive input # and batch input. # proc get_homedir { setglobal _input = '' if test -z $fflag { echo -n "Home directory [$(homeprefix)/$(username)]: " read _input } else { setglobal _input = $[echo $fileline | cut -f8 -d:] } if test -n $_input { setglobal uhome = $_input # if this is a configuration run, then user input is the home # directory prefix. Otherwise it is understood to # be $prefix/$user # test -z $configflag && setglobal homeprefix = $[dirname $uhome] || setglobal homeprefix = $uhome } else { setglobal uhome = ""$(homeprefix)/$(username)"" } } # get_homeperm # Reads the account's home directory permissions. # proc get_homeperm { setglobal uhomeperm = $defaultHomePerm setglobal _input = '' setglobal _prompt = '' if test -n $uhomeperm { setglobal _prompt = ""Home directory permissions [$(uhomeperm)]: "" } else { setglobal _prompt = '"Home directory permissions (Leave empty for default): '" } if test -z $fflag { echo -n $_prompt read _input } if test -n $_input { setglobal uhomeperm = $_input } } # get_uid # Reads a numeric userid in an interactive or batch session. Automatically # allocates one if it is not specified. # proc get_uid { setglobal uuid = $(uidstart) setglobal _input = '' setglobal _prompt = '' if test -n $uuid { setglobal uuid = $[get_nextuid $uuid] setglobal _prompt = ""Uid [$uuid]: "" } else { setglobal _prompt = '"Uid (Leave empty for default): '" } if test -z $fflag { echo -n $_prompt read _input } else { setglobal _input = $[echo $fileline | cut -f2 -d:] } test -n $_input && setglobal uuid = $_input setglobal uuid = $[get_nextuid $uuid] setglobal uidstart = $uuid } # get_class # Reads login class of account. Can be used in interactive or batch mode. # proc get_class { setglobal uclass = $defaultclass setglobal _input = '' setglobal _class = $(uclass:-"default") if test -z $fflag { echo -n "Login class [$_class]: " read _input } else { setglobal _input = $[echo $fileline | cut -f4 -d:] } test -n $_input && setglobal uclass = $_input } # get_logingroup # Reads user's login group. Can be used in both interactive and batch # modes. The specified value can be a group name or its numeric id. # This routine leaves the field blank if nothing is provided and # a default login group has not been set. The pw(8) command # will then provide a login group with the same name as the username. # proc get_logingroup { setglobal ulogingroup = $defaultLgroup setglobal _input = '' if test -z $fflag { echo -n "Login group [$(ulogingroup:-$username)]: " read _input } else { setglobal _input = $[echo $fileline | cut -f3 -d:] } # Pw(8) will use the username as login group if it's left empty test -n $_input && setglobal ulogingroup = $_input } # get_groups # Read additional groups for the user. It can be used in both interactive # and batch modes. # proc get_groups { setglobal ugroups = $defaultgroups setglobal _input = '' setglobal _group = $(ulogingroup:-"${username}") if test -z $configflag { test -z $fflag && echo -n "Login group is $_group. Invite $username" test -z $fflag && echo -n " into other groups? [$ugroups]: " } else { test -z $fflag && echo -n "Enter additional groups [$ugroups]: " } read _input test -n $_input && setglobal ugroups = $_input } # get_expire_dates # Read expiry information for the account and also for the password. This # routine is used only from batch processing mode. # proc get_expire_dates { setglobal upwexpire = $[echo $fileline | cut -f5 -d:] setglobal uexpire = $[echo $fileline | cut -f6 -d:] } # get_password # Read the password in batch processing mode. The password field matters # only when the password type is "yes" or "random". If the field is empty and the # password type is "yes", then it assumes the account has an empty passsword # and changes the password type accordingly. If the password type is "random" # and the password field is NOT empty, then it assumes the account will NOT # have a random password and set passwdtype to "yes." # proc get_password { # We may temporarily change a password type. Make sure it's changed # back to whatever it was before we process the next account. # test -n $savedpwtype && do { setglobal passwdtype = $savedpwtype setglobal savedpwtype = '' } # There may be a ':' in the password setglobal upass = $(fileline#*:*:*:*:*:*:*:*:*:) if test -z $upass { match $passwdtype { with yes # if it's empty, assume an empty password setglobal passwdtype = 'none' setglobal savedpwtype = 'yes' } } else { match $passwdtype { with random setglobal passwdtype = 'yes' setglobal savedpwtype = 'random' } } } # input_from_file # Reads a line of account information from standard input and # adds it to the user database. # proc input_from_file { setglobal _field = '' while read -r fileline { match $fileline { with \#*|'' with * get_user || continue get_gecos get_uid get_logingroup get_class get_shell get_homedir get_homeperm get_password get_expire_dates setglobal ugroups = $defaultgroups add_user } } } # input_interactive # Prompts for user information interactively, and commits to # the user database. # proc input_interactive { setglobal _disable = '' setglobal _pass = '' setglobal _passconfirm = '' setglobal _random = '"no'" setglobal _emptypass = '"no'" setglobal _usepass = '"yes'" setglobal _logingroup_ok = '"no'" setglobal _groups_ok = '"no'" match $passwdtype { with none setglobal _emptypass = '"yes'" setglobal _usepass = '"yes'" with no setglobal _usepass = '"no'" with random setglobal _random = '"yes'" } get_user get_gecos get_uid # The case where group = user is handled elsewhere, so # validate any other groups the user is invited to. while not test $_logingroup_ok = yes { get_logingroup setglobal _logingroup_ok = 'yes' if test -n $ulogingroup -a $username != $ulogingroup { if ! $(PWCMD) show group $ulogingroup > /dev/null !2 > !1 { echo "Group $ulogingroup does not exist!" setglobal _logingroup_ok = 'no' } } } while not test $_groups_ok = yes { get_groups setglobal _groups_ok = 'yes' for i in [$ugroups] { if test $username != $i { if ! $(PWCMD) show group $i > /dev/null !2 > !1 { echo "Group $i does not exist!" setglobal _groups_ok = 'no' } } } } get_class get_shell get_homedir get_homeperm while : { echo -n "Use password-based authentication? [$_usepass]: " read _input test -z $_input && setglobal _input = $_usepass match $_input { with [Nn][Oo]|[Nn] setglobal passwdtype = '"no'" with [Yy][Ee][Ss]|[Yy][Ee]|[Yy] while : { echo -n "Use an empty password? (yes/no) [$_emptypass]: " read _input test -n $_input && setglobal _emptypass = $_input match $_emptypass { with [Nn][Oo]|[Nn] echo -n "Use a random password? (yes/no) [$_random]: " read _input test -n $_input && setglobal _random = $_input match $_random { with [Yy][Ee][Ss]|[Yy][Ee]|[Yy] setglobal passwdtype = '"random'" break } setglobal passwdtype = '"yes'" test -n $configflag && break trap 'stty echo; exit' 0 1 2 3 15 stty -echo echo -n "Enter password: " read -r upass echo'' echo -n "Enter password again: " read -r _passconfirm echo '' stty echo # if user entered a blank password # explicitly ask again. test -z $upass -a -z $_passconfirm \ && continue with [Yy][Ee][Ss]|[Yy][Ee]|[Yy] setglobal passwdtype = '"none'" break; with * # invalid answer; repeat the loop continue } if test $upass != $_passconfirm { echo "Passwords did not match!" continue } break } with * # invalid answer; repeat loop continue } break; } setglobal _disable = $(disableflag:-"no") while : { echo -n "Lock out the account after creation? [$_disable]: " read _input test -z $_input && setglobal _input = $_disable match $_input { with [Nn][Oo]|[Nn] setglobal disableflag = '' with [Yy][Ee][Ss]|[Yy][Ee]|[Yy] setglobal disableflag = 'yes' with * # invalid answer; repeat loop continue } break } # Display the information we have so far and prompt to # commit it. # setglobal _disable = $(disableflag:-"no") test -z $configflag && printf "%-10s : %s\n" Username $username match $passwdtype { with yes setglobal _pass = ''*****'' with no setglobal _pass = '''' with none setglobal _pass = '''' with random setglobal _pass = '''' } test -z $configflag && printf "%-10s : %s\n" "Password" $_pass test -n $configflag && printf "%-10s : %s\n" "Pass Type" $passwdtype test -z $configflag && printf "%-10s : %s\n" "Full Name" $ugecos test -z $configflag && printf "%-10s : %s\n" "Uid" $uuid printf "%-10s : %s\n" "Class" $uclass printf "%-10s : %s %s\n" "Groups" $(ulogingroup:-$username) $ugroups printf "%-10s : %s\n" "Home" $uhome printf "%-10s : %s\n" "Home Mode" $uhomeperm printf "%-10s : %s\n" "Shell" $ushell printf "%-10s : %s\n" "Locked" $_disable while : { echo -n "OK? (yes/no): " read _input match $_input { with [Nn][Oo]|[Nn] return 1 with [Yy][Ee][Ss]|[Yy][Ee]|[Yy] add_user with * continue } break } return 0 } #### END SUBROUTINE DEFINITION #### setglobal THISCMD = $[/usr/bin/basename $0] setglobal DEFAULTSHELL = '/bin/sh' setglobal ADDUSERCONF = $(ADDUSERCONF:-/etc/adduser.conf) setglobal PWCMD = $(PWCMD:-/usr/sbin/pw) setglobal MAILCMD = $(MAILCMD:-mail) setglobal ETCSHELLS = $(ETCSHELLS:-/etc/shells) setglobal NOHOME = '"/nonexistent'" setglobal NOLOGIN = '"nologin'" setglobal NOLOGIN_PATH = '"/usr/sbin/nologin'" setglobal GREPCMD = '"/usr/bin/grep'" setglobal DATECMD = '"/bin/date'" # Set default values # setglobal username = '' setglobal uuid = '' setglobal uidstart = '' setglobal ugecos = '' setglobal ulogingroup = '' setglobal uclass = '' setglobal uhome = '' setglobal uhomeperm = '' setglobal upass = '' setglobal ushell = '' setglobal udotdir = '/usr/share/skel' setglobal ugroups = '' setglobal uexpire = '' setglobal upwexpire = '' setglobal shells = $[valid_shells] setglobal passwdtype = '"yes'" setglobal msgfile = '/etc/adduser.msg' setglobal msgflag = '' setglobal quietflag = '' setglobal configflag = '' setglobal fflag = '' setglobal infile = '' setglobal disableflag = '' setglobal Dflag = '' setglobal Sflag = '' setglobal readconfig = '"yes'" setglobal homeprefix = '"/home'" setglobal randompass = '' setglobal fileline = '' setglobal savedpwtype = '' setglobal defaultclass = '' setglobal defaultLgroup = '' setglobal defaultgroups = '' setglobal defaultshell = $(DEFAULTSHELL) setglobal defaultHomePerm = '' # Make sure the user running this program is root. This isn't a security # measure as much as it is a useful method of reminding the user to # 'su -' before he/she wastes time entering data that won't be saved. # setglobal procowner = $(procowner:-`/usr/bin/id -u`) if test $procowner != "0" { err 'you must be the super-user (uid 0) to use this utility.' exit 1 } # Override from our conf file # Quickly go through the commandline line to see if we should read # from our configuration file. The actual parsing of the commandline # arguments happens after we read in our configuration file (commandline # should override configuration file). # for _i in [$ifsjoin(Argv)] { if test $_i = "-N" { setglobal readconfig = '' break; } } if test -n $readconfig { # On a long-lived system, the first time this script is run it # will barf upon reading the configuration file for its perl predecessor. if shell { source ${ADDUSERCONF} > /dev/null 2>&1 } { test -r $(ADDUSERCONF) && source ${ADDUSERCONF} > /dev/null 2>&1 } }for _switch in @Argv { match $_switch { with -L setglobal defaultclass = $2 shift; shift with -C setglobal configflag = 'yes' shift with -D setglobal Dflag = 'yes' shift with -E setglobal disableflag = 'yes' shift with -k setglobal udotdir = $2 shift; shift with -f test $2 != "-" && setglobal infile = $2 setglobal fflag = 'yes' shift; shift with -g setglobal defaultLgroup = $2 shift; shift with -G setglobal defaultgroups = $2 shift; shift with -h show_usage exit 0 with -d setglobal homeprefix = $2 shift; shift with -m match $2 { with [Nn][Oo] setglobal msgflag = '' with * setglobal msgflag = 'yes' setglobal msgfile = $2 } shift; shift with -M setglobal defaultHomePerm = $2 shift; shift with -N setglobal readconfig = '' shift with -w match $2 { with no|none|random|yes setglobal passwdtype = $2 with * show_usage exit 1 } shift; shift with -q setglobal quietflag = 'yes' shift with -s setglobal defaultshell = $[fullpath_from_shell $2] shift; shift with -S setglobal Sflag = 'yes' shift with -u setglobal uidstart = $2 shift; shift } } # If the -f switch was used, get input from a file. Otherwise, # this is an interactive session. # if test -n $fflag { if test -z $infile { input_from_file } elif test -n $infile { if test -r $infile { input_from_file < $infile } else { err "File ($infile) is unreadable or does not exist." } } } else { input_interactive while : { if test -z $configflag { echo -n "Add another user? (yes/no): " } else { echo -n "Re-edit the default configuration? (yes/no): " } read _input match $_input { with [Yy][Ee][Ss]|[Yy][Ee]|[Yy] setglobal uidstart = $[get_nextuid $uidstart] input_interactive continue with [Nn][Oo]|[Nn] echo "Goodbye!" with * continue } break } }