xref: /dragonfly/usr.sbin/adduser/adduser.sh (revision 36a3d1d6)
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.4 2008/07/21 23:42:02 swildner Exp $
29#
30
31# err msg
32#       Display $msg on stderr, unless we're being quiet.
33#
34err() {
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#
43info() {
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#
55get_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#
77show_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#
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{
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#
189save_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#
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			_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#
337get_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#
373get_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#
393get_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#
432get_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#
457get_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#
482get_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#
504get_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#
523get_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#
543get_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#
556get_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#
590input_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#
619input_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
807THISCMD=`/usr/bin/basename $0`
808DEFAULTSHELL=/bin/sh
809ADDUSERCONF="${ADDUSERCONF:-/etc/adduser.conf}"
810PWCMD="${PWCMD:-/usr/sbin/pw}"
811MAILCMD="${MAILCMD:-mail}"
812ETCSHELLS="${ETCSHELLS:-/etc/shells}"
813NOHOME="/nonexistent"
814NOLOGIN="nologin"
815NOLOGIN_PATH="/sbin/nologin"
816GREPCMD="/usr/bin/grep"
817DATECMD="/bin/date"
818
819# Set default values
820#
821username=
822uuid=
823uidstart=
824ugecos=
825ulogingroup=
826uclass=
827uhome=
828upass=
829ushell=
830udotdir=/usr/share/skel
831ugroups=
832uexpire=
833upwexpire=
834shells="`valid_shells`"
835passwdtype="yes"
836msgfile=/etc/adduser.msg
837msgflag=
838quietflag=
839configflag=
840fflag=
841infile=
842disableflag=
843Dflag=
844Sflag=
845readconfig="yes"
846homeprefix="/home"
847randompass=
848fileline=
849savedpwtype=
850defaultclass=
851defaultLgroup=
852defaultgroups=
853defaultshell="${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#
859procowner=${procowner:-`/usr/bin/id -u`}
860if [ "$procowner" != "0" ]; then
861	err 'you must be the super-user (uid 0) to use this utility.'
862	exit 1
863fi
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#
871for _i in $* ; do
872	if [ "$_i" = "-N" ]; then
873		readconfig=
874		break;
875	fi
876done
877if [ -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
883fi
884
885# Process command-line options
886#
887for _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
975done
976
977# If the -f switch was used, get input from a file. Otherwise,
978# this is an interactive session.
979#
980if [ -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
990else
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
1014fi
1015