*BSD News Article 86642


Return to BSD News archive

Path: euryale.cc.adfa.oz.au!newshost.carno.net.au!harbinger.cc.monash.edu.au!news.mel.connect.com.au!news.mel.aone.net.au!grumpy.fl.net.au!news.webspan.net!ix.netcom.com!news.enteract.com!feed1.news.erols.com!cpk-news-hub1.bbnplanet.com!news.bbnplanet.com!su-news-hub1.bbnplanet.com!arclight.uoregon.edu!newsfeed.direct.ca!nntp.portal.ca!van-bc!n1van.istar!van.istar!west.istar!ott.istar!istar.net!tor.istar!east.istar!news1.istar.ca!news
From: Hume Smith <hclsmith@tallships.istar.ca>
Newsgroups: comp.unix.bsd.netbsd.misc
Subject: Re: Why no addusr?
Date: 10 Jan 1997 21:43:38 GMT
Organization: North End TTA Fans and Reed Organ Repairers
Lines: 277
Sender: hclsmith@three-elms.isisnet.com
Message-ID: <5b6d6a$fut@news.istar.ca>
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>
	<none-ya023480000201971953550001@news.infi.net>
	<5aqnum$maq@innocence.interface-business.de>
	<5b1d03$76@xciv.demon.co.uk>
	<none-ya023480000801972255110001@news.infi.net>
	<5b1u1h$spl@keyhole.west.spy.net>
	<none-ya023480000901971700220001@news.infi.net>
Reply-To: Hume Smith <hclsmith@tallships.istar.ca>
NNTP-Posting-Host: pt020.tallships.istar.ca
In-reply-to: none@domain.com's message of Thu, 09 Jan 1997 17:00:22 -0400
return-receipt-to: Hume Smith <hclsmith@tallships.istar.ca>
X-Newsreader: Gnus v5.0.15
Xref: euryale.cc.adfa.oz.au comp.unix.bsd.netbsd.misc:5150

In article <none-ya023480000901971700220001@news.infi.net> none@domain.com (Jeremy Williams) writes:

> You see, that main problem that I have is total perl-stupidity.  I have no
> clue.  I'm not a programmer (I'va always ment to learn, but hasn't
> everybody).  I do scripting on the mac and abject orented programming in
> hypercard, but that's useless here.

so use tclsh.  perl sux anyway; it's for people that loove to show their
friends all the wierd symbols they can memorise :)

i wrote my mkuser in tcl, i suppose i might as well post it...
that's not to say it's robust by any stretch of the imagination; but it's
"good enough" for me.  and it's a bit longer than 24 lines, but hell, you
can write uudecode in three (short) lines in perl; perl is bloated.

i probably do a lot of stuff *in* the script that should be foisted off
to commands, but...

#!/usr/local/bin/tclsh7.6
# Hume Smith  <hclsmith@tallships.istar.ca>
# 1996 Dec 27
# - converted for NetBSD-Amiga/1.0
# 1996 Sep 1

# create a new user, editing /etc/master.passwd

# change	time to change password: null or a number of days added to current time
# class 	default class
# expire	account expire time: null or a number of days added to current time
# group 	default group (name or id)
# home  	default home directory: login name is appended
# hphone	default home phone
# office	default office location
# shell 	default shell
# skeleton	directory copied into new home directories
# uidmax	greatest default id
# uidmin	least default id
# wphone	default work phone

array set cfg {
    uidmin 1000
    uidmax 32767
    group users
    class {}
    change {}
    expire {}
    home /local/home/
    shell /usr/local/bin/tcsh
    skeleton /etc/skel
    office {}
    hphone {}
    wphone {}} ;# for three-elms
# {uidmin 500 uidmax 32767 group users home /home/ shell /bin/tcsh skeleton /etc/skel} ;# for dess


set pwdfile /etc/master.passwd

# return boolean stating whether string is all digits
proc notalldigits {str} { string length [string trim $str 0123456789] }

# return boolean stating whether string is unsafe as a passwd field
proc unsafe {str} { regexp \[:\n\] $str }

# for testing
proc echo args { puts $args }


foreach i {
	{office -office -offic -offi -off -of -o}
	{wphone -wphone -wphon -wpho -wph -wp -w}
	{hphone -hphone -hphon -hpho -hpo -hp}
	{uid                         -uid -ui -u}
	{group          -group -grou -gro -gr -g}
	{home                  -home -hom -ho}
	{shell          -shell -shel -she -sh -s}
	{expire -expire -expir -expi -exp -ex -e}
	{change -change -chang -chan -cha -ch}
	{class          -class -clas -cla -cl}} {
	set x [lindex $i 0]
	foreach j [lrange $i 1 end] {
		set opt($j) $x
	}
}



# handle commandline arguments
if {[llength $argv] < 2} {
	puts stderr "$argv0: login and real required"
	exit 10
}
set new(login) [lindex $argv 0]
set new(real) [lindex $argv 1]
array set new [array get cfg]
set new(home) $cfg(home)$new(login)
unset new(uidmin) new(uidmax)

