*BSD News Article 86141


Return to BSD News archive

Path: euryale.cc.adfa.oz.au!newshost.carno.net.au!harbinger.cc.monash.edu.au!munnari.OZ.AU!news.Hawaii.Edu!news.lava.net!news.pixi.com!news.dod.hawaii.gov!paf-news.hqpacaf.af.mil!wrdiss1.robins.af.mil!news.sgi.com!howland.erols.net!news.mathworks.com!uunet!in1.uu.net!192.109.159.3!news.gtn.com!news.netuse.de!tpki.toppoint.de!rimki.toppoint.de!chrimek
Date: 04 Jan 1997 21:51:00 +0100
From: chrimek@rimki.toppoint.de (Christoph Rimek)
Newsgroups: comp.unix.bsd.netbsd.misc
Message-ID: <6OD1p0qZo$B@rimki.toppoint.de>
References: <none-ya023480001912962244220001@news.infi.net>
	<mason-ya023380002012960019340001@news.four.net>
	<none-ya023480002012962112290001@news.infi.net>
	<x7zpz8wnxt.fsf@dumbcat.codewright.com>
	<3a13e94547@emw4maba.slip.gp.fht-esslingen.de>
Subject: Re: Why no addusr?
X-Newsreader: CrossPoint v3.11 R/C6513
Organization: Toppoint Mailbox e.V.
Reply-To: chrimek@toppoint.de
Lines: 366
Xref: euryale.cc.adfa.oz.au comp.unix.bsd.netbsd.misc:5057

Hello

In article <3a13e94547@emw4maba.slip.gp.fht-esslingen.de>,
emw4maba (emw4maba@gp.fht-esslingen.de) wrote:

