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