set var {}
foreach a [lrange $argv 2 end] {
	if {"" != "$var"} {
		set $var $a
		set var {}
	} else {
		if {[catch {set opt($a)} var]} {
			puts stderr "$argv0: invalid option \"$a\""
		}
		set var new($var)
	}
}
if {"" != "$var"} {
	puts "$argv0: dangling option"
	exit 22
}


if 0 { puts stderr "Usage: $argv0 `login' `real' \[-option value ...\]
`login'\tlogin name to be assigned
`real'\tuser's real name (or other arbitrary string)
options (may be abbreviated):
-uid\tUID (defaults to first unused one between $cfg(uidmin) and $cfg(uidmax))
-group\tgroup (defaults to $cfg(group))
-home\thome directory (defaults to $cfg(home)`login')
-shell\tlogin shell (defaults to $cfg(shell))
-class\tclass (defaults to \"$cfg(class)\")
-change\tdays to password change (defaults to $cfg(change))
-expire\tdays to account expiry (defaults to $cfg(expire))
-office\toffice location
-hphone\thome phone
-wphone\twork phone"
}

# validate real name

# validate login name
if {![regexp ^\[a-z0-9\]+$ $new(login)]} {
	# this is restrictive but easily done
	puts stderr "$arg0: login must be digits and lowercase letters"
	exit 6
}
if {[string length $new(login)] > 8} {
	puts stderr "$arg0: login must not be more than 8 chars"
	exit 7
}
if {![regexp {[a-z]} $new(login)]} {
	puts stderr "$arg0: login must contain a letter"
	exit 8
}


# read uids and logins from /etc/passwd
# fixme: lock it
set fid [open $pwdfile r]
set ln 0
while {-1 < [gets $fid line]} {
	incr ln
	set l [split $line :]
	if {[llength $l] == 10} {
		set login [lindex $l 0]
		if {[info exists uid($login)]} {
			puts stderr "$argv0: $pwdfile line $l: duplicate login \"$login\""
		} {
			set uid($login) [lindex $l 2]
			set uidx($uid($login)) {}
		}
	} {
		puts stderr "$argv0: $pwdfile line $l: wrong number of fields"
	}
}
close $fid

# confirm uniqeness of login
if {[info exists uid($new(login))]} {
	puts stderr "$argv0: login \"$new(login)\" is already in use"
	exit 2
}

# default uid
if {![info exists new(uid)]} {
	for {set i $cfg(uidmin)} {$i <= $cfg(uidmax)} {incr i} {
		if {![info exists uidx($i)]} {
			set new(uid) $i
			break
		}
	}
}
if {![info exists new(uid)]} {
	puts stderr "$argv0: no available UIDs in range"
	exit 1
} elseif {[notalldigits $new(uid)]} {
	puts stderr "$argv0: UID \"$new(uid)\" is not all digits"
	exit 3
}

# default gid
if {[notalldigits $new(group)]} {
	# convert it to numeric form
	# (i can't find a convenient command for this)
	if {[catch {exec grep ^$new(group): /etc/group} gl] ||
		![regexp {^[^:]+:[^:]*:([0-9]+):} $gl {} gid]} {
		puts stderr "$argv0: no group \"$new(group)\""
		exit 12
	}
	set new(group) $gid
}

# default expire
if {[notalldigits $new(expire)]} {
	puts stderr "$arg0: expire \"$new(expire)\" is not a number"
	exit 12
}
if {[string length $new(expire)]} { set new(expire) [expr 24*60*60*$new(expire)+[clock ]] }

# default change
if {[notalldigits $new(change)]} {
	puts stderr "$arg0: change \"$new(expire)\" is not a number"
	exit 12
}
if {[string length $new(change)]} { set new(change) [expr 24*60*60*$new(change)+[clock ]] }


# assemble the gecos field
foreach i {real office wphone hphone} {
	if {[regexp , $new($i)]} {
		puts stderr "$arg0: \"$new($i)\" contains ','"
		exit 11
	}
	lappend gecos $new($i)
	unset new($i)
}
set new(gecos) [join $gecos ,]

# check that there are no colons
foreach i [array names new] {
	if {[unsafe $new($i)]} {
		puts stderr "$arg0: \"$new($i)\" contains ':'"
		exit 11
	}
}


#
# now we attempt to make changes to the system
#


# create home directory
set exec exec ;# for real
#set exec echo ;# for testing
if {![file exists $new(home)]} {
	if {[file isdir $cfg(skeleton)]} {
		$exec cp -R $cfg(skeleton) $new(home)
	} {
		$exec mkdir $new(home)
	}
	$exec chown -R $new(uid):$new(group) $new(home)
}
# fixme: it is possible the path already existed but was not a directory


set x {}
set new(pw) *
foreach i {login pw uid group class change expire gecos home shell} {
	lappend x $new($i)
}
set tmpfn /etc/chpass.[pid]
set fid [open $tmpfn w]
exec cat $pwdfile >@ $fid
puts $fid [join $x :]
close $fid
exec pwd_mkdb $tmpfn

exit 0

# eof
-- 
Hume Smith  <URL:mailto:hclsmith@tallships.istar.ca>
            <URL:http://dess.tallships.istar.ca/%7Ehclsmith/>