> In article <x7zpz8wnxt.fsf@dumbcat.codewright.com>
>           Marco S Hyman <marc@dumbcat.codewright.com> wrote:
>
> > Because ask 25 admins the "best" way to set up users and you will
> > get at least 30 answers!  Addusr scripts tend to implement policy,
> > and every site is almost guaranteed to have policies that differ
> > from that which the script implements.  Some examples:
> >
> > 	where do home directories go?
> > 	what is the initial password for a new user?
> > 	do all users get the same dot files?
> > 	how are mail aliases handled?
> >
>
> This answer drives me up the wall, really.
> Replies like this make us NetBSD users look like a bunch of snobs.
> :-(((
>
> This is a bad argument [....]
>
> I repeat: The fact that there is no ideal solution is no argument not
> to provide anything. Any solution is better than nothing!
> This applies to other things in NetBSD, too.
>


Here is my suggestion, far from being ideal, but it works for our needs.
The original is the script found under point 4.4 of the BSD-FAQ.
You may use this script freely if it is of any value to you. If anyone
has better solutions for parts of its text please let me know.

This script is /usr/local/sbin/mkuser for our system "servus" which is
mailserver for a small department (some 80 users). It has a compagnion
file "mkuser.default" in the same directory that specifies some of the
system dependent default settings, and there is a collection of
configuration files for new users in /usr/share/skel.

You will get a short help screen if you either read the text or call the
script with any argument that is not an option. Try "mkuser help".


--------------------------cut here (start)-----------------------------
#!/bin/sh
#
# NAME:
#	mkuser[.sh] - interactive make user script
#
# SYNOPSIS:
#	
#	mkuser [-G "Group"] [-H "Homes"] [-S "Shell"]  \\
#		[-u "min_uid[,max_uid]"] [-f] [-e] [-p] [-q] [text]
#
# AUTHOR:
#	Simon J. Gerraty <sjg@zen.void.oz.au>
#	Please send copies of changes and bug-fixes to:
#	sjg@zen.void.oz.au
#
# HISTORY:
#	BSD-FAQ, Q 4.4: Where is the 'adduser' program? A: Here.
#	This copy modified to serve our local needs (NetBSD).
#	Modifications made by Ch. Rimek:
#	chrimek@toppoint.de  or  chrimek@psychologie.uni-kiel.de
#
#	Enter "mkuser {anytext}" for HELP screen.
################################################


#
#  Variables, Settings
#

Myname=`basename $0 .sh`
Mydir=`dirname $0`
[ "$Mydir" = "." ] && Mydir=`pwd`

Defaults="${Myname}.default"
SKEL=/usr/share/skel
ETC=/etc
GF=${ETC}/group
PF=${ETC}/passwd
SF=${ETC}/shells

# things that the defaults file may override.
host=`hostname -s 2>/dev/null`
Homes=/home/${host:-`uname -n | cut -d. -f1 -s`}
Shell=/bin/sh
Group=users
UIDrange=100

# look for a defaults file, stop after first one found.
for d in $HOME $Mydir
do
  [ -s $d/$Defaults ] && { . $d/$Defaults; break; }
done

# define temporary files' names
#EXF=/tmp/${Myname}.e$$
TF=/tmp/${Myname}.u$$

# check capabilities of echo command
case `echo -n .` in
-n*)	N=;C="\c";;
*)	N=-n;C=;;
esac


################################################
#       Function declarations                  #
################################################


mk_dirs()
{
  # check whether absolute or relative pathname specified
  case $1 in
    /*)	pp=/;;
    *)	pp=;;
  esac
  # replace "/" with " " in argument(s) to separate all parts of full path
  for p in `echo $1 | tr / " "`
  do
    # build directory tree successively
    case "$pp" in
     "")  pp=$p;;
      /)  pp=/$p;;
      *)  pp=$pp/$p;;
    esac
    # make directoy, if it does not exist
    [ -d $pp ] || mkdir $pp || exit 5
  done
}


upd_passwd()
{
  # set the local environment for vipw
  EDITOR=ed
  VISUAL=ed
  export EDITOR VISUAL

  # for user information, show /etc/passwd entries, line format is:
  # "username:passwd:uid:gid:Gecos:home:shell"
  echo "adding  $1:*:$2:$3:$4:$5:$6"

  # vipw uses /etc/master.passwd as file, line format is:
  # "username:passwd:uid:gid:class:change:expire:Gecos:home:shell"
  echo "$1:*:$2:$3::0:0:$4:$5:$6" > $TF

  # find line number of NIS-entry, if any.
  line=`grep -n '^+:' $PF | cut -d: -f1`
  # insert line from TF one line before $line or current (usually last)
  ( sleep 1; echo ${line}-1r $TF; echo w; echo q; ) | ${VIPW:-vipw}
}


get_uid()
{
  # read ID bounds from parameters
  min=$1
  max=$2
  # preset return value
  rv=-1

  if [ $first ]; then
    #
    # option "first" selected: return first unused UID within given range.
    #
    i=$min
    while [ $i -le $max ]
    do
      [ "`cut -d: -f3 $PF | grep -w "$i"`" ] || { rv=$i; break; }
      i=`expr $i + 1`
    done
  else
    #
    # option "first" not set, use standard method:
    # return first UID one above highest UID used in given range.
    #
    i=$max
    while [ $i -ge $min ]
    do
      [ "`cut -d: -f3 $PF | grep -w "$i"`" ] && break
      rv=$i
      i=`expr $i - 1`
    done
  fi
  echo $rv
}



################################################
#       the main program                       #
################################################

set -- `getopt H:S:G:u:fepq $*`

for i in $*
do
  case "$i" in
   --)	shift; break;;
   -H)	Homes=$2; shift 2;;
   -S)	Shell=$2; shift 2;;
   -G)	Group=$2; shift 2;;
   -u)	rawUID=$2; shift 2;;
   -f)	first=first; shift;;
   -e)	empty=empty; shift;;
   -p)	pword=pword; shift;;
   -q)	quiet=quiet; shift;;
  esac
done

# give HELP screen if any non-option parameter given.
if [ $* ]; then
  clear
  echo "${Myname} HELP screen  --  interactive make user script"
  echo ""
  echo "Syntax:    ${Myname} [options] [text]"
  echo ""
  echo "Options:"
  echo "	-G \"Group\"      name of new user's group"
  echo "	-H \"Homes\"      base for user login directory"
  echo "	-S \"Shell\"      user login shell"
  echo "	-u \"min[,max]\"  UID range (default= GID..(GID+100))"
  echo "	-f              first: take first unused UID (default=last)"
  echo "	-e              empty: do not copy configuration sample files"
  echo "	-p              pword: prompt for passwords"
  echo " 	-q              quiet: do not display default settings"
  echo ""
  echo "Any text other than these options leads to this HELP screen."
  echo ""
  echo "Error conditions (return values):"
  echo "	error 1:  user not root user"
  echo "	error 2:  specified group does not exist"
  echo "	error 3:  specified shell not in /etc/shells file"
  echo "	error 4:  shell file non-existent or not executable"
  echo "	error 5:  no UID available in specified range"
  echo ""
  exit 0
fi

#
#  test for error conditions
#

# Make sure only the super user uses this utility.
if [ `id -un` != "root" ]; then
  echo "${Myname}: error 1: only the root user may create new user accounts."
  exit 1
fi

# get ID of $Group, exit with error if non-existent
GID=`grep "^$Group:" $GF | cut -d: -f3 -s`
if [ -z "$GID" ]; then
  echo "${Myname}: error 2: group \"$Group\" does not exist."
  exit 2
fi

# check $Shell parameter
if [ -z `grep "^$Shell$" $SF` ]; then
  echo "${Myname}: error 3: specified shell not in trusted shell database."
  exit 3
fi

# check $Shell file
if [ ! -x $Shell ]; then
  echo "${Myname}: error 4: specified shell file not existent/executable."
  exit 4
fi


# set up range of User IDs
# if "rawUID" (raw User ID range) given, separate (and order) Min and Max,
if [ $rawUID ]; then
  minUID=`echo $rawUID | cut -d, -f1`
  maxUID=`echo $rawUID | cut -d, -f2 -s`
fi
[ $minUID ] || minUID=$GID
[ $maxUID ] || maxUID=`expr $minUID + $UIDrange`
if [ $minUID -gt $maxUID ]; then
  tmpUID=$minUID; minUID=$maxUID; maxUID=$tmpUID
fi

if [ ! $quiet ]; then
  echo "${Myname} settings:"
  for v in Group Homes Shell
  do
    eval echo "\	$v = \$$v"
  done
  echo "    UID range = ${minUID}..${maxUID}"
  echo ""
fi


echo "Interactive make user script - for help type \"${Myname} help\""
echo "Enter username and fullname, no quotes needed for spaced fullname."
echo "An empty line (no username) terminates input."

#
# the main program loop
#

while true
do
  # Get actual UID
  # Call get_uid, parameter minUID maxUID (one single UID, if equal)
  # return actUID: may be any valid number in range [minUID..maxUID]
  # if return value = 0 (root) or negative then no UID found -> exit.

  actUID=`get_uid $minUID $maxUID`
  if [ $actUID -lt 0 ]; then
    echo "${Myname}: error 5: no UID available in specified range."
    exit 5
  fi

  # the prompt line
  echo ""
  echo $N "($actUID)	login [realname]: $C"
  read Login Gecos
  [ "$Login" ] || exit 0

  # here comes the real job: call upd_passwd.
  upd_passwd "$Login" "$actUID" "$GID" "$Gecos" "$Homes/$Login" "$Shell"

  # make home directory, if not already exists
  if [ ! -d $Homes/$Login ]; then
    mk_dirs $Homes/$Login && \
      chown $Login $Homes/$Login && \
        chgrp $Group $Homes/$Login && \
          chmod 2755 $Homes/$Login
  fi

  # copy standard configuration files to user's home directory
  if [ -z $empty ]; then
    cp $SKEL/* $Homes/$Login
    chown $Login $Homes/$Login/*
  fi

  # if option p specified, inquire password for new user
  [ $pword ] && passwd $Login

  # process next user
done

# finally remove the temporary stuff
rm -f $TF

## end.
--------------------------cut here (end)-------------------------------


The "mkuser.default" file for our system has the following three lines:

Group=users
Homes=/home/servus
Shell=/bin/sh


-cr
--
Christoph Rimek, Kiel, Germany  (+49 431 57601)      chrimek@toppoint.de