4ad966b2d44a5c0066c861604aa7ec7499acde42
[dragonfly.git] / usr.sbin / adduser / adduser.sh
1 #!/bin/sh
2 #
3 # Copyright (c) 2002-2004 Michael Telahun Makonnen. All rights reserved.
4 #
5 # Redistribution and use in source and binary forms, with or without
6 # modification, are permitted provided that the following conditions
7 # are met:
8 # 1. Redistributions of source code must retain the above copyright
9 #    notice, this list of conditions and the following disclaimer.
10 # 2. Redistributions in binary form must reproduce the above copyright
11 #    notice, this list of conditions and the following disclaimer in the
12 #    documentation and/or other materials provided with the distribution.
13 #
14 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 #
25 #       Email: Mike Makonnen <mtm@FreeBSD.Org>
26 #
27 # $FreeBSD: src/usr.sbin/adduser/adduser.sh,v 1.31 2007/10/20 00:45:31 mtm Exp $
28 # $DragonFly: src/usr.sbin/adduser/adduser.sh,v 1.3 2008/05/18 03:02:53 pavalos Exp $
29 #
30
31 # err msg
32 #       Display $msg on stderr, unless we're being quiet.
33
34 err() {
35         if [ -z "$quietflag" ]; then
36                 echo 1>&2 ${THISCMD}: ERROR: $*
37         fi
38 }
39
40 # info msg
41 #       Display $msg on stdout, unless we're being quiet.
42
43 info() {
44         if [ -z "$quietflag" ]; then
45                 echo ${THISCMD}: INFO: $*
46         fi
47 }
48
49 # get_nextuid
50 #       Output the value of $_uid if it is available for use. If it
51 #       is not, output the value of the next higher uid that is available.
52 #       If a uid is not specified, output the first available uid, as indicated
53 #       by pw(8).
54
55 get_nextuid () {
56         _uid=$1
57         _nextuid=
58
59         if [ -z "$_uid" ]; then
60                 _nextuid="`${PWCMD} usernext | cut -f1 -d:`"
61         else
62                 while : ; do
63                         ${PWCMD} usershow $_uid > /dev/null 2>&1
64                         if [ ! "$?" -eq 0 ]; then
65                                 _nextuid=$_uid
66                                 break
67                         fi
68                         _uid=$(($_uid + 1))
69                 done
70         fi
71         echo $_nextuid
72 }
73
74 # show_usage
75 #       Display usage information for this utility.
76 #
77 show_usage() {
78         echo "usage: ${THISCMD} [options]"
79         echo "  options may include:"
80         echo "  -C              save to the configuration file only"
81         echo "  -D              do not attempt to create the home directory"
82         echo "  -E              disable this account after creation"
83         echo "  -G              additional groups to add accounts to"
84         echo "  -L              login class of the user"
85         echo "  -N              do not read configuration file"
86         echo "  -S              a nonexistent shell is not an error"
87         echo "  -d              home directory"
88         echo "  -f              file from which input will be received"
89         echo "  -g              default login group"
90         echo "  -h              display this usage message"
91         echo "  -k              path to skeleton home directory"
92         echo "  -m              user welcome message file"
93         echo "  -q              absolute minimal user feedback"
94         echo "  -s              shell"
95         echo "  -u              uid to start at"
96         echo "  -w              password type: no, none, yes or random"
97 }
98
99 # valid_shells
100 #       Outputs a list of valid shells from /etc/shells. Only the
101 #       basename of the shell is output.
102 #
103 valid_shells() {
104         _prefix=
105         cat ${ETCSHELLS} |
106         while read _path _junk ; do
107                 case $_path in
108                 \#*|'')
109                         ;;
110                 *)
111                         echo -n "${_prefix}`basename $_path`"
112                         _prefix=' '
113                         ;;
114                 esac
115         done
116
117         # /sbin/nologin is a special case
118         [ -x "${NOLOGIN_PATH}" ] && echo -n " ${NOLOGIN}"
119 }
120
121 # fullpath_from_shell shell
122 #       Given $shell, which is either the full path to a shell or
123 #       the basename component of a valid shell, get the
124 #       full path to the shell from the /etc/shells file.
125 #
126 fullpath_from_shell() {
127         _shell=$1
128         [ -z "$_shell" ] && return 1
129
130         # /sbin/nologin is a special case; it needs to be handled
131         # before the cat | while loop, since a 'return' from within
132         # a subshell will not terminate the function's execution, and
133         # the path to the nologin shell might be printed out twice.
134         #
135         if [ "$_shell" = "${NOLOGIN}" -o \
136             "$_shell" = "${NOLOGIN_PATH}" ]; then
137                 echo ${NOLOGIN_PATH}
138                 return 0;
139         fi
140
141         cat ${ETCSHELLS} |
142         while read _path _junk ; do
143                 case "$_path" in
144                 \#*|'')
145                         ;;
146                 *)
147                         if [ "$_path" = "$_shell" -o \
148                             "`basename $_path`" = "$_shell" ]; then
149                                 echo $_path
150                                 return 0
151                         fi
152                         ;;
153                 esac
154         done
155
156         return 1
157 }
158
159 # shell_exists shell
160 #       If the given shell is listed in ${ETCSHELLS} or it is
161 #       the nologin shell this function will return 0.
162 #       Otherwise, it will return 1. If shell is valid but
163 #       the path is invalid or it is not executable it
164 #       will emit an informational message saying so.
165 #
166 shell_exists()
167 {
168         _sh="$1"
169         _shellchk="${GREPCMD} '^$_sh$' ${ETCSHELLS} > /dev/null 2>&1"
170
171         if ! eval $_shellchk; then
172                 # The nologin shell is not listed in /etc/shells.
173                 if [ "$_sh" != "${NOLOGIN_PATH}" ]; then
174                         err "Invalid shell ($_sh) for user $username."
175                         return 1
176                 fi
177         fi
178         ! [ -x "$_sh" ] &&
179             info "The shell ($_sh) does not exist or is not executable."
180
181         return 0
182 }
183
184 # save_config
185 #       Save some variables to a configuration file.
186 #       Note: not all script variables are saved, only those that
187 #             it makes sense to save.
188 #
189 save_config() {
190         echo "# Configuration file for adduser(8)."     >  ${ADDUSERCONF}
191         echo "# NOTE: only *some* variables are saved." >> ${ADDUSERCONF}
192         echo "# Last Modified on `${DATECMD}`."         >> ${ADDUSERCONF}
193         echo ''                         >> ${ADDUSERCONF}
194         echo "defaultLgroup=$ulogingroup" >> ${ADDUSERCONF}
195         echo "defaultclass=$uclass"     >> ${ADDUSERCONF}
196         echo "defaultgroups=$ugroups"   >> ${ADDUSERCONF}
197         echo "passwdtype=$passwdtype"   >> ${ADDUSERCONF}
198         echo "homeprefix=$homeprefix"   >> ${ADDUSERCONF}
199         echo "defaultshell=$ushell"     >> ${ADDUSERCONF}
200         echo "udotdir=$udotdir"         >> ${ADDUSERCONF}
201         echo "msgfile=$msgfile"         >> ${ADDUSERCONF}
202         echo "disableflag=$disableflag" >> ${ADDUSERCONF}
203         echo "uidstart=$uidstart"       >> ${ADDUSERCONF}
204 }
205
206 # add_user
207 #       Add a user to the user database. If the user chose to send a welcome
208 #       message or lock the account, do so.
209 #
210 add_user() {
211
212         # Is this a configuration run? If so, don't modify user database.
213         #
214         if [ -n "$configflag" ]; then
215                 save_config
216                 return
217         fi
218
219         _uid=
220         _name=
221         _comment=
222         _gecos=
223         _home=
224         _group=
225         _grouplist=
226         _shell=
227         _class=
228         _dotdir=
229         _expire=
230         _pwexpire=
231         _passwd=
232         _upasswd=
233         _passwdmethod=
234
235         _name="-n '$username'"
236         [ -n "$uuid" ] && _uid='-u "$uuid"'
237         [ -n "$ulogingroup" ] && _group='-g "$ulogingroup"'
238         [ -n "$ugroups" ] && _grouplist='-G "$ugroups"'
239         [ -n "$ushell" ] && _shell='-s "$ushell"'
240         [ -n "$uclass" ] && _class='-L "$uclass"'
241         [ -n "$ugecos" ] && _comment='-c "$ugecos"'
242         [ -n "$udotdir" ] && _dotdir='-k "$udotdir"'
243         [ -n "$uexpire" ] && _expire='-e "$uexpire"'
244         [ -n "$upwexpire" ] && _pwexpire='-p "$upwexpire"'
245         if [ -z "$Dflag" -a -n "$uhome" ]; then
246                 # The /nonexistent home directory is special. It
247                 # means the user has no home directory.
248                 if [ "$uhome" = "$NOHOME" ]; then
249                         _home='-d "$uhome"'
250                 else
251                         _home='-m -d "$uhome"'
252                 fi
253         elif [ -n "$Dflag" -a -n "$uhome" ]; then
254                 _home='-d "$uhome"'
255         fi
256         case $passwdtype in
257         no)
258                 _passwdmethod="-w no"
259                 _passwd="-h -"
260                 ;;
261         yes)
262                 # Note on processing the password: The outer double quotes
263                 # make literal everything except ` and \ and $.
264                 # The outer single quotes make literal ` and $.
265                 # We can ensure the \ isn't treated specially by specifying
266                 # the -r switch to the read command used to obtain the input.
267                 #
268                 _passwdmethod="-w yes"
269                 _passwd="-h 0"
270                 _upasswd='echo "$upass" |'
271                 ;;
272         none)
273                 _passwdmethod="-w none"
274                 ;;
275         random)
276                 _passwdmethod="-w random"
277                 ;;
278         esac
279
280         _pwcmd="$_upasswd ${PWCMD} useradd $_uid $_name $_group $_grouplist $_comment"
281         _pwcmd="$_pwcmd $_shell $_class $_home $_dotdir $_passwdmethod $_passwd"
282         _pwcmd="$_pwcmd $_expire $_pwexpire"
283
284         if ! _output=`eval $_pwcmd` ; then
285                 err "There was an error adding user ($username)."
286                 return 1
287         else
288                 info "Successfully added ($username) to the user database."
289                 if [ "random" = "$passwdtype" ]; then
290                         randompass="$_output"
291                         info "Password for ($username) is: $randompass"
292                 fi
293         fi
294
295         if [ -n "$disableflag" ]; then
296                 if ${PWCMD} lock $username ; then
297                         info "Account ($username) is locked."
298                 else
299                         info "Account ($username) could NOT be locked."
300                 fi
301         fi
302
303         _line=
304         _owner=
305         _perms=
306         if [ -n "$msgflag" ]; then
307                 [ -r "$msgfile" ] && {
308                         # We're evaluating the contents of an external file.
309                         # Let's not open ourselves up for attack. _perms will
310                         # be empty if it's writeable only by the owner. _owner
311                         # will *NOT* be empty if the file is owned by root.
312                         #
313                         _dir="`dirname $msgfile`"
314                         _file="`basename $msgfile`"
315                         _perms=`/usr/bin/find $_dir -name $_file -perm +07022 -prune`
316                         _owner=`/usr/bin/find $_dir -name $_file -user 0 -prune`
317                         if [ -z "$_owner" -o -n "$_perms" ]; then
318                                 err "The message file ($msgfile) may be writeable only by root."
319                                 return 1
320                         fi
321                         cat "$msgfile" |
322                         while read _line ; do
323                                 eval echo "$_line"
324                         done | ${MAILCMD} -s"Welcome" ${username}
325                         info "Sent welcome message to ($username)."
326                 }
327         fi
328 }
329
330 # get_user
331 #       Reads username of the account from standard input or from a global
332 #       variable containing an account line from a file. The username is
333 #       required. If this is an interactive session it will prompt in
334 #       a loop until a username is entered. If it is batch processing from
335 #       a file it will output an error message and return to the caller.
336 #
337 get_user() {
338         _input=
339
340         # No need to take down user names if this is a configuration saving run.
341         [ -n "$configflag" ] && return
342
343         while : ; do
344                 if [ -z "$fflag" ]; then
345                         echo -n "Username: "
346                         read _input
347                 else
348                         _input="`echo "$fileline" | cut -f1 -d:`"
349                 fi
350
351                 # There *must* be a username, and it must not exist. If
352                 # this is an interactive session give the user an
353                 # opportunity to retry.
354                 #
355                 if [ -z "$_input" ]; then
356                         err "You must enter a username!"
357                         [ -z "$fflag" ] && continue
358                 fi
359                 ${PWCMD} usershow $_input > /dev/null 2>&1
360                 if [ "$?" -eq 0 ]; then
361                         err "User exists!"
362                         [ -z "$fflag" ] && continue
363                 fi
364                 break
365         done
366         username="$_input"
367 }
368
369 # get_gecos
370 #       Reads extra information about the user. Can be used both in interactive
371 #       and batch (from file) mode.
372 #
373 get_gecos() {
374         _input=
375
376         # No need to take down additional user information for a configuration run.
377         [ -n "$configflag" ] && return
378
379         if [ -z "$fflag" ]; then
380                 echo -n "Full name: "
381                 read _input
382         else
383                 _input="`echo "$fileline" | cut -f7 -d:`"
384         fi
385         ugecos="$_input"
386 }
387
388 # get_shell
389 #       Get the account's shell. Works in interactive and batch mode. It
390 #       accepts either the base name of the shell or the full path.
391 #       If an invalid shell is entered it will simply use the default shell.
392 #
393 get_shell() {
394         _input=
395         _fullpath=
396         ushell="$defaultshell"
397
398         # Make sure the current value of the shell is a valid one
399         if [ -z "$Sflag" ]; then
400                 if ! shell_exists $ushell ; then
401                         info "Using default shell ${defaultshell}."
402                         ushell="$defaultshell"
403                 fi
404         fi
405
406         if [ -z "$fflag" ]; then
407                 echo -n "Shell ($shells) [`basename $ushell`]: "
408                 read _input
409         else
410                 _input="`echo "$fileline" | cut -f9 -d:`"
411         fi
412         if [ -n "$_input" ]; then
413                 if [ -n "$Sflag" ]; then
414                         ushell="$_input"
415                 else
416                         _fullpath=`fullpath_from_shell $_input`
417                         if [ -n "$_fullpath" ]; then
418                                 ushell="$_fullpath"
419                         else
420                                 err "Invalid shell ($_input) for user $username."
421                                 info "Using default shell ${defaultshell}."
422                                 ushell="$defaultshell"
423                         fi
424                 fi
425         fi
426 }
427
428 # get_homedir
429 #       Reads the account's home directory. Used both with interactive input
430 #       and batch input.
431 #
432 get_homedir() {
433         _input=
434         if [ -z "$fflag" ]; then
435                 echo -n "Home directory [${homeprefix}/${username}]: "
436                 read _input
437         else
438                 _input="`echo "$fileline" | cut -f8 -d:`"
439         fi
440
441         if [ -n "$_input" ]; then
442                 uhome="$_input"
443                 # if this is a configuration run, then user input is the home
444                 # directory prefix. Otherwise it is understood to
445                 # be $prefix/$user
446                 #
447                 [ -z "$configflag" ] && homeprefix="`dirname $uhome`" || homeprefix="$uhome"
448         else
449                 uhome="${homeprefix}/${username}"
450         fi
451 }
452
453 # get_uid
454 #       Reads a numeric userid in an interactive or batch session. Automatically
455 #       allocates one if it is not specified.
456 #
457 get_uid() {
458         uuid=${uidstart}
459         _input=
460         _prompt=
461
462         if [ -n "$uuid" ]; then
463                 _prompt="Uid [$uuid]: "
464         else
465                 _prompt="Uid (Leave empty for default): "
466         fi
467         if [ -z "$fflag" ]; then
468                 echo -n "$_prompt"
469                 read _input
470         else
471                 _input="`echo "$fileline" | cut -f2 -d:`"
472         fi
473
474         [ -n "$_input" ] && uuid=$_input
475         uuid=`get_nextuid $uuid`
476         uidstart=$uuid
477 }
478
479 # get_class
480 #       Reads login class of account. Can be used in interactive or batch mode.
481 #
482 get_class() {
483         uclass="$defaultclass"
484         _input=
485         _class=${uclass:-"default"}
486
487         if [ -z "$fflag" ]; then
488                 echo -n "Login class [$_class]: "
489                 read _input
490         else
491                 _input="`echo "$fileline" | cut -f4 -d:`"
492         fi
493
494         [ -n "$_input" ] && uclass="$_input"
495 }
496
497 # get_logingroup
498 #       Reads user's login group. Can be used in both interactive and batch
499 #       modes. The specified value can be a group name or its numeric id.
500 #       This routine leaves the field blank if nothing is provided and
501 #       a default login group has not been set. The pw(8) command
502 #       will then provide a login group with the same name as the username.
503 #
504 get_logingroup() {
505         ulogingroup="$defaultLgroup"
506         _input=
507
508         if [ -z "$fflag" ]; then
509                 echo -n "Login group [${ulogingroup:-$username}]: "
510                 read _input
511         else
512                 _input="`echo "$fileline" | cut -f3 -d:`"
513         fi
514
515         # Pw(8) will use the username as login group if it's left empty
516         [ -n "$_input" ] && ulogingroup="$_input"
517 }
518
519 # get_groups
520 #       Read additional groups for the user. It can be used in both interactive
521 #       and batch modes.
522 #
523 get_groups() {
524         ugroups="$defaultgroups"
525         _input=
526         _group=${ulogingroup:-"${username}"}
527
528         if [ -z "$configflag" ]; then
529                 [ -z "$fflag" ] && echo -n "Login group is $_group. Invite $username"
530                 [ -z "$fflag" ] && echo -n " into other groups? [$ugroups]: "
531         else
532                 [ -z "$fflag" ] && echo -n "Enter additional groups [$ugroups]: "
533         fi
534         read _input
535
536         [ -n "$_input" ] && ugroups="$_input"
537 }
538
539 # get_expire_dates
540 #       Read expiry information for the account and also for the password. This
541 #       routine is used only from batch processing mode.
542 #
543 get_expire_dates() {
544         upwexpire="`echo "$fileline" | cut -f5 -d:`"
545         uexpire="`echo "$fileline" | cut -f6 -d:`"
546 }
547
548 # get_password
549 #       Read the password in batch processing mode. The password field matters
550 #       only when the password type is "yes" or "random". If the field is empty and the
551 #       password type is "yes", then it assumes the account has an empty passsword
552 #       and changes the password type accordingly. If the password type is "random"
553 #       and the password field is NOT empty, then it assumes the account will NOT
554 #       have a random password and set passwdtype to "yes."
555 #
556 get_password() {
557         # We may temporarily change a password type. Make sure it's changed
558         # back to whatever it was before we process the next account.
559         #
560         [ -n "$savedpwtype" ] && {
561                 passwdtype=$savedpwtype
562                 savedpwtype=
563         }
564
565         # There may be a ':' in the password
566         upass=${fileline#*:*:*:*:*:*:*:*:*:}
567
568         if [ -z "$upass" ]; then
569                 case $passwdtype in
570                 yes)
571                         # if it's empty, assume an empty password
572                         passwdtype=none
573                         savedpwtype=yes
574                         ;;
575                 esac
576         else
577                 case $passwdtype in
578                 random)
579                         passwdtype=yes
580                         savedpwtype=random
581                         ;;
582                 esac
583         fi
584 }
585
586 # input_from_file
587 #       Reads a line of account information from standard input and
588 #       adds it to the user database.
589 #
590 input_from_file() {
591         _field=
592
593         while read -r fileline ; do
594                 case "$fileline" in
595                 \#*|'')
596                         ;;
597                 *)
598                         get_user || continue
599                         get_gecos
600                         get_uid
601                         get_logingroup
602                         get_class
603                         get_shell
604                         get_homedir
605                         get_password
606                         get_expire_dates
607                         ugroups="$defaultgroups"
608
609                         add_user
610                         ;;
611                 esac
612         done
613 }
614
615 # input_interactive
616 #       Prompts for user information interactively, and commits to
617 #       the user database.
618 #
619 input_interactive() {
620
621         _disable=
622         _pass=
623         _passconfirm=
624         _random="no"
625         _emptypass="no"
626         _usepass="yes"
627         _logingroup_ok="no"
628         _groups_ok="no"
629         case $passwdtype in
630         none)
631                 _emptypass="yes"
632                 _usepass="yes"
633                 ;;
634         no)
635                 _usepass="no"
636                 ;;
637         random)
638                 _random="yes"
639                 ;;
640         esac
641
642         get_user
643         get_gecos
644         get_uid
645
646         # The case where group = user is handled elsewhere, so
647         # validate any other groups the user is invited to.
648         until [ "$_logingroup_ok" = yes ]; do
649                 get_logingroup
650                 _logingroup_ok=yes
651                 if [ -n "$ulogingroup" -a "$username" != "$ulogingroup" ]; then
652                         if ! ${PWCMD} show group $ulogingroup > /dev/null 2>&1; then
653                                 echo "Group $ulogingroup does not exist!"
654                                 _logingroup_ok=no
655                         fi
656                 fi
657         done
658         until [ "$_groups_ok" = yes ]; do
659                 get_groups
660                 _groups_ok=yes
661                 for i in $ugroups; do
662                         if [ "$username" != "$i" ]; then
663                                 if ! ${PWCMD} show group $i > /dev/null 2>&1; then
664                                         echo "Group $i does not exist!"
665                                         _groups_ok=no
666                                 fi
667                         fi
668                 done
669         done
670
671         get_class
672         get_shell
673         get_homedir
674
675         while : ; do
676                 echo -n "Use password-based authentication? [$_usepass]: "
677                 read _input
678                 [ -z "$_input" ] && _input=$_usepass
679                 case $_input in
680                 [Nn][Oo]|[Nn])
681                         passwdtype="no"
682                         ;;
683                 [Yy][Ee][Ss]|[Yy][Ee]|[Yy])
684                         while : ; do
685                                 echo -n "Use an empty password? (yes/no) [$_emptypass]: "
686                                 read _input
687                                 [ -n "$_input" ] && _emptypass=$_input
688                                 case $_emptypass in
689                                 [Nn][Oo]|[Nn])
690                                         echo -n "Use a random password? (yes/no) [$_random]: "
691                                         read _input
692                                         [ -n "$_input" ] && _random="$_input"
693                                         case $_random in
694                                         [Yy][Ee][Ss]|[Yy][Ee]|[Yy])
695                                                 passwdtype="random"
696                                                 break
697                                                 ;;
698                                         esac
699                                         passwdtype="yes"
700                                         [ -n "$configflag" ] && break
701                                         trap 'stty echo; exit' 0 1 2 3 15
702                                         stty -echo
703                                         echo -n "Enter password: "
704                                         read -r upass
705                                         echo''
706                                         echo -n "Enter password again: "
707                                         read -r _passconfirm
708                                         echo ''
709                                         stty echo
710                                         # if user entered a blank password
711                                         # explicitly ask again.
712                                         [ -z "$upass" -a -z "$_passconfirm" ] \
713                                             && continue
714                                         ;;
715                                 [Yy][Ee][Ss]|[Yy][Ee]|[Yy])
716                                         passwdtype="none"
717                                         break;
718                                         ;;
719                                 *)
720                                         # invalid answer; repeat the loop
721                                         continue
722                                         ;;
723                                 esac
724                                 if [ "$upass" != "$_passconfirm" ]; then
725                                         echo "Passwords did not match!"
726                                         continue
727                                 fi
728                                 break
729                         done
730                         ;;
731                 *)
732                         # invalid answer; repeat loop
733                         continue
734                         ;;
735                 esac
736                 break;
737         done
738         _disable=${disableflag:-"no"}
739         while : ; do
740                 echo -n "Lock out the account after creation? [$_disable]: "
741                 read _input
742                 [ -z "$_input" ] && _input=$_disable
743                 case $_input in
744                 [Nn][Oo]|[Nn])
745                         disableflag=
746                         ;;
747                 [Yy][Ee][Ss]|[Yy][Ee]|[Yy])
748                         disableflag=yes
749                         ;;
750                 *)
751                         # invalid answer; repeat loop
752                         continue
753                         ;;
754                 esac
755                 break
756         done
757         
758         # Display the information we have so far and prompt to
759         # commit it.
760         #
761         _disable=${disableflag:-"no"}
762         [ -z "$configflag" ] && printf "%-10s : %s\n" Username $username
763         case $passwdtype in
764         yes)
765                 _pass='*****'
766                 ;;
767         no)
768                 _pass='<disabled>'
769                 ;;
770         none)
771                 _pass='<blank>'
772                 ;;
773         random)
774                 _pass='<random>'
775                 ;;
776         esac
777         [ -z "$configflag" ] && printf "%-10s : %s\n" "Password" "$_pass"
778         [ -n "$configflag" ] && printf "%-10s : %s\n" "Pass Type" "$passwdtype"
779         [ -z "$configflag" ] && printf "%-10s : %s\n" "Full Name" "$ugecos"
780         [ -z "$configflag" ] && printf "%-10s : %s\n" "Uid" "$uuid"
781         printf "%-10s : %s\n" "Class" "$uclass"
782         printf "%-10s : %s %s\n" "Groups" "${ulogingroup:-$username}" "$ugroups"
783         printf "%-10s : %s\n" "Home" "$uhome"
784         printf "%-10s : %s\n" "Shell" "$ushell"
785         printf "%-10s : %s\n" "Locked" "$_disable"
786         while : ; do
787                 echo -n "OK? (yes/no): "
788                 read _input
789                 case $_input in
790                 [Nn][Oo]|[Nn])
791                         return 1
792                         ;;
793                 [Yy][Ee][Ss]|[Yy][Ee]|[Yy])
794                         add_user
795                         ;;
796                 *)
797                         continue
798                         ;;
799                 esac
800                 break
801         done
802         return 0
803 }
804
805 #### END SUBROUTINE DEFINITION ####
806
807 THISCMD=`/usr/bin/basename $0`
808 DEFAULTSHELL=/bin/sh
809 ADDUSERCONF="${ADDUSERCONF:-/etc/adduser.conf}"
810 PWCMD="${PWCMD:-/usr/sbin/pw}"
811 MAILCMD="${MAILCMD:-mail}"
812 ETCSHELLS="${ETCSHELLS:-/etc/shells}"
813 NOHOME="/nonexistent"
814 NOLOGIN="nologin"
815 NOLOGIN_PATH="/sbin/nologin"
816 GREPCMD="/usr/bin/grep"
817 DATECMD="/bin/date"
818
819 # Set default values
820 #
821 username=
822 uuid=
823 uidstart=
824 ugecos=
825 ulogingroup=
826 uclass=
827 uhome=
828 upass=
829 ushell=
830 udotdir=/usr/share/skel
831 ugroups=
832 uexpire=
833 upwexpire=
834 shells="`valid_shells`"
835 passwdtype="yes"
836 msgfile=/etc/adduser.msg
837 msgflag=
838 quietflag=
839 configflag=
840 fflag=
841 infile=
842 disableflag=
843 Dflag=
844 Sflag=
845 readconfig="yes"
846 homeprefix="/home"
847 randompass=
848 fileline=
849 savedpwtype=
850 defaultclass=
851 defaultLgroup=
852 defaultgroups=
853 defaultshell="${DEFAULTSHELL}"
854
855 # Make sure the user running this program is root. This isn't a security
856 # measure as much as it is a useful method of reminding the user to
857 # 'su -' before he/she wastes time entering data that won't be saved.
858 #
859 procowner=${procowner:-`/usr/bin/id -u`}
860 if [ "$procowner" != "0" ]; then
861         err 'you must be the super-user (uid 0) to use this utility.'
862         exit 1
863 fi
864
865 # Overide from our conf file
866 # Quickly go through the commandline line to see if we should read
867 # from our configuration file. The actual parsing of the commandline
868 # arguments happens after we read in our configuration file (commandline
869 # should override configuration file).
870 #
871 for _i in $* ; do
872         if [ "$_i" = "-N" ]; then
873                 readconfig=
874                 break;
875         fi
876 done
877 if [ -n "$readconfig" ]; then
878         # On a long-lived system, the first time this script is run it
879         # will barf upon reading the configuration file for its perl predecessor.
880         if ( . ${ADDUSERCONF} > /dev/null 2>&1 ); then
881                 [ -r ${ADDUSERCONF} ] && . ${ADDUSERCONF} > /dev/null 2>&1
882         fi
883 fi 
884
885 # Proccess command-line options
886 #
887 for _switch ; do
888         case $_switch in
889         -L)
890                 defaultclass="$2"
891                 shift; shift
892                 ;;
893         -C)
894                 configflag=yes
895                 shift
896                 ;;
897         -D)
898                 Dflag=yes
899                 shift
900                 ;;
901         -E)
902                 disableflag=yes
903                 shift
904                 ;;
905         -k)
906                 udotdir="$2"
907                 shift; shift
908                 ;;
909         -f)
910                 [ "$2" != "-" ] && infile="$2"
911                 fflag=yes
912                 shift; shift
913                 ;;
914         -g)
915                 defaultLgroup="$2"
916                 shift; shift
917                 ;;
918         -G)
919                 defaultgroups="$2"
920                 shift; shift
921                 ;;
922         -h)
923                 show_usage
924                 exit 0
925                 ;;
926         -d)
927                 homeprefix="$2"
928                 shift; shift
929                 ;;
930         -m)
931                 case "$2" in
932                 [Nn][Oo])
933                         msgflag=
934                         ;;
935                 *)
936                         msgflag=yes
937                         msgfile="$2"
938                         ;;
939                 esac
940                 shift; shift
941                 ;;
942         -N)
943                 readconfig=
944                 shift
945                 ;;
946         -w)
947                 case "$2" in
948                 no|none|random|yes)
949                         passwdtype=$2
950                         ;;
951                 *)
952                         show_usage
953                         exit 1
954                         ;;
955                 esac
956                 shift; shift
957                 ;;
958         -q)
959                 quietflag=yes
960                 shift
961                 ;;
962         -s)
963                 defaultshell="`fullpath_from_shell $2`"
964                 shift; shift
965                 ;;
966         -S)
967                 Sflag=yes
968                 shift
969                 ;;
970         -u)
971                 uidstart=$2
972                 shift; shift
973                 ;;
974         esac
975 done
976
977 # If the -f switch was used, get input from a file. Otherwise,
978 # this is an interactive session.
979 #
980 if [ -n "$fflag" ]; then
981         if [ -z "$infile" ]; then
982                 input_from_file
983         elif [ -n "$infile" ]; then
984                 if [ -r "$infile" ]; then
985                         input_from_file < $infile
986                 else
987                         err "File ($infile) is unreadable or does not exist."
988                 fi
989         fi
990 else
991         input_interactive
992         while : ; do
993                 if [ -z "$configflag" ]; then
994                         echo -n "Add another user? (yes/no): "
995                 else
996                         echo -n "Re-edit the default configuration? (yes/no): "
997                 fi
998                 read _input
999                 case $_input in
1000                 [Yy][Ee][Ss]|[Yy][Ee]|[Yy])
1001                         uidstart=`get_nextuid $uidstart`
1002                         input_interactive
1003                         continue
1004                         ;;
1005                 [Nn][Oo]|[Nn])
1006                         echo "Goodbye!"
1007                         ;;
1008                 *)
1009                         continue
1010                         ;;
1011                 esac
1012                 break
1013         done
1014 fi