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