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