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