rlm@1: #!/bin/bash
rlm@1: # Copyright (c) 2004 Matthias S. Benkmann <article AT winterdrache DOT de>
rlm@1: # You may do everything with this code except misrepresent its origin.
rlm@1: # PROVIDED `AS IS' WITH ABSOLUTELY NO WARRANTY OF ANY KIND!
rlm@1: 
rlm@1: #Package user home directories will be located under this directory
rlm@1: homebase=/usr/src
rlm@1: 
rlm@1: #Contents of following directory are copied into home directory when creating 
rlm@1: #a new package user (existing files will not be overwritten)
rlm@1: skel=/etc/pkgusr/skel-package
rlm@1: 
rlm@1: if [ $# -lt 7 ]; then
rlm@1:   echo 1>&2 'USAGE: '
rlm@1:   echo 1>&2 'add_package_user <description> <name> <minuid> <maxuid> \'
rlm@1:   echo 1>&2 '                              <group> <mingid> <maxgid> [-d <home>]'
rlm@1:   echo 1>&2
rlm@1:   echo 1>&2 'If a user account called <name> exists, a message will be printed and'
rlm@1:   echo 1>&2 'everything will be left as-is. If a user account called <name> does not'
rlm@1:   echo 1>&2 'exist, one will be created.'
rlm@1:   echo 1>&2 'The account'"'"'s primary group will be <group> and the /etc/passwd'
rlm@1:   echo 1>&2 'description field will be set to <description>. If a group called <group>'
rlm@1:   echo 1>&2 'does not already exist, one will be created.'
rlm@1:   echo 1>&2 'The new account will get the "install" group as a supplementary group. If'
rlm@1:   echo 1>&2 'a group named "install" does not exist, one will be created.'
rlm@1:   echo 1>&2
rlm@1:   echo 1>&2 '<description> needs to be a valid string for the /etc/passwd description'
rlm@1:   echo 1>&2 '  field. This means, among other things, that it must not contain ":".'
rlm@1:   echo 1>&2 '  Don'"'"'t forget to properly quote <description> if it contains spaces or'
rlm@1:   echo 1>&2 '  other characters interpreted by the shell!'
rlm@1:   echo 1>&2
rlm@1:   echo 1>&2 '<minuid>(incl.) and <maxuid>(excl.) determine the numeric range from which'
rlm@1:   echo 1>&2 '  the new account'"'"'s UID will be picked in the following way:'
rlm@1:   echo 1>&2
rlm@1:   echo 1>&2 '  1. If the range contains no unused UID => Exit with error.'
rlm@1:   echo 1>&2 '  2. If <maxuid>-1 is still unused, find the greatest UID from the range'
rlm@1:   echo 1>&2 '     that is used and pick the number after that.'
rlm@1:   echo 1>&2 '  3. If <maxuid>-1 is in use, pick the first unused number from the range.'
rlm@1:   echo 1>&2
rlm@1:   echo 1>&2 '<mingid>(incl.) and <maxgid>(excl.) determine the numeric range from which'
rlm@1:   echo 1>&2 '  to pick the GID for group <group> and/or group "install", if it needs to be'
rlm@1:   echo 1>&2 '  created. The process for picking the GID is the same as that for the UID.'
rlm@1:   echo 1>&2 ''
rlm@1:   echo 1>&2 '<home> specifies the new user'"'"'s home directory. If it is not provided,'
rlm@1:   echo 1>&2 '  it will default to '"$homebase/<name> ."
rlm@1:   echo 1>&2 '  If the home directory does not exist yet it will be created, otherwise'
rlm@1:   echo 1>&2 '  the existing directory will be recursively chown'"'"'ed to the new user.'
rlm@1:   echo 1>&2 '  The home directory will be populated with a copy of the contents of'
rlm@1:   echo 1>&2 "  $skel, but pre-existing files in the home directory"
rlm@1:   echo 1>&2 '  will not be overwritten. Note that symlinks will be copied as symlinks!'
rlm@1:   echo 1>&2 ''
rlm@1:   exit 1
rlm@1: fi
rlm@1: 
rlm@1: grpfile=/etc/group
rlm@1: passwd=/etc/passwd
rlm@1: 
rlm@1: 
rlm@1: 
rlm@1: description=$1
rlm@1: name=$2
rlm@1: minuid=$3
rlm@1: maxuid=$4
rlm@1: gname=$5
rlm@1: mingid=$6
rlm@1: maxgid=$7
rlm@1: home=$homebase/$name
rlm@1: 
rlm@1: set -- "$@" _eNd_OF_lisT_
rlm@1: while [ "$1" != "_eNd_OF_lisT_" ]; do
rlm@1:   case "$1" in
rlm@1:     -d) shift 1
rlm@1:         if [ "$1" = "_eNd_OF_lisT_" ]; then
rlm@1:           echo 1>&2 "-d directory name missing!"
rlm@1:           exit 1
rlm@1:         fi
rlm@1:         home="$1"
rlm@1:         shift 1
rlm@1:         ;;
rlm@1:     *) temp="$1" 
rlm@1:        shift 1
rlm@1:        set -- "$@" "$temp"
rlm@1:        ;;
rlm@1:   esac     
rlm@1: done
rlm@1: shift 1 #throw away _eNd_OF_lisT_
rlm@1: 
rlm@1: if [ $UID -ne 0 ]; then echo Please run this script as root. ; exit 1; fi
rlm@1: 
rlm@1: #test if user already exists
rlm@1: grep "^$name:.*" $passwd
rlm@1: if [ $? -eq 0 ]; then 
rlm@1:   echo 'Package user does already exist! Do su '$name' to do maintenance work.'
rlm@1:   exit 1
rlm@1: fi 
rlm@1: 
rlm@1: #test if minuid, maxuid, mingid and maxgid are integers, otherwise error
rlm@1: error=0
rlm@1: expr ${minuid} + 1 2>/dev/null 1>&2 || error=1
rlm@1: expr ${maxuid} + 1 2>/dev/null 1>&2 || error=1
rlm@1: expr ${mingid} + 1 2>/dev/null 1>&2 || error=1
rlm@1: expr ${maxgid} + 1 2>/dev/null 1>&2 || error=1
rlm@1: 
rlm@1: if [ $error -eq 1 ]; then
rlm@1:   echo Error: Illegal numeric value!
rlm@1:   exit 1
rlm@1: fi
rlm@1: 
rlm@1: if [ $minuid -ge $maxuid ]; then
rlm@1:   echo 'Error: minuid must be less than maxuid !' 
rlm@1:   exit 1
rlm@1: fi
rlm@1: 
rlm@1: if [ $mingid -ge $maxgid ]; then
rlm@1:   echo 'Error: mingid must be less than maxgid !' 
rlm@1:   exit 1
rlm@1: fi
rlm@1: 
rlm@1: 
rlm@1: uidlist=`cut -d : -f 3 $passwd | sort -n`
rlm@1: 
rlm@1: #find last used UID within range
rlm@1: u=0
rlm@1: for i in $uidlist
rlm@1: do
rlm@1:   if [ $i -ge $maxuid ]; then break; fi
rlm@1:   if [ $i -ge $minuid ]; then u=$i; fi 
rlm@1: done
rlm@1: 
rlm@1: #if no UID from the range is used, pick the first, otherwise pick the one
rlm@1: #immediately following the last UID in use.
rlm@1: if [ $u -eq 0 ]; then u=$minuid; else u=`expr $u + 1`; fi
rlm@1: 
rlm@1: #if the UID determined above is >= maxuid (i.e. illegal)
rlm@1: #then we look for the first unused uid in the range.
rlm@1: if [ $u -ge $maxuid ]; then
rlm@1:   u=$minuid
rlm@1:   for i in $uidlist
rlm@1:   do
rlm@1:     if [ $u -eq $i ]; then u=`expr $u + 1` ; fi
rlm@1:     if [ $i -ge $maxuid ]; then break; fi
rlm@1:   done  
rlm@1: 
rlm@1:   if [ $u -ge $maxuid ]; then
rlm@1:     echo Error: UID range is full!
rlm@1:     exit 1
rlm@1:   fi
rlm@1: fi
rlm@1: 
rlm@1: echo Will create user $name with uid: $u
rlm@1: 
rlm@1: unset uidlist
rlm@1: 
rlm@1: #############################################################################
rlm@1: #                                 group
rlm@1: #############################################################################
rlm@1: 
rlm@1: #execute the following for gname and "install" to get gids for those 2 groups
rlm@1: 
rlm@1: g=0
rlm@1: creategroup=0
rlm@1: for group in install $gname
rlm@1: do
rlm@1:   oldg=$g #save gid from previous run
rlm@1:   createinstall=$creategroup
rlm@1:   creategroup=0
rlm@1:  
rlm@1:   #test if group already exists and extract gid if so
rlm@1:   g=`grep ^${group}:.\* $grpfile | cut -d : -f 3 -`
rlm@1: 
rlm@1:   #if group does not exist, then check range for a free gid
rlm@1:   if [ z$g = z ]; then 
rlm@1:     creategroup=1
rlm@1:     
rlm@1:     gidlist=`cut -d : -f 3 $grpfile | sort -n`
rlm@1: 
rlm@1:     #find last used GID within range
rlm@1:     g=0
rlm@1:     for i in $gidlist
rlm@1:     do
rlm@1:       if [ $i -ge $maxgid ]; then break; fi
rlm@1:       if [ $i -ge $mingid ]; then g=$i; fi
rlm@1:     done
rlm@1: 
rlm@1:     #if no GID from the range is used, pick the first, otherwise pick the one
rlm@1:     #immediately following the last GID in use.
rlm@1:     if [ $g -eq 0 ]; then g=$mingid; else g=`expr $g + 1`; fi
rlm@1:     
rlm@1:     #don't reuse gid from previous run 
rlm@1:     if [ $g -eq $oldg ]; then g=`expr $g + 1`; fi
rlm@1: 
rlm@1:     #if the GID determined above is >= maxgid (i.e. illegal)
rlm@1:     #then we look for the first unused gid in the range.
rlm@1:     if [ $g -ge $maxgid ]; then
rlm@1:       g=$mingid
rlm@1:       for i in $gidlist
rlm@1:       do
rlm@1:         if [ $g -eq $i ]; then g=`expr $g + 1` ; fi
rlm@1:         if [ $g -eq $oldg ]; then g=`expr $g + 1` ; fi
rlm@1:         if [ $i -ge $maxgid ]; then break; fi
rlm@1:       done  
rlm@1: 
rlm@1:       if [ $g -ge $maxgid ]; then
rlm@1:         echo Error: GID range is full!
rlm@1:         exit 1
rlm@1:       fi
rlm@1:     fi
rlm@1:   fi
rlm@1: done
rlm@1: 
rlm@1: unset gidlist
rlm@1: 
rlm@1: if [ $createinstall -eq 1 ]; then
rlm@1:   echo Creating group install with gid $oldg
rlm@1:   groupadd -g $oldg install || exit 1
rlm@1: else
rlm@1:   echo Group install has gid $oldg
rlm@1: fi
rlm@1: if [ $creategroup -eq 1 ]; then
rlm@1:   echo Creating group $gname with gid $g
rlm@1:   groupadd -g $g $gname || exit 1
rlm@1: else 
rlm@1:   echo Group $gname has gid $g
rlm@1: fi
rlm@1: 
rlm@1: 
rlm@1: 
rlm@1: useradd -c "${description}" -d ${home} -g ${gname} -G install \
rlm@1:         -s /bin/bash -u ${u} ${name}  || exit 1
rlm@1: 
rlm@1: mkdir -p $home || exit 1
rlm@1: 
rlm@1: yes n|cp -ai -R ${skel}/{[^.],.[^.],..?}* ${home} 2>/dev/null >/dev/null
rlm@1: 
rlm@1: cd ${home}
rlm@1: chown --recursive ${u}:${g} .
rlm@1: 
rlm@1: 
rlm@1: exit 0