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